From 33b431bbdae419230afb7bdb5955dfabc9f7f525 Mon Sep 17 00:00:00 2001 From: tsouth89 Date: Thu, 16 Jul 2026 21:12:46 -0400 Subject: [PATCH 1/4] Add first-run checklist and per-provider data-source explainer (SOU-157, SOU-179) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## SOU-179 — "How Ceiling gets this data" New DataSourceSection in every provider detail pane (right under the identity block) with a short, source-verified provenance line plus a precise privacy note. Copy is checked against the real fetch paths: - Dedicated copy for Claude, Codex, Cursor, Copilot, Gemini (providerProvenance.ts maps id -> locale key); every other provider gets a precise generic line. A test asserts the five keep dedicated copy so a new/renamed provider can't silently drop to generic. - Privacy note follows the guardrail: no "never leaves your PC" claim (credentials are sent to the provider's own endpoint to fetch usage); it states data is never sent to Ceiling-operated servers, only to the provider itself. - New docs/DATA_SOURCES.md (maintained per-provider reference), linked from the panel and the README. Corrected the README's inaccurate "never leave your machine" line to precise language. ## SOU-157 — first-run checklist Dismissible 3-step checklist (enable providers, connect/confirm auth, turn on the floatbar/tray) shown in the empty PopOut dashboard. Steps reflect real state and deep-link into the matching Settings tab. Dismissal persists in localStorage; a returning user with working auth never sees it (the dashboard shows their providers instead of the empty state). Loading still shows the spinner. Verified: pnpm test (48 files / 235 tests), pnpm run build (check-locale + tsc + vite), cargo check (shared crate). Both surfaces are visual and need a Windows eyeball (dark + light) before merge. --- README.md | 2 +- .../src/components/FirstRunChecklist.test.tsx | 52 ++++++++ .../src/components/FirstRunChecklist.tsx | 105 +++++++++++++++ apps/desktop-tauri/src/i18n/keys.ts | 18 +++ .../src/lib/providerProvenance.test.ts | 34 +++++ .../src/lib/providerProvenance.ts | 31 +++++ apps/desktop-tauri/src/styles.css | 122 ++++++++++++++++++ .../src/surfaces/PopOutPanel.tsx | 33 ++++- .../settings/providers/ProviderDetailPane.tsx | 3 + .../sections/DataSourceSection.test.tsx | 28 ++++ .../providers/sections/DataSourceSection.tsx | 41 ++++++ docs/DATA_SOURCES.md | 48 +++++++ rust/src/locale.rs | 20 +++ rust/src/locale/en-US.ftl | 20 +++ 14 files changed, 552 insertions(+), 5 deletions(-) create mode 100644 apps/desktop-tauri/src/components/FirstRunChecklist.test.tsx create mode 100644 apps/desktop-tauri/src/components/FirstRunChecklist.tsx create mode 100644 apps/desktop-tauri/src/lib/providerProvenance.test.ts create mode 100644 apps/desktop-tauri/src/lib/providerProvenance.ts create mode 100644 apps/desktop-tauri/src/surfaces/settings/providers/sections/DataSourceSection.test.tsx create mode 100644 apps/desktop-tauri/src/surfaces/settings/providers/sections/DataSourceSection.tsx create mode 100644 docs/DATA_SOURCES.md diff --git a/README.md b/README.md index eab2b345..9dad714a 100755 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Ceiling runs on Windows 10 and 11. **[Download for Windows →](https://ceiling.win/download)**  ·  [ceiling.win](https://ceiling.win)  ·  [all releases](https://github.com/tsouth89/ceiling/releases) -The installer and portable build are code-signed. Everything stays local-first: your credentials and usage data never leave your machine. +The installer and portable build are code-signed. Ceiling is local-first: it reads usage from sources on your PC or from each provider's own usage endpoint, and never sends your credentials or usage data to Ceiling-operated servers. See [How Ceiling gets your data](docs/DATA_SOURCES.md) for the per-provider detail. ## Development diff --git a/apps/desktop-tauri/src/components/FirstRunChecklist.test.tsx b/apps/desktop-tauri/src/components/FirstRunChecklist.test.tsx new file mode 100644 index 00000000..8e94adef --- /dev/null +++ b/apps/desktop-tauri/src/components/FirstRunChecklist.test.tsx @@ -0,0 +1,52 @@ +import { fireEvent, render, screen } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import { FirstRunChecklist } from "./FirstRunChecklist"; + +const t = (k: string) => k; + +function setup(overrides: Partial[0]> = {}) { + const props = { + enabledCount: 0, + hasWorkingAuth: false, + floatbarEnabled: false, + onOpenProviders: vi.fn(), + onOpenDisplay: vi.fn(), + onDismiss: vi.fn(), + t, + ...overrides, + }; + render(); + return props; +} + +describe("FirstRunChecklist", () => { + it("renders all three steps as actionable when nothing is set up", () => { + setup(); + expect(screen.getByText("FirstRunStepEnable")).toBeInTheDocument(); + expect(screen.getByText("FirstRunStepAuth")).toBeInTheDocument(); + expect(screen.getByText("FirstRunStepFloatbar")).toBeInTheDocument(); + // Steps 1 and 2 both link to provider settings. + expect(screen.getAllByText("FirstRunOpenProviders")).toHaveLength(2); + }); + + it("marks a completed step done and drops its action button", () => { + setup({ enabledCount: 2, floatbarEnabled: true }); + const enable = screen.getByText("FirstRunStepEnable").closest("li"); + expect(enable?.className).toContain("first-run__step--done"); + // Only the auth step's action remains (enable + floatbar are done). + expect(screen.getAllByText("FirstRunOpenProviders")).toHaveLength(1); + expect(screen.queryByText("FirstRunOpenDisplay")).toBeNull(); + }); + + it("dismiss calls onDismiss", () => { + const props = setup(); + fireEvent.click(screen.getByText("FirstRunDismiss")); + expect(props.onDismiss).toHaveBeenCalled(); + }); + + it("the floatbar step opens display settings", () => { + const props = setup(); + fireEvent.click(screen.getByText("FirstRunOpenDisplay")); + expect(props.onOpenDisplay).toHaveBeenCalled(); + }); +}); diff --git a/apps/desktop-tauri/src/components/FirstRunChecklist.tsx b/apps/desktop-tauri/src/components/FirstRunChecklist.tsx new file mode 100644 index 00000000..243eb376 --- /dev/null +++ b/apps/desktop-tauri/src/components/FirstRunChecklist.tsx @@ -0,0 +1,105 @@ +import type { LocaleKey } from "../i18n/keys"; + +const DISMISS_KEY = "ceiling.first-run.dismissed.v1"; + +/** Whether the user has dismissed the first-run checklist on this machine. */ +export function firstRunDismissed(): boolean { + try { + return localStorage.getItem(DISMISS_KEY) === "1"; + } catch { + return false; + } +} + +/** Remember that the first-run checklist was dismissed. */ +export function dismissFirstRun(): void { + try { + localStorage.setItem(DISMISS_KEY, "1"); + } catch { + // Ignore storage failures (private mode, disabled storage, etc.). + } +} + +interface Props { + enabledCount: number; + hasWorkingAuth: boolean; + floatbarEnabled: boolean; + onOpenProviders: () => void; + onOpenDisplay: () => void; + onDismiss: () => void; + t: (key: LocaleKey) => string; +} + +/** + * Light, dismissible first-run checklist shown in the empty dashboard when no + * provider has produced usage yet (SOU-157). Each step reflects real state and + * deep-links into the matching Settings tab. Dismissal is remembered in + * localStorage (same pattern as the detected-accounts card); a returning user + * who already has working auth never lands here because the dashboard shows + * their providers instead of the empty state. + */ +export function FirstRunChecklist({ + enabledCount, + hasWorkingAuth, + floatbarEnabled, + onOpenProviders, + onOpenDisplay, + onDismiss, + t, +}: Props) { + const steps = [ + { + done: enabledCount > 0, + label: t("FirstRunStepEnable"), + action: t("FirstRunOpenProviders"), + onAction: onOpenProviders, + }, + { + done: hasWorkingAuth, + label: t("FirstRunStepAuth"), + action: t("FirstRunOpenProviders"), + onAction: onOpenProviders, + }, + { + done: floatbarEnabled, + label: t("FirstRunStepFloatbar"), + action: t("FirstRunOpenDisplay"), + onAction: onOpenDisplay, + }, + ]; + + return ( +
+
+

{t("FirstRunTitle")}

+ +
+
    + {steps.map((step, index) => ( +
  1. +
  2. + ))} +
