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..1e2717a0 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,28 @@ export default function PopOutPanel({ onManage={() => openSettingsWindow("providers")} /> {sorted.length === 0 ? ( - + isRefreshing && !hasCachedData ? ( + + ) : firstRunHidden ? ( + + ) : ( + + settings.enabledProviders.includes( + provider.providerId, + ) && !provider.error, + )} + floatbarEnabled={ + settings.floatBarEnabled || settings.taskbarWidgetEnabled + } + onOpenProviders={() => 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..9f71aa1e --- /dev/null +++ b/docs/DATA_SOURCES.md @@ -0,0 +1,49 @@ +# 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 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. + +## 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..3e9c1db8 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 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, 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 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 +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/es-MX.ftl b/rust/src/locale/es-MX.ftl index a8236ea4..bc8ddf5d 100644 --- a/rust/src/locale/es-MX.ftl +++ b/rust/src/locale/es-MX.ftl @@ -591,3 +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. + +# 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 50a64105..daddd3b2 100644 --- a/rust/src/locale/ja-JP.ftl +++ b/rust/src/locale/ja-JP.ftl @@ -591,3 +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. + +# 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 6fa847c1..f2bcdbb3 100644 --- a/rust/src/locale/ko-KR.ftl +++ b/rust/src/locale/ko-KR.ftl @@ -591,3 +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. + +# 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 86f1844d..8fa2e564 100644 --- a/rust/src/locale/zh-CN.ftl +++ b/rust/src/locale/zh-CN.ftl @@ -591,3 +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. + +# 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 f12c6ad8..14e5e885 100644 --- a/rust/src/locale/zh-TW.ftl +++ b/rust/src/locale/zh-TW.ftl @@ -591,3 +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. + +# 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