From 677bf65436934ccd3a9ffb10ed44e9db3b1b0d20 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 16 Aug 2026 21:38:03 +0800 Subject: [PATCH 1/5] docs: track portable project guidance --- .gitignore | 4 - AGENTS.md | 45 ++ CODEX_HANDOFF.md | 753 ++++++++++++++++++++ DESIGN.md | 262 +++++++ docs/ARCHITECTURE.md | 101 +++ docs/BILINGUAL_LIBRARY.md | 37 + docs/NOTE_AGENT.md | 41 ++ docs/NOTE_AGENT_SAVINGS.md | 88 +++ docs/RELEASE.md | 102 +++ docs/THIRD_PARTY_NOTICES.md | 73 ++ docs/import-pipeline-plan.md | 87 +++ docs/reference-analysis-awesome-llm-apps.md | 73 ++ 12 files changed, 1662 insertions(+), 4 deletions(-) create mode 100644 AGENTS.md create mode 100644 CODEX_HANDOFF.md create mode 100644 DESIGN.md create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/BILINGUAL_LIBRARY.md create mode 100644 docs/NOTE_AGENT.md create mode 100644 docs/NOTE_AGENT_SAVINGS.md create mode 100644 docs/RELEASE.md create mode 100644 docs/THIRD_PARTY_NOTICES.md create mode 100644 docs/import-pipeline-plan.md create mode 100644 docs/reference-analysis-awesome-llm-apps.md diff --git a/.gitignore b/.gitignore index 36977c6..3909f4a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,11 +3,7 @@ dist/ src-tauri/target/ tmp/ references/ -docs/ 设计思路/ -CODEX_HANDOFF.md -AGENTS.md -DESIGN.md .DS_Store Thumbs.db *.log diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e6342e0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,45 @@ +# Coffee Note agent notes + +Coffee Note is a sister product forked from the original longevity app. Its visible +product name is **Coffee Note**. The repository directory uses `Coffee-Note`; +internal package names, storage keys, and identifiers use `coffee-note` or +`CoffeeNote` where spaces are invalid; the reverse-domain bundle identifier is +`app.coffeenote.desktop`. +The copied longevity content is transitional and will be replaced with a clean, +general-purpose note model. The homepage tier list remains the core experience. + +Before making product or design changes, read `CODEX_HANDOFF.md`. It is the +portable project memory for continuing development on another machine. +Also read `DESIGN.md` before changing the desktop UI; it is the desktop design +source of truth and does not apply to the separate public website. + +Project rules: + +- Treat the desktop app and the future public website as separate products sharing + one brand. The current `website/` directory intentionally keeps only the + Windows remote installer reference until the public site is rebuilt. +- The desktop app must use its own product library. Never bind it to + `C:\Life extension` or any developer-specific directory. +- Preserve the bilingual starter library and run `npm run library:check` after + changing it. +- Keep user data local by default. Persist the AI provider configuration, + including API keys, as plaintext JSON only in the current user's app-data + directory; never write it into the repository or knowledge library. +- Preserve the restrained neutral black/gray visual language, large readable + type, and low-chrome desktop UI. Avoid hover tooltips, unnecessary borders, + redundant labels, and web-like decoration inside the desktop app. +- No native `title` attributes anywhere in the desktop app — we are not a + website. Hover tooltips are a web pattern; show information inline or behind + an explicit click and keep `aria-label` for accessibility. One explicit + rule: non-interactive readouts (like the AI composer model ID) are plain + text — no cursor change, no hover effects, no tooltip. +- Enforce the desktop minimum type floor from `DESIGN.md`: no readable UI text + below 13px, no metadata below 12px, 15px left navigation, 14px directory/file + tree. Never ship smaller text to fix a layout; enlarge the component instead. +- Do not commit generated dependencies or build output (`node_modules/`, + `dist/`, `src-tauri/target/`). +- Keep `website/install.ps1` and `website/version.json` usable as the remote + Windows install/update infrastructure. The remaining public website will be + rebuilt later. +- Commit as `codex` (lowercase, the https://github.com/codex identity) in this + repository; local `user.name` and `user.email` are already configured for it. diff --git a/CODEX_HANDOFF.md b/CODEX_HANDOFF.md new file mode 100644 index 0000000..0d3d6ad --- /dev/null +++ b/CODEX_HANDOFF.md @@ -0,0 +1,753 @@ +# Coffee Note — portable Codex project memory + +Updated: 2026-08-15 + +This file preserves the decisions and working context needed to continue the +project on another machine. It contains no API keys, private user parameters, +or temporary deployment credentials. + +## Coffee Note fork status + +- This repository is the Coffee Note sister product forked from the original longevity app. +- Visible product name: **Coffee Note**. +- The repository directory is `Coffee-Note`; internal identifiers use `coffee-note` + or `CoffeeNote` where spaces are invalid, with `app.coffeenote.desktop` as the + reverse-domain bundle identifier. +- Coffee Note is a local-first, general-purpose workspace agent rather than a + fixed note system or a longevity product. The user selects an ordinary directory + and asks the Agent to work in it. Notes are common work, but source code, documents, + data, and other tasks are equally valid. +- Coffee Note uses a clean application identity with no predecessor-brand + compatibility layer: `app.coffeenote.desktop`, `Coffee Note` app-data, + `.coffee-note` knowledge metadata, and `coffee-note:` browser storage keys. +- GitHub release and feedback URLs use the `edison7009/Coffee-Note` repository. +- The homepage editable T1–T5 tier list is the core interaction model because it + makes prioritization and decisions immediately legible. +- Longevity-specific starter content and product copy below are inherited context + awaiting replacement; they are not the Coffee Note product direction. + +## Workspace Agent foundation (2026-08-15) + +- The selected directory is the workspace. It may be a code repository, writing + project, note collection, or any other folder. Never infer a required note + hierarchy and never create `inbox`, `dossiers`, or another category directory + merely because Coffee Note is the visible product name. +- The Agent must accept programming, debugging, writing, research, organization, + and other workspace tasks. It must not refuse work simply because the request is + programming or not note-related. +- General files use `list_workspace`, `read_workspace_file`, + `write_workspace_file`, and `replace_workspace_text`. Code remains code; it must + never be wrapped in a Markdown note or routed through `save_note`. +- `save_note`, semantic Markdown search, T1-T5 priorities, My information, and the + homepage tier list are optional product capabilities, not the filesystem model. + `save_note` defaults to the workspace root and uses a subdirectory only when the + user explicitly asks or the existing workspace makes that destination clear. +- The directory tree must not hide or offer bulk deletion of folders based on old + Coffee Note category names. A folder named `papers`, `templates`, `inbox`, or + similar may be ordinary user project data. +- The current Agent tool surface can edit text files but cannot execute terminal + commands. It must state that honestly and never claim builds or tests ran. Add + shell execution only together with an explicit user approval and safety model. + +## Desktop redesign direction (2026-08-07) + +- **Ambient Home weather (2026-08-09):** The open area to the right of the Home + greeting holds only a compact animated condition image: no city, temperature, + forecast text, provider name, or enclosing card appears on Home. Explicit click + opens a read-only lightweight panel with current conditions and four forecast days. + Its compact header keeps the forecast title and city on one line, with a gear shortcut + directly to Settings > Appearance. All city search, recent-city, one-time device location, + and Open-Meteo attribution controls live there. Weather has no removal action; choosing + another city replaces the current one. Device coordinates are rounded + to 0.1 degrees before saving/requesting, and results are cached locally for 30 + minutes. It never prompts for location on launch. City search stores up to ten + deduplicated recent cities locally and exposes them as direct, non-breaking text actions, + replacing generic privacy helper copy. Settings changes dispatch a local browser + event so Home refreshes immediately without a restart. The mark is absolutely + positioned in the hero + background, never consumes horizontal layout space, and may sit behind the + greeting as the center narrows. The condition scene is the one Home exception to + the static-motion rule: slow cloud/rain/snow motion only, with the global + reduced-motion rule providing a static fallback. Keep the weather provider behind + the normalized frontend module so production licensing or a proxy can replace the + prototype endpoint without changing Home. +- **Typography floor (2026-08-07):** Desktop UI text must never render below + **13px** for readable/interactive text and **12px** for technical metadata + (counts, shortcuts, timestamps, badges). Left navigation uses **15px**, + matching the EchoBird sister app's sidebar (`E:\EchoBird`); the user + directory/library tree uses **14px**; the right contextual rail uses 14px + list titles, 13px messages/meta, and 12.5px section labels; body text is + 16px. `DESIGN.md` is the source of truth for these rules. +- **No `title` attributes / hover tooltips (2026-08-07):** The desktop app + must never render native `title` attributes — we are not a website — and must + not use hover-revealed tips as the primary way to surface information. + Tooltips are a web pattern (`website/` may use them); inside the desktop app, + information belongs inline or behind an explicit click, with `aria-label` + kept for accessibility. Non-interactive readouts (e.g. the AI composer model + ID) are plain text: no cursor change, no hover effects, no tooltip. +- **AI activity rows (2026-08-14):** Thinking and tool calls in AI chat render as + compact, unframed disclosure rows rather than stacked full-width cards. Tool rows + use small static status icons and aligned 26px summaries. While a turn is active, + one separate EchoBird-style line appears after the activity stream. Its warm + orange label cycles the complete supported-language verb lists with the left + flower glyph sequence, typewriter erase/type timing, 2.4-second text shimmer, + and a caret during rewrites. Elapsed time appears only after 15 seconds and stays + muted and static; reduced motion disables the shimmer and caret. Completed rows + remain static muted text, failures keep a semantic error state, and provider + reasoning content remains internal. +- **One-shot composer skills (2026-08-14):** A skill selected in the AI composer is + captured for the submitted turn, then cleared immediately so it cannot silently + affect the next message. The Import Your Materials flow passes the built-in media + skill directly to its generated turn and must not leave that skill selected in the + composer afterward. +- **Custom My Contexts (2026-08-14):** The action inside My Contexts is Add Context, + not Add Material. It creates a Markdown page in Coffee Note's dedicated local + My Contexts directory, immediately lists it on that page, and gives it the same + default-on independent AI retrieval switch as the built-in context pages. General + material creation remains available from the library controls outside My Contexts. +- **Local Agent Loop (2026-08-14):** Coffee Note uses its focused Rust ReAct + loop as the only agent runtime. `agent_loop.rs` owns model streaming, tool + orchestration, durable provider transcripts, token accounting, and local + context compaction. Rust also owns Library Graph, My Contexts routing, note + tools, web reading, memory suggestions, and channel delivery. Skills and Git + plugin sources remain supported as prompt/tool extensions through `skills.rs`; + there is no Node sidecar, runtime archive, or private console launcher. +- **Capture recognition preference (2026-08-14):** The Import Your Materials dialog + stores its last cloud/local speech-recognition choice under the local UI key + `coffee-note:capture-transcription-mode:v1` and restores it the next time the dialog + or desktop app opens. +- **Media environment prewarm (2026-08-14):** Media-to-text is a frequently reused + built-in capability. After launch, Coffee Note silently prepares the pinned media + fetcher in the current user's app-data directory, verifies and reuses an existing + copy, and retries on the actual media request if background preparation failed. + Startup and first-use preparation share one process-local lock so they never write + the same partial download concurrently. Speech-recognition runtimes, CUDA support, + and models remain explicit user downloads; the AI never asks the user to configure + or repeatedly install the media-import environment. +- **Windows local transcription compatibility (2026-08-15):** The pinned whisper.cpp + Windows runtime normally selects its fastest CPU backend. If it exits immediately + after loading that backend, Coffee Note retries in an isolated temporary runtime + containing only the baseline `ggml-cpu-x64.dll`. This keeps optimized CPUs fast while + covering machines that crash after selecting the Haswell backend. Failed attempts + report the process status and diagnostics instead of treating the final successful + backend-load log line as the cause. +- **Working conversation indicator (2026-08-14):** While the agent is busy, the + active conversation card in the right history rail replaces its delete action with + a small always-visible, geometrically centered theme-color dot and exposes `aria-busy`. + Delete returns when work finishes, with a 16px icon inside a 30px hit area. History + rows are unframed and transparent at rest; only hover/current state adds a neutral + background. Working rows remain highlighted with the static dot; a conversation that + completes while it is not being viewed remains highlighted as unread until opened. + Completion is keyed by the event's conversation ID before background events are + ignored, so it cannot leak A's content into B. The current frontend still permits + only one working conversation and blocks switching conversations mid-run, but the + row-level presentation is ready to consume multiple working conversation IDs later. +- **Neutral chat surfaces (2026-08-10):** AI chat uses a warm-neutral composer + surface (`#f7f7f7`) and the navigation surface for user bubbles in both themes, + with neutral ink; do not restore the inherited green/teal composer or bubble. + Small estimated costs keep up to four decimals but trim meaningless trailing zeros + (`0.0040` displays as `0.004`), while the no-request baseline remains `0.00`. +- **Composer runtime summary (2026-08-14):** Usage below the AI composer is one + centered, wrapping summary rather than fixed equal-width columns. Low-contrast + `|` characters separate request, cache, token, context, and cost groups; `·` + joins related values inside a group, currently input and output tokens. A + deliberate narrow layout keeps groups intact without leaving a separator at a + row edge. Only backend-measured values appear; latency, first + token, throughput, turn, or step metrics can join the same grammar after the + runtime starts reporting them. +- **Softer workspace text (2026-08-11):** The center workspace and right contextual + rail reuse the fixed interaction neutral as their scoped ink: `#3a3b3d` in light + mode and `#c7c7c7` in dark mode. The left navigation keeps its existing hierarchy. + Tier-note labels and chat content must follow the same scoped ink rather than + restoring brighter hard-coded colors. +- **Auto-hiding scrollbars (2026-08-10):** Main content, settings, provider lists, + and Markdown editors follow the Coffee-CLI pattern: native WebView scrollbars are + fully hidden and a narrow real-DOM slider floats over the edge. The slider has no + native arrow buttons or visible track, supports dragging, fades over 220ms, and + begins hiding 450ms after inactivity; reduced motion switches immediately. Sidebar + and contextual-rail scrollbars remain fully hidden. +- **AI transcript context menu (2026-08-10):** Conversation records reuse the + desktop reader context menu. Copy uses the current transcript selection when one + exists; otherwise it copies the user, assistant, memory, or tool record under the + pointer. Select all selects the complete visible transcript. +- **AI composer context menu (2026-08-10):** The AI prompt textarea reuses the + full desktop editor menu: undo, redo, cut, copy, paste, delete, and select all. + Clipboard read access is invoked only by the explicit Paste action. +- **Conversation-card context menu (2026-08-10):** Right-clicking a saved + conversation offers inline rename, copy conversation UUID, copy its local JSON + path, reveal that file in the system file manager, and delete. Manual titles are + persisted as custom titles and are not replaced by later automatic first-message + title updates. Inline rename fades in without changing the card surface, height, + typography, or spacing, and the editable title has no visible border. + Rename/delete stay disabled while the single active agent is busy. +- The desktop app is moving to a calm, Codex-informed note-workspace style. This + supersedes older instructions below that require a colorful dashboard shell or + a blue-green gradient title bar. +- Coffee Note now uses a monochrome black, graphite, and gray shell with no selectable + brand color. The homepage is the deliberate exception: its three entry cards and + T1-T5 map use restrained category colors. Appearance settings keep system/light/dark + modes and language, but no longer expose accent choices. +- Light mode follows the Codex/iOS surface hierarchy: the navigation shell and title + drag bar share a very soft environmental gray-green (`#f0f5f0`), while the inner + work surface remains white and grouped controls use warm gray-white. This tint is + a surface boundary, never an icon, text, or brand accent. +- The custom title bar is a flat neutral surface. The center workspace should feel + large and quiet; hierarchy comes from typography, whitespace, and selection + states rather than decorative cards, gradients, shadows, or multiple accents. +- The shell topology is the defining Codex reference: the top bar and left rail are + one continuous outer surface, while the center workspace and right contextual rail + form one continuous inner panel. The inner panel begins below the top bar with a + visible top-left radius and border; the right rail must not read as a second sidebar. +- The shared title bar spans the full window width. The left rail starts below + it with Home as the first row; macOS reserves extra title-bar clearance for + native traffic-light controls. +- The homepage tier list remains the product core. T1-T5 use muted rose, amber, + yellow, teal, and green, limited to the tier strip, softly tinted label cells, + the compact note priority button, and the menu's small tier swatches. The button + keeps its matching tinted background unchanged on hover/open; menu hover and + selection use one neutral background with no colored border or glow. +- The display brand uses the `Coffee Note` wordmark in the title bar and AI-chat + empty state. The AI-chat empty state intentionally has no logo image. The in-app + mark source is `src-tauri/icons/logo.png`, which must + remain byte-identical to the copied UI source at `public/brand/logo-new.png`. +- OS app icons are generated with `tauri icon` from full-bleed square sources in + `src-tauri/icons/sources/`: `logo-windows.png` (Windows: `icon.ico`, the + `Square*Logo.png` set, and `StoreLogo.png`; Android: the `icons/android/` + set) and `logo-square.png` (macOS: `icon.icns`; Linux: `icon.png`, `32x32.png`, + `64x64.png`, `128x128.png`, and `128x128@2x.png`; iOS: the `icons/ios/` set). + The sources are full-bleed so the mark is not shrunken by transparent padding; + macOS and iOS auto-apply their rounded masks, and Linux keeps square corners. + Regenerate per platform with `npx tauri icon -o ` and copy + the platform files into `src-tauri/icons/`. +- The title-bar wordmark reuses the AI-chat identity's bundled `Lora` + bold-italic face at 16px. The AI-chat empty-state logo and wordmark keep their + separate 42px identity treatment. +- The left navigation keeps a modest top inset above Home so the first row does + not feel crowded against the shared title bar. +- `DESIGN.md` is the desktop design source of truth. The public `website/` remains + a separate product surface with its existing expressive brand direction. +- **Cursor rule (2026-08-08):** the desktop app uses the system default arrow + for ordinary controls and content drag-and-drop; text inputs keep the native + I-beam. The two pane resizers are the sole resize-cursor exception: use + `col-resize` on divider hover and throughout an active pane resize. +- **Text-input focus (2026-08-14):** All desktop text inputs and textareas use + only their own 1px border as the focus indicator. The shared CSS removes outer + outlines and focus shadows from text-entry controls so newly added fields do + not regress to a web-style glow. Buttons and links retain keyboard-visible + focus treatment. +- Do not keep cloned reference repositories, debugging screenshots, or temporary + research scripts in the project root. Re-fetch external sources when a focused + implementation task requires them, then remove them after use. + +## Legacy source-product context + +The remaining notes describe the source product and are retained temporarily so +the working behavior is not lost during migration. The Coffee Note rules above take +precedence whenever they conflict. + +## Product identity + +- Product name: **Coffee Note**. +- Chinese product name: **科学延寿**. Use **延寿** consistently in Chinese + product, website, documentation, and starter-library copy; keep the previous + product-domain term out of new copy. +- Version: **0.1.1**. +- Goal: a productized, local-first scientific-longevity desktop application for + Windows, macOS, and Linux—not a personal wrapper around the developer's notes. +- Product origin: inspired by the developer's `C:\Life extension` notes, but the + shipped application and its data library must be completely independent of + that folder. +- Default library locations are documented in `README.md`; users may explicitly + choose another directory later. +- Initial languages: Simplified Chinese and English. +- Starter knowledge is product-neutral and must not contain the developer's + personal health parameters. + +## Core product model + +The application combines: + +1. General Agent work inside a user-selected local directory, including code and + ordinary text files. +2. Optional knowledge reading, internal note links, capture, and structured note + creation when the user asks for note work. +3. Optional T1-T5 prioritization and personal-context features layered over + Markdown without imposing a directory hierarchy. + +The default starter library currently contains **88 Chinese documents plus 88 +English companion documents**. Run `npm run library:check` to verify the pairs. + +Primary knowledge categories: + +- Longevity strategies +- People / public cases +- Longevity stories and anecdotes + +The application must open reference websites in the user's system browser. +Article keywords may link internally to other knowledge pages, and content pages +use a minimal icon-only back action beside the category label. + +## Strategy priorities + +The home page uses an editable T1–T5 priority map. The order is a starting +reference based on public protocols (including Bryan Johnson) and evidence +maturity; it is not a universal medical ranking. Users can reorder it or ask AI +to help. + +Current default examples: + +- T1: strength training, aerobic exercise, high-quality/healthy diet +- T2: creatine, soluble dietary fiber, Omega-3 +- T3: vitamin D3, magnesium, vitamin C +- T4: CoQ10, NAD+, spermidine +- T5: ergothioneine, PQQ, Ca-AKG + +Use **NAD+** as the visible umbrella term rather than “NMN / NR”. +The library also includes healthy diet, Yamanaka factors, and mouse longevity +gene-editing material. Do not restore the removed Lü Liangwei-specific content +to the product template. + +## Desktop experience decisions + +- Visible desktop slogan remains: + **由 AI 和科学来驱动,你的延寿计划** +- Left navigation label is **首页 / Home**. +- Left navigation is one fixed hierarchical tree: top-level categories expand + to second-level notes. People and longevity anecdotes are expandable too. +- Avoid a visible sidebar scrollbar, but keep mouse-wheel scrolling. +- The left, center, and right panes are resizable. While dragging, only the + divider being manipulated is highlighted; the opposite divider stays idle. +- The left pane keeps its 210-380px product range. The right rail has no fixed + maximum; it may expand until the center workspace reaches its 560px minimum. +- The top title/drag bar spans the full window width. Its left side shows a + 16 x 16 product icon, the closed `Coffee Note` wordmark in the bundled `Lora` + bold-italic face, functional back/forward navigation, and File/Edit/Help menus. + File keeps the library-switch action; the right edge of the Home navigation + row also exposes the same action as a discoverable global folder icon, matching + the placement of future global actions such as search. Edit may remain a + restrained placeholder until editing commands are wired; Help links to product + help and feedback. +- The left rail starts directly below the title bar with Home as its first row. + Do not repeat the logo/wordmark there. Keep the Home content lifted so its top + area aligns visually with the right-rail header divider. +- Windows/Linux use custom window controls; macOS uses native traffic-light + controls on the left. +- The desktop app is single-instance. Launching it again must restore and focus + the existing main window instead of opening another process/window. +- Settings opens from the **Settings** text action immediately to the right of Help in the + title-bar menu; do not duplicate it near the window controls. It is a global + workspace mode rather than a modal dialog: below the shared title bar, a + dedicated settings navigation rail replaces the normal library rail while one + continuous work surface replaces both the center pane and contextual right + rail. Keep two focused categories: model and appearance. Knowledge-library + switching stays on the Home row and File menu, so it is not duplicated in + Settings. The model page places one compact Currency label with `¥` / `$` + symbol controls directly below Refresh Catalog; do not create a separate + currency section or add explanatory pricing copy. A visible Back to app action exits settings; + Escape does the same. The settings rail has no redundant Settings heading; + version and Feedback share one unwrapped footer row. The model navigation item + is named **Models** to match common AI app settings language. Settings scrollbars, including the + outer work surface and provider directory, stay thin with transparent tracks + and low-contrast thumbs. The settings navigation rail is 220px wide on the + ordinary desktop layout. Appearance uses one continuous grouped surface: theme + and language are compact label/control rows, and Weather forecast follows with + current city at the header's right, recent cities below, and the current-location + action beside city search. Open-Meteo attribution + is plain text in the weather section description, with no separate footer or link. + Do not collapse unrelated settings + into one long form. +- Remove redundant headers, helper labels, dark duplicate divider lines, + “30 秒结论”, model IDs in the chat box, local-context labels, and knowledge + context cards. +- Keep the chat composer in a persistent bottom layout row. The content above + it scrolls independently so the final message is never covered and the + composer never disappears while scrolling. The center content scroller uses + stable gutters on both edges so page content stays visually centered despite + the visible right scrollbar and aligns with the composer. +- Keep the composer compact. Below it, show one restrained metrics line with + provider-reported cache hit rate and token usage, API request count, local + context percentage, and a cost estimate when current model pricing is known. + The metrics are stored per conversation and switch with the active chat. + The visible cost-unit control offers only `¥` and `$`; a legacy Auto preference + resolves from the interface language until the user makes an explicit choice. + CNY and USD use DeepSeek's official + regional token prices directly rather than converting through an exchange + rate. Persist the preference locally under `coffee-note:currency`. +- The composer skill picker and Settings > Skills share one source-backed + catalog. Adding a skill plugin asks only for a Git repository URL and a Coffee + Note category; package names, descriptions, versions, and skill instructions + are read from the repository's manifest and `SKILL.md` files and are never + duplicated into editable app-owned copies. Repositories are cached once under + the current user's app-data `skill-sources/` directory for offline use and + updates. Selecting a skill reads its original `SKILL.md` and adds it only to + that AI request's prompt. Coffee Note does not execute third-party scripts, + hooks, runtimes, or MCP servers from these repositories. +- Settings > Skills is a two-level skill management workspace. The category view lists + installed skill packages/sources and keeps package update, move, removal, and + master enable controls there. Opening a package shows a simple back action and + every discovered child skill in an uncapped, two-column list with its own + persisted enable switch. Packages with hundreds of `SKILL.md` entries must not + be truncated; the settings workspace scrolls naturally. A disabled child is + removed from the composer catalog and rejected by Rust prompt loading, while + the package master switch still provides one-click all-off/all-on behavior. + Codex plugin artwork is read from each nearest `.codex-plugin/plugin.json` + (`interface.composerIcon`, then `interface.logo`) and shown on package/skill + rows directly, without an extra shared backing tile or border; the neutral + Sparkles mark is shown by itself as fallback. Icon paths must remain inside + their plugin root, supported raster/SVG assets are size-limited, and shared + icons are deduplicated in the catalog rather than repeated for every child. +- Chat uses a minimal two-sided conversation layout: user messages are compact + bubbles aligned to the right, while Coffee Note answers remain readable, + unframed content aligned to the left. Do not show participant names or avatars; + message position already communicates the speaker. +- Opening AI chat or switching conversations must position the message area at + the bottom before paint, with no visible scroll animation. New output follows + only while the user remains near the bottom; reading older messages must not + pull the user back down. +- Provider reasoning/thinking content stays internal to the model session. Never + emit it into the chat UI or persist it in UI messages; only the final answer + is user-visible. When loading conversations, remove reasoning-detail markup + written by versions affected by the v0.0.6 streaming bug. +- The chat composer shows context usage as a percentage in its metrics line. + Request-only context maintenance follows the staged Reasonix approach: at + about 60% of the 1,000,000-byte app budget, stale tool results are snipped; + at 80%, they are pruned to placeholders; if the request is still too large, + older history is compacted while about 500 KB of recent messages and up to + 150 KB of deterministic digest remain. The system prompt stays byte-stable + between rare compactions to improve provider prefix-cache reuse, and the + complete original conversation always stays on disk. The actual provider + limit may be lower. + AI chat exposes New chat actions in three places: the conversation header, + beside AI chat in the fixed left navigation, and in the fixed right-rail + header. On non-AI pages, the right-rail action returns to AI chat instead. + Selecting a saved conversation is read-only and must not move it to the top of + history; only new conversation content changes its recency. Each history item + ends with its actual last-updated date and time. +- Avoid hover tooltips and decorative hover motion throughout the app. +- Use generous, older-adult-friendly typography, especially in the center + reading area. +- The home tier list keeps the original card presentation: + a rounded bordered card with thin row dividers, tinted tier label cells with + a left color strip, and plain large text items sized to their content. Do + not render items as bordered buttons or equal-width grid cells, and do not + add arrows. Pointer drag-and-drop reordering between and within tiers stays + enabled in this presentation. Drag hit-testing has no logical gaps: each + wrapped visual line is continuously partitioned at card center lines, so the + visible spacing belongs to its adjacent insertion slot instead of falling + through to an incorrect append-at-end target. +- The right pane stacks two persistent sections: **Favorites** on top and + **My Plan** shortcuts (supplements, exercise, diet, daily routine, health log) below; when a note + is open its sources appear as a third section. The old header star toggle is + gone (favorites are always visible). On first launch the favorites are seeded + once with Bryan Johnson (flag `coffee-note:favorites-seeded:v1`); a user's + later edits are never overwritten. +- The **My Plan** rail has five sections: supplements (补剂计划), exercise (运动计划), diet (饮食计划), daily routine (作息计划), and health log (健康记录). Clicking a section opens its own **note page** — `plans/supplements.md`, `plans/exercise.md`, `plans/diet.md`, `plans/daily-routine.md` — rendered like any other library note (new page, back navigation); the health log opens the per-day editor page. The AI maintains the four plan pages via the `update_plan` tool (standard format: goals, current status, concrete arrangements, review notes). +- AI tools follow the general workspace model described above. Generic text/code + tasks use the workspace list/read/write/exact-replace tools. Note-specific tools + remain available only when relevant: `save_note`, `update_note`, `update_plan`, + and `update_tier`. The home tier list is derived only from tiered Markdown in the + selected workspace, so a new workspace starts empty and switching roots never + leaks the Demo list. Loading reads only the first 32 KB needed for metadata rather + than parsing note bodies. Drag order is workspace-local metadata in + `.coffee-note/tier-order.json`; Markdown frontmatter remains the source of truth + for tier membership. Frontend workspace work is generation-scoped: selecting + another root immediately invalidates pending loads and mutations so an old root + can never republish its snapshot over the new workspace. The frontend reloads + after every Agent run so edits appear immediately. +- AI settings separate **provider** and **model**; wire protocol is derived. Coffee Note + reads the public `https://models.dev/api.json` catalog for provider names, + default OpenAI-compatible endpoints, models, capabilities, context limits, + reasoning effort options, and USD pricing; provider marks come from + `https://models.dev/logos/{provider}.svg`. The Rust backend caches the bounded, + validated catalog for 24 hours at `Coffee Note/models-dev-catalog.json` in the + current user's app-data directory and falls back to that cache while offline. + Provider UI uses the canonical models.dev display name only. Do not repeat the + internal lowercase provider ID below the name; composer model choices also use + the canonical name rather than a legacy stored label. + Selected providers may expose multiple composer models. The official Anthropic + provider always uses its native Messages API and shows a short inline note; + every other provider, including DeepSeek, always uses the OpenAI-compatible + protocol. DeepSeek's default base URL is `https://api.deepseek.com`; the old + mistaken `/anthropic` default is migrated automatically. Provider URL, API key, + selected models, active model, derived protocol, and reasoning + effort persist in plaintext `Coffee Note/config.json` in app-data only. Existing + two-protocol configs migrate without discarding their URL, model, or key. In + the provider directory, the default provider stays first and providers with + selected models move directly below it; untouched catalog providers follow. + A configured non-default provider with no selected models keeps its local URL + and key but shows no `0` badge. + Each provider's model list is fully expanded with no height cap, result limit, + or nested scrollbar; the outer settings surface handles the added height. + Normalization merges duplicate legacy records that resolve to the same provider + ID, preferring the active record and unioning its explicitly selected models. This + prevents a configured non-default provider from incorrectly displaying `0`. + The provider directory also offers **Add custom**. It asks for a custom name, + creates an OpenAI-compatible local configuration, and lets the user add model IDs + manually. Custom providers stay together at the top of the directory, use a + neutral cube mark, and replace the catalog + documentation link with an explicit trash action that removes that provider only. + Manually entered model IDs are local records: they are enabled immediately, render + once without a duplicate display name, icon, or guessed price, and use the same + checkbox as catalog models. Unchecking only removes the model from the composer; + its separate local record remains until the row's trash action deletes it. Catalog + refreshes may enrich matching metadata but never + remove local model selections; even a catalog-deprecated selected model stays visible. + The former seeded `deepseek-v4-flash` default is cleared when its legacy + OpenAI/Anthropic slot is migrated. Composer model text is valid only when the + model remains in the explicit selected-model array; otherwise it reads Choose model. +- The AI composer model menu and reasoning control are functional configuration, + not previews. Model changes update the active provider/model immediately. + Every catalog-declared reasoning model exposes Coffee Note's fixed five choices: + `low`, `medium`, `high`, `xhigh`, and `max`. Do not filter or clamp these from + models.dev `reasoning_options`; provider endpoints may normalize unsupported + intermediate values themselves. Choices are sent as `reasoning_effort` for OpenAI-compatible Chat + Completions or `output_config.effort` for Anthropic Messages. Models without a + catalog-declared reasoning capability show a non-interactive Standard readout and + receive no guessed reasoning parameter. +- Every outbound model request identifies the app to compatible upstreams with + `HTTP-Referer: https://note.coffeecli.com`, `X-OpenRouter-Title: Coffee Note`, and the + backward-compatible `X-Title: Coffee Note`. This attribution replaces any inherited + EchoBird identity and applies to both streaming and + synchronous OpenAI-compatible/Anthropic requests. +- The desktop app's Coffee Note brand link opens `https://note.coffeecli.com/`. Download and + update endpoints remain separately configured until their hosting is migrated. +- AI provider settings, including API keys, persist as plaintext JSON in the + current user's app-data directory (`Coffee Note/config.json`). They must + never be written into the repository or knowledge library. +- Borderless modal headers share one text-only standard component. Use typography + for hierarchy, never a decorative or category icon to the left of the title; + keep the explicit close action aligned on the right and do not maintain separate + modal-title alignment rules. +- Local knowledge retrieval uses an in-process Rust knowledge map: cached + language-aware Markdown parsing, weighted title/path/heading/body matching, + one-hop Markdown-link graph expansion, and relevant excerpt selection. + Automatic grounding and the `search_library` tool share this retriever. It + uses no embedding API, external service, vector database, or indexing tokens. +- Evidence-oriented questions automatically use an app-managed live research + layer: the configured model produces a concise English biomedical query that + is instructed to exclude personal identifiers and measurements, + then the backend searches PubMed, ClinicalTrials.gov, and bioRxiv (through + Europe PMC). Answers receive a deterministic source list and must distinguish + peer-reviewed papers, trial registrations/results, and preprints. +- The app silently checks the latest GitHub release. When an update exists, a + small teal update control appears beside the sidebar product name. Windows + downloads the published NSIS installer with circular progress and launches + it; unsupported platforms or failed installs fall back to the product + website. +- Workspace Agent uses one completion-first path. Cache reuse, targeted memory and + Library Graph retrieval, duplicate-call detection, and context maintenance are + always enabled; there is no user-selectable economy/full quality mode. The + global emergency ceiling only produces an honest final status instead of + stopping with a hard tool-loop error. +- The temporary product-positioning memo for the Agent's cost-saving focus + lives at `docs/NOTE_AGENT_SAVINGS.md`; keep it in mind when refining product + copy, pricing, model recommendations, or agent behavior. +- Memory routing keeps two user-visible sources of truth separate: confirmed + facts are written into the managed `我的资料/plans/*.md` pages, while the + user-selected workspace remains the general work and research/source directory. The + app-data `memory.json` is only a rebuildable source index; personal context is + retrieved from Markdown with a small question-aware byte budget and is placed + in the user message so the system prompt stays cache-stable. +- The visible My Contexts (`我的设定`) page exposes AI-retrieval switches for its five content notes. + All five default on and persist locally under + `coffee-note:my-info-retrieval:v1`; Add Material remains an action rather than a + retrievable section. Every agent request sends the enabled stable IDs, and + Rust applies the resulting localized-path allowlist to both question-aware + Library Graph context and always-on memory injection. My Contexts cards are direct + navigation entries with no selected-card state; their light neutral surface is + `#f1f1f1`. Enabled/context states use iOS blue (`#007aff`) in light mode and + egg-yolk yellow (`#e7be15`) in dark mode. The same state color marks the active + library multi-select control, selected-note checkmarks, and the removable note + context pill in the AI composer. + +## Website + +- The public website is active again at `https://note.coffeecli.com/`. The + single-page site under `website/` is independent from the desktop UI and keeps + its own brand, copy, download routes, and screenshot assets. +- Product screenshots are paired by locale. English and Simplified Chinese use + matching desktop captures, and the in-place language switch updates both copy + and screenshots. Other incomplete locales continue to fall back to English. +- Remote usage: `irm https://note.coffeecli.com/install.ps1 | iex`. The script + first tries the website's version/download routes and falls back to the latest + GitHub Release asset, so it remains useful while the public site is offline or + being rebuilt. +- Primary product URL: `https://note.coffeecli.com/`. + +## Architecture and important paths + +- Desktop frontend: React + TypeScript + Vite under `src/`. +- Desktop shell/backend: Tauri 2 + Rust under `src-tauri/`. +- Product starter library: `starter-knowledge/`. +- Desktop visual assets: `public/`, `src-tauri/icons/`, and `设计思路/`. +- Architecture notes: `docs/ARCHITECTURE.md`. +- Bilingual-library rules: `docs/BILINGUAL_LIBRARY.md`. +- Cross-platform release workflow: `.github/workflows/release.yml`. +- Website: static product page, Cloudflare Worker download routes, remote install + scripts, release version metadata, brand assets, and localized screenshots. + +## Restore development dependencies on a new machine + +Prerequisites: + +- Node.js and npm +- Rust toolchain +- Tauri platform prerequisites for the operating system + +Desktop: + +```powershell +cd C:\Coffee-Note +npm install +npm run library:check +npm run typecheck +npm run tauri:dev +``` + +Website: + +```powershell +cd C:\Coffee-Note\website +npx serve . +``` + +Recreating desktop `node_modules/`, `dist/`, or `src-tauri/target/` is expected. +These are deliberately excluded from portable copies because they are generated +and machine-specific. The website itself has no generated build directory. + +## Recommended continuation + +1. Continue refining the desktop experience and verify pane resizing, native + title-bar behavior, keyboard navigation, and readable scaling. +2. Add and test night mode. +3. Finish product-grade AI provider configuration and knowledge-grounded tool + calls. +4. Review the bilingual starter library for scientific sourcing and product + neutrality. +5. Test the published `v0.1.1` installers on real Windows, macOS, and Linux + machines. Future production releases should add Windows and Apple code + signing when certificates are available. + +## Agent-logic optimization references (added 2026-08-07) + +The AI-chat empty state advertises three capabilities that Coffee Note already +implements partially. When optimizing the agent loop later, borrow from these +references: + +- DeepSeek cache optimization and hit rate — [Reasonix](https://github.com/esengine/DeepSeek-Reasonix): + keep the system prompt byte-stable, reuse provider context caches, and report + cache hit rate in the composer metrics line. +- Anti-over-engineering prompts — [ponytail](https://github.com/DietrichGebert/ponytail): + keep agent instructions minimal and specific; avoid bloated system prompts. +- Library Graph — Markdown version of [code-review-graph](https://github.com/tirth8205/code-review-graph): + build an in-process graph over the local Markdown library (links, paths, + headings) instead of external services. + +The UI copy on the empty chat screen: **DeepSeek 缓存极致优化 + 高命中率记忆路由 + +Library Graph 检索 + 短输出、少调用、自动压缩** (English: DeepSeek cache +optimization + high-hit memory routing + Library Graph retrieval + shorter +output, fewer calls, automatic compaction). The empty state is a +semi-transparent watermark that disappears as soon as a conversation starts, +with the closing value line **让每一个 Token,产出至少两倍价值。** (English: +Make every token deliver at least twice the value.) + +## Starter data ownership (2026-08-08) + +The managed Demo library and My Info pages are starter data only. Coffee Note +creates their bilingual seed content once, and then treats every file as user +data. A permanent `.starter-pack-initialized` marker prevents later launches, +upgrades, or starter-content revisions from overwriting or recreating edited or +deleted files. When upgrading a pre-marker installation, any existing file means +the directory is adopted as-is; Coffee Note writes the marker without backfilling +missing seed files. Current Chinese directory names and My Info section IDs are +transitional implementation details, not permanent product contracts. + +My Info retrieval switches are enforced in both request-scoped retrieval and AI +tool calls. If the selected workspace is the My Info directory or one +of its ancestors/descendants, the managed My Info subtree is excluded from the +general library route and only the enabled personal-section allowlist may expose +its contents. + +## Audio-to-text import foundation (2026-08-10) + +The desktop Audio to text settings are backed by real app-data state rather +than UI-only placeholders. Provider configuration is stored as plaintext JSON +at the current user's local app-data `Coffee Note/transcription.json`; it is never +written into the repository or selected workspace. Provider records are kept +independently so switching services does not discard their endpoint, model, or +API key. The hosted path has separate request adapters for OpenAI-compatible, +Deepgram, and AssemblyAI protocols and streams audio files instead of reading +the whole upload into memory. + +The local path downloads pinned runtime and model resources into the current +user's local app-data `Coffee Note/transcription/` directory. Every fixed resource +has an expected byte size and SHA-256 digest; archives are extracted only after +verification. Windows supports the CPU and NVIDIA CUDA runtimes, Linux supports +the CPU runtime, and macOS is intentionally shown as unavailable until a pinned +runtime package is defined. Local input is decoded and converted to 16 kHz +mono PCM WAV before the selected runtime and model are executed. + +The home capture flow now treats YouTube, Bilibili, TikTok, Douyin, Xiaohongshu, +X, and their supported short-link hosts as media sources. It first asks the +media source for public subtitles and cleans VTT/SRT-style cues, tags, and +duplicate lines. If no usable subtitle is available, it validates the selected +hosted or local configuration, downloads the best compatible audio into a +temporary directory, transcribes it, and then sends the transcript through the +existing note-organization model to produce editable Markdown. Ordinary web +URLs retain the existing HTML extraction path. Direct local audio/video file +selection is not implemented yet. + +## Phone message channels (updated 2026-08-14) + +Coffee Note supports two paired private phone channels: Tencent Weixin iLink +and Telegram Bot API. The Rust channel runtime stores credentials and a +durable task queue under the current user's local app-data `Coffee Note/` +directory; neither credentials nor pending jobs enter the selected workspace. +Weixin follows Tencent's MIT-licensed `openclaw-weixin` HTTP/JSON protocol for +QR login, optional phone verification, long polling, `context_token` replies, +and sync-cursor recovery. Telegram uses Bot API long polling, private chats, +and a one-time pairing code after the user supplies a BotFather token. + +Inbound text now enters the same local Agent Loop as the desktop composer. +Ordinary text is ordinary conversation and must never be saved merely because it +came from a phone. A message consisting primarily of a public URL is the one +implicit action: fetch, organize, and save the source as a local note. Explicit +requests may use the same note search/read/create/update and memory tools as the +desktop client. Terminal access, app-settings mutation, unpaired senders, group +chats, WhatsApp, and arbitrary attachments remain outside this channel surface. + +Each connected channel stores a durable conversation ID and continues that local +conversation across phone turns. Agent transcripts and UI messages are written to +the normal `conversations/` store, so phone conversations appear and can be +continued from the desktop right rail. Jobs are persisted before the inbound cursor +is acknowledged and removed only after the final phone reply is delivered. + +## Next-work plan + +1. **Phase 1 — Finish import preprocessing**: media URL audio download and + hosted/local transcription are implemented. Next, add direct local file + selection and conversion for md/html/txt/pdf/docx/audio/video, then define + deterministic fallback and error reporting for unsupported sources. + Keep processing local by default, SSRF-safe, and free of login-cookie + channels. Acceptance: selecting a local document or media file produces an + editable Markdown note through the same capture dialog. +2. **Phase 2 — Library Graph**: + evolve `knowledge_map.rs` into a persistent registry + backlinks + orphan/ + broken-link lint + deterministic metadata rebuild after agent runs; + measure token and tool-call savings. +3. **Phase 3 — Cache hit rate and cost**: keep the stable Coffee Note system + prompt small and compare provider-reported cache usage across model providers. +4. **Phase 4 — Evidence-retrieval self-healing routing**: + primary/fallback endpoints for Europe PMC / PubMed / ClinicalTrials with + health checks and automatic degradation. + +## Provider prefix-cache continuity (2026-08-10) + +AI conversations now have one UI transcript and one durable Agent Loop provider +transcript in Coffee Note. Conversation mutations still share one process-local +storage lock to protect UI JSON and its index. + +Cache usage remains the provider-reported aggregate. DeepSeek does not return +token attribution by prompt section, so Coffee Note must not claim a synthetic +rate that subtracts personal-context tokens. The composer labels this value +`累计命中` / `Total cache` to make the cold-start-inclusive scope explicit. diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..9a1167c --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,262 @@ +# Coffee Note Desktop Design System + +## Product Context + +- **What this is:** A local-first, general-purpose note workspace where an editable T1-T5 list makes priorities visible and an AI note agent helps organize local material. +- **Who it is for:** People who want one calm place to collect, rank, read, and refine notes without giving up local ownership. +- **Project type:** Cross-platform desktop productivity application. The public `website/` is a separate branded product surface and does not inherit these desktop rules. + +## Aesthetic Direction + +- **Direction:** Calm native workspace, informed by Codex rather than copied from it. +- **Decoration:** Minimal. Hierarchy comes from type, whitespace, alignment, and state, not colorful cards or ornamental chrome. +- **Borderless modal headers:** Use a text-only title block with the explicit close + action on the right. Do not place decorative or category icons to the left of + modal titles; typography and whitespace provide the hierarchy. +- **Hover tooltips:** Prohibited inside the desktop app — including native + `title` attributes, which must never appear in the app DOM (we are not a + website). Information belongs inline or behind an explicit click; `aria-label` + stays for assistive technology. This rule does not apply to the public + `website/`. Non-interactive readouts (like the AI composer model ID) are + plain text: no cursor change, no hover highlight, no tooltip. +- **Cursor:** The desktop app uses the system default arrow cursor for ordinary + controls and drag-and-drop content. Text fields keep the browser-native I-beam. + The two pane resizers are the only resize-cursor exception: use `col-resize` + both while hovering over a divider and throughout an active pane resize. +- **Mood:** Serious, quiet, capable, and spacious. Coffee Note should feel like a professional work surface, not a personalized dashboard template. +- **Memorable quality:** A Codex-like note tool whose primary object is the user's tiered knowledge, not chat. + +## Product Structure + +- Keep the homepage tier list as the core first screen. +- The Home greeting may include one compact animated weather mark on its right. + Home shows only the condition image: no city, temperature, forecast text, + provider name, enclosing card, or dashboard treatment. An explicit click opens + a read-only lightweight detail panel containing only current conditions and several + forecast days. Its title and city share the action row, where a gear opens + Settings > Appearance directly. City search, recent cities, one-time device + location and source attribution live only there. Once configured, weather stays + enabled as part of Home; users switch cities rather than removing the feature. Never request + location on launch. The + desktop prohibition on hover tooltips still applies to this mark. Treat the + mark as an absolutely positioned background element: it never consumes layout + width or compresses the greeting, and may sit behind the greeting when the + center workspace becomes narrow. Keep a small inset from the top-right edge + and allow the weather canvas to overflow so halos and clouds are never clipped. + Settings keeps up to ten deduplicated recent cities locally as direct text + actions. Multi-word names remain intact and the row wraps only between cities; + do not spend space on generic location-privacy helper copy. +- Use one outer shell for the top bar and persistent left navigation rail. +- Let the shared title bar span the full window width. Place the `Coffee Note` wordmark at its left edge, followed by back/forward navigation and the File, Edit, and Help menus. On macOS, reserve clearance for native traffic-light controls. +- Start the left rail below the title bar with Home as its first row. Do not repeat the product icon or wordmark there. Library switching remains in the File menu and is also exposed as a discoverable global-action icon at the right edge of the Home row, matching the placement of global actions such as search in Codex. +- Leave a modest top inset above the Home row so the left rail breathes under the title bar rather than feeling pinned to it. +- Place the center workspace and right contextual rail inside one continuous inner panel. Its top-left border and radius are the primary visual boundary; the right rail is not a second sidebar. +- Keep the left navigation pane within its 210-380px range. The right contextual rail has no product-level maximum; its only upper bound is the remaining window width after preserving the main workspace's 560px minimum. +- Treat AI chat as one workspace mode, not the visual identity of the whole product. +- Treat Settings as another global workspace mode, opened from the Settings text + action immediately to the right of Help in the title-bar menu. Do not duplicate + this action beside the window controls. + Below the shared title bar, replace the normal left rail with a settings navigation + rail and merge the center workspace plus contextual right rail into one continuous, + scrollable settings surface. Settings is not a modal and uses no backdrop or floating + dialog boundary. Keep model and appearance as distinct pages. Library switching + already belongs to the Home row and File menu and is not duplicated here. + All settings scroll surfaces use the same narrow, transparent-track, low-contrast + scrollbar treatment as the main workspace; avoid native heavy scrollbar chrome. + Keep the settings navigation rail compact at 220px on the ordinary desktop layout. + Name its model destination Models to match common AI app settings language, and + keep the version and Feedback actions together on one unwrapped footer row. + Appearance is one continuous grouped surface rather than three distant cards: + theme and language use compact label/control rows, followed by a structured + Weather forecast section with the current-city readout at the header's right, + recent cities below, and the current-location action beside city search. Provider attribution belongs in the section's + introductory sentence rather than consuming its own row. Weather has no + removal action; selecting another city replaces the current one. +- Message settings support only Tencent Weixin and Telegram. Present both as + restrained connection rows in the continuous settings surface, with status, + pairing, and recovery information shown inline after an explicit action. + A paired private phone chat is another entry point into the same AI note agent + used by the desktop composer: ordinary text remains conversation, while a + message consisting primarily of one public link implicitly asks AI to fetch, + organize, and save it as a local note. Phone turns persist in the shared local + conversation history and appear in the desktop right rail. This is not a remote + terminal or group-chat bot. Keep credentials in the current user's app-data + directory and never expose them back to the webview. +- Model settings use a directory/detail split inside the model page: searchable + providers on the left and one provider's URL, local key, protocol note, + and selectable models on the right. Provider marks are compact monochrome + identifiers, not decorative cards. Show only the catalog's canonical provider + display name; never repeat its lowercase/internal provider ID beneath it, including + in the composer model menu. Keep OpenAI-compatible as the ordinary path; + only the official Anthropic provider uses its native Messages API, indicated by + a short inline note rather than a control. Model rows may show technical metadata at the 12px floor (model + ID, context size, and input/output price), while actions and names remain 13px + or larger. Keep the default provider first in the directory, followed by every + provider with selected models, then the untouched catalog entries. A compact + Add custom action in the provider directory asks for a custom name and creates + an OpenAI-compatible local provider whose model IDs are entered manually. Custom + providers stay grouped at the top and use a neutral cube mark. Their details + replace the catalog documentation action with a trash action; + the explicit trash action deletes immediately and removes only that provider's + local configuration. + Manually entered model IDs are local records and show the ID once, with no guessed + name, icon, or price. Adding one enables it immediately; it uses the same checkbox + interaction as catalog models, while a dedicated trash action removes its saved + local record. Unchecking only removes it from the composer. A catalog refresh must + never remove or hide a + locally saved model, including one newly marked deprecated upstream. + The AI composer exposes only models selected here. Every reasoning-capable + model gets Coffee Note's fixed five effort levels (low, medium, high, xhigh, max); + models.dev reasoning-option metadata must not filter or clamp them. A provider's model list always expands in + full inside the outer settings scroller; do not cap its result count or add a + nested model-list scrollbar. Non-default providers show a count only when at + least one model is selected; never render a zero badge. The composer model + readout must come only from the provider's explicitly selected model list; + never fall back to a seeded or example model ID. +- On My Contexts (`我的设定`), use five direct-entry content cards with independent retrieval + switches, all enabled by default. Cards have no selected state: their light + surface remains `#f1f1f1` during hover and navigation, with a neutral dark-mode + counterpart. Enabled/context states use iOS blue (`#007aff`) in light mode and + egg-yolk yellow (`#e7be15`) in dark mode. Reuse that state color for the active + library multi-select control, selected-note checkmarks, and the removable note + context pill in the AI composer. Add Material is an action and has no retrieval switch. +- Keep the composer in a stable bottom row when chat is active. +- Present the composer metrics as one compact runtime summary rather than equal + dashboard columns. Separate metric groups with a low-contrast `|`, join + closely related values such as input and output with `·`, and keep each group + intact in the deliberate narrow-layout rows. Show only values the runtime actually measures; + future latency, speed, turn, or step metrics join this same grammar once the + backend reports them. +- Render AI thinking and tool activity as compact, unframed disclosure rows rather + than full-width cards. Tool rows use small static status icons, with aligned + 26px summaries; clicking the chevron reveals arguments and results inline. + While the turn is active, keep one separate EchoBird-style status line after + the activity stream. It uses Coffee Note's warm orange status color, the complete + supported-language verb lists, the left flower glyph sequence, typewriter erase/type + timing, a caret during rewrites, and the EchoBird left-to-right text shimmer. Add + an elapsed clock only after 15 seconds; the muted elapsed clock does not shimmer. + Completed rows become + static muted text, failures retain a clear semantic error state, and raw provider + reasoning remains private. +- In the right conversation history, replace the active working conversation's + delete action with an always-visible loading indicator. Restore delete only after + work finishes; its icon and hit area must remain comfortably legible without + dominating the row. Conversation rows have no persistent borders: ordinary rows + are transparent, while hover and the current conversation use one soft neutral + background. A working row keeps that background with a small static theme-color dot. When work + completes outside the conversation currently being viewed, keep the same background + as an unread-completion signal until the user opens that conversation. Keep the + status dot geometrically centered in the action area. +- In light mode, the chat composer and user message bubble use layered neutral gray + surfaces with neutral ink, never the inherited green/teal treatment. The user + bubble keeps a compact Codex-like rounded shape rather than becoming a card. +- Reserve matching scrollbar gutters on both sides of the center scroll area so + page content remains optically centered and aligns with the fixed composer. +- Desktop scrollbars hide the native WebView chrome and use a narrow, real-DOM, + low-contrast neutral slider with no track or arrow buttons. The slider appears only + during scrolling, pointer/keyboard interaction, or editor activity, then fades after + a short idle delay. It floats over the edge so content never shifts; scrolling and + thumb dragging always remain available. +- AI conversation records use the same restrained desktop context menu as note + content. Copy prioritizes selected text and otherwise copies the record under the + pointer; Select all covers the visible transcript. +- The AI composer textarea uses the complete desktop editing context menu. Keep + selection-sensitive commands disabled when they have no applicable text, and read + the clipboard only after the user explicitly invokes Paste. +- Saved conversation rows expose a restrained right-click menu for rename, identity, + local-file access, and deletion. Rename fades into place inline without changing the + row height, surface, typography, or spacing; its editable title has no visible border. + Destructive hover uses semantic red, while all other actions remain neutral. +- Remove redundant dashboard cards, helper copy, metrics decoration, and duplicated labels as each screen is migrated. +- Prefer one clear action per region. Secondary actions use familiar icons from Lucide. + +## Typography + +- **UI and body target:** Source Sans 3 with Noto Sans SC for Chinese coverage. Self-host before switching production CSS. +- **Current migration fallback:** Segoe UI Variable, Segoe UI, Noto Sans SC, Microsoft YaHei, sans-serif. +- **Data:** Use tabular numerals for token, cost, date, and count values. +- **Scale:** 16px body, 20px section title, 28-32px page title; 15px left + navigation (matches the EchoBird sister app); 14px directory/file tree and + controls; 13px minimum for all readable/interactive UI text; 12px metadata + floor for counts, shortcuts, timestamps, and tiny badges only. +- **Minimum size rule:** No readable desktop UI text below **13px** and nothing + below **12px** anywhere. If a component cannot fit its label at 13px, enlarge + the component; never shrink the type to fit. +- **Weight:** Use 400 for body, 550-600 for controls and navigation, and 650-700 only for page titles. +- **Letter spacing:** 0. Avoid uppercase tracking except tiny technical metadata. +- **Brand exception:** The compact title-bar `Coffee Note` wordmark reuses the bundled `Lora` bold-italic face from the AI-chat identity at 16px. + +## Color + +- **Approach:** Codex/iOS surface hierarchy. Shell controls, icons, and text stay monochrome; a barely tinted environmental surface separates navigation from the white work area without becoming a brand color. The homepage alone may use restrained category color to make its core prioritization model memorable. +- **Light:** ink `#2c2c2e`, muted `#77777b`, canvas `#ffffff`, sidebar `#f0f5f0`, grouped surface `#f7f7f5`, line `rgba(60, 60, 67, 0.12)`. +- **Interaction:** graphite `#3a3b3d`; hover `#252628`; soft state `rgba(58, 59, 61, 0.07)`. +- **Dark:** canvas `#1c1c1e`, sidebar `#242426`, ink and interaction `#f2f2f7` / `#d1d1d6`. +- **Workspace content ink:** the center workspace and contextual rail reuse the + fixed interaction neutral (`#3a3b3d` in light mode and `#c7c7c7` in dark mode) + instead of introducing another color token. Keep the left navigation's existing + hierarchy unchanged. +- **Semantic color:** Reserve red, amber, and green for errors, warnings, and success only. Do not use semantic colors as branding or decoration. +- **Tier color:** T1-T5 use muted rose, amber, yellow, teal, and green. Color stays in the homepage tier strip, softly tinted label cells, the compact note priority button, and the menu's small tier swatches; it does not enter navigation, article content, or settings surfaces. The priority button keeps its tier-tinted background unchanged on hover/open. Menu hover and selection use one neutral background with no colored border or glow. + +## Spacing And Shape + +- **Base unit:** 4px. +- **Density:** Comfortable and work-focused: 8px between related controls, 16px within regions, 24-32px between major regions. +- **Radius:** 4px for compact controls, 7px for inputs and list selections, 8px maximum for cards and dialogs. Pills are reserved for statuses. +- **Borders:** Use only for pane boundaries, inputs, dialogs, and meaningful grouping. Avoid cards inside cards. +- **Text-input focus:** Desktop text inputs and textareas use only their own + 1px border to indicate focus. Never add an outer outline, glow, or focus shadow; + this is the shared default for new fields. Buttons and links may retain a + keyboard-visible outline for accessibility. +- **Shadows:** Dialogs and floating menus only. Main page regions stay flat. + +## Motion + +- **Approach:** Minimal and functional. +- **Timing:** 90ms for hover/focus changes, 150-200ms for panels and dialogs. +- **Behavior:** No decorative movement, scale-on-hover, or animated gradients. Respect reduced-motion settings. +- **AI activity:** Tool rows and history indicators stay static. The active turn's + EchoBird-style status line is the chat animation exception: its warm orange text + shimmer runs linearly for 2.4 seconds while the left flower glyph and rewrite caret + follow the matching typewriter cycle. The elapsed-time text updates once per second + after the first 15 seconds but stays muted and static. Reduced motion disables the + shimmer and caret. +- **Home weather exception:** Tiny condition-specific motion may communicate the + current weather inside the greeting readout: slow cloud drift and sparse rain + or snow only. It stops under reduced-motion preferences and must remain lower + contrast than the greeting and tier list. Do not animate the surrounding page. + +## Migration Order + +1. Establish the monochrome interaction system and neutralize the title bar. +2. Rebuild the app shell and left navigation around a Codex-like quiet hierarchy. +3. Simplify the homepage so the tier list dominates and dashboard cards disappear. +4. Unify note reading and AI chat inside the same center-workspace grammar. +5. Rework the right rail as contextual information that can collapse when it is not useful. + +## Decisions Log + +| Date | Decision | Rationale | +| --- | --- | --- | +| 2026-08-07 | Adopt a Codex-informed desktop direction | The current multi-color dashboard treatment makes the product feel smaller and less focused than the local-first tiered-note model deserves. | +| 2026-08-07 | Remove accent-color customization | One fixed visual system reduces noise and prevents user themes from fragmenting the product identity. | +| 2026-08-07 | Join the top bar to the left rail and the center to the right rail | This reproduces the spatial grammar the user values in Codex: one quiet navigation shell around one coherent work surface. | +| 2026-08-07 | Make the desktop product monochrome | Neutral black and gray give the repeated-use workspace broader appeal; color is reserved for semantic feedback rather than branding. | +| 2026-08-07 | Adopt Codex/iOS surface colors | A soft environmental sidebar, white work surface, warm grouped controls, and dark-gray type remove the cold electronic-screen feeling while keeping icons and emphasis monochrome. | +| 2026-08-07 | Make the homepage the color exception | Three low-saturation entry cards and muted T1-T5 colors give the core screen identity while the repeated-use shell and note-reading surfaces stay quiet. | +| 2026-08-07 | Keep website design separate | The public site is expressive brand communication; the desktop app is a repeated-use work surface. | +| 2026-08-07 | Enforce a minimum type scale | Left navigation, directory tree, and right-rail text at 10-13px was too small to read. Floor: 13px readable / 12px metadata; 15px navigation (EchoBird reference), 14px directory tree. | +| 2026-08-08 | Use the default arrow except for pane resizing | A hand cursor is a web pattern; ordinary controls and content keep the default arrow, while the dividers between the three panes use native directional feedback during resize. | +| 2026-08-08 | Move the product identity and app menus into the shared title bar | A compact 16 x 16 icon, Lora bold-italic `Coffee Note` wordmark, page history, and File/Edit/Help menus match the native Codex-like shell while letting Home align directly with the workspace top edge. | +| 2026-08-08 | Gate My Contexts retrieval per note | Five default-on local switches make AI inclusion explicit; a backend allowlist enforces the same state for question-aware and always-on personal context. Cards remain direct neutral navigation entries rather than selectable options. | +| 2026-08-08 | Treat managed starter content as user-owned after first creation | Demo and My Contexts seed files are generated only for an empty first-run directory. A permanent marker prevents upgrades from overwriting, backfilling, or recreating edited and deleted files; current localized names remain temporary. | +| 2026-08-09 | Replace the settings dialog with a global settings workspace | A persistent category rail and one merged content surface match the desktop shell, remove the dated modal feel, and give model and appearance settings room to remain legible. Library switching remains with Home and File. | +| 2026-08-09 | Use models.dev for provider/model directory data; keep Coffee Note request configuration independent | A live catalog removes hard-coded model names and prices. Anthropic alone uses its native Messages API; every other provider uses the industry-default OpenAI-compatible path. Coffee Note always exposes five reasoning levels for reasoning models and lets provider endpoints normalize them. Composer selections change actual requests. | +| 2026-08-09 | Add ambient weather beside the Home greeting | Home keeps only a quiet animated condition image; its click panel is a read-only current and multi-day forecast with a compact single-line city header and a direct gear shortcut to Settings > Appearance. City selection, location, history, and provider attribution live in Appearance; users replace the current city rather than removing weather. This preserves the calm workspace while connecting it to the user's day. Location is requested only from Settings after an explicit click, rounded to city-level precision, and stored locally; manual city search remains the universal fallback. | +| 2026-08-14 | Make paired phone chats full Coffee Note conversations | A linked private chat should behave like the desktop composer rather than force every message through link capture. Ordinary messages get ordinary AI replies, link-only messages implicitly become saved notes, and all turns share the local conversation history visible in the desktop rail. Pairing, credentials, cursors, and recoverable jobs remain local. | +| 2026-08-14 | Adopt a restrained turn-status shimmer | One theme-colored activity line with a left-to-right text shimmer and delayed static elapsed time replaces the repeated Braille spinner; tool rows and conversation history keep static marks. | +| 2026-08-15 | Remove leading icons from borderless modal headers | Text-only titles make the lightweight dialogs quieter and more refined while one shared header keeps title and close-action alignment consistent. | +| 2026-08-16 | Treat Skills as two-level management | The first level manages installed packages and source actions; opening a package reveals an uncapped two-column list of its concrete skills with independent switches and a clear back action. This separates installation from capability control and remains usable for repositories containing hundreds of skills without implying an in-app download marketplace. | +| 2026-08-16 | Preserve official plugin artwork in Skills | Package and child-skill rows use the nearest Codex plugin manifest's compact icon directly, without a second backing tile or border, and fall back to the standalone neutral skill mark. This makes large official collections scannable without adding labels or card decoration. | +| 2026-08-16 | Make Tier drag channels geometrically continuous | Visual spacing remains airy, but every wrapped line is divided at card center lines so each pixel maps to a deterministic insertion slot. A gap must never fall through to the row-end position. | diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..ede8157 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,101 @@ +# Coffee Note 架构说明 + +## 产品定位 + +Coffee Note 是一个本地优先的纯笔记工作台:用户用 T1-T5 分级组织本地 +Markdown 笔记,AI 以「省钱的 Note Agent」形式协助整理与治理笔记。 + +产品边界见 [NOTE_AGENT_SAVINGS.md](./NOTE_AGENT_SAVINGS.md):它不是 +coding agent,不绑定昂贵模型,以完成笔记治理任务为优先,尽量复用本地 +笔记与个人资料,减少重复上下文与无效调用。 + +## 技术栈 + +- Tauri 2:Windows、macOS、Linux 桌面壳与本地权限边界; +- React + TypeScript + Vite:三栏知识阅读和对话界面; +- Rust:路径安全、本地 Markdown 读取、收录写入、轻量检索与 DSH 适配; +- DeepSeek Harness:模型请求、Agent 循环、工具编排、持久会话、Token 计量与上下文压缩; +- Markdown/CSV:开放、可迁移的产品资料格式。 + +## 独立资料库 + +Coffee Note 的默认资料库位于各平台的应用数据目录: + +```text +Windows %USERPROFILE%/.coffee-note/演示笔记 +macOS ~/.coffee-note/演示笔记 +Linux ~/.coffee-note/演示笔记 +``` + +首次启动由应用创建目录和入门内容。`starter-knowledge/` 只包含公开的 +结构示例、用户资料空模板和通用安全边界。 + +外部 Markdown 资料通过显式的「导入」流程复制或转换到产品资料库,不会 +成为产品运行依赖。 + +## Agent:DeepSeek Harness 单一运行时 + +Agent 循环完全交由固定版本的 DeepSeek Harness 运行时负责,包括模型流式 +请求、工具编排、持久会话、Token 计量与上下文压缩。Coffee Note 不再维护 +并行的自研循环;`dsh_runtime.rs` 只承担 Tauri 事件适配、进程生命周期、 +产品上下文注入和本地工具桥接。 + +产品能力仍由 Rust 提供:`agent_tools.rs` 中的本地知识检索、笔记读写、 +Library Graph、记忆路由和网页读取通过仅监听 localhost、带随机令牌的私有 +桥暴露给 DSH。DSH 配置不启用任意 Shell、编码工具或不受限文件写入。 + +## 本地知识与个人记忆 + +Coffee Note 的记忆路由有两个用户可见的真源,以及一份可重建索引: + +- **我的资料**(应用管理的 Markdown):用户确认的长期目标、偏好、约束、 + 经验与健康背景写入对应的 `plans/*.md` 页面;这是个人事实的唯一真源。 +- **当前笔记目录**(用户选择的 Markdown 根目录):研究材料、项目笔记和 + 外部知识由 Library Graph 按问题检索,不会复制到「我的资料」。 +- **对话记录与记忆索引**(应用数据目录):完整会话保留在本机; + `memory.json` 是用于去重和来源追踪的本地索引,丢失时不会影响 Markdown + 中的个人事实,下一次确认记忆会从可见页面重新建立索引。 + +每次 Agent 请求由 Coffee Note 路由器组合三类上下文:当前问题相关的 +「我的资料」摘要、当前笔记目录的 Library Graph 命中,以及首次迁移时的 +最近对话。个人资料检索保持在约 16 KB;当前笔记只注入相关片段。之后的 +会话维护和压缩由 DSH 负责。系统提示保持稳定,动态资料放入用户消息, +从而保留 DeepSeek 前缀缓存复用的机会。 + +模型上下文优先级: + +1. 当前打开的笔记; +2. `profile/about-me.md`、`plans/*.md`、`records/*.md` 等「我的资料」; +3. 与问题关键词命中的笔记(Library Graph 检索); +4. 模型通用知识。 + +当前使用内置的零外部依赖「本地知识地图」检索: + +- 按当前界面语言扫描并缓存 Markdown,文件大小或修改时间变化时自动重建; +- 综合标题、路径、章节标题和正文词频排序; +- 解析 Markdown 内链,并从高相关笔记扩展一层出边与入边邻居; +- 只截取命中问题的少量段落进入模型上下文,个人资料、个人方案和当前页面 + 保持最高优先级; +- 自动上下文注入与 Agent 的 `search_library` 工具复用同一检索器。 + +这一设计借鉴 Microsoft GraphRAG 的「知识图 + 原始文本片段」查询方式, +以及本地 CodeGraph 工具的「预解析关系图、按需返回少量相关内容」方式, +但没有复制或嵌入其运行时。完整 GraphRAG 索引需要额外的 LLM 抽取成本, +现阶段不适合只有几十到几百篇 Markdown 的本地桌面应用。当前方案不调用 +嵌入 API,不需要 Python、向量数据库或额外 Token。知识库规模进一步增长 +后,再评估 SQLite FTS5、可选本地嵌入与重排序。 + +参考: + +- [Microsoft GraphRAG](https://github.com/microsoft/graphrag) +- [LightRAG](https://github.com/HKUDS/LightRAG) +- [CodeGraph](https://github.com/colbymchenry/codegraph) + +## 安全与隐私 + +- 所有读操作限制在用户选择的知识目录; +- 路径 canonicalize 后检查,阻止 `../` 越界; +- AI 收录只写入 `knowledge/inbox/`,且不会覆盖同名文件; +- AI 服务商配置(包括 API Key)以明文 JSON 保存在当前用户的应用数据 + 目录 `Coffee Note/config.json`,不写入仓库或知识库; +- 个人资料只在用户发起模型请求时发送给其配置的模型服务商。 diff --git a/docs/BILINGUAL_LIBRARY.md b/docs/BILINGUAL_LIBRARY.md new file mode 100644 index 0000000..f4a67d6 --- /dev/null +++ b/docs/BILINGUAL_LIBRARY.md @@ -0,0 +1,37 @@ +# Bilingual starter library + +Coffee Note keeps one logical knowledge library rather than two independent copies. + +## File pairing + +- The canonical Chinese starter article remains `name.md`. +- Its maintained English companion is `name.en.md`. +- Both files keep the same logical `id`. +- English companions include: + +```yaml +locale: en +translation_of: path/to/name.md +``` + +The application selects `name.en.md` when the interface language is English. If the companion is +missing, it falls back to `name.md` instead of hiding the article. + +## User-authored notes + +User-created Markdown is not duplicated automatically. A note without a companion remains +available in its original language under either interface language. This avoids machine +translation silently changing personal records. + +## Translation rules + +- Preserve URLs, citations, identifiers, tables, numbers, units, evidence grades, and safety + boundaries. +- Do not strengthen causal language or add claims absent from the source. +- Keep product names, study names, and scientific terminology traceable to the original. +- Update both members of a maintained pair when starter content changes. + +## Retrieval + +AI retrieval prefers the companion matching the current interface language. User-authored notes +and selected page context remain eligible regardless of language. diff --git a/docs/NOTE_AGENT.md b/docs/NOTE_AGENT.md new file mode 100644 index 0000000..5426aef --- /dev/null +++ b/docs/NOTE_AGENT.md @@ -0,0 +1,41 @@ +# Coffee Note Note Agent + +Coffee Note is a **Note Agent**, not a coding agent or a general-purpose shell +harness. Its job is to make a user's notes useful, searchable, and cheap to +maintain. + +## Product promise + +- **Manage notes, not a codebase.** The agent reads the local Markdown library, + links related notes, captures source material, and writes only through + domain-specific note tools. +- **Stay local by default.** The library is plain Markdown on the user's + machine. Provider credentials remain in the app-data configuration file and + are never copied into the repository or library. +- **Spend tokens where they matter.** Completion comes first. The agent keeps + answers concise, retrieves only relevant excerpts, reuses stable prompt + prefixes, and blocks duplicate calls without putting task quality behind a + user-selectable economy switch. + +## Note Agent vs. coding agent + +| Coding agent | Coffee Note Note Agent | +| --- | --- | +| edits source files and runs commands | curates Markdown notes and plans | +| optimizes for implementation speed | optimizes for clarity, recall, and cost | +| broad repository context | selected local notes and Library Graph context | +| model choice is usually secondary | cheap DeepSeek-compatible models are a first-class path | + +The safe capability surface is intentionally narrow: search and read local +notes, save or update notes, update personal plan pages, and adjust the T1-T5 +priority map. There is no arbitrary shell tool. + +## Cost controls + +Cost controls are always on and are not a separate quality mode. The Agent keeps +the current task running until it reaches a genuine completion, user +cancellation, provider failure, or the global emergency safety ceiling. Within +that path it uses targeted local retrieval, stable system-prompt prefixes for +cache reuse, duplicate-call detection, stale tool-result maintenance, and +request-level context compaction. Provider cache-hit and token totals remain +visible in the conversation metrics line. diff --git a/docs/NOTE_AGENT_SAVINGS.md b/docs/NOTE_AGENT_SAVINGS.md new file mode 100644 index 0000000..4c3036b --- /dev/null +++ b/docs/NOTE_AGENT_SAVINGS.md @@ -0,0 +1,88 @@ +# Coffee Note 产品亮点说明 + +> 状态:产品定位备忘(docs/ 不参与版本,本地阅读) +> +> 这份文档记录 Coffee Note 的三个核心亮点——它们共同构成「本地优先的 +> AI 第二大脑」的产品特色,也是对外沟通、官网文案和定价说明的底稿。 + +--- + +## 亮点一:省钱 —— 深度优化 DeepSeek 和其他模型 + +**Coffee Note 是省钱的 Note Agent。** 它不是要求用户购买 Claude、GPT 或 +Opus 的 coding 工具,也不是把用户推向更昂贵的模型;而是让用户选择自己 +负担得起的模型(深度优化 DeepSeek 等低价模型),用更少 token 管好自己的 +笔记。 + +> 一句话:**让每一个 token,产出至少两倍价值。** + +这里的"至少两倍"是产品目标和价值主张,不是未经基准测试的成本保证。 + +### 已有的成本优化机制 + +- **完成优先的 Agent 循环**:不人为截断工具链,重复调用会被阻止。 +- **DeepSeek 兼容与缓存友好**:稳定的系统提示和请求结构,尽量复用供应商 + 前缀缓存,界面展示 cache hit 和 token 使用情况。 +- **高命中率记忆路由**:先取当前打开的笔记和"我的资料",再按问题选择少量 + 相关记忆,不把整本资料库重复塞给模型。 +- **Library Graph 检索**:本地 Markdown 库内按标题、路径、层级、正文和链接 + 扩展检索;不依赖 embedding API、向量数据库或额外索引 token。 +- **短输出、少重复、自动压缩**:优先可执行的短答案;过长上下文按阶段裁剪、 + 压缩、保留近期内容。 +- **本地优先**:个人资料、笔记和记忆索引留在本机,只有用户发起 AI 请求时 + 才发送必要上下文。 +- **模型可替换**:协议、地址、模型和密钥分开保存,不锁定在某一家昂贵模型。 +- **成本可见**:对话中展示 token、请求次数、缓存命中率和可用时的成本估算。 + +--- + +## 亮点二:任意视频网址 → 笔记 + +**把任意视频网址导入成结构化笔记。** 用户粘贴 YouTube、B 站、抖音、 +小红书、X 等平台的视频链接,Coffee Note 自动把视频转成文字(音频转写), +再让 Agent 整理成有条理的 Markdown 笔记——视频讲过的内容,从此可以检索、 +引用和复习。 + +> 从"看过就忘"到"视频即笔记"。不再需要一边看视频一边手动记,Agent 帮你 +> 把口播内容变成可管理的文字资产。 + +### 已实现 + +- 支持平台:YouTube、B 站、TikTok、抖音、小红书、X/Twitter 等 +- 流程:视频链接 → 音频下载 → 转写(API 或本地引擎)→ Agent 整理 → 存库 +- 本地音视频文件同样支持:拖入 mp3/mp4/m4a/wav 等 → 转录 → 整理 → 笔记 + +--- + +## 亮点三:多选 + 技能 → 生成 PPT / 视频 + +**多选多个笔记/资料,让 Agent 用各种技能一键生成 PPT 或视频。** + +把多份笔记或资料选中,告诉 Agent 你想要什么,它调用对应技能(PPT 生成、 +视频生成等)产出成品。不用复制粘贴、不用手动排版——**从笔记到演示,一条 +指令完成**。 + +> 笔记不只是记,还能"长"出 PPT 和视频。多选即素材,技能即生产力。 + +### 愿景(规划中) + +- 多选多个笔记 → 技能菜单(生成 PPT / 生成视频 / 其他) +- Agent 读取所选素材 → 提炼结构 → 调用技能生成成品 +- 与亮点二衔接:视频 → 笔记 → 再生成 PPT,形成闭环 + +--- + +## 三个亮点如何构成产品特色 + +``` +省钱(亮点一) → 用得起:便宜模型 + 少 token,人人可负担 +视频→笔记(亮点二)→ 收得进:任意视频变成可管理的文字资产 +多选→PPT/视频(亮点三)→ 出得来:笔记一键长成演示成品 +``` + +**收得进、用得起、出得来** —— 这是 Coffee Note 区别于其他笔记工具和 +coding agent 的完整产品故事。 + +## 一句话方向 + +**先用便宜的 AI 把视频和资料收进笔记,再让笔记一键长成 PPT 和视频。** diff --git a/docs/RELEASE.md b/docs/RELEASE.md new file mode 100644 index 0000000..99bed05 --- /dev/null +++ b/docs/RELEASE.md @@ -0,0 +1,102 @@ +# Coffee Note 发布流程 + +> 本文档汇总了历次发版踩过的坑,按步骤执行即可正确发布。 +> 版本号统一在 **5 个文件**,必须全部一致。 + +## 版本文件清单(5 处,缺一不可) + +| 文件 | 字段 | 示例 | +|---|---|---| +| `package.json` | `version` | `"0.0.2"` | +| `src-tauri/tauri.conf.json` | `version` | `"0.0.2"` | +| `src-tauri/Cargo.toml` | `version =` | `version = "0.0.2"` | +| `src-tauri/Cargo.lock` | `name="coffee-note"` 下一行 `version =` | `version = "0.0.2"` | +| `website/version.json` | `version` | `{"version":"0.0.2"}` | + +> **release.yml 的 tag 校验**:`tag == v{package.json.version}`。tag 和 package.json 不一致会直接失败(曾踩坑:打 v0.0.2 但 package.json 还是 0.0.1 → CI 21 秒失败)。 + +## 一、修改版本号 + +从 0.0.2 → 0.0.3 为例: + +```bash +# 1. 改 5 处版本号 +sed -i 's/"version": "0.0.2"/"version": "0.0.3"/' package.json src-tauri/tauri.conf.json website/version.json +sed -i 's/^version = "0.0.2"/version = "0.0.3"/' src-tauri/Cargo.toml +# Cargo.lock 里 coffee-note 包条目(不是所有 0.0.2,用精确匹配) +sed -i '0,/^name = "coffee-note"/{/^name = "coffee-note"/{n; s/version = "0.0.2"/version = "0.0.3"/}}' src-tauri/Cargo.lock + +# 2. 验证一致 +npm run release:check # 输出 "Release version X is consistent" +npm run typecheck # 前端 +cd src-tauri && cargo check # Rust + +# 3. 提交 +git add package.json src-tauri/tauri.conf.json src-tauri/Cargo.toml src-tauri/Cargo.lock website/version.json +git commit -m "chore: bump version to 0.0.3" +git push +``` + +## 二、打 tag 触发 CI 发布 + +```bash +git tag v0.0.3 +git push origin v0.0.3 +``` + +CI(`.github/workflows/release.yml`)自动执行: +1. **Release checks**:质量校验(npm build + 前端测试 + cargo check + release:check + tag 匹配) +2. **Create release**:建 draft release +3. **Build**:5 平台矩阵(Windows x64 / macOS ARM / macOS Intel / Linux x64 / **Linux arm64**) + - Linux arm64 用 **native `ubuntu-24.04-arm` runner**(不要交叉编译——glib-sys pkg-config 会失败) +4. **rename-assets**:统一资产命名 + - arm64 rpm 匹配 `*.aarch64.rpm`(Tauri 生成名,不是 `_arm64`)——曾踩坑导致孤儿资产 +5. **Publish release**:发布 + +**监控**:`gh run watch` 或 `gh run list --repo edison7009/Coffee-Note`。构建约 15-20 分钟。 + +## 三、发布后:bump-version 自动同步 + +`.github/workflows/bump-version.yml` 在 **release published** 时自动把 5 处版本文件升到下个版本占位——所以**发版后 main 分支版本号会变**,下次只需改下个版本号即可。 + +> 若某次不想自动 bump(如 hotfix),可临时禁用 workflow 或手动改回。 + +## 四、软件内自更新 + +应用内「下载更新」逻辑(`run_windows_update`): +- 优先 `https://note.coffeecli.com/download/windows` +- **若返回 HTML**(Cloudflare SPA fallback)→ 自动 fallback 到 GitHub release 的 `*_Windows_x64-setup.exe` +- 版本检测读 `https://note.coffeecli.com/version.json?platform=windows` + +**⚠️ 关键**:自更新逻辑在**发布的新版本**里才生效。当前 v0.0.2 安装包是旧代码,装新版(含 fallback 修复)后才能自更新。**旧版必须手动下载更新**。 + +## 五、网站部署(Cloudflare) + +网站源码在 `website/`,Cloudflare Pages 连着 GitHub 自动同步(push main 即更新): +- `index.html` → 单页双语(英文默认 + 中文切换 + 只有中文显示中国网盘) +- `install.ps1` / `install.sh` → 命令安装(优先走网站 `/download`,失败自动回退 GitHub Release) +- `version.json` → 静态兜底版本文件 +- **`_worker.js` → 关键:`/download/*` 下载代理 + 动态 `/version.json`**(复制自 Coffee-CLI `Web-Home/_worker.js` 和 OpenLongevity `website/_worker.js` 的 Pages Function) + +**⚠️ 首次部署必须在 Cloudflare Pages 启用 Advanced mode**(否则 `_worker.js` 不生效,`/download/*` 仍返回 HTML,`irm …|iex` 会报"文件或目录损坏"): +1. Cloudflare 控制台 → Workers & Pages → 本项目 → Settings → Functions → 开启 **Advanced mode** +2. 确认 `_worker.js` 已随网站部署到站点根目录(push main 自动同步) +3. 验证:`curl -I https://note.coffeecli.com/download/windows` 应返回 `Content-Type: application/octet-stream`,而非 `text/html` + +`_worker.js` 路由: +- `/download/` → 流式代理 GitHub Release 对应安装包。支持的 slug:`windows`、`windows-msi`、`darwin-aarch64`、`darwin-x64`、`macos-arm`/`macos-intel`、`linux-x64`、`linux-aarch64`、`linux-deb`/`linux-rpm`/`linux-appimage`、`linux-arm64-deb`/`-rpm`/`-appimage` +- `/version.json?platform=X` → 动态返回最新**已发布**版本;对应平台安装包还没上传时返回空串,避免"版本号先更新但包还在 CI 构建"的竞态(app 会把空串当"还没发布"处理) +- `/*` → 静态文件 + +`install.ps1` / `install.sh` 即使 Worker 未部署也能用:`/download/*` 返回 HTML 时脚本会检测并自动回退到 GitHub Release 直连下载(`install.ps1` 校验 PE 的 `MZ` 魔数;`install.sh` 校验首字节非 `<`)。 + +## 六、验收清单 + +- [ ] 5 个版本文件一致 + `release:check` 通过 +- [ ] `git tag vX.Y.Z` 与 package.json 完全一致 +- [ ] CI 全绿,**9 个资产**齐全(含 Linux arm64 deb/rpm) +- [ ] 资产命名统一(`Coffee.Note__*`) +- [ ] GitHub release 已发布(非 draft) +- [ ] bump-version 自动把版本号升到下一占位 +- [ ] 网站 version.json 已更新(Cloudflare 同步) +- [ ] 自更新在新版安装后可用(旧版需手动更新) diff --git a/docs/THIRD_PARTY_NOTICES.md b/docs/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..3798bdc --- /dev/null +++ b/docs/THIRD_PARTY_NOTICES.md @@ -0,0 +1,73 @@ +# Third-party notices + +## Open-Meteo + +Coffee Note's optional weather panel uses geocoding and forecast data from +[Open-Meteo](https://open-meteo.com/). Home itself shows only the compact +condition image and read-only forecast; the Open-Meteo attribution appears in +Settings > Appearance alongside the weather configuration. Open-Meteo combines open data +from multiple national weather services; its API data is provided under the +[Creative Commons Attribution 4.0 International licence](https://creativecommons.org/licenses/by/4.0/). + +The no-key public endpoint is suitable for development and non-commercial use. +A commercial Coffee Note release must use an appropriately licensed endpoint or a +compliant self-hosted/proxied deployment. + +## models.dev + +Coffee Note reads provider names, API endpoints, model capabilities, context limits, +pricing metadata, and provider logo assets from +[anomalyco/models.dev](https://github.com/anomalyco/models.dev). The catalog is +cached only in the current user's app-data directory. Provider names and logos +may also be trademarks of their respective owners. + +MIT License + +Copyright (c) 2025 models.dev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +## DeepSeek-Reasonix + +Coffee Note's staged context maintenance and provider cache-usage normalization +were adapted from ideas and implementation patterns in +[esengine/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix), +reviewed at commit `2c54501`. + +MIT License + +Copyright (c) 2026 Reasonix Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/import-pipeline-plan.md b/docs/import-pipeline-plan.md new file mode 100644 index 0000000..93c0531 --- /dev/null +++ b/docs/import-pipeline-plan.md @@ -0,0 +1,87 @@ +# 「导入资料」→ Agent 读取 → Markdown 开发计划 + +> 状态:待评审 | 日期:2026-08-12 | 分支:codex/note-agent(功能开发) +> 前置调研:Task 1(现状调研)✅、Task 2(GitHub 工具盘点)✅ + +--- + +## 0. 核心思路(一句话) + +**不转格式,只送内容。** 用户在「导入你的资料」界面选择本地文件(或粘贴链接/本地路径),Coffee-Note 把来源变成**文字内容**送进 Agent 上下文,**Agent 自己整理成 Markdown 并保存**(`save_note` 已有)。 + +- 文字类来源:**0 转化**——读到就是内容 +- 唯一真正的格式转换是**音频类**(视频/音频 → 文字),`transcription.rs` 已实现 +- 不做文档转换工具(pandoc / docx-rs / pdf-extract / markitdown 全不用) +- 不管模型接入与计费(设置页已有 API 页面) + +--- + +## 1. 现状事实(已确认) + +| 环节 | 位置 | 说明 | +|---|---|---| +| 前端导入入口 | `i18n.ts:129` `captureTitle: '导入你的资料'` | 已有页面,当前只收文本/URL | +| URL 抓取 | `web_reader.rs`(direct/firecrawl/jina) | 网页 → 文字内容 | +| 音视频转写 | `transcription.rs` `supports_media_url` / `audio_to_wav` / 转写 | 视频平台链接 + 本地音视频 → 文字 | +| LLM 整理 | `lib.rs:2464` `prepare_capture` | 文字 → LLM → Markdown 草稿,已工作 | +| Agent 工具 | `agent_tools.rs`:`save_note` / `read_note` / `web_fetch` | 注册 = `match name` 加分支 | +| 拖放 | `tauri.conf.json` `dragDropEnabled: false` | 当前关闭,需开启 | + +**缺口(唯一要补的)**:本地文件 → Agent 上下文。 + +--- + +## 2. 统一管线(所有来源一条线) + +``` +粘贴链接(网页) → web_reader 抓页面文字 ─┐ +选择/拖入文件(文本) → 直读文字 ─┤ +粘贴链接(视频平台) → 转音频 → 转文字 ─┼→ 文字内容 → Agent → 整理成 MD → save_note +选择本地视频/音频 → 转音频 → 转文字 ─┤ +选择图片/扫描件 → 图片内容(多模态) ─┘ +``` + +--- + +## 3. 唯一技术点:`read_file_content` + +| 输入 | 处理 | 成本 | +|---|---|---| +| `.txt` / `.md` | `fs::read_to_string` 直读 | 0 | +| `.html` | 复用 `web_reader` | 0 | +| `.docx` | 解 ZIP 提取 document.xml 文本 | 轻 | +| `.pdf` | 文本层提取(`pdf-extract` crate 或等效) | 轻 | +| `.png` / `.jpg` / `.webp` | 原样送多模态(base64) | 0 | +| `.mp3` / `.mp4` / `.m4a` / `.wav` 等 | **复用 transcription**:`audio_to_wav` + 转写 | 已有 | +| 扫描型 PDF | 逐页渲染为图片送多模态(后置可选) | 后置 | + +> 提取 ≠ 转换:只捞文本,不做排版、表格重建——那是 Agent 的事。 + +--- + +## 4. 实施步骤 + +1. **前端**:导入资料页支持文件选择 + 拖放(开启 `dragDropEnabled`)+ 识别粘贴的本地路径(`C:\...`、`/Users/...` 存在则当文件读) +2. **Rust**:新增 `read_file_content(path)` 命令,按扩展名分流读取(文本直读 / 音频走 transcription / 图片原样) +3. **Agent**:`agent_tools.rs` 新增 `read_local_file(path)` 工具;系统提示词告知 Agent「可以读取本地文件,读到内容后整理成 Markdown 用 save_note 保存」 +4. **验收**:拖入 `.pdf`/`.docx`/图片/`.txt`/`.mp3` → Agent 读出内容 → 整理成结构化 .md 入库 + +## 5. 涉及文件 + +| 文件 | 改动 | +|---|---| +| `src-tauri/src/lib.rs` | 新增 `read_file_content` 命令 + 注册 | +| `src-tauri/src/file_reader.rs`(新) | 按扩展名提取内容(文本/图片/音频) | +| `src-tauri/src/agent_tools.rs` | 新增 `read_local_file` 工具 | +| `src-tauri/tauri.conf.json` | 开启 `dragDropEnabled` | +| `src/api.ts` / `src/types.ts` | 新增 `readFileContent` API + 类型 | +| `src/App.tsx` | 导入资料页:文件选择/拖放/路径识别 | +| `src/i18n.ts` | 新增文案(zh/en) | +| `tests/` | 新增流程测试 | + +## 6. 不做(明确排除) + +- ❌ pandoc / docx-rs / pdf-extract 等转换工具 +- ❌ 格式检测分流、转换预览、降级路径 +- ❌ 模型接入、计费、API 管理(设置页已有) +- ❌ 图片 OCR 引擎(交给多模态 LLM) diff --git a/docs/reference-analysis-awesome-llm-apps.md b/docs/reference-analysis-awesome-llm-apps.md new file mode 100644 index 0000000..2498613 --- /dev/null +++ b/docs/reference-analysis-awesome-llm-apps.md @@ -0,0 +1,73 @@ +# 参考项目分析:awesome-llm-apps + +> 日期:2026-08-12 | 克隆:`references/awesome-llm-apps`(不参与版本) +> 仓库:https://github.com/Shubhamsaboo/awesome-llm-apps(132k⭐,Apache 2.0,Python 为主) + +--- + +## 概览 + +100+ AI Agents / Agent Skills / RAG Apps,6 大类。技术栈以 Python +(LangGraph/FastAPI)为主,**代码不能直接搬**(Coffee-Note 是 Rust+Tauri), +但**思路和产品形态值得借鉴**。 + +## 最值得借鉴的 5 个点 + +### 1. AI Knowledge Explorer(`generative_ui_agents/ai-knowledge-explorer`)— 最相关 + +拖文件进对话 → Agent 抽实体/概念/关系 → 渲染交互知识图谱,可点击展开节点。 + +- 验证了「**拖文件 → Agent 读内容 → 产出结构化东西**」这个交互模型 +- 不是"转换",是"**抽取 + 可视化**"——对我们已有的 `knowledge_map.rs` 是现成前瞻 +- 关键细节:用"return only JSON"指令解析,不依赖 function-calling schema, + 任何模型都能 drop-in——印证我们「不绑工具链,靠 Agent 自己组织输出」的思路 + +### 2. advisor-orchestrator-worker(`agent_skills/`)— 成本治理 + +三层模型团队:便宜 worker 并行干活,贵的 advisor 只在"承诺边界"被咨询, +每步之间加验证门,预算封顶防烧钱。 + +- 直接可抄:简单任务用小模型/少 token,关键动作才用大模型 +- 对齐我们的 `cost-optimized Note Agent memory routing`(codex/note-agent 分支) +- 验证门:每一步验证后再提交,防幻觉进入笔记库 + +### 3. Knowledge Graph RAG with Verifiable Citations(`rag_tutorials/`) + +每个回答的每个论断都链接到来源文档,多跳推理 + 可验证引用。 + +- 不需要向量 RAG,但「**可验证引用**」值得借鉴:AI 整理笔记时每条结论 + 标注来源文件,用户可溯源 +- 结合已有 `knowledge_map.rs`,「AI 整理 → 图谱关联 → 出处可查」是差异化卖点 + +### 4. Agent Skills 的标准(`agent_skills/README.md`) + +「The bar」清单是高质量 skill 的标尺: +- 真实脚本(确定性工作跑代码,不烧 token) +- 可核查的引用(每个论断可验证) +- 本地默认私密(不声明就不联网) +- 上线前真实验证(不是 happy-path 假数据) + +- 可借鉴:`read_local_file` 工具设计对标这个标准——**确定性工作(读文件、 + 捞文本)用代码,不确定的(整理、提炼)才用 LLM** + +### 5. Voice AI 与 MCP agents — 前瞻储备 + +- `voice_ai_agents/`:Coffee-Note 已有 transcription,未来可扩展语音笔记 +- `mcp_ai_agents/`(multi_mcp_agent_router 等):未来接 MCP 生态的路由参考 + +## 不建议借鉴 + +- Python 技术栈(LangGraph/FastAPI/CopilotKit)——只借思路不借代码 +- Neo4j/向量库/RAG 全家桶——本地优先不需要重型图数据库 +- Streamlit 演示型 app——产品质感不符 + +## 落地建议 + +| 优先级 | 借鉴点 | 落到 Coffee-Note | +|---|---|---| +| P0 | 拖文件→Agent→结构化产物 | 导入流水线(进行中) | +| P0 | 确定性工作用代码,思考用 LLM | `read_local_file` 工具设计 | +| P1 | 验证门/成本治理 | Agent 整理笔记前先验证 | +| P1 | 可验证引用 | AI 产出标注来源文件 | +| P2 | 知识图谱抽取 | 对齐已有 knowledge_map | +| P3 | Voice/MCP | 后续扩展 | From c88e9da183cba87b09743bf5cd42cec08fb598e3 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 16 Aug 2026 21:40:01 +0800 Subject: [PATCH 2/5] chore: establish Rust quality baseline --- src-tauri/src/file_reader.rs | 14 ++++++++---- src-tauri/src/knowledge_map.rs | 18 ++++++++-------- src-tauri/src/lib.rs | 39 +++++++++++++++++++++++----------- src-tauri/src/transcription.rs | 4 +++- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src-tauri/src/file_reader.rs b/src-tauri/src/file_reader.rs index f8aadd7..98aad0c 100644 --- a/src-tauri/src/file_reader.rs +++ b/src-tauri/src/file_reader.rs @@ -259,8 +259,8 @@ fn read_zip_entry(path: &Path, entry: &str) -> Result { /// Read a single entry from a zip file; None if the entry does not exist. fn read_zip_entry_opt(path: &Path, entry: &str) -> Result, String> { let file = fs::File::open(path).map_err(|error| format!("Could not open file: {error}"))?; - let mut archive = zip::ZipArchive::new(file) - .map_err(|error| format!("Not a valid Office file: {error}"))?; + let mut archive = + zip::ZipArchive::new(file).map_err(|error| format!("Not a valid Office file: {error}"))?; let mut found = None; for i in 0..archive.len() { let mut item = archive @@ -322,7 +322,10 @@ fn extract_shared_strings(xml: &str) -> Vec { let bytes = xml.as_bytes(); while idx < bytes.len() { if bytes[idx] == b'<' { - let end = xml[idx..].find('>').map(|v| idx + v + 1).unwrap_or(bytes.len()); + let end = xml[idx..] + .find('>') + .map(|v| idx + v + 1) + .unwrap_or(bytes.len()); let tag = &xml[idx..end]; if tag.starts_with(" String { let bytes = xml.as_bytes(); while idx < bytes.len() { if bytes[idx] == b'<' { - let end = xml[idx..].find('>').map(|v| idx + v + 1).unwrap_or(bytes.len()); + let end = xml[idx..] + .find('>') + .map(|v| idx + v + 1) + .unwrap_or(bytes.len()); let tag = &xml[idx..end]; if tag.starts_with(" GraphDiagnostics { .notes .iter() .flat_map(|note| { - note.links.iter().filter_map(|target| { - (!index.by_path.contains_key(target)) - .then(|| format!("{} -> {}", note.path, target)) - }) + note.links + .iter() + .filter(|target| !index.by_path.contains_key(target.as_str())) + .map(|target| format!("{} -> {}", note.path, target)) }) .collect::>(); broken_links.sort(); @@ -637,10 +637,10 @@ pub fn graph_diagnostics(root: &Path, locale: &str) -> GraphDiagnostics { .notes .iter() .enumerate() - .filter_map(|(note_index, note)| { - (note.links.is_empty() && index.incoming[note_index].is_empty()) - .then(|| note.path.clone()) + .filter(|(note_index, note)| { + note.links.is_empty() && index.incoming[*note_index].is_empty() }) + .map(|(_, note)| note.path.clone()) .collect::>(); orphan_notes.sort(); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 71db654..af19340 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -418,7 +418,8 @@ fn load_transcription_config() -> Result, St load_transcription_config_from(&transcription_config_path()) } -pub(crate) fn load_transcription_config_for_agent() -> Result, String> { +pub(crate) fn load_transcription_config_for_agent( +) -> Result, String> { load_transcription_config_from(&transcription_config_path()) } @@ -1976,15 +1977,29 @@ fn find_capture_url(input: &str) -> Option<&str> { let rest = &input[start..]; let end = rest .find(|character: char| { - character.is_whitespace() - || ",。!?;:、\"'<>()[]()【】{}".contains(character) + character.is_whitespace() || ",。!?;:、\"'<>()[]()【】{}".contains(character) }) .unwrap_or(rest.len()); let candidate = rest[..end].trim_end_matches(|character: char| { matches!( character, - ',' | '.' | ';' | ':' | ',' | '。' | '!' | '?' | ';' | ':' - | '、' | ')' | ']' | '}' | '>' | '"' | '\'' | '…' + ',' | '.' + | ';' + | ':' + | ',' + | '。' + | '!' + | '?' + | ';' + | ':' + | '、' + | ')' + | ']' + | '}' + | '>' + | '"' + | '\'' + | '…' ) }); if !candidate.is_empty() { @@ -2340,7 +2355,11 @@ async fn prepare_capture(request: PrepareCaptureRequest) -> Result content.text, file_reader::ContentKind::Image => { - format!("[Image file: {}]\n{}", content.label, content.image_path.unwrap_or_default()) + format!( + "[Image file: {}]\n{}", + content.label, + content.image_path.unwrap_or_default() + ) } file_reader::ContentKind::Unsupported => { return Err(format!( @@ -3099,7 +3118,7 @@ async fn run_windows_update(app: tauri::AppHandle) -> Result<(), String> { // release asset if it returns an HTML page (e.g. a static-host SPA // fallback for /download/windows) instead of a real installer. let website_download = client.get(WEBSITE_WINDOWS_DOWNLOAD).send().await; - let website_html = website_download.as_ref().map_or(false, |response| { + let website_html = website_download.as_ref().is_ok_and(|response| { response .headers() .get(reqwest::header::CONTENT_TYPE) @@ -3107,11 +3126,7 @@ async fn run_windows_update(app: tauri::AppHandle) -> Result<(), String> { .is_some_and(|content_type| content_type.contains("text/html")) }); let (mut response, expected_asset_size) = match website_download { - Ok(response) - if response.status().is_success() && !website_html => - { - (response, 0) - } + Ok(response) if response.status().is_success() && !website_html => (response, 0), _ => { let release = latest_release(&client).await?; let asset = release diff --git a/src-tauri/src/transcription.rs b/src-tauri/src/transcription.rs index e2929d5..d395aa7 100644 --- a/src-tauri/src/transcription.rs +++ b/src-tauri/src/transcription.rs @@ -1208,7 +1208,9 @@ fn media_download_directory(knowledge_root: &Path) -> Result { .canonicalize() .map_err(|error| format!("Could not access the Downloads folder: {error}"))?; if !canonical_downloads.starts_with(&canonical_root) { - return Err("The Downloads folder must stay inside the selected AI work directory".to_string()); + return Err( + "The Downloads folder must stay inside the selected AI work directory".to_string(), + ); } Ok(canonical_downloads) } From 29b6ec517f5c342e59e1052a56b4077d38aefa32 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 16 Aug 2026 21:42:08 +0800 Subject: [PATCH 3/5] ci: validate pushes and pull requests --- .github/workflows/ci.yml | 86 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..389d365 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,86 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + frontend: + name: Frontend quality + runs-on: ubuntu-22.04 + steps: + - name: Check out source + uses: actions/checkout@v6 + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Check types + run: npm run typecheck + + - name: Run frontend tests + run: npm test + + - name: Check bilingual starter library + run: npm run library:check + + - name: Check release version consistency + run: npm run release:check + + - name: Build frontend + run: npm run build + + backend: + name: Rust quality + runs-on: ubuntu-22.04 + steps: + - name: Check out source + uses: actions/checkout@v6 + + - name: Install Linux build dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + patchelf \ + libssl-dev \ + libgtk-3-dev + + - name: Install Rust 1.93 + uses: dtolnay/rust-toolchain@1.93 + with: + components: clippy, rustfmt + + - name: Cache Rust build + uses: swatinem/rust-cache@v2 + with: + workspaces: "./src-tauri -> target" + + - name: Check Rust formatting + run: cargo fmt --manifest-path src-tauri/Cargo.toml -- --check + + - name: Run Clippy + run: cargo clippy --manifest-path src-tauri/Cargo.toml --locked --all-targets -- -D warnings + + - name: Run Rust tests + run: cargo test --manifest-path src-tauri/Cargo.toml --locked From 7d64365658e51f95e5b60f835692245934986ae4 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 16 Aug 2026 23:22:39 +0800 Subject: [PATCH 4/5] fix: gate Windows-only update code --- src-tauri/src/lib.rs | 5 +++++ src-tauri/src/transcription.rs | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index af19340..e865fb4 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -34,7 +34,9 @@ const MAX_RESEARCH_CONTEXT_BYTES: usize = 32_000; const LATEST_RELEASE_API: &str = "https://api.github.com/repos/edison7009/Coffee-Note/releases/latest"; const WEBSITE_VERSION_API: &str = "https://note.coffeecli.com/version.json?platform=windows"; +#[cfg(target_os = "windows")] const WEBSITE_WINDOWS_DOWNLOAD: &str = "https://note.coffeecli.com/download/windows"; +#[cfg(target_os = "windows")] const RELEASE_DOWNLOAD_PREFIX: &str = "https://github.com/edison7009/Coffee-Note/releases/download/"; const TRANSCRIPTION_CONFIG_FILE: &str = "transcription.json"; @@ -141,9 +143,11 @@ struct CaptureRequest { #[derive(Debug, Deserialize)] struct GithubRelease { tag_name: String, + #[cfg(target_os = "windows")] assets: Vec, } +#[cfg(target_os = "windows")] #[derive(Debug, Clone, Deserialize)] struct GithubReleaseAsset { name: String, @@ -156,6 +160,7 @@ struct WebsiteVersion { version: String, } +#[cfg(target_os = "windows")] #[derive(Debug, Clone, Serialize)] struct SelfUpdateProgress { status: &'static str, diff --git a/src-tauri/src/transcription.rs b/src-tauri/src/transcription.rs index d395aa7..8037eda 100644 --- a/src-tauri/src/transcription.rs +++ b/src-tauri/src/transcription.rs @@ -31,9 +31,9 @@ static MEDIA_FETCHER_READY: tokio::sync::OnceCell = tokio::sync::OnceCe #[cfg(windows)] const CREATE_NO_WINDOW: u32 = 0x0800_0000; -fn configure_hidden_command(command: &mut Command) { +fn configure_hidden_command(_command: &mut Command) { #[cfg(windows)] - command.creation_flags(CREATE_NO_WINDOW); + _command.creation_flags(CREATE_NO_WINDOW); } #[derive(Default)] From f7f344dc1788781e5da793470daf0f33f082f6f9 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 16 Aug 2026 23:29:19 +0800 Subject: [PATCH 5/5] fix: reject Windows paths on every platform --- src-tauri/src/agent_tools.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src-tauri/src/agent_tools.rs b/src-tauri/src/agent_tools.rs index 85d0bcd..217448e 100644 --- a/src-tauri/src/agent_tools.rs +++ b/src-tauri/src/agent_tools.rs @@ -538,6 +538,10 @@ fn validate_workspace_relative_path(path: &str, allow_empty: bool) -> Result= 2 && bytes[0].is_ascii_alphabetic() && bytes[1] == b':' { + return Err("Path must stay inside the selected workspace".to_string()); + } let mut parts = Vec::new(); for component in Path::new(&normalized).components() { match component { @@ -2037,6 +2041,10 @@ mod tests { exec_update_note(&json!({"path": "C:/escape.md", "content": "x"}), &dir, "zh"); assert!(!absolute.success); assert!(absolute.output.contains("Invalid 'path'")); + let drive_relative = + exec_update_note(&json!({"path": "C:escape.md", "content": "x"}), &dir, "zh"); + assert!(!drive_relative.success); + assert!(drive_relative.output.contains("Invalid 'path'")); let _ = fs::remove_dir_all(&dir); }