+
+ ); +} diff --git a/apps/desktop-tauri/src/i18n/keys.ts b/apps/desktop-tauri/src/i18n/keys.ts index fe758cba..90c89d1c 100644 --- a/apps/desktop-tauri/src/i18n/keys.ts +++ b/apps/desktop-tauri/src/i18n/keys.ts @@ -602,6 +602,24 @@ export const ALL_LOCALE_KEYS = [ "TrayPaceBadgeBurning", "TrayResetsInLabel", "TrayResetsDueNow", + // Provider data-source / privacy explainer (SOU-179) + "DataSourceSectionTitle", + "DataSourcePrivacyNote", + "DataSourceLearnMore", + "DataSourceClaude", + "DataSourceCodex", + "DataSourceCursor", + "DataSourceCopilot", + "DataSourceGemini", + "DataSourceGeneric", + // First-run checklist (SOU-157) + "FirstRunTitle", + "FirstRunStepEnable", + "FirstRunStepAuth", + "FirstRunStepFloatbar", + "FirstRunOpenProviders", + "FirstRunOpenDisplay", + "FirstRunDismiss", ] as const; export type LocaleKey = (typeof ALL_LOCALE_KEYS)[number]; diff --git a/apps/desktop-tauri/src/lib/providerProvenance.test.ts b/apps/desktop-tauri/src/lib/providerProvenance.test.ts new file mode 100644 index 00000000..66b1ccfa --- /dev/null +++ b/apps/desktop-tauri/src/lib/providerProvenance.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; +import { ALL_LOCALE_KEYS } from "../i18n/keys"; +import { + DETAILED_PROVENANCE_PROVIDERS, + providerProvenanceKey, +} from "./providerProvenance"; + +describe("providerProvenance", () => { + it("gives the five first-class providers dedicated (non-generic) copy", () => { + expect([...DETAILED_PROVENANCE_PROVIDERS].sort()).toEqual( + ["claude", "codex", "copilot", "cursor", "gemini"].sort(), + ); + for (const id of DETAILED_PROVENANCE_PROVIDERS) { + expect(providerProvenanceKey(id)).not.toBe("DataSourceGeneric"); + } + }); + + it("falls back to the generic line for providers without dedicated copy", () => { + expect(providerProvenanceKey("openrouter")).toBe("DataSourceGeneric"); + expect(providerProvenanceKey("brand-new-provider")).toBe("DataSourceGeneric"); + }); + + // Guardrail (SOU-179): every provenance key a provider can resolve to must + // exist in the locale set, so a provider can never render a missing string. + it("every provenance key exists in the locale key set", () => { + const keys = [ + ...DETAILED_PROVENANCE_PROVIDERS.map(providerProvenanceKey), + providerProvenanceKey("unknown"), + ]; + for (const key of keys) { + expect(ALL_LOCALE_KEYS).toContain(key); + } + }); +}); diff --git a/apps/desktop-tauri/src/lib/providerProvenance.ts b/apps/desktop-tauri/src/lib/providerProvenance.ts new file mode 100644 index 00000000..c127085b --- /dev/null +++ b/apps/desktop-tauri/src/lib/providerProvenance.ts @@ -0,0 +1,31 @@ +import type { LocaleKey } from "../i18n/keys"; + +/** + * Providers with hand-written, source-verified provenance copy for the + * "How Ceiling gets this data" block (SOU-179). Every other provider falls + * back to `DataSourceGeneric`. + * + * The copy behind these keys is verified against the real fetch paths in + * `rust/src/providers/*` and must stay in sync with `docs/DATA_SOURCES.md`. + * A test (`providerProvenance.test.ts`) asserts these five keep dedicated + * copy so a rename can't silently drop a first-class provider to the generic + * line. + */ +const PROVENANCE_KEYS: Record = { + claude: "DataSourceClaude", + codex: "DataSourceCodex", + cursor: "DataSourceCursor", + copilot: "DataSourceCopilot", + gemini: "DataSourceGemini", +}; + +/** Provider ids that ship dedicated (non-generic) provenance copy. */ +export const DETAILED_PROVENANCE_PROVIDERS = Object.keys(PROVENANCE_KEYS); + +/** + * Locale key describing how Ceiling obtains this provider's data. Providers + * without dedicated copy get the precise generic statement. + */ +export function providerProvenanceKey(providerId: string): LocaleKey { + return PROVENANCE_KEYS[providerId] ?? "DataSourceGeneric"; +} diff --git a/apps/desktop-tauri/src/styles.css b/apps/desktop-tauri/src/styles.css index fb6e04c1..8a12b6b9 100644 --- a/apps/desktop-tauri/src/styles.css +++ b/apps/desktop-tauri/src/styles.css @@ -8289,3 +8289,125 @@ body:has(.dashboard-shell) #root { color: var(--provider-row-text-primary); border: 1px solid var(--provider-icon-ring); } + +/* ── Provider data-source explainer (SOU-179) ─────────────────────────── */ +.provider-detail-datasource__source { + margin: 0 0 8px; + font-size: 0.82rem; + line-height: 1.5; + color: var(--provider-row-text-primary); +} +.provider-detail-datasource__privacy { + margin: 0 0 8px; + font-size: 0.76rem; + line-height: 1.5; + color: var(--provider-row-text-secondary); +} +.provider-detail-datasource__link { + appearance: none; + background: none; + border: none; + padding: 0; + font: inherit; + font-size: 0.78rem; + color: var(--accent); + cursor: pointer; + text-decoration: underline; +} +.provider-detail-datasource__link:hover { + opacity: 0.85; +} + +/* ── First-run checklist (SOU-157) ────────────────────────────────────── */ +.first-run { + margin: 0 0 12px; + padding: 14px 16px; + border-radius: 10px; + border: 1px solid var(--provider-icon-ring, rgba(255, 255, 255, 0.08)); + background: var(--provider-row-bg, rgba(255, 255, 255, 0.03)); +} +.first-run__header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 10px; +} +.first-run__title { + margin: 0; + font-size: 0.92rem; + font-weight: 600; + color: var(--text-primary); +} +.first-run__dismiss { + appearance: none; + background: none; + border: none; + padding: 2px 4px; + font: inherit; + font-size: 0.76rem; + color: var(--provider-row-text-secondary); + cursor: pointer; +} +.first-run__dismiss:hover { + color: var(--text-primary); +} +.first-run__steps { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 8px; +} +.first-run__step { + display: flex; + align-items: center; + gap: 10px; + font-size: 0.82rem; + color: var(--text-primary); +} +.first-run__check { + flex: none; + width: 16px; + height: 16px; + border-radius: 50%; + border: 1.5px solid var(--provider-icon-ring, rgba(255, 255, 255, 0.25)); + position: relative; +} +.first-run__step--done .first-run__check { + background: var(--accent); + border-color: var(--accent); +} +.first-run__step--done .first-run__check::after { + content: ""; + position: absolute; + left: 5px; + top: 2px; + width: 4px; + height: 8px; + border: solid #fff; + border-width: 0 1.5px 1.5px 0; + transform: rotate(45deg); +} +.first-run__step--done .first-run__label { + color: var(--provider-row-text-secondary); + text-decoration: line-through; +} +.first-run__label { + flex: 1; +} +.first-run__action { + appearance: none; + flex: none; + background: var(--accent); + color: #fff; + border: none; + border-radius: 6px; + padding: 4px 10px; + font: inherit; + font-size: 0.76rem; + cursor: pointer; +} +.first-run__action:hover { + opacity: 0.9; +} diff --git a/apps/desktop-tauri/src/surfaces/PopOutPanel.tsx b/apps/desktop-tauri/src/surfaces/PopOutPanel.tsx index 26b054a0..a417e31d 100644 --- a/apps/desktop-tauri/src/surfaces/PopOutPanel.tsx +++ b/apps/desktop-tauri/src/surfaces/PopOutPanel.tsx @@ -13,6 +13,11 @@ import ChartsPanel from "./ChartsPanel"; import AccountsPanel from "./AccountsPanel"; import ProviderDetailView from "./ProviderDetailView"; import { MenuEmpty } from "../components/MenuSurface"; +import { + FirstRunChecklist, + firstRunDismissed, + dismissFirstRun, +} from "../components/FirstRunChecklist"; import UpdateBanner from "../components/UpdateBanner"; import DetectedAccountsCard from "../components/DetectedAccountsCard"; import { orderProviderSnapshots } from "../lib/providerOrder"; @@ -191,6 +196,13 @@ export default function PopOutPanel({ const openSettings = useCallback(() => { openSettingsWindow("general"); }, []); + const [firstRunHidden, setFirstRunHidden] = useState(() => + firstRunDismissed(), + ); + const handleDismissFirstRun = useCallback(() => { + dismissFirstRun(); + setFirstRunHidden(true); + }, []); const quitApp = useCallback(() => { void quitApplication(); }, []); @@ -352,10 +364,23 @@ export default function PopOutPanel({ onManage={() => openSettingsWindow("providers")} /> {sorted.length === 0 ? ( - + isRefreshing && !hasCachedData ? ( + + ) : firstRunHidden ? ( + + ) : ( + openSettingsWindow("providers")} + onOpenDisplay={() => openSettingsWindow("menu")} + onDismiss={handleDismissFirstRun} + t={t} + /> + ) ) : (
+ + {detail.lastError && ( ({ + openExternalUrl: vi.fn(() => Promise.resolve()), +})); + +const t = (k: string) => k; + +function provider(id: string): ProviderDetail { + return { id, displayName: id } as unknown as ProviderDetail; +} + +describe("DataSourceSection", () => { + it("shows provider-specific provenance for a first-class provider", () => { + render(); + expect(screen.getByText("DataSourceClaude")).toBeInTheDocument(); + expect(screen.getByText("DataSourcePrivacyNote")).toBeInTheDocument(); + expect(screen.getByText("DataSourceLearnMore")).toBeInTheDocument(); + }); + + it("falls back to the generic line for providers without dedicated copy", () => { + render(); + expect(screen.getByText("DataSourceGeneric")).toBeInTheDocument(); + }); +}); diff --git a/apps/desktop-tauri/src/surfaces/settings/providers/sections/DataSourceSection.tsx b/apps/desktop-tauri/src/surfaces/settings/providers/sections/DataSourceSection.tsx new file mode 100644 index 00000000..e4df7de7 --- /dev/null +++ b/apps/desktop-tauri/src/surfaces/settings/providers/sections/DataSourceSection.tsx @@ -0,0 +1,41 @@ +import type { ProviderDetail } from "../../../../types/bridge"; +import type { LocaleKey } from "../../../../i18n/keys"; +import { openExternalUrl } from "../../../../lib/tauri"; +import { providerProvenanceKey } from "../../../../lib/providerProvenance"; + +const DATA_SOURCES_DOC_URL = + "https://github.com/tsouth89/ceiling/blob/main/docs/DATA_SOURCES.md"; + +interface Props { + provider: ProviderDetail; + t: (key: LocaleKey) => string; +} + +/** + * "How Ceiling gets this data" — a short, provider-specific provenance line + * plus a precise privacy note (SOU-179). The copy is verified against the real + * fetch paths documented in `docs/DATA_SOURCES.md`; providers without dedicated + * copy fall back to a precise generic statement. Storage/DPAPI detail lives in + * the separate CredentialStorageSection, so this block stays about *where the + * data comes from* and *what is (and isn't) sent over the network*. + */ +export function DataSourceSection({ provider, t }: Props) { + return ( +
+

{t("DataSourceSectionTitle")}

+

+ {t(providerProvenanceKey(provider.id))} +

+

+ {t("DataSourcePrivacyNote")} +

+ +
+ ); +} diff --git a/docs/DATA_SOURCES.md b/docs/DATA_SOURCES.md new file mode 100644 index 00000000..000905c8 --- /dev/null +++ b/docs/DATA_SOURCES.md @@ -0,0 +1,48 @@ +# How Ceiling gets your data + +Ceiling is a local-first Windows app. It reads your AI usage from sources that +already live on your PC, or by calling each provider's own usage endpoint. +**Your provider credentials and usage data stay on this PC. Ceiling does not +send them to any Ceiling-operated server.** Network requests go only to the +provider you enabled (and, for Wayfinder, to the local gateway URL you +configure yourself). + +This document is the maintained reference behind the "How Ceiling gets this +data" panel in a provider's Settings detail view. + +## What Ceiling stores locally + +| Data | Where | Protection | +|------|-------|------------| +| API keys | `%APPDATA%\Ceiling` (ApiKeys store) | Windows DPAPI, user-only file ACL | +| Manual cookies | `%APPDATA%\Ceiling` (ManualCookies store) | Windows DPAPI, user-only file ACL | +| OAuth token accounts | `%APPDATA%\Ceiling` (token store) | Windows DPAPI, user-only file ACL | +| Settings | `%APPDATA%\Ceiling\settings.json` | Local file | + +Ceiling also *reads* credentials that other tools already wrote (for example +`~/.codex/auth.json`, the Cursor IDE session database, or your browser's cookie +database). It does not copy those into its own storage unless you explicitly +import them. See [COOKIES.md](COOKIES.md) for browser cookie extraction and +App-Bound Encryption details. + +## Per-provider sources + +| Provider | How Ceiling reads usage | Talks to | +|----------|-------------------------|----------| +| **Claude** | Your signed-in Claude Code / Claude Desktop session, an OAuth token, or claude.ai cookies you provide; plus local `~/.claude` logs for cost and tokens | api.anthropic.com, claude.ai | +| **Codex / OpenAI** | The OAuth token from your Codex CLI (`~/.codex/auth.json`) plus local `~/.codex/sessions` logs | chatgpt.com | +| **Cursor** | Your signed-in Cursor IDE session (`state.vscdb`), or cursor.com cookies you provide | cursor.com | +| **GitHub Copilot** | Your GitHub CLI / Git Credential Manager token (or a device-flow sign-in) | api.github.com, or your GitHub Enterprise host | +| **Gemini** | The OAuth token from the Gemini CLI (`~/.gemini/oauth_creds.json`) | Google Code Assist and OAuth endpoints | +| Other providers | A local session or token on this PC, or the provider's own usage endpoint | The provider's own domain only | + +## Network policy + +- The Ceiling desktop app makes no analytics or telemetry calls to + Ceiling-operated servers. +- Every usage request targets the provider's own domain listed above. +- The only non-provider destination is **Wayfinder**, which calls the local + gateway URL you configure yourself. + +If you enable a provider and don't see it described here or in the in-app panel, +that's a bug. Please open an issue. diff --git a/rust/src/locale.rs b/rust/src/locale.rs index ba21a963..a322054a 100644 --- a/rust/src/locale.rs +++ b/rust/src/locale.rs @@ -854,6 +854,26 @@ locale_keys! { TrayPaceBadgeBurning, TrayResetsInLabel, TrayResetsDueNow, + + // Provider data-source / privacy explainer (SOU-179) + DataSourceSectionTitle, + DataSourcePrivacyNote, + DataSourceLearnMore, + DataSourceClaude, + DataSourceCodex, + DataSourceCursor, + DataSourceCopilot, + DataSourceGemini, + DataSourceGeneric, + + // First-run checklist (SOU-157) + FirstRunTitle, + FirstRunStepEnable, + FirstRunStepAuth, + FirstRunStepFloatbar, + FirstRunOpenProviders, + FirstRunOpenDisplay, + FirstRunDismiss, } #[cfg(test)] diff --git a/rust/src/locale/en-US.ftl b/rust/src/locale/en-US.ftl index ea1f5814..ef2bf45c 100644 --- a/rust/src/locale/en-US.ftl +++ b/rust/src/locale/en-US.ftl @@ -591,3 +591,23 @@ TrayPaceBadgeRacing = Racing TrayPaceBadgeBurning = Burning TrayResetsInLabel = Resets in { "{}" } TrayResetsDueNow = Resetting… + +# Provider data-source / privacy explainer (SOU-179) +DataSourceSectionTitle = How Ceiling gets this data +DataSourcePrivacyNote = Ceiling reads your usage from local app sessions and logs on this PC, or by calling the provider's own usage endpoint. Your credentials and usage data stay on this PC. They are never sent to Ceiling-operated servers, only to the provider itself. +DataSourceLearnMore = Learn how Ceiling gets each provider's data +DataSourceClaude = Reads usage from your signed-in Claude Code or Claude Desktop session, an OAuth token, or claude.ai cookies you provide. Talks only to api.anthropic.com and claude.ai. +DataSourceCodex = Uses the OAuth token from your Codex CLI (~/.codex) plus local session logs on this PC. Talks only to chatgpt.com. +DataSourceCursor = Uses your signed-in Cursor IDE session, or cursor.com cookies you provide. Talks only to cursor.com. +DataSourceCopilot = Uses your GitHub CLI or Git Credential Manager token. Talks only to api.github.com, or your configured GitHub Enterprise host. +DataSourceGemini = Reads the OAuth token from the Gemini CLI (~/.gemini). Talks only to Google's Code Assist and OAuth endpoints. +DataSourceGeneric = Reads this provider's usage from a local session or token on this PC, or by calling the provider's own usage endpoint. Your credentials stay on this PC and are only sent to the provider. + +# First-run checklist (SOU-157) +FirstRunTitle = Finish setting up Ceiling +FirstRunStepEnable = Enable the providers you use +FirstRunStepAuth = Connect or confirm your sign-in +FirstRunStepFloatbar = Turn on the floating bar or taskbar glance +FirstRunOpenProviders = Open provider settings +FirstRunOpenDisplay = Open display settings +FirstRunDismiss = Dismiss From 9ac25562b25b0303b1e5840513d164c2e8e0a7f0 Mon Sep 17 00:00:00 2001 From: tsouth89 Date: Thu, 16 Jul 2026 21:26:11 -0400 Subject: [PATCH 2/4] Localize the new onboarding + data-source strings (fix Rust/shared CI) The shared-crate test `test_all_locale_keys_have_all_languages` requires every LocaleKey to resolve non-empty in all six locales, not just en-US. Add the 16 new SOU-157/SOU-179 keys to es-MX, zh-CN, zh-TW, ja-JP, and ko-KR. The privacy-sensitive strings (DataSourcePrivacyNote and per-provider lines) are translated faithfully, keeping the exact claim: data stays on this PC and is only sent to the provider, never to Ceiling-operated servers. These new translations are AI-generated and should get a native-speaker pass before the v1 tag. --- rust/src/locale/es-MX.ftl | 20 ++++++++++++++++++++ rust/src/locale/ja-JP.ftl | 20 ++++++++++++++++++++ rust/src/locale/ko-KR.ftl | 20 ++++++++++++++++++++ rust/src/locale/zh-CN.ftl | 20 ++++++++++++++++++++ rust/src/locale/zh-TW.ftl | 20 ++++++++++++++++++++ 5 files changed, 100 insertions(+) diff --git a/rust/src/locale/es-MX.ftl b/rust/src/locale/es-MX.ftl index a8236ea4..bfa02d53 100644 --- a/rust/src/locale/es-MX.ftl +++ b/rust/src/locale/es-MX.ftl @@ -591,3 +591,23 @@ PredictivePaceWarningTitle = Aviso de ritmo de { "{}" } ({ "{}" }) PredictivePaceWarningBody = La cuota puede agotarse en { "{}" } ShowResetWhenExhausted = Mostrar reinicio al agotar la cuota ShowResetWhenExhaustedHelper = Reemplaza el porcentaje agotado con la cuenta regresiva del reinicio + +# Provider data-source / privacy explainer (SOU-179) +DataSourceSectionTitle = Cómo obtiene Ceiling estos datos +DataSourcePrivacyNote = Ceiling lee tu uso desde sesiones y registros de apps locales en esta PC, o llamando al propio endpoint de uso del proveedor. Tus credenciales y datos de uso permanecen en esta PC. Nunca se envían a servidores operados por Ceiling, solo al proveedor. +DataSourceLearnMore = Descubre cómo Ceiling obtiene los datos de cada proveedor +DataSourceClaude = Lee el uso desde tu sesión de Claude Code o Claude Desktop, un token OAuth o cookies de claude.ai que proporciones. Solo se comunica con api.anthropic.com y claude.ai. +DataSourceCodex = Usa el token OAuth de tu CLI de Codex (~/.codex) y los registros de sesión locales en esta PC. Solo se comunica con chatgpt.com. +DataSourceCursor = Usa tu sesión del IDE de Cursor, o cookies de cursor.com que proporciones. Solo se comunica con cursor.com. +DataSourceCopilot = Usa el token de tu CLI de GitHub o del Administrador de credenciales de Git. Solo se comunica con api.github.com, o con tu host de GitHub Enterprise configurado. +DataSourceGemini = Lee el token OAuth de la CLI de Gemini (~/.gemini). Solo se comunica con los endpoints de Code Assist y OAuth de Google. +DataSourceGeneric = Lee el uso de este proveedor desde una sesión o token local en esta PC, o llamando al propio endpoint de uso del proveedor. Tus credenciales permanecen en esta PC y solo se envían al proveedor. + +# First-run checklist (SOU-157) +FirstRunTitle = Termina de configurar Ceiling +FirstRunStepEnable = Habilita los proveedores que usas +FirstRunStepAuth = Conecta o confirma tu inicio de sesión +FirstRunStepFloatbar = Activa la barra flotante o el vistazo en la barra de tareas +FirstRunOpenProviders = Abrir configuración de proveedores +FirstRunOpenDisplay = Abrir configuración de pantalla +FirstRunDismiss = Descartar diff --git a/rust/src/locale/ja-JP.ftl b/rust/src/locale/ja-JP.ftl index 50a64105..0b070263 100644 --- a/rust/src/locale/ja-JP.ftl +++ b/rust/src/locale/ja-JP.ftl @@ -591,3 +591,23 @@ PredictivePaceWarningTitle = { "{}" } ? { "{}" } ????? PredictivePaceWarningBody = ?? { "{}" } ??????????????? ShowResetWhenExhausted = ??????????????? ShowResetWhenExhaustedHelper = ?????????????????????????????? + +# Provider data-source / privacy explainer (SOU-179) +DataSourceSectionTitle = Ceiling がこのデータを取得する方法 +DataSourcePrivacyNote = Ceiling は、この PC 上のローカルアプリのセッションやログから、またはプロバイダー自身の使用状況エンドポイントを呼び出して使用状況を読み取ります。認証情報と使用状況データはこの PC に留まり、Ceiling が運用するサーバーに送信されることは決してなく、プロバイダー本体にのみ送信されます。 +DataSourceLearnMore = Ceiling が各プロバイダーのデータを取得する方法を見る +DataSourceClaude = ログイン済みの Claude Code または Claude Desktop のセッション、OAuth トークン、または提供された claude.ai の Cookie から使用状況を読み取ります。api.anthropic.com と claude.ai のみと通信します。 +DataSourceCodex = Codex CLI (~/.codex) の OAuth トークンと、この PC 上のローカルセッションログを使用します。chatgpt.com のみと通信します。 +DataSourceCursor = ログイン済みの Cursor IDE セッション、または提供された cursor.com の Cookie を使用します。cursor.com のみと通信します。 +DataSourceCopilot = GitHub CLI または Git 資格情報マネージャーのトークンを使用します。api.github.com、または設定した GitHub Enterprise ホストのみと通信します。 +DataSourceGemini = Gemini CLI (~/.gemini) から OAuth トークンを読み取ります。Google の Code Assist と OAuth のエンドポイントのみと通信します。 +DataSourceGeneric = この PC 上のローカルセッションまたはトークンから、あるいはプロバイダー自身の使用状況エンドポイントを呼び出して、このプロバイダーの使用状況を読み取ります。認証情報はこの PC に留まり、プロバイダーにのみ送信されます。 + +# First-run checklist (SOU-157) +FirstRunTitle = Ceiling のセットアップを完了する +FirstRunStepEnable = 使用するプロバイダーを有効にする +FirstRunStepAuth = サインインを接続または確認する +FirstRunStepFloatbar = フローティングバーまたはタスクバーグランスをオンにする +FirstRunOpenProviders = プロバイダー設定を開く +FirstRunOpenDisplay = 表示設定を開く +FirstRunDismiss = 閉じる diff --git a/rust/src/locale/ko-KR.ftl b/rust/src/locale/ko-KR.ftl index 6fa847c1..7b545406 100644 --- a/rust/src/locale/ko-KR.ftl +++ b/rust/src/locale/ko-KR.ftl @@ -591,3 +591,23 @@ PredictivePaceWarningTitle = { "{}" } { "{}" } ?? ?? ?? PredictivePaceWarningBody = { "{}" } ? ???? ??? ? ???? ShowResetWhenExhausted = ?? ? ??? ?? ?? ShowResetWhenExhaustedHelper = ??? ??? ??? ??? ??????? ???? + +# Provider data-source / privacy explainer (SOU-179) +DataSourceSectionTitle = Ceiling이 이 데이터를 가져오는 방법 +DataSourcePrivacyNote = Ceiling은 이 PC의 로컬 앱 세션과 로그에서, 또는 제공업체 자체의 사용량 엔드포인트를 호출하여 사용량을 읽습니다. 자격 증명과 사용량 데이터는 이 PC에 남아 있으며, Ceiling이 운영하는 서버로 전송되지 않고 제공업체에만 전송됩니다. +DataSourceLearnMore = Ceiling이 각 제공업체의 데이터를 가져오는 방법 알아보기 +DataSourceClaude = 로그인된 Claude Code 또는 Claude Desktop 세션, OAuth 토큰, 또는 사용자가 제공한 claude.ai 쿠키에서 사용량을 읽습니다. api.anthropic.com과 claude.ai에만 통신합니다. +DataSourceCodex = Codex CLI(~/.codex)의 OAuth 토큰과 이 PC의 로컬 세션 로그를 사용합니다. chatgpt.com에만 통신합니다. +DataSourceCursor = 로그인된 Cursor IDE 세션 또는 사용자가 제공한 cursor.com 쿠키를 사용합니다. cursor.com에만 통신합니다. +DataSourceCopilot = GitHub CLI 또는 Git 자격 증명 관리자 토큰을 사용합니다. api.github.com 또는 구성한 GitHub Enterprise 호스트에만 통신합니다. +DataSourceGemini = Gemini CLI(~/.gemini)에서 OAuth 토큰을 읽습니다. Google의 Code Assist 및 OAuth 엔드포인트에만 통신합니다. +DataSourceGeneric = 이 PC의 로컬 세션이나 토큰에서, 또는 제공업체 자체의 사용량 엔드포인트를 호출하여 이 제공업체의 사용량을 읽습니다. 자격 증명은 이 PC에 남아 있으며 제공업체에만 전송됩니다. + +# First-run checklist (SOU-157) +FirstRunTitle = Ceiling 설정 완료하기 +FirstRunStepEnable = 사용하는 제공업체 활성화 +FirstRunStepAuth = 로그인 연결 또는 확인 +FirstRunStepFloatbar = 플로팅 바 또는 작업 표시줄 미리 보기 켜기 +FirstRunOpenProviders = 제공업체 설정 열기 +FirstRunOpenDisplay = 디스플레이 설정 열기 +FirstRunDismiss = 닫기 diff --git a/rust/src/locale/zh-CN.ftl b/rust/src/locale/zh-CN.ftl index 86f1844d..c086a8fd 100644 --- a/rust/src/locale/zh-CN.ftl +++ b/rust/src/locale/zh-CN.ftl @@ -591,3 +591,23 @@ PredictivePaceWarningTitle = { "{}" } { "{}" }?????? PredictivePaceWarningBody = ????? { "{}" } ??? ShowResetWhenExhausted = ????????? ShowResetWhenExhaustedHelper = ????????????????? + +# Provider data-source / privacy explainer (SOU-179) +DataSourceSectionTitle = Ceiling 如何获取这些数据 +DataSourcePrivacyNote = Ceiling 从本机的本地应用会话和日志读取你的用量,或调用服务商自己的用量接口。你的凭据和用量数据保留在本机,绝不会发送到 Ceiling 运营的服务器,只会发送给服务商本身。 +DataSourceLearnMore = 了解 Ceiling 如何获取各服务商的数据 +DataSourceClaude = 从你已登录的 Claude Code 或 Claude Desktop 会话、OAuth 令牌,或你提供的 claude.ai Cookie 读取用量。仅与 api.anthropic.com 和 claude.ai 通信。 +DataSourceCodex = 使用你的 Codex CLI (~/.codex) 中的 OAuth 令牌以及本机的本地会话日志。仅与 chatgpt.com 通信。 +DataSourceCursor = 使用你已登录的 Cursor IDE 会话,或你提供的 cursor.com Cookie。仅与 cursor.com 通信。 +DataSourceCopilot = 使用你的 GitHub CLI 或 Git 凭据管理器令牌。仅与 api.github.com 或你配置的 GitHub Enterprise 主机通信。 +DataSourceGemini = 从 Gemini CLI (~/.gemini) 读取 OAuth 令牌。仅与 Google 的 Code Assist 和 OAuth 接口通信。 +DataSourceGeneric = 从本机的本地会话或令牌读取该服务商的用量,或调用服务商自己的用量接口。你的凭据保留在本机,只会发送给服务商。 + +# First-run checklist (SOU-157) +FirstRunTitle = 完成 Ceiling 设置 +FirstRunStepEnable = 启用你使用的服务商 +FirstRunStepAuth = 连接或确认你的登录 +FirstRunStepFloatbar = 打开悬浮条或任务栏概览 +FirstRunOpenProviders = 打开服务商设置 +FirstRunOpenDisplay = 打开显示设置 +FirstRunDismiss = 忽略 diff --git a/rust/src/locale/zh-TW.ftl b/rust/src/locale/zh-TW.ftl index f12c6ad8..915531b6 100644 --- a/rust/src/locale/zh-TW.ftl +++ b/rust/src/locale/zh-TW.ftl @@ -591,3 +591,23 @@ PredictivePaceWarningTitle = { "{}" } { "{}" }?????? PredictivePaceWarningBody = ????? { "{}" } ??? ShowResetWhenExhausted = ????????? ShowResetWhenExhaustedHelper = ???????????????? + +# Provider data-source / privacy explainer (SOU-179) +DataSourceSectionTitle = Ceiling 如何取得這些資料 +DataSourcePrivacyNote = Ceiling 會從本機的本機應用程式工作階段和記錄讀取你的用量,或呼叫服務商自己的用量端點。你的憑證和用量資料會保留在本機,絕不會傳送到 Ceiling 營運的伺服器,只會傳送給服務商本身。 +DataSourceLearnMore = 了解 Ceiling 如何取得各服務商的資料 +DataSourceClaude = 從你已登入的 Claude Code 或 Claude Desktop 工作階段、OAuth 權杖,或你提供的 claude.ai Cookie 讀取用量。僅與 api.anthropic.com 和 claude.ai 通訊。 +DataSourceCodex = 使用你的 Codex CLI (~/.codex) 中的 OAuth 權杖以及本機的本機工作階段記錄。僅與 chatgpt.com 通訊。 +DataSourceCursor = 使用你已登入的 Cursor IDE 工作階段,或你提供的 cursor.com Cookie。僅與 cursor.com 通訊。 +DataSourceCopilot = 使用你的 GitHub CLI 或 Git 認證管理員權杖。僅與 api.github.com 或你設定的 GitHub Enterprise 主機通訊。 +DataSourceGemini = 從 Gemini CLI (~/.gemini) 讀取 OAuth 權杖。僅與 Google 的 Code Assist 和 OAuth 端點通訊。 +DataSourceGeneric = 從本機的本機工作階段或權杖讀取該服務商的用量,或呼叫服務商自己的用量端點。你的憑證會保留在本機,只會傳送給服務商。 + +# First-run checklist (SOU-157) +FirstRunTitle = 完成 Ceiling 設定 +FirstRunStepEnable = 啟用你使用的服務商 +FirstRunStepAuth = 連接或確認你的登入 +FirstRunStepFloatbar = 開啟浮動列或工作列一覽 +FirstRunOpenProviders = 開啟服務商設定 +FirstRunOpenDisplay = 開啟顯示設定 +FirstRunDismiss = 略過 From e19d51d0feb65b7f6153840731db81d1b2a3cedd Mon Sep 17 00:00:00 2001 From: tsouth89 Date: Thu, 16 Jul 2026 21:43:05 -0400 Subject: [PATCH 3/4] Drop fabricated translations; ship English in every locale (English-only) Ceiling only supports English. The all-locales test requires each key in all 6 .ftl files, so the new SOU-157/SOU-179 keys carry the English text in every file rather than machine-translated copy. --- rust/src/locale/es-MX.ftl | 37 ++++++++++++++++++------------------- rust/src/locale/ja-JP.ftl | 37 ++++++++++++++++++------------------- rust/src/locale/ko-KR.ftl | 37 ++++++++++++++++++------------------- rust/src/locale/zh-CN.ftl | 37 ++++++++++++++++++------------------- rust/src/locale/zh-TW.ftl | 37 ++++++++++++++++++------------------- 5 files changed, 90 insertions(+), 95 deletions(-) diff --git a/rust/src/locale/es-MX.ftl b/rust/src/locale/es-MX.ftl index bfa02d53..bc8ddf5d 100644 --- a/rust/src/locale/es-MX.ftl +++ b/rust/src/locale/es-MX.ftl @@ -591,23 +591,22 @@ PredictivePaceWarningTitle = Aviso de ritmo de { "{}" } ({ "{}" }) PredictivePaceWarningBody = La cuota puede agotarse en { "{}" } ShowResetWhenExhausted = Mostrar reinicio al agotar la cuota ShowResetWhenExhaustedHelper = Reemplaza el porcentaje agotado con la cuenta regresiva del reinicio +# Provider data-source / privacy explainer (SOU-179) — English only +DataSourceSectionTitle = How Ceiling gets this data +DataSourcePrivacyNote = Ceiling reads your usage from local app sessions and logs on this PC, or by calling the provider's own usage endpoint. Your credentials and usage data stay on this PC. They are never sent to Ceiling-operated servers, only to the provider itself. +DataSourceLearnMore = Learn how Ceiling gets each provider's data +DataSourceClaude = Reads usage from your signed-in Claude Code or Claude Desktop session, an OAuth token, or claude.ai cookies you provide. Talks only to api.anthropic.com and claude.ai. +DataSourceCodex = Uses the OAuth token from your Codex CLI (~/.codex) plus local session logs on this PC. Talks only to chatgpt.com. +DataSourceCursor = Uses your signed-in Cursor IDE session, or cursor.com cookies you provide. Talks only to cursor.com. +DataSourceCopilot = Uses your GitHub CLI or Git Credential Manager token. Talks only to api.github.com, or your configured GitHub Enterprise host. +DataSourceGemini = Reads the OAuth token from the Gemini CLI (~/.gemini). Talks only to Google's Code Assist and OAuth endpoints. +DataSourceGeneric = Reads this provider's usage from a local session or token on this PC, or by calling the provider's own usage endpoint. Your credentials stay on this PC and are only sent to the provider. -# Provider data-source / privacy explainer (SOU-179) -DataSourceSectionTitle = Cómo obtiene Ceiling estos datos -DataSourcePrivacyNote = Ceiling lee tu uso desde sesiones y registros de apps locales en esta PC, o llamando al propio endpoint de uso del proveedor. Tus credenciales y datos de uso permanecen en esta PC. Nunca se envían a servidores operados por Ceiling, solo al proveedor. -DataSourceLearnMore = Descubre cómo Ceiling obtiene los datos de cada proveedor -DataSourceClaude = Lee el uso desde tu sesión de Claude Code o Claude Desktop, un token OAuth o cookies de claude.ai que proporciones. Solo se comunica con api.anthropic.com y claude.ai. -DataSourceCodex = Usa el token OAuth de tu CLI de Codex (~/.codex) y los registros de sesión locales en esta PC. Solo se comunica con chatgpt.com. -DataSourceCursor = Usa tu sesión del IDE de Cursor, o cookies de cursor.com que proporciones. Solo se comunica con cursor.com. -DataSourceCopilot = Usa el token de tu CLI de GitHub o del Administrador de credenciales de Git. Solo se comunica con api.github.com, o con tu host de GitHub Enterprise configurado. -DataSourceGemini = Lee el token OAuth de la CLI de Gemini (~/.gemini). Solo se comunica con los endpoints de Code Assist y OAuth de Google. -DataSourceGeneric = Lee el uso de este proveedor desde una sesión o token local en esta PC, o llamando al propio endpoint de uso del proveedor. Tus credenciales permanecen en esta PC y solo se envían al proveedor. - -# First-run checklist (SOU-157) -FirstRunTitle = Termina de configurar Ceiling -FirstRunStepEnable = Habilita los proveedores que usas -FirstRunStepAuth = Conecta o confirma tu inicio de sesión -FirstRunStepFloatbar = Activa la barra flotante o el vistazo en la barra de tareas -FirstRunOpenProviders = Abrir configuración de proveedores -FirstRunOpenDisplay = Abrir configuración de pantalla -FirstRunDismiss = Descartar +# First-run checklist (SOU-157) — English only +FirstRunTitle = Finish setting up Ceiling +FirstRunStepEnable = Enable the providers you use +FirstRunStepAuth = Connect or confirm your sign-in +FirstRunStepFloatbar = Turn on the floating bar or taskbar glance +FirstRunOpenProviders = Open provider settings +FirstRunOpenDisplay = Open display settings +FirstRunDismiss = Dismiss diff --git a/rust/src/locale/ja-JP.ftl b/rust/src/locale/ja-JP.ftl index 0b070263..daddd3b2 100644 --- a/rust/src/locale/ja-JP.ftl +++ b/rust/src/locale/ja-JP.ftl @@ -591,23 +591,22 @@ PredictivePaceWarningTitle = { "{}" } ? { "{}" } ????? PredictivePaceWarningBody = ?? { "{}" } ??????????????? ShowResetWhenExhausted = ??????????????? ShowResetWhenExhaustedHelper = ?????????????????????????????? +# Provider data-source / privacy explainer (SOU-179) — English only +DataSourceSectionTitle = How Ceiling gets this data +DataSourcePrivacyNote = Ceiling reads your usage from local app sessions and logs on this PC, or by calling the provider's own usage endpoint. Your credentials and usage data stay on this PC. They are never sent to Ceiling-operated servers, only to the provider itself. +DataSourceLearnMore = Learn how Ceiling gets each provider's data +DataSourceClaude = Reads usage from your signed-in Claude Code or Claude Desktop session, an OAuth token, or claude.ai cookies you provide. Talks only to api.anthropic.com and claude.ai. +DataSourceCodex = Uses the OAuth token from your Codex CLI (~/.codex) plus local session logs on this PC. Talks only to chatgpt.com. +DataSourceCursor = Uses your signed-in Cursor IDE session, or cursor.com cookies you provide. Talks only to cursor.com. +DataSourceCopilot = Uses your GitHub CLI or Git Credential Manager token. Talks only to api.github.com, or your configured GitHub Enterprise host. +DataSourceGemini = Reads the OAuth token from the Gemini CLI (~/.gemini). Talks only to Google's Code Assist and OAuth endpoints. +DataSourceGeneric = Reads this provider's usage from a local session or token on this PC, or by calling the provider's own usage endpoint. Your credentials stay on this PC and are only sent to the provider. -# Provider data-source / privacy explainer (SOU-179) -DataSourceSectionTitle = Ceiling がこのデータを取得する方法 -DataSourcePrivacyNote = Ceiling は、この PC 上のローカルアプリのセッションやログから、またはプロバイダー自身の使用状況エンドポイントを呼び出して使用状況を読み取ります。認証情報と使用状況データはこの PC に留まり、Ceiling が運用するサーバーに送信されることは決してなく、プロバイダー本体にのみ送信されます。 -DataSourceLearnMore = Ceiling が各プロバイダーのデータを取得する方法を見る -DataSourceClaude = ログイン済みの Claude Code または Claude Desktop のセッション、OAuth トークン、または提供された claude.ai の Cookie から使用状況を読み取ります。api.anthropic.com と claude.ai のみと通信します。 -DataSourceCodex = Codex CLI (~/.codex) の OAuth トークンと、この PC 上のローカルセッションログを使用します。chatgpt.com のみと通信します。 -DataSourceCursor = ログイン済みの Cursor IDE セッション、または提供された cursor.com の Cookie を使用します。cursor.com のみと通信します。 -DataSourceCopilot = GitHub CLI または Git 資格情報マネージャーのトークンを使用します。api.github.com、または設定した GitHub Enterprise ホストのみと通信します。 -DataSourceGemini = Gemini CLI (~/.gemini) から OAuth トークンを読み取ります。Google の Code Assist と OAuth のエンドポイントのみと通信します。 -DataSourceGeneric = この PC 上のローカルセッションまたはトークンから、あるいはプロバイダー自身の使用状況エンドポイントを呼び出して、このプロバイダーの使用状況を読み取ります。認証情報はこの PC に留まり、プロバイダーにのみ送信されます。 - -# First-run checklist (SOU-157) -FirstRunTitle = Ceiling のセットアップを完了する -FirstRunStepEnable = 使用するプロバイダーを有効にする -FirstRunStepAuth = サインインを接続または確認する -FirstRunStepFloatbar = フローティングバーまたはタスクバーグランスをオンにする -FirstRunOpenProviders = プロバイダー設定を開く -FirstRunOpenDisplay = 表示設定を開く -FirstRunDismiss = 閉じる +# First-run checklist (SOU-157) — English only +FirstRunTitle = Finish setting up Ceiling +FirstRunStepEnable = Enable the providers you use +FirstRunStepAuth = Connect or confirm your sign-in +FirstRunStepFloatbar = Turn on the floating bar or taskbar glance +FirstRunOpenProviders = Open provider settings +FirstRunOpenDisplay = Open display settings +FirstRunDismiss = Dismiss diff --git a/rust/src/locale/ko-KR.ftl b/rust/src/locale/ko-KR.ftl index 7b545406..f2bcdbb3 100644 --- a/rust/src/locale/ko-KR.ftl +++ b/rust/src/locale/ko-KR.ftl @@ -591,23 +591,22 @@ PredictivePaceWarningTitle = { "{}" } { "{}" } ?? ?? ?? PredictivePaceWarningBody = { "{}" } ? ???? ??? ? ???? ShowResetWhenExhausted = ?? ? ??? ?? ?? ShowResetWhenExhaustedHelper = ??? ??? ??? ??? ??????? ???? +# Provider data-source / privacy explainer (SOU-179) — English only +DataSourceSectionTitle = How Ceiling gets this data +DataSourcePrivacyNote = Ceiling reads your usage from local app sessions and logs on this PC, or by calling the provider's own usage endpoint. Your credentials and usage data stay on this PC. They are never sent to Ceiling-operated servers, only to the provider itself. +DataSourceLearnMore = Learn how Ceiling gets each provider's data +DataSourceClaude = Reads usage from your signed-in Claude Code or Claude Desktop session, an OAuth token, or claude.ai cookies you provide. Talks only to api.anthropic.com and claude.ai. +DataSourceCodex = Uses the OAuth token from your Codex CLI (~/.codex) plus local session logs on this PC. Talks only to chatgpt.com. +DataSourceCursor = Uses your signed-in Cursor IDE session, or cursor.com cookies you provide. Talks only to cursor.com. +DataSourceCopilot = Uses your GitHub CLI or Git Credential Manager token. Talks only to api.github.com, or your configured GitHub Enterprise host. +DataSourceGemini = Reads the OAuth token from the Gemini CLI (~/.gemini). Talks only to Google's Code Assist and OAuth endpoints. +DataSourceGeneric = Reads this provider's usage from a local session or token on this PC, or by calling the provider's own usage endpoint. Your credentials stay on this PC and are only sent to the provider. -# Provider data-source / privacy explainer (SOU-179) -DataSourceSectionTitle = Ceiling이 이 데이터를 가져오는 방법 -DataSourcePrivacyNote = Ceiling은 이 PC의 로컬 앱 세션과 로그에서, 또는 제공업체 자체의 사용량 엔드포인트를 호출하여 사용량을 읽습니다. 자격 증명과 사용량 데이터는 이 PC에 남아 있으며, Ceiling이 운영하는 서버로 전송되지 않고 제공업체에만 전송됩니다. -DataSourceLearnMore = Ceiling이 각 제공업체의 데이터를 가져오는 방법 알아보기 -DataSourceClaude = 로그인된 Claude Code 또는 Claude Desktop 세션, OAuth 토큰, 또는 사용자가 제공한 claude.ai 쿠키에서 사용량을 읽습니다. api.anthropic.com과 claude.ai에만 통신합니다. -DataSourceCodex = Codex CLI(~/.codex)의 OAuth 토큰과 이 PC의 로컬 세션 로그를 사용합니다. chatgpt.com에만 통신합니다. -DataSourceCursor = 로그인된 Cursor IDE 세션 또는 사용자가 제공한 cursor.com 쿠키를 사용합니다. cursor.com에만 통신합니다. -DataSourceCopilot = GitHub CLI 또는 Git 자격 증명 관리자 토큰을 사용합니다. api.github.com 또는 구성한 GitHub Enterprise 호스트에만 통신합니다. -DataSourceGemini = Gemini CLI(~/.gemini)에서 OAuth 토큰을 읽습니다. Google의 Code Assist 및 OAuth 엔드포인트에만 통신합니다. -DataSourceGeneric = 이 PC의 로컬 세션이나 토큰에서, 또는 제공업체 자체의 사용량 엔드포인트를 호출하여 이 제공업체의 사용량을 읽습니다. 자격 증명은 이 PC에 남아 있으며 제공업체에만 전송됩니다. - -# First-run checklist (SOU-157) -FirstRunTitle = Ceiling 설정 완료하기 -FirstRunStepEnable = 사용하는 제공업체 활성화 -FirstRunStepAuth = 로그인 연결 또는 확인 -FirstRunStepFloatbar = 플로팅 바 또는 작업 표시줄 미리 보기 켜기 -FirstRunOpenProviders = 제공업체 설정 열기 -FirstRunOpenDisplay = 디스플레이 설정 열기 -FirstRunDismiss = 닫기 +# First-run checklist (SOU-157) — English only +FirstRunTitle = Finish setting up Ceiling +FirstRunStepEnable = Enable the providers you use +FirstRunStepAuth = Connect or confirm your sign-in +FirstRunStepFloatbar = Turn on the floating bar or taskbar glance +FirstRunOpenProviders = Open provider settings +FirstRunOpenDisplay = Open display settings +FirstRunDismiss = Dismiss diff --git a/rust/src/locale/zh-CN.ftl b/rust/src/locale/zh-CN.ftl index c086a8fd..8fa2e564 100644 --- a/rust/src/locale/zh-CN.ftl +++ b/rust/src/locale/zh-CN.ftl @@ -591,23 +591,22 @@ PredictivePaceWarningTitle = { "{}" } { "{}" }?????? PredictivePaceWarningBody = ????? { "{}" } ??? ShowResetWhenExhausted = ????????? ShowResetWhenExhaustedHelper = ????????????????? +# Provider data-source / privacy explainer (SOU-179) — English only +DataSourceSectionTitle = How Ceiling gets this data +DataSourcePrivacyNote = Ceiling reads your usage from local app sessions and logs on this PC, or by calling the provider's own usage endpoint. Your credentials and usage data stay on this PC. They are never sent to Ceiling-operated servers, only to the provider itself. +DataSourceLearnMore = Learn how Ceiling gets each provider's data +DataSourceClaude = Reads usage from your signed-in Claude Code or Claude Desktop session, an OAuth token, or claude.ai cookies you provide. Talks only to api.anthropic.com and claude.ai. +DataSourceCodex = Uses the OAuth token from your Codex CLI (~/.codex) plus local session logs on this PC. Talks only to chatgpt.com. +DataSourceCursor = Uses your signed-in Cursor IDE session, or cursor.com cookies you provide. Talks only to cursor.com. +DataSourceCopilot = Uses your GitHub CLI or Git Credential Manager token. Talks only to api.github.com, or your configured GitHub Enterprise host. +DataSourceGemini = Reads the OAuth token from the Gemini CLI (~/.gemini). Talks only to Google's Code Assist and OAuth endpoints. +DataSourceGeneric = Reads this provider's usage from a local session or token on this PC, or by calling the provider's own usage endpoint. Your credentials stay on this PC and are only sent to the provider. -# Provider data-source / privacy explainer (SOU-179) -DataSourceSectionTitle = Ceiling 如何获取这些数据 -DataSourcePrivacyNote = Ceiling 从本机的本地应用会话和日志读取你的用量,或调用服务商自己的用量接口。你的凭据和用量数据保留在本机,绝不会发送到 Ceiling 运营的服务器,只会发送给服务商本身。 -DataSourceLearnMore = 了解 Ceiling 如何获取各服务商的数据 -DataSourceClaude = 从你已登录的 Claude Code 或 Claude Desktop 会话、OAuth 令牌,或你提供的 claude.ai Cookie 读取用量。仅与 api.anthropic.com 和 claude.ai 通信。 -DataSourceCodex = 使用你的 Codex CLI (~/.codex) 中的 OAuth 令牌以及本机的本地会话日志。仅与 chatgpt.com 通信。 -DataSourceCursor = 使用你已登录的 Cursor IDE 会话,或你提供的 cursor.com Cookie。仅与 cursor.com 通信。 -DataSourceCopilot = 使用你的 GitHub CLI 或 Git 凭据管理器令牌。仅与 api.github.com 或你配置的 GitHub Enterprise 主机通信。 -DataSourceGemini = 从 Gemini CLI (~/.gemini) 读取 OAuth 令牌。仅与 Google 的 Code Assist 和 OAuth 接口通信。 -DataSourceGeneric = 从本机的本地会话或令牌读取该服务商的用量,或调用服务商自己的用量接口。你的凭据保留在本机,只会发送给服务商。 - -# First-run checklist (SOU-157) -FirstRunTitle = 完成 Ceiling 设置 -FirstRunStepEnable = 启用你使用的服务商 -FirstRunStepAuth = 连接或确认你的登录 -FirstRunStepFloatbar = 打开悬浮条或任务栏概览 -FirstRunOpenProviders = 打开服务商设置 -FirstRunOpenDisplay = 打开显示设置 -FirstRunDismiss = 忽略 +# First-run checklist (SOU-157) — English only +FirstRunTitle = Finish setting up Ceiling +FirstRunStepEnable = Enable the providers you use +FirstRunStepAuth = Connect or confirm your sign-in +FirstRunStepFloatbar = Turn on the floating bar or taskbar glance +FirstRunOpenProviders = Open provider settings +FirstRunOpenDisplay = Open display settings +FirstRunDismiss = Dismiss diff --git a/rust/src/locale/zh-TW.ftl b/rust/src/locale/zh-TW.ftl index 915531b6..14e5e885 100644 --- a/rust/src/locale/zh-TW.ftl +++ b/rust/src/locale/zh-TW.ftl @@ -591,23 +591,22 @@ PredictivePaceWarningTitle = { "{}" } { "{}" }?????? PredictivePaceWarningBody = ????? { "{}" } ??? ShowResetWhenExhausted = ????????? ShowResetWhenExhaustedHelper = ???????????????? +# Provider data-source / privacy explainer (SOU-179) — English only +DataSourceSectionTitle = How Ceiling gets this data +DataSourcePrivacyNote = Ceiling reads your usage from local app sessions and logs on this PC, or by calling the provider's own usage endpoint. Your credentials and usage data stay on this PC. They are never sent to Ceiling-operated servers, only to the provider itself. +DataSourceLearnMore = Learn how Ceiling gets each provider's data +DataSourceClaude = Reads usage from your signed-in Claude Code or Claude Desktop session, an OAuth token, or claude.ai cookies you provide. Talks only to api.anthropic.com and claude.ai. +DataSourceCodex = Uses the OAuth token from your Codex CLI (~/.codex) plus local session logs on this PC. Talks only to chatgpt.com. +DataSourceCursor = Uses your signed-in Cursor IDE session, or cursor.com cookies you provide. Talks only to cursor.com. +DataSourceCopilot = Uses your GitHub CLI or Git Credential Manager token. Talks only to api.github.com, or your configured GitHub Enterprise host. +DataSourceGemini = Reads the OAuth token from the Gemini CLI (~/.gemini). Talks only to Google's Code Assist and OAuth endpoints. +DataSourceGeneric = Reads this provider's usage from a local session or token on this PC, or by calling the provider's own usage endpoint. Your credentials stay on this PC and are only sent to the provider. -# Provider data-source / privacy explainer (SOU-179) -DataSourceSectionTitle = Ceiling 如何取得這些資料 -DataSourcePrivacyNote = Ceiling 會從本機的本機應用程式工作階段和記錄讀取你的用量,或呼叫服務商自己的用量端點。你的憑證和用量資料會保留在本機,絕不會傳送到 Ceiling 營運的伺服器,只會傳送給服務商本身。 -DataSourceLearnMore = 了解 Ceiling 如何取得各服務商的資料 -DataSourceClaude = 從你已登入的 Claude Code 或 Claude Desktop 工作階段、OAuth 權杖,或你提供的 claude.ai Cookie 讀取用量。僅與 api.anthropic.com 和 claude.ai 通訊。 -DataSourceCodex = 使用你的 Codex CLI (~/.codex) 中的 OAuth 權杖以及本機的本機工作階段記錄。僅與 chatgpt.com 通訊。 -DataSourceCursor = 使用你已登入的 Cursor IDE 工作階段,或你提供的 cursor.com Cookie。僅與 cursor.com 通訊。 -DataSourceCopilot = 使用你的 GitHub CLI 或 Git 認證管理員權杖。僅與 api.github.com 或你設定的 GitHub Enterprise 主機通訊。 -DataSourceGemini = 從 Gemini CLI (~/.gemini) 讀取 OAuth 權杖。僅與 Google 的 Code Assist 和 OAuth 端點通訊。 -DataSourceGeneric = 從本機的本機工作階段或權杖讀取該服務商的用量,或呼叫服務商自己的用量端點。你的憑證會保留在本機,只會傳送給服務商。 - -# First-run checklist (SOU-157) -FirstRunTitle = 完成 Ceiling 設定 -FirstRunStepEnable = 啟用你使用的服務商 -FirstRunStepAuth = 連接或確認你的登入 -FirstRunStepFloatbar = 開啟浮動列或工作列一覽 -FirstRunOpenProviders = 開啟服務商設定 -FirstRunOpenDisplay = 開啟顯示設定 -FirstRunDismiss = 略過 +# First-run checklist (SOU-157) — English only +FirstRunTitle = Finish setting up Ceiling +FirstRunStepEnable = Enable the providers you use +FirstRunStepAuth = Connect or confirm your sign-in +FirstRunStepFloatbar = Turn on the floating bar or taskbar glance +FirstRunOpenProviders = Open provider settings +FirstRunOpenDisplay = Open display settings +FirstRunDismiss = Dismiss From 229f1a97dd667f92161c2f9c9785e49bb89d0cf5 Mon Sep 17 00:00:00 2001 From: tsouth89 Date: Thu, 16 Jul 2026 23:21:36 -0400 Subject: [PATCH 4/4] Address CodeRabbit review on #40 - PopOutPanel: derive FirstRunChecklist `hasWorkingAuth` from real provider state (any enabled provider with a non-errored snapshot) instead of hardcoding false, so an authenticated-but-empty dashboard shows the auth step as done. - Privacy copy: separate local storage from network transport and name the Wayfinder local-gateway exception in DataSourcePrivacyNote, DataSourceGeneric (en-US.ftl) and docs/DATA_SOURCES.md. The server-boundary claim (never sent to Ceiling-operated servers) stays. - DataSourceCopilot: mention device-flow sign-in to match DATA_SOURCES.md. --- apps/desktop-tauri/src/surfaces/PopOutPanel.tsx | 7 ++++++- docs/DATA_SOURCES.md | 9 +++++---- rust/src/locale/en-US.ftl | 6 +++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/apps/desktop-tauri/src/surfaces/PopOutPanel.tsx b/apps/desktop-tauri/src/surfaces/PopOutPanel.tsx index a417e31d..1e2717a0 100644 --- a/apps/desktop-tauri/src/surfaces/PopOutPanel.tsx +++ b/apps/desktop-tauri/src/surfaces/PopOutPanel.tsx @@ -371,7 +371,12 @@ export default function PopOutPanel({ ) : ( + settings.enabledProviders.includes( + provider.providerId, + ) && !provider.error, + )} floatbarEnabled={ settings.floatBarEnabled || settings.taskbarWidgetEnabled } diff --git a/docs/DATA_SOURCES.md b/docs/DATA_SOURCES.md index 000905c8..9f71aa1e 100644 --- a/docs/DATA_SOURCES.md +++ b/docs/DATA_SOURCES.md @@ -2,10 +2,11 @@ Ceiling is a local-first Windows app. It reads your AI usage from sources that already live on your PC, or by calling each provider's own usage endpoint. -**Your provider credentials and usage data stay on this PC. Ceiling does not -send them to any Ceiling-operated server.** Network requests go only to the -provider you enabled (and, for Wayfinder, to the local gateway URL you -configure yourself). +**Your provider credentials and usage data are stored only on this PC, and +Ceiling never sends them to a Ceiling-operated server.** Fetching usage does +make network requests, but only to the provider you enabled (and, for Wayfinder, +to the local gateway URL you configure yourself), never to Ceiling's own +servers. This document is the maintained reference behind the "How Ceiling gets this data" panel in a provider's Settings detail view. diff --git a/rust/src/locale/en-US.ftl b/rust/src/locale/en-US.ftl index ef2bf45c..3e9c1db8 100644 --- a/rust/src/locale/en-US.ftl +++ b/rust/src/locale/en-US.ftl @@ -594,14 +594,14 @@ TrayResetsDueNow = Resetting… # Provider data-source / privacy explainer (SOU-179) DataSourceSectionTitle = How Ceiling gets this data -DataSourcePrivacyNote = Ceiling reads your usage from local app sessions and logs on this PC, or by calling the provider's own usage endpoint. Your credentials and usage data stay on this PC. They are never sent to Ceiling-operated servers, only to the provider itself. +DataSourcePrivacyNote = Ceiling reads your usage from local app sessions and logs on this PC, or by calling the provider's own usage endpoint. Your credentials and usage are stored only on this PC and are never sent to Ceiling-operated servers. Usage requests go only to the provider you enabled, or for Wayfinder the local gateway you configure. DataSourceLearnMore = Learn how Ceiling gets each provider's data DataSourceClaude = Reads usage from your signed-in Claude Code or Claude Desktop session, an OAuth token, or claude.ai cookies you provide. Talks only to api.anthropic.com and claude.ai. DataSourceCodex = Uses the OAuth token from your Codex CLI (~/.codex) plus local session logs on this PC. Talks only to chatgpt.com. DataSourceCursor = Uses your signed-in Cursor IDE session, or cursor.com cookies you provide. Talks only to cursor.com. -DataSourceCopilot = Uses your GitHub CLI or Git Credential Manager token. Talks only to api.github.com, or your configured GitHub Enterprise host. +DataSourceCopilot = Uses your GitHub CLI or Git Credential Manager token, or a device-flow sign-in. Talks only to api.github.com, or your configured GitHub Enterprise host. DataSourceGemini = Reads the OAuth token from the Gemini CLI (~/.gemini). Talks only to Google's Code Assist and OAuth endpoints. -DataSourceGeneric = Reads this provider's usage from a local session or token on this PC, or by calling the provider's own usage endpoint. Your credentials stay on this PC and are only sent to the provider. +DataSourceGeneric = Reads this provider's usage from a local session or token on this PC, or by calling the provider's own usage endpoint. Your credentials are stored only on this PC and are never sent to Ceiling-operated servers; usage requests go only to that provider (or, for Wayfinder, your configured gateway). # First-run checklist (SOU-157) FirstRunTitle = Finish setting up Ceiling