Skip to content

Commit 36264c8

Browse files
committed
Fix Excel Trova e sostituisci tracciato come file, rilevamento login Claude Desktop mancante
- Excel: interpreta_titolo non tratta più un titolo senza " - Excel" come se fosse un nome file — verificato dal vivo con log/screenshot reali dell'issue GitHub #4: "Trova e sostituisci" compariva come riga fasulla separata nella corsia Excel della Timeline. - Claude Desktop: nuovo comando claude_desktop_stato() distingue "non installata" da "installata ma non ancora autenticata" (bug reale trovato testando su un secondo PC: l'app Desktop collegata non basta, il CLI bundlato ha un proprio login separato da completare una tantum). Impostazioni mostra ora istruzioni passo-passo con un comando pronto da copiare; la chat traduce l'errore grezzo del CLI in un messaggio comprensibile con collegamento diretto alle impostazioni.
1 parent ae23a44 commit 36264c8

11 files changed

Lines changed: 313 additions & 38 deletions

File tree

aw-watcher-excel-rust/src/main.rs

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,29 @@
2121
//! comunque meno grave di dati frammentati o sbagliati.
2222
//!
2323
//! IMPORTANTE (2026-08-12): costruito e compilato su una macchina senza
24-
//! Excel installato — l'utente lo testerà sul PC di lavoro. Il parsing
25-
//! del titolo è quindi deliberatamente permissivo invece di provare a
26-
//! coprire ogni variante esatta di formato titolo (modalità
27-
//! compatibilità, sola lettura, cloud/OneDrive, ecc.), che non è stato
28-
//! possibile verificare empiricamente come già fatto per VS Code: cerca
29-
//! la sotto-stringa fissa " - Excel" e taglia lì, così qualunque testo
30-
//! aggiuntivo dopo (" (Modalità compatibilità)", " (Sola lettura)", ...)
31-
//! viene scartato senza bisogno di elencare ogni variante. Se il
32-
//! pattern non si trova (titolo mai visto), usa comunque il titolo
33-
//! intero come nome file invece di scartare l'evento — meglio un
34-
//! raggruppamento imperfetto che perdere dati. Da rivedere con dati
35-
//! reali alla prima sessione di test dell'utente.
24+
//! Excel installato — il parsing del titolo cerca la sotto-stringa
25+
//! fissa " - Excel" e taglia lì, così qualunque testo aggiuntivo dopo
26+
//! (" (Modalità compatibilità)", " (Sola lettura)", ...) viene scartato
27+
//! senza bisogno di elencare ogni variante.
28+
//!
29+
//! Bug reale segnalato da un utente (issue GitHub #4, con log e
30+
//! screenshot reali da Excel 2021 MSO/Microsoft 365): un titolo che NON
31+
//! contiene " - Excel" non è affatto un file mai visto prima — è quasi
32+
//! sempre una finestra di UTILITÀ di Excel (es. "Find and Replace"/
33+
//! "Trova e sostituisci", ma lo stesso vale per "Formato celle",
34+
//! "Imposta pagina", ecc.), un'altra finestra top-level separata dello
35+
//! stesso processo excel.exe, non un documento. La versione precedente
36+
//! di questa funzione trattava comunque quel titolo intero come nome
37+
//! file (v. commento storico rimosso da qui) "per non perdere dati" —
38+
//! risultato osservato dal vivo: "Find and Replace" compariva come una
39+
//! RIGA A SÉ nella corsia Excel della Timeline, con una sessione fasulla
40+
//! di pochi secondi, mentre il file vero continuava ad essere tracciato
41+
//! correttamente in parallelo sulla sua riga. Con dati reali da 6 file
42+
//! Excel diversi che rispettano tutti il pattern " - Excel" e UN solo
43+
//! caso di fallback che si è rivelato essere proprio una finestra di
44+
//! dialogo, il fallback permissivo fa più danni (falsi file) che
45+
//! benefici (dati salvati) — ora un titolo che non rispetta il pattern
46+
//! viene semplicemente ignorato, non trasformato in un file fantasma.
3647
3748
use std::collections::HashMap;
3849
use std::io::Write;
@@ -59,24 +70,21 @@ fn is_excel_exe(app: &str) -> bool {
5970
/// - Excel (Sola lettura)", ecc. — taglia alla sotto-stringa fissa
6071
/// " - Excel" invece di elencare ogni variante di suffisso (vedi
6172
/// commento in cima al file sul perché). Nessun file aperto (titolo
62-
/// bare "Excel") o titolo vuoto -> None.
73+
/// bare "Excel"), titolo vuoto, o titolo che non rispetta il pattern
74+
/// (quasi sempre una finestra di utilità/dialogo, non un documento —
75+
/// vedi il commento in cima al file sul bug reale che questo evita)
76+
/// -> None.
6377
fn interpreta_titolo(titolo: &str) -> Option<String> {
6478
let pulito = titolo.trim();
6579
if pulito.is_empty() || pulito.eq_ignore_ascii_case("excel") {
6680
return None;
6781
}
68-
match pulito.find(" - Excel") {
69-
Some(pos) => {
70-
let file = pulito[..pos].trim();
71-
if file.is_empty() {
72-
None
73-
} else {
74-
Some(file.to_string())
75-
}
76-
}
77-
// Pattern non riconosciuto: usa comunque il titolo intero
78-
// piuttosto che scartare l'evento.
79-
None => Some(pulito.to_string()),
82+
let pos = pulito.find(" - Excel")?;
83+
let file = pulito[..pos].trim();
84+
if file.is_empty() {
85+
None
86+
} else {
87+
Some(file.to_string())
8088
}
8189
}
8290

@@ -326,12 +334,22 @@ mod tests {
326334
}
327335

328336
#[test]
329-
fn interpreta_titolo_pattern_sconosciuto_usa_titolo_intero() {
330-
// Formato titolo mai visto (non testabile su questa macchina,
331-
// niente Excel installato) — fallback: meglio un raggruppamento
332-
// imperfetto che perdere l'evento.
337+
fn interpreta_titolo_pattern_sconosciuto_viene_ignorato() {
338+
// Bug reale (issue GitHub #4): un titolo senza " - Excel" è
339+
// quasi sempre una finestra di utilità/dialogo (es. "Find and
340+
// Replace"), non un documento — va ignorato, non trattato come
341+
// un file fantasma (vedi il commento in cima al file).
333342
let r = interpreta_titolo("Qualcosa di inatteso");
334-
assert_eq!(r, Some("Qualcosa di inatteso".to_string()));
343+
assert_eq!(r, None);
344+
}
345+
346+
#[test]
347+
fn interpreta_titolo_finestra_trova_e_sostituisci_viene_ignorata() {
348+
// Caso reale osservato dal vivo nell'issue #4: la finestra
349+
// "Find and Replace" compariva come una riga fasulla a sé nella
350+
// corsia Excel della Timeline.
351+
let r = interpreta_titolo("Find and Replace");
352+
assert_eq!(r, None);
335353
}
336354

337355
#[test]

docs/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ quella della lingua attiva, per la versione in esecuzione (vedi
55
`src-tauri/src/about.rs`, che scarica questo file da GitHub Pages).
66
Non toccare questo formato senza aggiornare anche quel parser.
77

8+
## 0.1.24
9+
10+
### it
11+
- Excel: corretto un bug per cui finestre di utilità come "Trova e sostituisci" venivano tracciate come se fossero file veri, comparendo come righe fasulle nella corsia Excel della Timeline.
12+
- Impostazioni → Agente AI: con il provider "Claude (abbonamento Desktop)", ora viene rilevato se manca ancora un login separato del componente usato da TrackFlow (indipendente da quello dell'app Claude Desktop stessa) — con istruzioni passo-passo e un comando pronto da copiare, invece di scoprirlo solo con un errore poco chiaro nella chat.
13+
- La chat dell'assistente AI mostra ora un messaggio comprensibile (con collegamento diretto alle impostazioni) quando quel collegamento non è ancora completo, invece del messaggio grezzo del componente sottostante.
14+
15+
### en
16+
- Excel: fixed a bug where utility windows like "Find and Replace" were tracked as if they were real files, showing up as fake rows in the Excel Timeline lane.
17+
- Settings → AI Agent: with the "Claude (Desktop subscription)" provider, TrackFlow now detects when the component it uses still needs its own separate sign-in (independent from signing into the Claude Desktop app itself) — with step-by-step instructions and a ready-to-copy command, instead of only surfacing as an unclear error in the chat.
18+
- The AI assistant chat now shows a clear message (with a direct link to settings) when that sign-in isn't complete yet, instead of the underlying component's raw error text.
19+
820
## 0.1.23
921

1022
### it

docs/changelog.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ <h1>Changelog</h1>
4242
errore mostrato.
4343
-->
4444
<div id="changelog-root">
45+
<section class="changelog-entry">
46+
<h2>0.1.24</h2>
47+
<ul>
48+
<li>Excel: fixed a bug where utility windows like "Find and Replace" were tracked as if they were real files, showing up as fake rows in the Excel Timeline lane.</li>
49+
<li>Settings → AI Agent: with the "Claude (Desktop subscription)" provider, TrackFlow now detects when the component it uses still needs its own separate sign-in (independent from signing into the Claude Desktop app itself) — with step-by-step instructions and a ready-to-copy command, instead of only surfacing as an unclear error in the chat.</li>
50+
<li>The AI assistant chat now shows a clear message (with a direct link to settings) when that sign-in isn't complete yet, instead of the underlying component's raw error text.</li>
51+
</ul>
52+
</section>
4553
<section class="changelog-entry">
4654
<h2>0.1.23</h2>
4755
<ul>
0 Bytes
Binary file not shown.

src-tauri/src/claude_subscription.rs

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,46 @@ pub fn claude_desktop_disponibile() -> bool {
127127
trova_claude_exe().is_some()
128128
}
129129

130+
/// Vero se il CLI Claude Code ha già completato il proprio login — bug
131+
/// reale segnalato da un utente (portatile di lavoro, issue interna
132+
/// 2026-09-03): avere Claude Desktop (l'app grafica) collegata al
133+
/// proprio abbonamento NON significa che il CLI bundlato (un processo
134+
/// separato, con una propria autenticazione OAuth) l'abbia già fatto —
135+
/// il CLI richiede un primo avvio interattivo una tantum (scelta tema,
136+
/// conferma di fiducia della cartella, login) prima di poter essere
137+
/// usato in modalità automatica (`-p`) come fa questo modulo. Verifica
138+
/// solo la presenza del file di credenziali che quel primo avvio scrive
139+
/// — non garantisce che siano ancora valide (potrebbero essere scadute),
140+
/// ma è lo stesso controllo che l'utente può fare da sé aprendo quella
141+
/// cartella, e costa zero (nessun processo da avviare).
142+
fn credenziali_presenti() -> bool {
143+
std::env::var_os("USERPROFILE")
144+
.map(|h| PathBuf::from(h).join(".claude").join(".credentials.json"))
145+
.map(|p| p.is_file())
146+
.unwrap_or(false)
147+
}
148+
149+
/// Stato completo per le Impostazioni — sostituisce `claude_desktop_disponibile`
150+
/// (lasciata per compatibilità) con le due informazioni che servono per
151+
/// distinguere "non installata" da "installata ma non ancora autenticata"
152+
/// e per costruire il comando pronto da copiare (vedi `AiAgentSettings.vue`).
153+
#[derive(serde::Serialize)]
154+
pub struct StatoClaudeDesktop {
155+
pub trovato: bool,
156+
pub autenticato: bool,
157+
pub percorso_exe: Option<String>,
158+
}
159+
160+
#[tauri::command]
161+
pub fn claude_desktop_stato() -> StatoClaudeDesktop {
162+
let percorso = trova_claude_exe();
163+
StatoClaudeDesktop {
164+
trovato: percorso.is_some(),
165+
autenticato: credenziali_presenti(),
166+
percorso_exe: percorso.map(|p| p.display().to_string()),
167+
}
168+
}
169+
130170
/// Genera un token casuale per proteggere il server MCP locale (vedi
131171
/// `avvia`) — nessuna dipendenza nuova solo per questo: ogni
132172
/// `RandomState` di libreria standard è seminato dal sistema operativo
@@ -817,11 +857,36 @@ async fn manda_turno(sessione: &mut SessioneAttiva, testo: &str) -> Result<Rispo
817857
"Nessuna risposta da Claude Code — verifica che Claude Desktop sia installata e connessa.".to_string()
818858
})?;
819859
if errore_finale {
820-
return Err(testo_finale);
860+
return Err(traduci_errore_cli(&testo_finale));
821861
}
822862
Ok(RispostaAgente { testo: testo_finale, strumenti_usati })
823863
}
824864

865+
/// Marcatore stabile (non un messaggio in italiano) per il caso "CLI non
866+
/// autenticato" — bug reale segnalato da un utente (portatile di
867+
/// lavoro): Claude Desktop era collegata regolarmente, ma il CLI
868+
/// bundlato non aveva mai completato il proprio primo avvio (vedi
869+
/// `credenziali_presenti`), e il messaggio grezzo che restituisce ("Not
870+
/// logged in · Please run /login") non significa nulla per chi non sa
871+
/// cos'è un CLI o un comando "/login". Un CODICE invece di una frase
872+
/// già in italiano: il testo vero e proprio (con azione cliccabile verso
873+
/// Impostazioni) lo mostra il frontend, che sa già gestire lingua IT/EN
874+
/// — vedi `AiChatWidget.vue`.
875+
pub const ERRORE_NON_AUTENTICATO: &str = "CLAUDE_DESKTOP_NON_AUTENTICATO";
876+
877+
/// Traduce l'errore grezzo del CLI — solo il pattern "non autenticato"
878+
/// viene riconosciuto e sostituito col marcatore sopra; qualunque altro
879+
/// errore del CLI passa invariato (meglio il testo originale, utile per
880+
/// una segnalazione, che nasconderlo dietro un messaggio generico
881+
/// sbagliato).
882+
fn traduci_errore_cli(testo: &str) -> String {
883+
if testo.to_lowercase().contains("not logged in") {
884+
ERRORE_NON_AUTENTICATO.to_string()
885+
} else {
886+
testo.to_string()
887+
}
888+
}
889+
825890
/// Punto di ingresso da `agent::ai_agent_send_message` quando il
826891
/// provider configurato è questo — vedi il commento in cima al file per
827892
/// il design completo e su `SessioneAttiva` per il perché di un
@@ -1020,6 +1085,31 @@ pub async fn prewarm_se_configurato(app_handle: &AppHandle, app_data_dir: PathBu
10201085
}
10211086
}
10221087

