claude companion: add English translations for user facing strings (#84)
This commit is contained in:
@@ -12,6 +12,9 @@
|
|||||||
|
|
||||||
local KEY = "claude.answer"
|
local KEY = "claude.answer"
|
||||||
|
|
||||||
|
-- User-facing display strings live in translations/<lang>.json.
|
||||||
|
local function tr(key, args) return noctalia.tr(key, args) end
|
||||||
|
|
||||||
-- The panel surface is 460 logical px wide (plugin.toml); wrap text inside the
|
-- The panel surface is 460 logical px wide (plugin.toml); wrap text inside the
|
||||||
-- padding with room for the scrollbar.
|
-- padding with room for the scrollbar.
|
||||||
local WRAP = 408
|
local WRAP = 408
|
||||||
@@ -29,11 +32,11 @@ local function fingerprint(a)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function render(a)
|
local function render(a)
|
||||||
local rows = { ui.label({ text = "Claude — quick ask", fontWeight = "bold" }) }
|
local rows = { ui.label({ text = tr("answer.title"), fontWeight = "bold" }) }
|
||||||
local body
|
local body
|
||||||
if type(a) ~= "table" or type(a.text) ~= "string" or a.text == "" then
|
if type(a) ~= "table" or type(a.text) ~= "string" or a.text == "" then
|
||||||
body = ui.label({
|
body = ui.label({
|
||||||
text = "No answers yet. Ask one from the launcher: /claude ? <question>",
|
text = tr("answer.empty"),
|
||||||
maxWidth = WRAP,
|
maxWidth = WRAP,
|
||||||
opacity = 0.7,
|
opacity = 0.7,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ local function shq(s)
|
|||||||
return "'" .. tostring(s):gsub("'", "'\\''") .. "'"
|
return "'" .. tostring(s):gsub("'", "'\\''") .. "'"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- User-facing display strings live in translations/<lang>.json; resolve them at
|
||||||
|
-- call time (the noctalia global is ready by then). Model-facing prompts
|
||||||
|
-- (ASK_NOTE, SYSTEM_NOTE below) are deliberately NOT translated — they are
|
||||||
|
-- instructions to the model, not UI, and rewording them can shift its behavior.
|
||||||
|
local function tr(key, args) return noctalia.tr(key, args) end
|
||||||
|
|
||||||
-- A double-quoted sh argument: unlike shq it lets the shell expand $VAR (we need
|
-- A double-quoted sh argument: unlike shq it lets the shell expand $VAR (we need
|
||||||
-- $HOME in the shim path so nothing user-specific is hardcoded). ONLY use this on
|
-- $HOME in the shim path so nothing user-specific is hardcoded). ONLY use this on
|
||||||
-- fixed, trusted strings — never on user input, which must go through shq().
|
-- fixed, trusted strings — never on user input, which must go through shq().
|
||||||
@@ -150,7 +156,7 @@ end
|
|||||||
-- 401 whenever no terminal session has run recently. Two layers, both fail-open:
|
-- 401 whenever no terminal session has run recently. Two layers, both fail-open:
|
||||||
-- a pre-flight that skips the launch when the token is positively expired, and
|
-- a pre-flight that skips the launch when the token is positively expired, and
|
||||||
-- an error-text match that swaps the raw API error for the remedy.
|
-- an error-text match that swaps the raw API error for the remedy.
|
||||||
local AUTH_REMEDY = "Claude login expired — start a Claude session in a terminal to refresh it, then re-ask."
|
-- (remedy text: tr("notify.auth_remedy"))
|
||||||
|
|
||||||
local CREDS_PATH = "~/.claude/.credentials.json"
|
local CREDS_PATH = "~/.claude/.credentials.json"
|
||||||
|
|
||||||
@@ -220,14 +226,14 @@ local function trim(s) return (s:gsub("^%s+", ""):gsub("%s+$", "")) end
|
|||||||
local function answer_row()
|
local function answer_row()
|
||||||
local a = noctalia.state.get("claude.answer")
|
local a = noctalia.state.get("claude.answer")
|
||||||
if type(a) ~= "table" or type(a.text) ~= "string" or a.text == "" then return nil end
|
if type(a) ~= "table" or type(a.text) ~= "string" or a.text == "" then return nil end
|
||||||
return { id = "answer", title = "Show last answer", subtitle = a.q, glyph = "message-2" }
|
return { id = "answer", title = tr("launcher.show_answer"), subtitle = a.q, glyph = "message-2" }
|
||||||
end
|
end
|
||||||
|
|
||||||
function onQuery(query)
|
function onQuery(query)
|
||||||
local text = trim(query)
|
local text = trim(query)
|
||||||
if text == "" then
|
if text == "" then
|
||||||
local results = {
|
local results = {
|
||||||
{ id = "continue", title = "Resume last Claude session", glyph = "robot" },
|
{ id = "continue", title = tr("launcher.continue"), glyph = "robot" },
|
||||||
}
|
}
|
||||||
results[#results + 1] = answer_row()
|
results[#results + 1] = answer_row()
|
||||||
launcher.setResults(query, results)
|
launcher.setResults(query, results)
|
||||||
@@ -237,19 +243,19 @@ function onQuery(query)
|
|||||||
if ask ~= nil then
|
if ask ~= nil then
|
||||||
if ask == "" then
|
if ask == "" then
|
||||||
local results = {
|
local results = {
|
||||||
{ id = "_askhint", title = "Ask Claude (one-shot)", subtitle = "Type a question after ?", glyph = "message-dots" },
|
{ id = "_askhint", title = tr("launcher.ask"), subtitle = tr("launcher.ask_hint"), glyph = "message-dots" },
|
||||||
}
|
}
|
||||||
results[#results + 1] = answer_row()
|
results[#results + 1] = answer_row()
|
||||||
launcher.setResults(query, results)
|
launcher.setResults(query, results)
|
||||||
else
|
else
|
||||||
launcher.setResults(query, {
|
launcher.setResults(query, {
|
||||||
{ id = "ask:" .. ask, title = "Ask Claude (one-shot)", subtitle = ask, glyph = "message-dots" },
|
{ id = "ask:" .. ask, title = tr("launcher.ask"), subtitle = ask, glyph = "message-dots" },
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
launcher.setResults(query, {
|
launcher.setResults(query, {
|
||||||
{ id = "task:" .. text, title = "Launch Claude Code", subtitle = text, glyph = "robot" },
|
{ id = "task:" .. text, title = tr("launcher.launch"), subtitle = text, glyph = "robot" },
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -278,8 +284,9 @@ function onActivate(id)
|
|||||||
-- and the remedy needs a terminal anyway — so say so now and skip the
|
-- and the remedy needs a terminal anyway — so say so now and skip the
|
||||||
-- launch. No claude.state touch: no ask session ever starts.
|
-- launch. No claude.state touch: no ask session ever starts.
|
||||||
if token_expired() then
|
if token_expired() then
|
||||||
publish_answer(ask, AUTH_REMEDY, true)
|
local remedy = tr("notify.auth_remedy")
|
||||||
if not panel_showing() then noctalia.notifyError("Claude", AUTH_REMEDY) end
|
publish_answer(ask, remedy, true)
|
||||||
|
if not panel_showing() then noctalia.notifyError(tr("notify.title"), remedy) end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
set_state(EVENT.turn_start)
|
set_state(EVENT.turn_start)
|
||||||
@@ -295,26 +302,26 @@ function onActivate(id)
|
|||||||
elseif ev.kind == EVENT.turn_end then
|
elseif ev.kind == EVENT.turn_end then
|
||||||
local final = (ev.text and ev.text ~= "") and ev.text or acc
|
local final = (ev.text and ev.text ~= "") and ev.text or acc
|
||||||
if final == "" then
|
if final == "" then
|
||||||
noctalia.notify("Claude", "(no output)")
|
noctalia.notify(tr("notify.title"), tr("notify.no_output"))
|
||||||
else
|
else
|
||||||
publish_answer(ask, final, false)
|
publish_answer(ask, final, false)
|
||||||
if not panel_showing() then
|
if not panel_showing() then
|
||||||
local body, clipped = preview(final)
|
local body, clipped = preview(final)
|
||||||
if clipped then body = body .. "\nFull answer: click the bar pulse" end
|
if clipped then body = body .. "\n" .. tr("notify.full_answer_hint") end
|
||||||
noctalia.notify("Claude", body)
|
noctalia.notify(tr("notify.title"), body)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif ev.kind == EVENT.error then
|
elseif ev.kind == EVENT.error then
|
||||||
local final = (ev.text and ev.text ~= "") and ev.text or acc
|
local final = (ev.text and ev.text ~= "") and ev.text or acc
|
||||||
if final == "" then final = "ask failed" end
|
if final == "" then final = tr("notify.ask_failed") end
|
||||||
-- a 401 that slipped past the pre-flight: deliver the remedy, not the
|
-- a 401 that slipped past the pre-flight: deliver the remedy, not the
|
||||||
-- raw API error blob
|
-- raw API error blob
|
||||||
if is_auth_error(final) then final = AUTH_REMEDY end
|
if is_auth_error(final) then final = tr("notify.auth_remedy") end
|
||||||
publish_answer(ask, final, true)
|
publish_answer(ask, final, true)
|
||||||
if not panel_showing() then
|
if not panel_showing() then
|
||||||
local body, clipped = preview(final)
|
local body, clipped = preview(final)
|
||||||
if clipped then body = body .. "\nFull answer: click the bar pulse" end
|
if clipped then body = body .. "\n" .. tr("notify.full_answer_hint") end
|
||||||
noctalia.notifyError("Claude", body)
|
noctalia.notifyError(tr("notify.title"), body)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|||||||
+19
-12
@@ -34,14 +34,21 @@
|
|||||||
-- ~4s), opacity floors stay high so the icon glows down rather than blinking to dark,
|
-- ~4s), opacity floors stay high so the icon glows down rather than blinking to dark,
|
||||||
-- and the size swing (below) is gentle. Urgency reads as a *faster, deeper* breath,
|
-- and the size swing (below) is gentle. Urgency reads as a *faster, deeper* breath,
|
||||||
-- but even "needs you" stays a breath, never a flicker.
|
-- but even "needs you" stays a breath, never a flicker.
|
||||||
|
-- User-facing display strings live in translations/<lang>.json; the per-state
|
||||||
|
-- status word is keyed by state name ("state.orb.<state>") and resolved at render
|
||||||
|
-- time. cur.state is always a known state (the watch below maps anything else to
|
||||||
|
-- "idle"), so no fallback guard is needed here.
|
||||||
|
local function tr(key, args) return noctalia.tr(key, args) end
|
||||||
|
local function orb_word(state) return tr("state.orb." .. state) end
|
||||||
|
|
||||||
local VISUAL = {
|
local VISUAL = {
|
||||||
idle = { glyph = "robot", color = "secondary", period = 11.0, omin = 0.45, omax = 0.80, word = "idle" },
|
idle = { glyph = "robot", color = "secondary", period = 11.0, omin = 0.45, omax = 0.80 },
|
||||||
turn_start = { glyph = "brain", color = "primary", period = 7.5, omin = 0.55, omax = 1.00, word = "thinking" },
|
turn_start = { glyph = "brain", color = "primary", period = 7.5, omin = 0.55, omax = 1.00 },
|
||||||
text = { glyph = "message-dots", color = "primary", period = 7.0, omin = 0.58, omax = 1.00, word = "responding" },
|
text = { glyph = "message-dots", color = "primary", period = 7.0, omin = 0.58, omax = 1.00 },
|
||||||
tool_start = { glyph = "tool", color = "secondary", period = 7.5, omin = 0.55, omax = 1.00, word = "running a tool" },
|
tool_start = { glyph = "tool", color = "secondary", period = 7.5, omin = 0.55, omax = 1.00 },
|
||||||
needs_attention = { glyph = "bell-ringing", color = "error", period = 4.5, omin = 0.55, omax = 1.00, word = "needs you" },
|
needs_attention = { glyph = "bell-ringing", color = "error", period = 4.5, omin = 0.55, omax = 1.00 },
|
||||||
turn_end = { glyph = "bell", color = "primary", period = 8.0, omin = 0.55, omax = 0.95, word = "done — ready for you" },
|
turn_end = { glyph = "bell", color = "primary", period = 8.0, omin = 0.55, omax = 0.95 },
|
||||||
error = { glyph = "alert-triangle", color = "error", period = 5.0, omin = 0.55, omax = 1.00, word = "error" },
|
error = { glyph = "alert-triangle", color = "error", period = 5.0, omin = 0.55, omax = 1.00 },
|
||||||
}
|
}
|
||||||
|
|
||||||
-- The orb IS the bar icon, swelling gently in and out — a desktop "bat signal". The
|
-- The orb IS the bar icon, swelling gently in and out — a desktop "bat signal". The
|
||||||
@@ -72,14 +79,14 @@ end
|
|||||||
-- One compact status line under the orb. Kept short so it doesn't get clipped by a
|
-- One compact status line under the orb. Kept short so it doesn't get clipped by a
|
||||||
-- narrow widget box: just the state word, or a session count, plus burn when there's
|
-- narrow widget box: just the state word, or a session count, plus burn when there's
|
||||||
-- a single attributable session.
|
-- a single attributable session.
|
||||||
local function subtitle(v)
|
local function subtitle()
|
||||||
if cur.count > 1 then
|
if cur.count > 1 then
|
||||||
return cur.count .. " sessions"
|
return tr("orb.sessions", { count = cur.count })
|
||||||
end
|
end
|
||||||
if cur.count == 1 and cur.model ~= "?" and (cur.tin + cur.tout) > 0 then
|
if cur.count == 1 and cur.model ~= "?" and (cur.tin + cur.tout) > 0 then
|
||||||
return v.word .. " · " .. kfmt(cur.tin) .. "/" .. kfmt(cur.tout)
|
return orb_word(cur.state) .. " · " .. kfmt(cur.tin) .. "/" .. kfmt(cur.tout)
|
||||||
end
|
end
|
||||||
return v.word
|
return orb_word(cur.state)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function render()
|
local function render()
|
||||||
@@ -98,7 +105,7 @@ local function render()
|
|||||||
width = GLYPH_BOX, height = GLYPH_BOX,
|
width = GLYPH_BOX, height = GLYPH_BOX,
|
||||||
}),
|
}),
|
||||||
ui.label({
|
ui.label({
|
||||||
text = subtitle(v), fontSize = 12, color = "on_surface_variant",
|
text = subtitle(), fontSize = 12, color = "on_surface_variant",
|
||||||
maxWidth = 180, maxLines = 1, textAlign = "center",
|
maxWidth = 180, maxLines = 1, textAlign = "center",
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
id = "lowcache/claude-companion"
|
id = "lowcache/claude-companion"
|
||||||
name = "Claude Companion"
|
name = "Claude Companion"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
# Oldest plugin API level this plugin needs (the [[panel]] kind, ui controls,
|
# Oldest plugin API level this plugin needs (the [[panel]] kind, ui controls,
|
||||||
# plugin IPC dispatch, the barWidget/onIpc surface). Bump when adopting a newer API.
|
# plugin IPC dispatch, the barWidget/onIpc surface). Bump when adopting a newer API.
|
||||||
plugin_api = 3
|
plugin_api = 3
|
||||||
|
|||||||
+30
-23
@@ -87,17 +87,30 @@ local function resolve_accents()
|
|||||||
ACCENTS.error = hex(m.mError) or ACCENTS.error
|
ACCENTS.error = hex(m.mError) or ACCENTS.error
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- User-facing display strings live in translations/<lang>.json; resolve at call
|
||||||
|
-- time. Per-state tips ("state.tip.<state>") and compact words ("state.word.
|
||||||
|
-- <state>") are keyed by state name. KNOWN_STATE guards a bogus state from a
|
||||||
|
-- manual CLI poke so it falls back to raw text / idle exactly as the old tables did.
|
||||||
|
local function tr(key, args) return noctalia.tr(key, args) end
|
||||||
|
local KNOWN_STATE = {
|
||||||
|
idle = true, turn_start = true, text = true, tool_start = true,
|
||||||
|
needs_attention = true, turn_end = true, error = true,
|
||||||
|
}
|
||||||
|
local function state_tip(s) return tr("state.tip." .. (KNOWN_STATE[s] and s or "idle")) end
|
||||||
|
local function state_word(s) return KNOWN_STATE[s] and tr("state.word." .. s) or s end
|
||||||
|
|
||||||
-- ── state map ────────────────────────────────────────────────────────────────
|
-- ── state map ────────────────────────────────────────────────────────────────
|
||||||
-- `color` names an ACCENTS role; `period` is the breath cycle in seconds —
|
-- `color` names an ACCENTS role; `period` is the breath cycle in seconds —
|
||||||
-- urgency reads as tempo (needs-you breathes fast, idle slow).
|
-- urgency reads as tempo (needs-you breathes fast, idle slow). Tooltip text is
|
||||||
|
-- resolved from translations by state name (state_tip/state_word above).
|
||||||
local VISUAL = {
|
local VISUAL = {
|
||||||
idle = { glyph = "robot", color = "secondary", tip = "Claude: idle", period = 8.0 },
|
idle = { glyph = "robot", color = "secondary", period = 8.0 },
|
||||||
turn_start = { glyph = "brain", color = "primary", tip = "Claude: thinking", period = 5.0 },
|
turn_start = { glyph = "brain", color = "primary", period = 5.0 },
|
||||||
text = { glyph = "message-dots", color = "primary", tip = "Claude: responding", period = 4.5 },
|
text = { glyph = "message-dots", color = "primary", period = 4.5 },
|
||||||
tool_start = { glyph = "tool", color = "secondary", tip = "Claude: running a tool", period = 5.0 },
|
tool_start = { glyph = "tool", color = "secondary", period = 5.0 },
|
||||||
needs_attention = { glyph = "bell-ringing", color = "error", tip = "Claude needs you", period = 3.0 },
|
needs_attention = { glyph = "bell-ringing", color = "error", period = 3.0 },
|
||||||
turn_end = { glyph = "bell", color = "primary", tip = "Claude: done — ready for you", period = 5.5 },
|
turn_end = { glyph = "bell", color = "primary", period = 5.5 },
|
||||||
error = { glyph = "alert-triangle", color = "error", tip = "Claude: error", period = 3.5 },
|
error = { glyph = "alert-triangle", color = "error", period = 3.5 },
|
||||||
}
|
}
|
||||||
|
|
||||||
-- With several sessions in different states, the bar shows the most urgent one:
|
-- With several sessions in different states, the bar shows the most urgent one:
|
||||||
@@ -106,12 +119,6 @@ local STATE_PRIO = {
|
|||||||
needs_attention = 6, error = 5, tool_start = 4,
|
needs_attention = 6, error = 5, tool_start = 4,
|
||||||
turn_start = 3, text = 3, turn_end = 2, idle = 1,
|
turn_start = 3, text = 3, turn_end = 2, idle = 1,
|
||||||
}
|
}
|
||||||
-- Compact per-session words for the multi-session tooltip.
|
|
||||||
local STATE_WORD = {
|
|
||||||
idle = "idle", turn_start = "thinking", text = "responding",
|
|
||||||
tool_start = "tool", needs_attention = "needs you",
|
|
||||||
turn_end = "done", error = "error",
|
|
||||||
}
|
|
||||||
|
|
||||||
-- sid -> { sid, state, model, tin, tout, cr, seq }. `seq` is a monotonic counter
|
-- sid -> { sid, state, model, tin, tout, cr, seq }. `seq` is a monotonic counter
|
||||||
-- (no os.time dependency in the widget sandbox) used to order by recency.
|
-- (no os.time dependency in the widget sandbox) used to order by recency.
|
||||||
@@ -185,8 +192,8 @@ local function has_burn(s)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function burn_line(s)
|
local function burn_line(s)
|
||||||
local l = string.format("%s · %s in / %s out", s.model, kfmt(s.tin), kfmt(s.tout))
|
local l = tr("pulse.burn", { model = s.model, tin = kfmt(s.tin), tout = kfmt(s.tout) })
|
||||||
if (s.cr or 0) > 0 then l = l .. " · " .. kfmt(s.cr) .. " cached" end
|
if (s.cr or 0) > 0 then l = l .. " · " .. tr("pulse.cached", { n = kfmt(s.cr) }) end
|
||||||
return l
|
return l
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -217,16 +224,16 @@ local function render()
|
|||||||
|
|
||||||
local tip
|
local tip
|
||||||
if count == 0 then
|
if count == 0 then
|
||||||
tip = VISUAL.idle.tip
|
tip = state_tip("idle")
|
||||||
elseif count == 1 then
|
elseif count == 1 then
|
||||||
local s = arr[1]
|
local s = arr[1]
|
||||||
local sv = VISUAL[s.state] or VISUAL.idle
|
local base = state_tip(s.state)
|
||||||
tip = has_burn(s) and (sv.tip .. "\n" .. burn_line(s)) or sv.tip
|
tip = has_burn(s) and (base .. "\n" .. burn_line(s)) or base
|
||||||
else
|
else
|
||||||
local lines = { string.format("Claude — %d sessions", count) }
|
local lines = { tr("pulse.sessions_header", { count = count }) }
|
||||||
local Tin, Tout = 0, 0
|
local Tin, Tout = 0, 0
|
||||||
for _, s in ipairs(arr) do
|
for _, s in ipairs(arr) do
|
||||||
local line = s.sid .. " · " .. (STATE_WORD[s.state] or s.state)
|
local line = s.sid .. " · " .. state_word(s.state)
|
||||||
if has_burn(s) then
|
if has_burn(s) then
|
||||||
line = line .. " · " .. s.model .. " " .. kfmt(s.tin) .. "/" .. kfmt(s.tout)
|
line = line .. " · " .. s.model .. " " .. kfmt(s.tin) .. "/" .. kfmt(s.tout)
|
||||||
Tin = Tin + s.tin; Tout = Tout + s.tout
|
Tin = Tin + s.tin; Tout = Tout + s.tout
|
||||||
@@ -234,7 +241,7 @@ local function render()
|
|||||||
lines[#lines + 1] = line
|
lines[#lines + 1] = line
|
||||||
end
|
end
|
||||||
if (Tin + Tout) > 0 then
|
if (Tin + Tout) > 0 then
|
||||||
lines[#lines + 1] = string.format("Σ %s in / %s out", kfmt(Tin), kfmt(Tout))
|
lines[#lines + 1] = tr("pulse.total", { tin = kfmt(Tin), tout = kfmt(Tout) })
|
||||||
end
|
end
|
||||||
tip = table.concat(lines, "\n")
|
tip = table.concat(lines, "\n")
|
||||||
end
|
end
|
||||||
@@ -323,7 +330,7 @@ end
|
|||||||
-- appeared dead in live testing). Dismiss is click-outside/Esc, panel idiom.
|
-- appeared dead in live testing). Dismiss is click-outside/Esc, panel idiom.
|
||||||
function onClick()
|
function onClick()
|
||||||
if not noctalia.runAsync("noctalia msg panel-open 'lowcache/claude-companion:answer'") then
|
if not noctalia.runAsync("noctalia msg panel-open 'lowcache/claude-companion:answer'") then
|
||||||
noctalia.notifyError("pulse", "could not launch the answer panel")
|
noctalia.notifyError(tr("pulse.title"), tr("pulse.panel_launch_failed"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,60 @@
|
|||||||
{}
|
{
|
||||||
|
"notify": {
|
||||||
|
"title": "Claude",
|
||||||
|
"no_output": "(no output)",
|
||||||
|
"ask_failed": "ask failed",
|
||||||
|
"full_answer_hint": "Full answer: click the bar pulse",
|
||||||
|
"auth_remedy": "Claude login expired — start a Claude session in a terminal to refresh it, then re-ask."
|
||||||
|
},
|
||||||
|
"launcher": {
|
||||||
|
"continue": "Resume last Claude session",
|
||||||
|
"show_answer": "Show last answer",
|
||||||
|
"ask": "Ask Claude (one-shot)",
|
||||||
|
"ask_hint": "Type a question after ?",
|
||||||
|
"launch": "Launch Claude Code"
|
||||||
|
},
|
||||||
|
"answer": {
|
||||||
|
"title": "Claude — quick ask",
|
||||||
|
"empty": "No answers yet. Ask one from the launcher: /claude ? <question>"
|
||||||
|
},
|
||||||
|
"pulse": {
|
||||||
|
"title": "pulse",
|
||||||
|
"panel_launch_failed": "could not launch the answer panel",
|
||||||
|
"sessions_header": "Claude — {count} sessions",
|
||||||
|
"burn": "{model} · {tin} in / {tout} out",
|
||||||
|
"cached": "{n} cached",
|
||||||
|
"total": "Σ {tin} in / {tout} out"
|
||||||
|
},
|
||||||
|
"orb": {
|
||||||
|
"sessions": "{count} sessions"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"tip": {
|
||||||
|
"idle": "Claude: idle",
|
||||||
|
"turn_start": "Claude: thinking",
|
||||||
|
"text": "Claude: responding",
|
||||||
|
"tool_start": "Claude: running a tool",
|
||||||
|
"needs_attention": "Claude needs you",
|
||||||
|
"turn_end": "Claude: done — ready for you",
|
||||||
|
"error": "Claude: error"
|
||||||
|
},
|
||||||
|
"word": {
|
||||||
|
"idle": "idle",
|
||||||
|
"turn_start": "thinking",
|
||||||
|
"text": "responding",
|
||||||
|
"tool_start": "tool",
|
||||||
|
"needs_attention": "needs you",
|
||||||
|
"turn_end": "done",
|
||||||
|
"error": "error"
|
||||||
|
},
|
||||||
|
"orb": {
|
||||||
|
"idle": "idle",
|
||||||
|
"turn_start": "thinking",
|
||||||
|
"text": "responding",
|
||||||
|
"tool_start": "running a tool",
|
||||||
|
"needs_attention": "needs you",
|
||||||
|
"turn_end": "done — ready for you",
|
||||||
|
"error": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user