1088+
#[cfg(test)]
1089+
mod test_stato {
1090+
//! A differenza di `test_manuale` sotto, questo NON spawna alcun
1091+
//! processo né consuma l'abbonamento — solo controlli sul filesystem
1092+
//! locale (percorso claude.exe, file di credenziali). Non `#[ignore]`
1093+
//! apposta: economico, gira in ogni `cargo test` normale. I valori
1094+
//! esatti dipendono dalla macchina (qui solo stampati per verifica
1095+
//! visiva con --nocapture), l'unica cosa asserita è che la funzione
1096+
//! non va in panico e la forma del risultato è quella attesa.
1097+
use super::*;
1098+
1099+
#[test]
1100+
fn claude_desktop_stato_non_va_in_panico() {
1101+
let stato = claude_desktop_stato();
1102+
println!(
1103+
"trovato={} autenticato={} percorso_exe={:?}",
1104+
stato.trovato, stato.autenticato, stato.percorso_exe
1105+
);
1106+
// Coerenza minima: se non è stato trovato nessun claude.exe, il
1107+
// percorso deve essere None (mai un trovato=false con un percorso
1108+
// valorizzato, o viceversa).
1109+
assert_eq!(stato.trovato, stato.percorso_exe.is_some());
1110+
}
1111+
}
1112+
10231113
#[cfg(test)]
10241114
mod test_manuale {
10251115
//! Test manuale, NON eseguito da un normale `cargo test` (`#[ignore]`)

src-tauri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,7 @@ pub fn run() {
11081108
agent::ai_agent_new_conversation,
11091109
agent::ai_agent_list_models,
11101110
claude_subscription::claude_desktop_disponibile,
1111+
claude_subscription::claude_desktop_stato,
11111112
categorization::elenca_app_conosciute,
11121113
devtools::apri_devtools,
11131114
diagnostics::log_frontend_diagnostica,

src/components/AiChatWidget.vue

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,15 @@ div.ai-chat-widget
5252
div.ai-chat-bubble.ai-chat-bubble-assistant.ai-chat-bubble-loading(v-if="inviando")
5353
| {{ $t('aiChat.thinking') }}
5454

55-
div.ai-chat-alert(v-if="errore") {{ errore }}
55+
//- Caso specifico "CLI non autenticato" (vedi ERRORE_NON_AUTENTICATO
56+
//- in claude_subscription.rs): stesso trattamento cliccabile della
57+
//- chiave API mancante sopra, invece del testo d'errore grezzo —
58+
//- l'utente deve poter arrivare in un clic alle istruzioni, non solo
59+
//- leggere che qualcosa non va.
60+
div.ai-chat-alert(v-if="erroreClaudeNonAutenticato")
61+
| {{ $t('aiChat.claudeDesktopNotAuthenticated') }}
62+
div.ai-chat-empty-action(@click="apriImpostazioniAgente") {{ $t('aiChat.claudeDesktopNotAuthenticatedAction') }}
63+
div.ai-chat-alert(v-else-if="errore") {{ errore }}
5664

5765
//- Wrapper unico attorno a citazione + campo di scrittura — quando la
5866
//- citazione è presente devono sembrare un solo "cubo" con due zone
@@ -136,6 +144,10 @@ export default {
136144
bozza: '',
137145
inviando: false,
138146
errore: '',
147+
// Vedi ERRORE_NON_AUTENTICATO in claude_subscription.rs — quando
148+
// l'errore ricevuto è quel marcatore, mostriamo il blocco cliccabile
149+
// dedicato sopra invece del testo grezzo in `errore`.
150+
erroreClaudeNonAutenticato: false,
139151
// null finché non è stata ancora controllata (primo apri()) — non
140152
// false di default, altrimenti lampeggerebbe per un istante l'avviso
141153
// "chiave mancante" anche quando è configurata, prima che la
@@ -312,6 +324,7 @@ export default {
312324
const testo = this.bozza.trim();
313325
if (!testo || this.inviando || this.apiConfigurata === false) return;
314326
this.errore = '';
327+
this.erroreClaudeNonAutenticato = false;
315328
// La bolla mostrata in chat resta SOLO quello che l'utente ha
316329
// scritto (più l'etichetta, non i dati veri — vedi contestoLabel
317330
// sotto) — il testo coi dati veri viene anteposto solo alla
@@ -340,7 +353,12 @@ export default {
340353
});
341354
this.riavviaTimerInattivita();
342355
} catch (e: any) {
343-
this.errore = e?.toString?.() ?? String(e);
356+
const messaggio = e?.toString?.() ?? String(e);
357+
if (messaggio.includes('CLAUDE_DESKTOP_NON_AUTENTICATO')) {
358+
this.erroreClaudeNonAutenticato = true;
359+
} else {
360+
this.errore = messaggio;
361+
}
344362
} finally {
345363
this.inviando = false;
346364
this.scrollToBottom();
@@ -349,6 +367,7 @@ export default {
349367
async nuovaConversazione() {
350368
this.messaggi = [];
351369
this.errore = '';
370+
this.erroreClaudeNonAutenticato = false;
352371
try {
353372
await invoke('ai_agent_new_conversation');
354373
} catch (e) {

src/i18n/locales/en.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ export default {
128128
'Uses the Claude Pro/Max subscription already active on Claude Desktop instead of a pay-per-token API key — no extra cost, no key to get. Requires Claude Desktop installed on this PC and already signed in.',
129129
claudeDesktopNotFound:
130130
'Claude Desktop does not appear to be installed (or up to date) on this PC — install it and make sure you\'re signed in before using this option.',
131+
claudeDesktopNotAuthenticatedTitle: 'One last step (just once)',
132+
claudeDesktopNotAuthenticatedBody:
133+
'Claude Desktop is installed, but the component TrackFlow uses to talk to it hasn\'t signed in yet — this is separate from signing into the Claude Desktop app itself, and only needs doing once on this PC:',
134+
claudeDesktopStep1: 'Open PowerShell (search "PowerShell" in the Start menu).',
135+
claudeDesktopStep2: 'Paste the command below and press Enter (use the button to copy it):',
136+
claudeDesktopStep3:
137+
'Follow the screens that appear: pick a theme, confirm with Enter, then choose "Claude account with subscription" to sign in with the same account as Claude Desktop.',
138+
claudeDesktopStep4: 'Done? Close PowerShell and press "Check again" below.',
139+
claudeDesktopCopyCommand: 'Copy command',
140+
claudeDesktopCopyCommandCopied: 'Copied ✓',
141+
claudeDesktopRecheck: 'Check again',
131142
apiKey: 'API key',
132143
apiKeyPlaceholder: 'sk-ant-…',
133144
apiKeyHint: 'Generated from the chosen provider\'s console — saved only on this computer.',
@@ -451,6 +462,8 @@ Clear all rules from the editor, leaving it as \`[]\` (or empty), and save.`,
451462
emptyHint: 'Ask something about your activity data — project time, categories, VPN sessions, VoiSpeed calls…',
452463
apiKeyMissing: 'An API key is needed before you can use the assistant.',
453464
apiKeyMissingAction: 'Go to Settings → AI Agent',
465+
claudeDesktopNotAuthenticated: 'Claude Desktop is installed, but one last step (just once) is still needed to connect it.',
466+
claudeDesktopNotAuthenticatedAction: 'Go to Settings → AI Agent',
454467
removeContext: 'Remove',
455468
thinking: 'Thinking…',
456469
placeholder: 'Type a message…',

0 commit comments

Comments
 (0)