From fe336da5662270d4eed7dde504911e26352748a2 Mon Sep 17 00:00:00 2001 From: jinliyl <6469360+jinliyl@users.noreply.github.com> Date: Tue, 15 Sep 2026 13:29:55 +0800 Subject: [PATCH] feat(studio): align branding with documentation site (#546) * feat(studio): align branding with documentation site * fix(studio): refine brand lockups * fix(studio): simplify welcome branding * docs(studio): add comprehensive visual guide * docs: promote Studio to top-level navigation * fix(studio): address review feedback --- .github/workflows/ci-docs.yml | 1 + .github/workflows/deploy-docs.yml | 1 + docs/.vitepress/config.mts | 34 +- github-pages/scripts/generate-content.mjs | 15 + github-pages/scripts/verify-build.mjs | 7 + github-pages/tests/generated-content.test.mjs | 23 ++ reme_studio/README.md | 357 +++++++++++++++--- reme_studio/README_ZH.md | 307 ++++++++++++--- .../files-workspace.module.css | 6 +- .../files-workspace/memory-graph.module.css | 6 +- reme_studio/app/globals.css | 266 ++++++++----- reme_studio/app/layout.tsx | 2 +- reme_studio/app/workspace.tsx | 48 ++- reme_studio/figures/agent-chat.png | Bin 0 -> 205609 bytes reme_studio/figures/appearance-dark.png | Bin 0 -> 200291 bytes reme_studio/figures/daily-notes.png | Bin 0 -> 189601 bytes reme_studio/figures/files-workspace.png | Bin 0 -> 232087 bytes reme_studio/figures/markdown-editor.png | Bin 0 -> 283836 bytes reme_studio/figures/memory-graph.png | Bin 0 -> 271439 bytes .../figures/settings-configuration.png | Bin 0 -> 276219 bytes reme_studio/figures/settings-index.png | Bin 0 -> 175205 bytes reme_studio/figures/settings-status.png | Bin 0 -> 282245 bytes reme_studio/figures/settings-version.png | Bin 0 -> 170844 bytes reme_studio/figures/studio-overview.png | Bin 0 -> 158098 bytes reme_studio/index.html | 2 +- reme_studio/package-lock.json | 4 +- reme_studio/package.json | 2 +- reme_studio/public/favicon.svg | 13 - reme_studio/public/reme-icon.svg | 27 ++ reme_studio/public/reme_logo.png | Bin 0 -> 290224 bytes reme_studio/pyproject.toml | 2 +- reme_studio/tests/brand-theme.test.mjs | 120 ++++++ reme_studio/tests/rendered-html.test.mjs | 8 + 33 files changed, 982 insertions(+), 269 deletions(-) create mode 100644 reme_studio/figures/agent-chat.png create mode 100644 reme_studio/figures/appearance-dark.png create mode 100644 reme_studio/figures/daily-notes.png create mode 100644 reme_studio/figures/files-workspace.png create mode 100644 reme_studio/figures/markdown-editor.png create mode 100644 reme_studio/figures/memory-graph.png create mode 100644 reme_studio/figures/settings-configuration.png create mode 100644 reme_studio/figures/settings-index.png create mode 100644 reme_studio/figures/settings-status.png create mode 100644 reme_studio/figures/settings-version.png create mode 100644 reme_studio/figures/studio-overview.png delete mode 100644 reme_studio/public/favicon.svg create mode 100644 reme_studio/public/reme-icon.svg create mode 100644 reme_studio/public/reme_logo.png create mode 100644 reme_studio/tests/brand-theme.test.mjs diff --git a/.github/workflows/ci-docs.yml b/.github/workflows/ci-docs.yml index 963b8e4e..84272023 100644 --- a/.github/workflows/ci-docs.yml +++ b/.github/workflows/ci-docs.yml @@ -15,6 +15,7 @@ on: - 'integrations/claude_code/README.md' - 'integrations/hermes_agent/README.md' - 'reme_studio/README*.md' + - 'reme_studio/figures/**' - 'reme_studio/public/og.jpg' - 'integrations/dsh/README*.md' - 'integrations/dsh/figures/**' diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 8f91e7b8..e468a5e7 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -12,6 +12,7 @@ on: - "README.md" - "README_ZH.md" - "reme_studio/README*.md" + - "reme_studio/figures/**" - "reme_studio/public/og.jpg" - "integrations/dsh/README*.md" - "integrations/dsh/figures/**" diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 5cc91080..5d8b06a6 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -106,6 +106,7 @@ function nav(language: "zh" | "en"): DefaultTheme.NavItem[] { return [ { text: zh ? "首页" : "Home", link: `/${language}/` }, { text: zh ? "文档" : "Docs", link: `/${language}/quick_start` }, + { text: "Studio", link: `/${language}/workspace/studio` }, { text: zh ? "集成" : "Integrations", link: `/${language}/integrations` }, { text: zh ? "插件" : "Plugins", link: `/${language}/plugin_management` }, { text: zh ? "评测" : "Benchmarks", link: `/${language}/benchmarks/longmemeval` }, @@ -185,25 +186,18 @@ function integrationsSidebar(language: "zh" | "en"): DefaultTheme.SidebarItem[] function pluginsSidebar(language: "zh" | "en"): DefaultTheme.SidebarItem[] { const zh = language === "zh"; - return [ - { - text: zh ? "工作区" : "Workspace", - collapsed: false, - items: [{ text: "ReMe Studio", link: `/${language}/workspace/studio` }], - }, - { - text: zh ? "插件" : "Plugins", - collapsed: false, - items: [ - { text: zh ? "插件管理" : "Plugin Management", link: `/${language}/plugin_management` }, - { text: zh ? "插件开发" : "Plugin Development", link: `/${language}/plugin_development` }, - { text: zh ? "每日论文" : "Daily Paper", link: `/${language}/plugins/daily-paper` }, - { text: "Auto Fin", link: `/${language}/plugins/auto-fin` }, - { text: "LME", link: `/${language}/plugins/lme` }, - { text: "BEAM", link: `/${language}/plugins/beam` }, - ], - }, - ]; + return [{ + text: zh ? "插件" : "Plugins", + collapsed: false, + items: [ + { text: zh ? "插件管理" : "Plugin Management", link: `/${language}/plugin_management` }, + { text: zh ? "插件开发" : "Plugin Development", link: `/${language}/plugin_development` }, + { text: zh ? "每日论文" : "Daily Paper", link: `/${language}/plugins/daily-paper` }, + { text: "Auto Fin", link: `/${language}/plugins/auto-fin` }, + { text: "LME", link: `/${language}/plugins/lme` }, + { text: "BEAM", link: `/${language}/plugins/beam` }, + ], + }]; } function benchmarksSidebar(language: "zh" | "en"): DefaultTheme.SidebarItem[] { @@ -235,7 +229,7 @@ function singlePageSidebar(language: "zh" | "en", page: "blog" | "faq"): Default function sidebars(language: "zh" | "en"): DefaultTheme.SidebarMulti { return { [`/${language}/integrations`]: integrationsSidebar(language), - [`/${language}/workspace/`]: pluginsSidebar(language), + [`/${language}/workspace/`]: [], [`/${language}/plugins/`]: pluginsSidebar(language), [`/${language}/plugin_management`]: pluginsSidebar(language), [`/${language}/plugin_development`]: pluginsSidebar(language), diff --git a/github-pages/scripts/generate-content.mjs b/github-pages/scripts/generate-content.mjs index 3504502c..0f0767cf 100644 --- a/github-pages/scripts/generate-content.mjs +++ b/github-pages/scripts/generate-content.mjs @@ -64,6 +64,18 @@ const externalDocumentRewrites = { "integrations/openclaw/README_ZH.md": [ ["(./README.md)", "(/en/integrations/openclaw)"], ], + "reme_studio/README.md": [ + ["(./README_ZH.md)", "(/zh/workspace/studio)"], + ["(./figures/", "(/figures/studio/"], + ["(https://github.com/agentscope-ai/ReMe/blob/main/reme_studio/README_ZH.md)", "(/zh/workspace/studio)"], + ["(https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/", "(/figures/studio/"], + ], + "reme_studio/README_ZH.md": [ + ["(./README.md)", "(/en/workspace/studio)"], + ["(./figures/", "(/figures/studio/"], + ["(https://github.com/agentscope-ai/ReMe/blob/main/reme_studio/README.md)", "(/en/workspace/studio)"], + ["(https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/", "(/figures/studio/"], + ], }; const externalDocumentPreambles = { @@ -220,6 +232,9 @@ for (const [destination, source] of externalDocuments) { await cp(path.join(repoDir, "integrations/dsh/figures"), path.join(outputDir, "public/figures/dsh"), { recursive: true, }); +await cp(path.join(repoDir, "reme_studio/figures"), path.join(outputDir, "public/figures/studio"), { + recursive: true, +}); for (const language of ["zh", "en"]) { await cp( diff --git a/github-pages/scripts/verify-build.mjs b/github-pages/scripts/verify-build.mjs index 86b214f8..12317dda 100644 --- a/github-pages/scripts/verify-build.mjs +++ b/github-pages/scripts/verify-build.mjs @@ -49,6 +49,8 @@ const requiredFiles = [ "en/configuration.html", "zh/services.html", "en/services.html", + "zh/workspace/studio.html", + "en/workspace/studio.html", "zh/reference/jobs.html", "en/reference/jobs.html", "zh/configuration/llms.txt", @@ -70,6 +72,11 @@ assert.match(ChineseHomepage, /用真实评测/); assert.match(ChineseHomepage, /89\.4%/); assert.match(ChineseHomepage, /公开、透明的访问趋势/); +const ChineseStudio = await readFile(path.join(outputDir, "zh/workspace/studio.html"), "utf8"); +assert.match(ChineseStudio, /class="VPLink link VPNavBarMenuLink active" href="\/zh\/workspace\/studio"/); +assert.match(ChineseStudio, /]*>Studio<\/span>/); +assert.doesNotMatch(ChineseStudio, /]*class="VPSidebar/); + const ChineseTraffic = await readFile(path.join(outputDir, "zh/traffic.html"), "utf8"); assert.match(ChineseTraffic, /S1OZK1PSDLEpyiU5/); diff --git a/github-pages/tests/generated-content.test.mjs b/github-pages/tests/generated-content.test.mjs index a1ca8446..89ae4edf 100644 --- a/github-pages/tests/generated-content.test.mjs +++ b/github-pages/tests/generated-content.test.mjs @@ -34,6 +34,7 @@ test("generates every required bilingual guide", async () => { await access(path.join(generatedDir, "zh/integrations/openclaw.md")); await access(path.join(generatedDir, "en/integrations/openclaw.md")); await access(path.join(generatedDir, "public/figures/dsh/reme-status-overview.png")); + await access(path.join(generatedDir, "public/figures/studio/studio-overview.png")); }); test("maps mirrored pages back to their canonical repository sources", async () => { @@ -69,6 +70,26 @@ test("publishes portable and accurate DSH instructions", async () => { assert.match(chinese, /每 5 秒仅刷新 DSH 插件的运行时计数/); }); +test("publishes Studio screenshots with site-safe links", async () => { + const english = await readFile(path.join(generatedDir, "en/workspace/studio.md"), "utf8"); + const chinese = await readFile(path.join(generatedDir, "zh/workspace/studio.md"), "utf8"); + assert.match(english, /\(\/figures\/studio\/studio-overview\.png\)/); + assert.match(chinese, /\(\/figures\/studio\/settings-status\.png\)/); + assert.match(english, /\(\/zh\/workspace\/studio\)/); + assert.match(chinese, /\(\/en\/workspace\/studio\)/); +}); + +test("keeps Studio source READMEs portable for package registries", async () => { + for (const name of ["README.md", "README_ZH.md"]) { + const source = await readFile(path.join(repoDir, "reme_studio", name), "utf8"); + assert.doesNotMatch(source, /\]\(\.\/figures\//); + assert.match( + source, + /\]\(https:\/\/raw\.githubusercontent\.com\/agentscope-ai\/ReMe\/main\/reme_studio\/figures\//, + ); + } +}); + test("generates the callable Job reference from default.yaml", async () => { const config = parseYaml(await readFile(path.join(repoDir, "reme/config/default.yaml"), "utf8")); const callableJobs = Object.entries(config.jobs) @@ -114,6 +135,8 @@ test("tracks every generated input in documentation CI and deployment", async () "integrations/dsh/README*.md", "integrations/dsh/figures/**", "integrations/openclaw/README*.md", + "reme_studio/README*.md", + "reme_studio/figures/**", "benchmark/toolmemory/gitcha.png", ]; for (const workflow of ["ci-docs.yml", "deploy-docs.yml"]) { diff --git a/reme_studio/README.md b/reme_studio/README.md index ecfac0e0..f9ddb5fa 100644 --- a/reme_studio/README.md +++ b/reme_studio/README.md @@ -2,13 +2,60 @@ English | [简体中文](https://github.com/agentscope-ai/ReMe/blob/main/reme_studio/README_ZH.md) -ReMe Studio is the local web workspace for ReMe. It lets you browse and edit user-owned workspace files, explore memory -links, and chat with the ReMe Agent without moving durable memory into a separate application database. Search indexes, -graphs, and other derived metadata remain rebuildable from the source files. +ReMe Studio is the local web workspace for [ReMe](https://github.com/agentscope-ai/ReMe). It provides one place to +browse and edit user-owned memory files, inspect the relationships between them, talk to the ReMe Agent, and understand +the health of the local service. -![ReMe Studio workspace](https://github.com/user-attachments/assets/7d0db0d4-69c5-49ef-b1ca-ef8c6cab1138) +Studio follows ReMe's local-first model: Markdown and other workspace files remain the durable source of truth. Search +indexes, catalogs, graphs, caches, and runtime metadata stay derived and rebuildable. -## Installation +![ReMe Studio overview](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/studio-overview.png) + +> All screenshots in this README were captured from a real local ReMe service with the Studio UI set to English. The +> isolated `Project Aurora` workspace is fictional and contains no user data, credentials, or private endpoints. + +## What Studio provides + +| Area | Capabilities | +| --------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| Workspace | Browse the complete workspace, focused daily notes, or durable knowledge; refresh automatically as files change | +| Markdown | Preview front matter and GitHub Flavored Markdown, edit with Monaco, save safely, download, and keep multiple tabs open | +| Memory graph | Explore indexed wikilinks by knowledge category, follow link direction, zoom, fit, and open source notes | +| Agent chat | Stream answers, reasoning, tool activity, approvals, data blocks, and usage; reference workspace files by dragging them into chat | +| Service center | Inspect process and component health, rebuild derived indexes, review the redacted effective configuration, and verify versions | +| Personalization | Switch between English and Chinese and select light, dark, or system appearance | + +## How it fits together + +```text +Your local workspace files + │ + ▼ + ReMe HTTP service + ├── file operations + ├── derived indexes + ├── wikilink graph + └── read-only Agent chat + │ + ▼ + ReMe Studio +``` + +Studio is a client of the ReMe HTTP service. It does not replace the workspace with a separate document database and it +does not make browser-local state the durable source of truth. Tabs and interface preferences are conveniences; the +files in the configured ReMe workspace remain authoritative. + +## Requirements + +- Python 3.11 or newer with ReMe installed. +- A running ReMe HTTP service. +- A working Agent and model configuration only if Agent chat is used. +- Node.js 22.13 or newer only when developing or building Studio from source. + +See the [ReMe repository README](https://github.com/agentscope-ai/ReMe#readme) for backend installation, workspace, and +model configuration. + +## Install Install Studio together with ReMe's optional integrations: @@ -16,40 +63,221 @@ Install Studio together with ReMe's optional integrations: pip install "reme-ai[core]" ``` -For Studio without the other optional integrations, use `pip install "reme-ai[web]"`. The base `reme-ai` package is -headless and does not include the frontend assets. +Install only the web extra when the other optional integrations are unnecessary: -The same prebuilt static workspace is available for Node.js applications: +```bash +pip install "reme-ai[web]" +``` + +The base `reme-ai` package is headless and does not include the frontend assets. + +Node.js applications can install the same prebuilt static workspace: ```bash npm install @agentscope-ai/reme_studio ``` -Its static entry point is installed at `@agentscope-ai/reme_studio/dist-static/index.html`. +The static entry point is installed at `@agentscope-ai/reme_studio/dist-static/index.html`. -## Features +## Start Studio -- **Workspace browsing**: browse the full workspace or focus on journal and knowledge files through dedicated views. The - navigator refreshes as files change on disk. -- **Markdown editing and preview**: open multiple files in tabs, render Markdown front matter and GitHub Flavored - Markdown, edit with Monaco, save with optimistic modification-time checks, and download files locally. -- **Memory graph**: inspect indexed wikilinks under the `wiki`, `personal`, and - `procedure` knowledge roots, follow inbound and outbound links, and open the corresponding Markdown source. -- **Agent chat**: stream conversations with the read-only workspace Agent, see tool calls and token usage, and drag - workspace files into the conversation as references. -- **Service management**: inspect service and component memory usage, review the effective redacted configuration and - version, and rebuild derived indexes without modifying source memory files. -- **Personalization**: switch between English and Chinese, and use light, dark, or system appearance. +Start the ReMe HTTP service: -## Requirements +```bash +reme start +``` -- Python 3.11 or newer with ReMe installed. -- A running ReMe HTTP service. Agent chat additionally requires a working Agent and model configuration. -- Node.js 22.13 or newer is required only when developing or building Studio from source. +Then open . The default HTTP service serves both the API and the packaged Studio frontend. -See the [repository README](https://github.com/agentscope-ai/ReMe#readme) for ReMe installation and backend configuration. +To use a different workspace or port, pass normal ReMe configuration overrides: -## Development +```bash +reme start workspace_dir=/absolute/path/to/workspace service.port=8000 +``` + +Studio displays the connected endpoint at the bottom of the navigator. A green indicator means the browser can reach +the service. If Studio is hosted separately, configure its API URL as described in +[Frontend configuration](#frontend-configuration). + +## Interface tour + +### 1. Workspace navigator + +The left navigator exposes four primary entry points: + +- **Files** shows supported files across the entire ReMe workspace. +- **Daily** focuses on files under the configured daily-memory directory. +- **Knowledge** focuses on the `wiki`, `personal`, and `procedure` directories under durable digest memory. +- **Chat** opens a fresh Agent conversation without closing files or graphs that are already open. + +Directories can be expanded independently. The navigator hides dotfiles and dot-directories, orders the newest files +first, applies a bounded result limit, and refreshes when files change on disk. The divider can be dragged to resize the +navigator, and the menu control in the top bar can collapse it. + +![Workspace navigator and Markdown preview](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/files-workspace.png) + +The tab strip keeps files, graphs, and conversations together. Closing a tab does not delete its file. When an edited +file has unsaved changes, Studio protects it from an accidental close and also supports closing the current tab or the +other tabs from the tab context menu. + +### 2. Daily notes + +The Daily view gathers both day-index pages and dated note directories without changing their on-disk paths. It is a +focused view of the same local workspace, so a note opened here can also appear in Files and remains available to ReMe's +indexing and Agent workflows. + +![Daily notes](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/daily-notes.png) + +Use Daily to review chronological memory, open generated resource notes, and move between recent work without browsing +the rest of the workspace tree. + +### 3. Markdown preview + +Preview mode renders: + +- YAML front matter in a dedicated summary block. +- Headings, lists, links, code, tables, task lists, and other GitHub Flavored Markdown. +- The original workspace-relative path above the document. + +The download control saves a local copy through the browser. Preview mode never modifies the source file. + +![Markdown preview](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/files-workspace.png) + +### 4. Markdown editing and safe saves + +Edit mode uses Monaco with Markdown syntax highlighting, line numbers, keyboard navigation, and a full-height editing +surface. Switch between **Preview** and **Edit** at any time. + +![Markdown editor](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/markdown-editor.png) + +Studio records the file modification time when it loads a document and sends that value back on save. If another +process changed the file in the meantime, the ReMe service rejects the stale write instead of silently overwriting the +newer content. After a successful save, Studio refreshes the file state and clears the unsaved marker. + +### 5. Knowledge and memory graphs + +Knowledge groups durable memory into three conventional roots: + +- `wiki` for entities, projects, topics, and stable knowledge. +- `personal` for user-specific preferences and long-lived context. +- `procedure` for repeatable workflows and operating knowledge. + +Each category has a **Graph** action. The graph is generated from ReMe's indexed wikilinks and preserves their original +source-to-target direction. + +![Memory graph](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/memory-graph.png) + +The graph view provides: + +- Indexed-file nodes, category roots, and directed wikilink edges. +- Node and link counts for the selected category. +- Zoom in, zoom out, and fit-to-view controls. +- A legend for file nodes and link direction. +- Direct opening of the corresponding Markdown source when a file node is selected. + +The graph is derived state. If it is empty, first allow ReMe to ingest the relevant files; rebuilding search indexes in +Settings does not rescan files or reconstruct the wikilink graph. + +### 6. Agent chat + +Chat opens in the same tab workspace, so files and graphs can remain available while a conversation is in progress. + +![Agent chat](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/agent-chat.png) + +The conversation surface supports: + +- Token-by-token streaming from the ReMe Agent. +- Separate rendering for answer text, reasoning, tool calls, structured data, approvals, and usage information. +- Collapsible completed reasoning and tool blocks to keep long conversations readable. +- Multiple independent chat tabs and resumable backend session identifiers. +- Starter prompts for common memory questions. +- Dragging a workspace file into the composer to insert a path reference. +- `Enter` to send and `Shift+Enter` for a new line. + +The built-in chat job is read-only with respect to the workspace. It can search and read context but should not be used +as a substitute for explicitly saving durable knowledge. Chat requires a valid Agent/model configuration; file browsing, +editing, graphs, and service inspection do not. + +### 7. Service status + +Open **Settings** from the top bar to inspect the backend that Studio is actually using. + +![Service status](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/settings-status.png) + +Status reports: + +- Whether the ReMe service is reachable and the endpoint in use. +- The current ReMe backend version. +- Process resident memory and total memory estimated for stateful components. +- The number of healthy components. +- Runtime facts for the file graph, file store, keyword index, and embedding store when configured, including nodes, + edges, chunks, documents, vocabulary, dimensions, cache size, and component memory where available. + +Use **Refresh** to request a new snapshot after files are indexed or service configuration changes. + +### 8. Index management + +The Index page provides an explicit maintenance action for derived search state. + +![Index management](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/settings-index.png) + +**Rebuild index** reconstructs the BM25, embedding, and tag indexes from chunks that ReMe has already ingested. Before +running, Studio asks for confirmation. This action does not scan the workspace, rechunk files, edit source memory, or +rebuild the wikilink graph. Those boundaries preserve ReMe's file-native source of truth. + +### 9. Effective configuration + +Configuration shows the fully resolved application configuration returned by the connected backend. + +![Effective configuration](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/settings-configuration.png) + +This is useful for confirming the active workspace, directory layout, service options, jobs, and component backends +after file configuration and command-line overrides have been merged. Sensitive fields are redacted by ReMe before the +configuration reaches the browser. + +### 10. Version and endpoint + +Version presents the ReMe backend version and service endpoint separately from the Studio version shown in the top-left +brand lockup. + +![Version and endpoint](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/settings-version.png) + +This distinction helps when diagnosing a frontend/backend mismatch: the top bar identifies the installed Studio build, +while Settings identifies the connected ReMe service. + +### 11. Language and appearance + +The top bar switches the entire interface between English and Chinese. Appearance supports **Light**, **Dark**, and +**System**; System follows the operating-system color preference. + +![ReMe Studio dark appearance](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/appearance-dark.png) + +The selected language and appearance are remembered in the browser. Both themes use the same ReMe documentation-site +brand palette and retain health, selection, focus, and graph contrast. + +## Frontend configuration + +Copy `.env.example` to `.env.local` when persistent local overrides are useful. The vinext/Sites build reads +`NEXT_PUBLIC_*`; the FastAPI/static build reads the matching `VITE_*` names. + +| Setting | Build | Purpose | +| --------------------------------------- | ------------ | -------------------------------------------------------------- | +| `NEXT_PUBLIC_REME_API_URL` | vinext/Sites | ReMe HTTP service URL; defaults to `http://127.0.0.1:2333` | +| `NEXT_PUBLIC_REME_WORKSPACE_EXTENSIONS` | vinext/Sites | Comma-separated file extensions shown in the workspace | +| `VITE_REME_API_URL` | static | ReMe HTTP service URL; use `/` for same-origin FastAPI hosting | +| `VITE_REME_WORKSPACE_EXTENSIONS` | static | Static-build counterpart of the workspace extension list | + +Markdown and text files are visible by default. To include additional text formats: + +```bash +NEXT_PUBLIC_REME_WORKSPACE_EXTENSIONS=md,txt,mdx +VITE_REME_WORKSPACE_EXTENSIONS=md,txt,mdx +``` + +Only enable formats the browser can safely treat as text. ReMe still enforces workspace containment, allowed paths, +encoding checks, size limits, and optimistic modification-time checks on its API boundary. + +## Develop from source Start ReMe from the repository root, then run the frontend in another terminal: @@ -63,14 +291,14 @@ npm install npm run dev ``` -Open . The frontend connects to -`http://127.0.0.1:2333` by default. Override it when needed: +Open . The development frontend connects to `http://127.0.0.1:2333` by default. Override it when +needed: ```bash NEXT_PUBLIC_REME_API_URL=http://127.0.0.1:8000 npm run dev ``` -## ReMe-hosted static build +## Build the ReMe-hosted static frontend ReMe can serve Studio from the same FastAPI process as its HTTP API. Build the static variant and restart ReMe: @@ -82,36 +310,52 @@ cd .. reme start ``` -Open . The static build uses same-origin requests by default. For standalone static development, -run `npm run dev:static` and set -`VITE_REME_API_URL` to the running ReMe service URL when necessary. - -The regular `npm run build` command remains the vinext/Sites deployment build; -`npm run build:static` creates `dist-static/` exclusively for FastAPI and Python package distribution. - -## Configuration - -Copy `.env.example` to `.env.local` when persistent local overrides are useful. The vinext/Sites build reads -`NEXT_PUBLIC_*`; the FastAPI/static build reads the matching `VITE_*` names: - -| Setting | Build | Purpose | -| --------------------------------------- | ------------ | -------------------------------------------------------------- | -| `NEXT_PUBLIC_REME_API_URL` | vinext/Sites | ReMe HTTP service URL; defaults to `http://127.0.0.1:2333` | -| `NEXT_PUBLIC_REME_WORKSPACE_EXTENSIONS` | vinext/Sites | Comma-separated file extensions visible in the workspace | -| `VITE_REME_API_URL` | static | ReMe HTTP service URL; use `/` for same-origin FastAPI hosting | -| `VITE_REME_WORKSPACE_EXTENSIONS` | static | Static-build counterpart of the workspace extension list | - -The workspace hides dotfiles and dot-directories. It displays Markdown and text files by default. For example: +Open . The static build uses same-origin requests by default. For standalone static development: ```bash -NEXT_PUBLIC_REME_WORKSPACE_EXTENSIONS=md,txt,mdx -VITE_REME_WORKSPACE_EXTENSIONS=md,txt,mdx +VITE_REME_API_URL=http://127.0.0.1:2333 npm run dev:static ``` -The memory graph requires an index built by ReMe. Rebuilding the index from the Studio settings regenerates derived data -from workspace files and does not modify the source memory. +`npm run build` remains the vinext/Sites deployment build. `npm run build:static` creates `dist-static/` exclusively for +FastAPI and Python/npm package distribution. Change frontend source rather than committing generated distribution files. -## Checks +## Troubleshooting + +### Studio says the service is unavailable + +- Confirm `reme start` is still running. +- Check the endpoint displayed at the bottom of the navigator. +- For a separately hosted frontend, set the correct `NEXT_PUBLIC_REME_API_URL` or `VITE_REME_API_URL` before starting or + building it. +- Open Settings → Status and use Refresh after correcting the endpoint. + +### Files are missing from the navigator + +- Confirm ReMe was started with the intended `workspace_dir`. +- Remember that dotfiles and dot-directories are intentionally hidden. +- Check `*_REME_WORKSPACE_EXTENSIONS` when the file is not Markdown or plain text. +- Use Files instead of the focused Daily or Knowledge view. + +### The graph is empty or incomplete + +- Confirm the notes are under `digest/wiki`, `digest/personal`, or `digest/procedure`. +- Confirm the source files contain wikilinks and have been ingested by ReMe. +- Do not expect Settings → Rebuild index to rescan files or rebuild the graph; it only rebuilds search indexes from + existing chunks. + +### A Markdown save is rejected + +The file changed after Studio loaded it. Preserve the newer on-disk version, reopen or refresh the file, reapply the +intended edit, and save again. This conflict is deliberate protection against silent data loss. + +### Chat does not start + +File features can work while chat is unavailable. Verify the configured Agent wrapper, model, API key, and provider +endpoint in the ReMe backend, then check Settings → Status. Do not place credentials in frontend environment variables. + +## Validation + +Run proportionate checks from `reme_studio/`: ```bash npm run format:check @@ -120,3 +364,6 @@ npm run build npm run build:static npm test ``` + +The screenshots above also serve as a visual tour of the supported interface, but automated checks remain the source of +truth for frontend behavior. diff --git a/reme_studio/README_ZH.md b/reme_studio/README_ZH.md index d238f788..9577686d 100644 --- a/reme_studio/README_ZH.md +++ b/reme_studio/README_ZH.md @@ -2,21 +2,67 @@ [English](https://github.com/agentscope-ai/ReMe/blob/main/reme_studio/README.md) | 简体中文 -ReMe Studio 是 ReMe 的本地 Web 工作区。你可以在这里浏览和编辑自己拥有的工作区文件、探索记忆之间的联系,并与 ReMe Agent -对话,而无需将持久记忆迁移到独立的应用数据库中。搜索索引、图谱和其他派生元数据均可根据源文件重建。 +ReMe Studio 是 [ReMe](https://github.com/agentscope-ai/ReMe) 的本地 Web 工作区。你可以在一个界面中浏览和编辑用户拥有的记忆文件、查看文件之间的关系、与 ReMe Agent 对话,并了解本地服务的运行状态。 -![ReMe Studio 工作区](https://github.com/user-attachments/assets/7d0db0d4-69c5-49ef-b1ca-ef8c6cab1138) +Studio 遵循 ReMe 的本地优先理念:Markdown 和其他工作区文件始终是持久数据的唯一事实来源;搜索索引、目录、图谱、缓存和运行时元数据都是可重建的派生数据。 + +![ReMe Studio 概览](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/studio-overview.png) + +> 本 README 中的所有截图均来自真实运行的本地 ReMe 服务,Studio 界面统一设置为英文。截图使用隔离的虚构 `Project Aurora` 工作区,不包含用户数据、凭证或私有服务地址。 + +## Studio 提供什么 + +| 区域 | 能力 | +| ---------- | ----------------------------------------------------------------------------------------------------- | +| 工作区 | 浏览完整工作区、聚焦日记或长期知识;磁盘文件变化后自动刷新 | +| Markdown | 预览 front matter 和 GitHub Flavored Markdown,使用 Monaco 编辑、安全保存、下载,并同时打开多个标签页 | +| 记忆图谱 | 按知识分类查看已索引的 wikilink、识别链接方向、缩放和适应画布,并打开源笔记 | +| Agent 对话 | 流式展示回答、推理、工具、审批、结构化数据和用量;可把工作区文件拖入对话作为引用 | +| 服务中心 | 查看进程和组件健康状态、重建派生索引、检查脱敏后的生效配置,并核对版本 | +| 个性化 | 切换中英文,以及浅色、深色或跟随系统的外观 | + +## 工作方式 + +```text +用户拥有的本地工作区文件 + │ + ▼ + ReMe HTTP 服务 + ├── 文件操作 + ├── 派生索引 + ├── wikilink 图谱 + └── 只读 Agent 对话 + │ + ▼ + ReMe Studio +``` + +Studio 是 ReMe HTTP 服务的客户端。它不会用独立文档数据库替代工作区,也不会把浏览器中的状态当作持久数据来源。标签页和界面偏好只是使用便利;配置的 ReMe 工作区中的文件始终具有权威性。 + +## 环境要求 + +- Python 3.11 或更高版本,并已安装 ReMe。 +- 正在运行的 ReMe HTTP 服务。 +- 仅在使用 Agent 对话时需要可用的 Agent 和模型配置。 +- 仅在从源码开发或构建 Studio 时需要 Node.js 22.13 或更高版本。 + +后端安装、工作区和模型配置请参阅 [ReMe 仓库中文 README](https://github.com/agentscope-ai/ReMe/blob/main/README_ZH.md)。 ## 安装 -安装 Studio 和 ReMe 的可选集成功能: +安装 Studio 及 ReMe 的可选集成功能: ```bash pip install "reme-ai[core]" ``` -如果只需要 Studio,不需要其他可选集成,可以使用 `pip install "reme-ai[web]"`。基础 `reme-ai` 包以无界面模式分发, -不包含前端资源。 +如果不需要其他可选集成,只安装 Web extra: + +```bash +pip install "reme-ai[web]" +``` + +基础 `reme-ai` 包以无界面模式分发,不包含前端资源。 Node.js 应用也可以安装同一份预构建静态工作区: @@ -26,26 +72,178 @@ npm install @agentscope-ai/reme_studio 静态入口安装在 `@agentscope-ai/reme_studio/dist-static/index.html`。 -## 功能 +## 启动 Studio -- **浏览工作区**:浏览完整工作区,或通过独立视图聚焦日记和知识文件;磁盘中的文件发生变化后,导航器会自动刷新。 -- **Markdown 编辑与预览**:在多个标签页中打开文件,渲染 Markdown front matter 和 GitHub Flavored Markdown,使用 Monaco - 编辑器编辑,通过修改时间检查安全保存,并可将文件下载到本地。 -- **记忆图谱**:查看知识库 `wiki`、`personal` 和 `procedure` 目录中已索引的 wikilink,检查入链和出链,并从图谱打开对应的 - Markdown 源文件。 -- **Agent 对话**:与只读工作区 Agent 进行流式对话,查看工具调用和 token 用量,还可将工作区文件拖入对话作为引用。 -- **服务管理**:查看服务及组件的内存使用情况、当前生效的脱敏配置和版本,并在不修改记忆源文件的情况下重建派生索引。 -- **个性化设置**:切换中英文界面,并使用浅色、深色或跟随系统的外观。 +启动 ReMe HTTP 服务: -## 环境要求 +```bash +reme start +``` -- Python 3.11 或更高版本,并已安装 ReMe。 -- 正在运行的 ReMe HTTP 服务。Agent 对话还需要可用的 Agent 和模型配置。 -- 只有从源码开发或构建 Studio 时才需要 Node.js 22.13 或更高版本。 +然后打开 。默认 HTTP 服务会同时提供 API 和打包后的 Studio 前端。 -ReMe 的安装和后端配置请参阅[仓库中文 README](https://github.com/agentscope-ai/ReMe/blob/main/README_ZH.md)。 +如需使用其他工作区或端口,可以传入正常的 ReMe 配置覆盖: -## 本地开发 +```bash +reme start workspace_dir=/absolute/path/to/workspace service.port=8000 +``` + +Studio 会在导航器底部显示当前连接的服务地址;绿色指示点表示浏览器能够访问该服务。如果 Studio 与后端分开托管,请按[前端配置](#前端配置)设置 API 地址。 + +## 界面与能力详解 + +### 1. 工作区导航器 + +左侧导航器提供四个主要入口: + +- **Files** 显示整个 ReMe 工作区中受支持的文件。 +- **Daily** 聚焦配置的日记目录。 +- **Knowledge** 聚焦长期摘要记忆下的 `wiki`、`personal` 和 `procedure` 目录。 +- **Chat** 新建 Agent 对话,同时保留已经打开的文件和图谱。 + +目录可以独立展开。导航器会隐藏点文件和点目录,优先显示最新文件,应用有界的结果数量,并在磁盘文件变化后刷新。可以拖动分隔线调整导航器宽度,也可以使用顶部菜单按钮将其收起。 + +![工作区导航器与 Markdown 预览](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/files-workspace.png) + +标签栏会把文件、图谱和对话放在同一个工作空间中。关闭标签页不会删除文件。文件存在未保存修改时,Studio 会阻止误关闭;标签页上下文菜单还支持关闭当前标签页或关闭其他标签页。 + +### 2. 日记 + +Daily 视图会汇总日期索引页面和按日期组织的笔记目录,但不会改变它们在磁盘上的路径。它只是同一本地工作区的聚焦视图,因此在这里打开的笔记也可以出现在 Files 中,并继续参与 ReMe 的索引和 Agent 工作流。 + +![日记视图](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/daily-notes.png) + +可以用 Daily 检查时间顺序记忆、打开资源生成的笔记,并在无需浏览完整工作区树的情况下查看近期工作。 + +### 3. Markdown 预览 + +预览模式支持: + +- 在独立摘要区展示 YAML front matter。 +- 渲染标题、列表、链接、代码、表格、任务列表和其他 GitHub Flavored Markdown。 +- 在文档上方显示原始的工作区相对路径。 + +下载按钮会通过浏览器保存文件副本;预览模式不会修改源文件。 + +![Markdown 预览](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/files-workspace.png) + +### 4. Markdown 编辑与安全保存 + +编辑模式使用 Monaco,提供 Markdown 语法高亮、行号、键盘导航和占满可用高度的编辑区域。可以随时在 **Preview** 和 **Edit** 之间切换。 + +![Markdown 编辑器](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/markdown-editor.png) + +Studio 会在加载文档时记录文件修改时间,并在保存时把该值发送给后端。如果其他进程已经修改文件,ReMe 服务会拒绝过期写入,而不是静默覆盖更新后的内容。保存成功后,Studio 会刷新文件状态并清除未保存标记。 + +### 5. 知识与记忆图谱 + +Knowledge 将长期记忆分为三个约定目录: + +- `wiki`:实体、项目、主题和稳定知识。 +- `personal`:用户偏好和长期个人上下文。 +- `procedure`:可重复执行的流程和操作知识。 + +每个分类都有 **Graph** 操作。图谱由 ReMe 已索引的 wikilink 生成,并保留原始的源到目标方向。 + +![记忆图谱](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/memory-graph.png) + +图谱视图提供: + +- 已索引文件节点、分类根节点和有向 wikilink 边。 +- 当前分类的节点数与链接数。 +- 放大、缩小和适应画布控制。 +- 文件节点和链接方向图例。 +- 选择文件节点后直接打开相应 Markdown 源文件。 + +图谱属于派生状态。如果图谱为空,请先让 ReMe 摄取相关文件;Settings 中的搜索索引重建不会重新扫描文件,也不会重新构建 wikilink 图谱。 + +### 6. Agent 对话 + +Chat 会在同一个标签工作区中打开,因此对话进行时仍可保留文件和图谱。 + +![Agent 对话](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/agent-chat.png) + +对话界面支持: + +- ReMe Agent 逐 token 流式输出。 +- 分别展示回答文本、推理、工具调用、结构化数据、审批和用量信息。 +- 自动折叠已经完成的推理和工具块,让长对话更易阅读。 +- 多个独立对话标签页和可恢复的后端 session id。 +- 常见记忆问题的起始提示。 +- 将工作区文件拖入输入框以插入路径引用。 +- 按 `Enter` 发送,按 `Shift+Enter` 换行。 + +内置 chat job 对工作区只读:它可以搜索和读取上下文,但不应替代明确保存长期知识的操作。Agent 对话需要有效的 Agent/模型配置;文件浏览、编辑、图谱和服务检查不需要模型。 + +### 7. 服务状态 + +从顶部栏打开 **Settings**,可以检查 Studio 实际连接的后端。 + +![服务状态](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/settings-status.png) + +Status 会显示: + +- ReMe 服务是否可达及当前使用的服务地址。 +- 当前 ReMe 后端版本。 +- 进程常驻内存及有状态组件估算的总内存。 +- 健康组件数量。 +- 已配置的文件图谱、文件存储、关键词索引和 embedding 存储的运行信息,包括节点、边、chunk、文档、词表、维度、缓存大小和组件内存等可用字段。 + +在文件完成索引或服务配置变化后,可以用 **Refresh** 获取新快照。 + +### 8. 索引管理 + +Index 页面为派生搜索状态提供明确的维护入口。 + +![索引管理](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/settings-index.png) + +**Rebuild index** 会基于 ReMe 已经摄取的 chunk 重建 BM25、embedding 和 tag 索引。Studio 会在执行前要求确认。该操作不会扫描工作区、重新分块、修改源记忆,也不会重建 wikilink 图谱。这些边界保证 ReMe 的文件仍是唯一事实来源。 + +### 9. 生效配置 + +Configuration 显示当前后端返回的完整解析后应用配置。 + +![生效配置](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/settings-configuration.png) + +在文件配置与命令行覆盖完成合并后,可以在这里确认实际工作区、目录布局、服务选项、jobs 和组件后端。ReMe 会在配置到达浏览器之前对敏感字段进行脱敏。 + +### 10. 版本与服务地址 + +Version 会显示 ReMe 后端版本和服务地址,并与左上角品牌区域显示的 Studio 版本明确区分。 + +![版本与服务地址](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/settings-version.png) + +这有助于定位前后端版本不一致的问题:顶部栏标识当前安装的 Studio 构建,Settings 标识当前连接的 ReMe 服务。 + +### 11. 语言与外观 + +顶部栏可以把整个界面切换为英文或中文。外观支持 **Light**、**Dark** 和 **System**;System 会跟随操作系统的颜色偏好。 + +![ReMe Studio 深色外观](https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/appearance-dark.png) + +语言和外观选择会保存在浏览器中。两套主题都复用 ReMe 文档站的品牌色,并为健康状态、选择状态、焦点和图谱保留足够对比度。 + +## 前端配置 + +如需持久保存本地覆盖,可以将 `.env.example` 复制为 `.env.local`。vinext/Sites 构建读取 `NEXT_PUBLIC_*`,FastAPI/static 构建读取对应的 `VITE_*` 变量。 + +| 配置项 | 构建类型 | 作用 | +| --------------------------------------- | ------------ | ------------------------------------------------ | +| `NEXT_PUBLIC_REME_API_URL` | vinext/Sites | ReMe HTTP 服务地址,默认 `http://127.0.0.1:2333` | +| `NEXT_PUBLIC_REME_WORKSPACE_EXTENSIONS` | vinext/Sites | 工作区中显示的扩展名,使用逗号分隔 | +| `VITE_REME_API_URL` | static | ReMe HTTP 服务地址;FastAPI 同源托管时使用 `/` | +| `VITE_REME_WORKSPACE_EXTENSIONS` | static | static 构建使用的工作区扩展名列表 | + +默认显示 Markdown 和文本文件。如需增加其他文本格式: + +```bash +NEXT_PUBLIC_REME_WORKSPACE_EXTENSIONS=md,txt,mdx +VITE_REME_WORKSPACE_EXTENSIONS=md,txt,mdx +``` + +只应启用浏览器能够安全视为文本的格式。在 API 边界,ReMe 仍会执行工作区范围限制、允许路径、编码检查、大小限制和乐观修改时间检查。 + +## 从源码开发 先在仓库根目录启动 ReMe,然后在另一个终端运行前端: @@ -59,15 +257,15 @@ npm install npm run dev ``` -打开 。前端默认连接 `http://127.0.0.1:2333`,需要时可覆盖该地址: +打开 。开发前端默认连接 `http://127.0.0.1:2333`,需要时可覆盖: ```bash NEXT_PUBLIC_REME_API_URL=http://127.0.0.1:8000 npm run dev ``` -## 由 ReMe 托管的静态构建 +## 构建由 ReMe 托管的静态前端 -ReMe 可以通过提供 HTTP API 的同一个 FastAPI 进程托管 Studio。构建静态版本并重启 ReMe: +ReMe 可以使用提供 HTTP API 的同一个 FastAPI 进程托管 Studio。构建静态版本并重启 ReMe: ```bash cd reme_studio @@ -77,34 +275,47 @@ cd .. reme start ``` -打开 。静态构建默认使用同源请求。进行独立的静态开发时,运行 -`npm run dev:static`;如有需要,将 `VITE_REME_API_URL` 设置为正在运行的 ReMe 服务地址。 - -常规的 `npm run build` 命令仍用于 vinext/Sites 部署构建;`npm run build:static` 仅为 FastAPI 和 Python 包分发生成 -`dist-static/`。 - -## 配置 - -如需持久保存本地覆盖,可以将 `.env.example` 复制为 `.env.local`。vinext/Sites 构建读取 -`NEXT_PUBLIC_*`,FastAPI/static 构建读取对应的 `VITE_*` 变量: - -| 配置项 | 构建类型 | 作用 | -| --------------------------------------- | ------------ | ------------------------------------------------- | -| `NEXT_PUBLIC_REME_API_URL` | vinext/Sites | ReMe HTTP 服务地址,默认 `http://127.0.0.1:2333` | -| `NEXT_PUBLIC_REME_WORKSPACE_EXTENSIONS` | vinext/Sites | 工作区允许显示的扩展名,使用逗号分隔 | -| `VITE_REME_API_URL` | static | ReMe HTTP 服务地址;由 FastAPI 同源托管时使用 `/` | -| `VITE_REME_WORKSPACE_EXTENSIONS` | static | static 构建使用的工作区扩展名列表 | - -工作区会隐藏点文件和点目录,并且默认只显示 Markdown 和文本文件。例如: +打开 。静态构建默认使用同源请求。进行独立静态开发时: ```bash -NEXT_PUBLIC_REME_WORKSPACE_EXTENSIONS=md,txt,mdx -VITE_REME_WORKSPACE_EXTENSIONS=md,txt,mdx +VITE_REME_API_URL=http://127.0.0.1:2333 npm run dev:static ``` -记忆图谱依赖 ReMe 的摄取流程。在 Studio 设置中重建索引时,只会基于已摄取的 chunks 重建 BM25 和 Embedding 索引;不会扫描工作区文件、重新分块、修改记忆源文件或重建 wikilink 图谱。 +`npm run build` 仍用于 vinext/Sites 部署构建;`npm run build:static` 仅为 FastAPI 以及 Python/npm 包分发生成 `dist-static/`。应修改前端源文件,而不是提交生成的分发文件。 -## 检查 +## 常见问题 + +### Studio 提示服务不可用 + +- 确认 `reme start` 仍在运行。 +- 检查导航器底部显示的服务地址。 +- 前后端分开托管时,请在启动或构建前设置正确的 `NEXT_PUBLIC_REME_API_URL` 或 `VITE_REME_API_URL`。 +- 修正地址后,打开 Settings → Status 并点击 Refresh。 + +### 导航器中缺少文件 + +- 确认 ReMe 使用预期的 `workspace_dir` 启动。 +- 点文件和点目录会被刻意隐藏。 +- 如果不是 Markdown 或纯文本,请检查 `*_REME_WORKSPACE_EXTENSIONS`。 +- 使用 Files,而不是仅显示部分目录的 Daily 或 Knowledge。 + +### 图谱为空或不完整 + +- 确认笔记位于 `digest/wiki`、`digest/personal` 或 `digest/procedure`。 +- 确认源文件包含 wikilink,并已被 ReMe 摄取。 +- Settings → Rebuild index 只从现有 chunk 重建搜索索引,不会扫描文件或重建图谱。 + +### Markdown 保存被拒绝 + +文件在 Studio 加载后被其他进程修改。请保留磁盘上的新版本,重新打开或刷新文件,再应用目标修改并保存。这是防止静默数据丢失的预期冲突保护。 + +### Chat 无法启动 + +文件功能可在 Chat 不可用时正常工作。请检查 ReMe 后端配置的 Agent wrapper、模型、API key 和提供商地址,再查看 Settings → Status。不要把凭证放进前端环境变量。 + +## 验证 + +在 `reme_studio/` 中运行与改动范围相称的检查: ```bash npm run format:check @@ -113,3 +324,5 @@ npm run build npm run build:static npm test ``` + +以上截图也构成受支持界面的可视化导览,但前端行为仍以自动化检查为准。 diff --git a/reme_studio/app/files-workspace/files-workspace.module.css b/reme_studio/app/files-workspace/files-workspace.module.css index 469a3f12..3a4ca7b7 100644 --- a/reme_studio/app/files-workspace/files-workspace.module.css +++ b/reme_studio/app/files-workspace/files-workspace.module.css @@ -17,7 +17,7 @@ min-width: 0; flex: 1; overflow: hidden; - color: #99918a; + color: var(--muted); font: 11px var(--mono); text-overflow: ellipsis; white-space: nowrap; @@ -58,7 +58,7 @@ cursor: pointer; } .modeSwitch .modeActive { - color: #8e3b12; + color: var(--accent-strong); background: var(--soft); } .iconBtn { @@ -74,7 +74,7 @@ } .iconBtn:hover { color: var(--accent); - border-color: #f2c7ad; + border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); } .iconBtn:disabled { opacity: 0.42; diff --git a/reme_studio/app/files-workspace/memory-graph.module.css b/reme_studio/app/files-workspace/memory-graph.module.css index ad017b24..90260fda 100644 --- a/reme_studio/app/files-workspace/memory-graph.module.css +++ b/reme_studio/app/files-workspace/memory-graph.module.css @@ -209,7 +209,7 @@ stroke-width: 3px; } .labels .activeLabel { - fill: #fff; + fill: var(--accent-foreground); stroke: var(--accent); stroke-width: 7px; font-size: 11px; @@ -298,7 +298,7 @@ cursor: pointer; } .openFile:hover { - color: #fff; + color: var(--accent-foreground); border-color: var(--accent); background: var(--accent); } @@ -370,7 +370,7 @@ max-height: 45%; border: 1px solid var(--line); border-radius: 10px; - box-shadow: 0 12px 32px rgba(35, 25, 18, 0.18); + box-shadow: 0 12px 32px var(--shadow); } .legend { display: none; diff --git a/reme_studio/app/globals.css b/reme_studio/app/globals.css index 189c8b00..2fc41164 100644 --- a/reme_studio/app/globals.css +++ b/reme_studio/app/globals.css @@ -3,35 +3,54 @@ :root { color-scheme: light; - --bg: #fffdfb; - --panel: #faf7f3; - --surface: #fff; - --subtle: #fbfaf8; - --topbar: #f9f8f4; - --hover: #f0ebe6; - --code-bg: #f3eee9; - --line: #e8e0d8; - --ink: #292522; - --muted: #7b746d; - --accent: #f36b21; - --soft: #fff0e7; + --bg: #ffffff; + --panel: #f4f7f5; + --surface: #ffffff; + --subtle: #f1f6f3; + --topbar: #ffffff; + --hover: #f1f6f3; + --code-bg: #f1f6f3; + --line: #dce5e0; + --ink: #17221d; + --muted: #526159; + --accent: #087f6a; + --accent-foreground: #ffffff; + --accent-strong: #086554; + --accent-bright: #19a98f; + --brand-blue: #3156d9; + --soft: rgba(8, 127, 106, 0.14); --danger: #d8433e; + --danger-foreground: #000000; + --success: #087f6a; + --shadow: rgba(19, 70, 91, 0.14); + --shadow-strong: rgba(19, 70, 91, 0.24); + --overlay: rgba(9, 32, 25, 0.28); --mono: var(--font-geist-mono), monospace; } :root[data-theme="dark"] { color-scheme: dark; - --bg: #171513; - --panel: #201d1a; - --surface: #24211e; - --subtle: #211e1b; - --topbar: #1c1917; - --hover: #302b27; - --code-bg: #302b27; - --line: #3b3530; - --ink: #eee9e4; - --muted: #aaa19a; - --soft: #432719; + --bg: #0d1512; + --panel: #09100d; + --surface: #14201b; + --subtle: #17251f; + --topbar: #0d1512; + --hover: #17251f; + --code-bg: #17251f; + --line: #283a33; + --ink: #edf7f3; + --muted: #bacbc4; + --accent: #57dfc3; + --accent-foreground: #0d1512; + --accent-strong: #35c6a9; + --accent-bright: #57dfc3; + --brand-blue: #6575e8; + --soft: rgba(87, 223, 195, 0.14); --danger: #f07872; + --danger-foreground: #0d1512; + --success: #57dfc3; + --shadow: rgba(2, 14, 10, 0.34); + --shadow-strong: rgba(2, 14, 10, 0.52); + --overlay: rgba(2, 14, 10, 0.58); } * { box-sizing: border-box; @@ -50,7 +69,12 @@ textarea { } body { color: var(--ink); - background: var(--bg); + background: radial-gradient( + circle at 8% 8%, + color-mix(in srgb, var(--accent-bright) 5.5%, transparent), + transparent 28rem + ), + var(--bg); font-size: 14px; } button { @@ -66,7 +90,7 @@ textarea:focus-visible { height: 8px; } ::-webkit-scrollbar-thumb { - background: #d9d1ca; + background: var(--line); border: 2px solid transparent; border-radius: 8px; background-clip: padding-box; @@ -80,7 +104,7 @@ textarea:focus-visible { height: 56px; flex: none; display: grid; - grid-template-columns: 34px 180px 1fr auto; + grid-template-columns: 34px 220px minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 0 18px; @@ -91,7 +115,19 @@ textarea:focus-visible { display: flex; align-items: center; font-size: 18px; - font-weight: 600; + font-weight: 750; + white-space: nowrap; +} +.brand-icon { + width: 28px; + height: 28px; + display: block; + flex: none; + background: url("/reme-icon.svg") center / contain no-repeat; +} +.brand-title { + margin-left: 8px; + letter-spacing: -0.02em; } .app-version-divider { width: 1px; @@ -258,7 +294,7 @@ textarea:focus-visible { border: 1px solid var(--line); border-radius: 11px; background: var(--surface); - box-shadow: 0 10px 30px rgba(35, 25, 18, 0.16); + box-shadow: 0 10px 30px var(--shadow); } .theme-menu button { width: 100%; @@ -382,9 +418,8 @@ textarea:focus-visible { .agent-logo { display: grid; place-items: center; - color: var(--accent); - background: var(--soft); border-radius: 10px; + background: url("/reme-icon.svg") center / contain no-repeat; } .source-tabs { height: 36px; @@ -422,29 +457,29 @@ textarea:focus-visible { background: var(--hover); } .source-tabs button.active { - color: #756ae0; - background: color-mix(in srgb, #756ae0 15%, transparent); + color: var(--brand-blue); + background: color-mix(in srgb, var(--brand-blue) 15%, transparent); } .source-tabs .chat-tab { height: 27px; margin-left: 2px; - border: 1px solid #f2c7ad; - color: #9a471d; - background: #fff7f1; + border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--line)); + color: var(--accent-strong); + background: var(--soft); } .source-tabs .chat-tab:hover { - border-color: #e9aa85; - color: #843912; + border-color: var(--accent); + color: var(--accent-strong); background: var(--soft); } :root[data-theme="dark"] .source-tabs .chat-tab { - border-color: #70452f; - color: #f2a170; - background: #342319; + border-color: color-mix(in srgb, var(--accent) 38%, var(--line)); + color: var(--accent); + background: var(--soft); } :root[data-theme="dark"] .source-tabs .chat-tab:hover { - border-color: #a9633d; - color: #ffc09a; + border-color: var(--accent); + color: var(--accent); } .tree { min-height: 0; @@ -508,7 +543,7 @@ textarea:focus-visible { flex: 1; } .tree-directory-row.graph-active { - background: color-mix(in srgb, #756ae0 12%, transparent); + background: color-mix(in srgb, var(--brand-blue) 12%, transparent); } .tree-graph-button { height: 23px; @@ -527,8 +562,8 @@ textarea:focus-visible { } .tree-graph-button:hover, .tree-directory-row.graph-active .tree-graph-button { - color: #756ae0; - background: color-mix(in srgb, #756ae0 13%, transparent); + color: var(--brand-blue); + background: color-mix(in srgb, var(--brand-blue) 13%, transparent); } .side-state { padding: 18px 10px; @@ -566,16 +601,13 @@ textarea:focus-visible { height: 7px; flex: none; border-radius: 50%; - background: #48a868; - box-shadow: 0 0 0 3px #e1f2e6; + background: var(--success); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--success) 14%, transparent); } .status-dot.offline { background: var(--danger); box-shadow: 0 0 0 3px #fae5e4; } -:root[data-theme="dark"] .status-dot { - box-shadow: 0 0 0 3px #263d2d; -} :root[data-theme="dark"] .status-dot.offline { box-shadow: 0 0 0 3px #492725; } @@ -641,7 +673,7 @@ textarea:focus-visible { border: 1px solid var(--line); border-radius: 9px; background: var(--surface); - box-shadow: 0 10px 30px rgba(35, 25, 18, 0.18); + box-shadow: 0 10px 30px var(--shadow); } .tab-context-menu button { width: 100%; @@ -700,6 +732,24 @@ textarea:focus-visible { font-size: 22px; letter-spacing: -0.03em; } +.welcome-brand { + display: flex; + align-items: center; + gap: 7px; +} +.welcome-wordmark { + width: 124px; + height: 42px; + display: block; + flex: none; + background: url("/reme_logo.png") center / contain no-repeat; +} +.welcome-brand > span:last-child { + font-size: 40px; + font-weight: 750; + line-height: 1; + letter-spacing: -0.045em; +} .welcome p, .chat-empty p { margin: 8px 0 22px; @@ -713,13 +763,14 @@ textarea:focus-visible { gap: 8px; border: 0; border-radius: 9px; - color: #fff; + color: var(--accent-foreground); background: var(--accent); cursor: pointer; } .welcome small { margin-top: 30px; - color: #a29992; + color: var(--muted); + opacity: 0.72; } .markdown { max-width: 780px; @@ -827,15 +878,21 @@ textarea:focus-visible { display: grid; place-items: center; border-radius: 9px; - color: #fff; + color: var(--accent-foreground); background: var(--accent); font-size: 11px; } .message.assistant .avatar { - background: color-mix(in srgb, var(--accent) 82%, #fff); + background: transparent; font-size: 14px; font-weight: 700; } +.assistant-logo { + width: 30px; + height: 30px; + display: block; + background: url("/reme-icon.svg") center / contain no-repeat; +} .message.user .avatar { color: var(--ink); background: var(--hover); @@ -928,12 +985,12 @@ textarea:focus-visible { background: var(--subtle); } .stream-block.tool { - background: color-mix(in srgb, #756ae0 9%, var(--panel)); - border-color: color-mix(in srgb, #756ae0 35%, var(--line)); + background: color-mix(in srgb, var(--brand-blue) 9%, var(--panel)); + border-color: color-mix(in srgb, var(--brand-blue) 35%, var(--line)); } .stream-block.data { - background: color-mix(in srgb, #48a868 8%, var(--panel)); - border-color: color-mix(in srgb, #48a868 35%, var(--line)); + background: color-mix(in srgb, var(--success) 8%, var(--panel)); + border-color: color-mix(in srgb, var(--success) 35%, var(--line)); } .stream-block.approval { background: color-mix(in srgb, #d49a2c 9%, var(--panel)); @@ -976,7 +1033,7 @@ textarea:focus-visible { border: 1px solid var(--line); border-radius: 16px; background: var(--surface); - box-shadow: 0 8px 28px rgba(35, 25, 18, 0.14); + box-shadow: 0 8px 28px var(--shadow); transition: border-color 0.15s, box-shadow 0.15s, @@ -987,7 +1044,7 @@ textarea:focus-visible { background: var(--soft); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent), - 0 8px 28px rgba(35, 25, 18, 0.14); + 0 8px 28px var(--shadow); } .composer textarea { min-height: 78px; @@ -1007,8 +1064,8 @@ textarea:focus-visible { flex: none; border: 0; border-radius: 10px; - color: #fff; - background: color-mix(in srgb, var(--accent) 82%, #fff); + color: var(--accent-foreground); + background: var(--accent); cursor: pointer; } .composer button:disabled { @@ -1054,7 +1111,7 @@ textarea:focus-visible { } @media (max-width: 760px) { .topbar { - grid-template-columns: 34px 60px 1fr auto; + grid-template-columns: 34px max-content minmax(0, 1fr) auto; padding: 0 10px; } .resource-links, @@ -1071,7 +1128,7 @@ textarea:focus-visible { bottom: 0; left: 0; z-index: 10; - box-shadow: 16px 0 42px rgba(75, 49, 30, 0.16); + box-shadow: 16px 0 42px var(--shadow-strong); } .navigator-resize-handle { display: none; @@ -1080,6 +1137,29 @@ textarea:focus-visible { left: 0; } } +@media (max-width: 480px) { + .topbar { + grid-template-columns: 30px minmax(0, 1fr) auto; + gap: 6px; + padding: 0 8px; + } + .topbar > span { + display: none; + } + .topbar-actions { + grid-column: 3; + gap: 4px; + } + .language-switch button { + min-width: 27px; + padding: 0 4px; + } + .theme-menu summary, + .settings-trigger { + width: 28px; + height: 28px; + } +} .settings-overlay { position: fixed; @@ -1090,7 +1170,7 @@ textarea:focus-visible { place-items: center; opacity: 0; pointer-events: none; - background: rgba(30, 25, 21, 0.26); + background: var(--overlay); backdrop-filter: blur(5px); transition: opacity 180ms ease; } @@ -1107,7 +1187,7 @@ textarea:focus-visible { border: 1px solid color-mix(in srgb, var(--line) 82%, transparent); border-radius: 20px; background: var(--surface); - box-shadow: 0 28px 80px rgba(35, 25, 18, 0.26); + box-shadow: 0 28px 80px var(--shadow-strong); transform: translateY(12px) scale(0.985); transition: transform 240ms cubic-bezier(0.22, 1, 0.36, 1); } @@ -1181,12 +1261,8 @@ textarea:focus-visible { background: var(--hover); } .settings-sidebar button.active { - color: #fff; - background: linear-gradient( - 135deg, - color-mix(in srgb, var(--accent) 82%, #fff), - var(--accent) - ); + color: var(--accent-foreground); + background: linear-gradient(135deg, var(--accent-strong), var(--accent)); box-shadow: 0 5px 14px color-mix(in srgb, var(--accent) 24%, transparent); } .settings-sidebar button > span { @@ -1277,19 +1353,19 @@ textarea:focus-visible { place-items: center; border-radius: 15px; color: #fff; - box-shadow: 0 8px 18px rgba(35, 25, 18, 0.14); + box-shadow: 0 8px 18px var(--shadow); } .settings-page-icon.status { - background: linear-gradient(145deg, #53b97a, #2f9b5b); + background: linear-gradient(145deg, #19a98f, #087f6a); } .settings-page-icon.index { - background: linear-gradient(145deg, #8b80e8, #7065d1); + background: linear-gradient(145deg, #6575e8, #3156d9); } .settings-page-icon.config { - background: linear-gradient(145deg, #f29a55, #e7762d); + background: linear-gradient(145deg, #e69a42, #c77724); } .settings-page-icon.version { - background: linear-gradient(145deg, #6ca7dc, #477fb7); + background: linear-gradient(145deg, #25a8dc, #159fc9); } .status-hero { position: relative; @@ -1301,14 +1377,14 @@ textarea:focus-visible { align-items: center; justify-content: space-between; gap: 24px; - border: 1px solid color-mix(in srgb, #39a767 24%, var(--line)); + border: 1px solid color-mix(in srgb, var(--success) 24%, var(--line)); border-radius: 17px; background: linear-gradient( 135deg, - color-mix(in srgb, #42a96d 9%, var(--surface)), + color-mix(in srgb, var(--success) 9%, var(--surface)), var(--surface) 62% ); - box-shadow: 0 5px 18px rgba(35, 25, 18, 0.05); + box-shadow: 0 5px 18px color-mix(in srgb, var(--shadow) 42%, transparent); } .status-hero::after { position: absolute; @@ -1316,7 +1392,7 @@ textarea:focus-visible { bottom: -92px; width: 210px; height: 210px; - border: 1px solid color-mix(in srgb, #42a96d 16%, transparent); + border: 1px solid color-mix(in srgb, var(--success) 16%, transparent); border-radius: 50%; content: ""; } @@ -1364,10 +1440,10 @@ textarea:focus-visible { display: inline-flex; align-items: center; gap: 6px; - border: 1px solid color-mix(in srgb, #2f9b5b 25%, transparent); + border: 1px solid color-mix(in srgb, var(--success) 25%, transparent); border-radius: 999px; - color: #25834a; - background: color-mix(in srgb, #42a96d 9%, transparent); + color: var(--success); + background: color-mix(in srgb, var(--success) 9%, transparent); font-size: 10px; font-weight: 600; white-space: nowrap; @@ -1381,8 +1457,8 @@ textarea:focus-visible { width: 7px; height: 7px; border-radius: 50%; - background: #32a25e; - box-shadow: 0 0 0 3px color-mix(in srgb, #32a25e 14%, transparent); + background: var(--success); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--success) 14%, transparent); } .health-badge.unhealthy, .component-health.unhealthy { @@ -1410,7 +1486,7 @@ textarea:focus-visible { border: 1px solid var(--line); border-radius: 15px; background: var(--surface); - box-shadow: 0 5px 18px rgba(35, 25, 18, 0.05); + box-shadow: 0 5px 18px color-mix(in srgb, var(--shadow) 42%, transparent); } .status-metrics small, .version-card small { @@ -1453,7 +1529,7 @@ textarea:focus-visible { border: 1px solid var(--line); border-radius: 14px; background: var(--surface); - box-shadow: 0 5px 18px rgba(35, 25, 18, 0.04); + box-shadow: 0 5px 18px color-mix(in srgb, var(--shadow) 34%, transparent); } .component-card::before { position: absolute; @@ -1463,7 +1539,7 @@ textarea:focus-visible { content: ""; } .component-card.healthy::before { - background: #39a767; + background: var(--success); } .component-card header { min-height: 70px; @@ -1528,7 +1604,7 @@ textarea:focus-visible { border: 1px solid var(--line); border-radius: 15px; background: var(--surface); - box-shadow: 0 5px 18px rgba(35, 25, 18, 0.05); + box-shadow: 0 5px 18px color-mix(in srgb, var(--shadow) 42%, transparent); } .settings-card h4 { margin: 8px 0 10px; @@ -1571,7 +1647,7 @@ textarea:focus-visible { align-items: center; gap: 8px; border: 0; - color: #fff; + color: var(--accent-foreground); background: var(--accent); } .primary-action:disabled { @@ -1602,7 +1678,7 @@ textarea:focus-visible { background: var(--surface); } .index-confirm .danger-action { - color: #fff; + color: var(--danger-foreground); border-color: var(--danger); background: var(--danger); } @@ -1613,8 +1689,8 @@ textarea:focus-visible { align-items: center; gap: 8px; border-radius: 9px; - color: #2f8c52; - background: color-mix(in srgb, #48a868 12%, transparent); + color: var(--success); + background: color-mix(in srgb, var(--success) 12%, transparent); font-size: 12px; } .config-json { diff --git a/reme_studio/app/layout.tsx b/reme_studio/app/layout.tsx index f686c5c0..de1f00ff 100644 --- a/reme_studio/app/layout.tsx +++ b/reme_studio/app/layout.tsx @@ -28,7 +28,7 @@ export async function generateMetadata(): Promise { title: "ReMe Studio", description: "Browse, edit, and discuss your local-first ReMe memory workspace.", - icons: { icon: "/favicon.svg", shortcut: "/favicon.svg" }, + icons: { icon: "/reme-icon.svg", shortcut: "/reme-icon.svg" }, openGraph: { title: "ReMe Studio", description: "本地优先的 Agent 记忆工作区", diff --git a/reme_studio/app/workspace.tsx b/reme_studio/app/workspace.tsx index 0ce9f2f9..d8187be3 100644 --- a/reme_studio/app/workspace.tsx +++ b/reme_studio/app/workspace.tsx @@ -22,7 +22,7 @@ import { SunMoon, X, } from "lucide-react"; -import { getReMeVersion, readWorkspaceFile, streamChat } from "./api"; +import { readWorkspaceFile, streamChat } from "./api"; import { chatStreamError, formatStreamPayloads } from "./chat-stream"; import FilesNavigator from "./files-workspace/FilesNavigator"; import MemoryGraphView from "./files-workspace/MemoryGraphView"; @@ -42,6 +42,7 @@ import { } from "./workspace-drag"; import SettingsCenter from "./settings-center"; import { hasUnsavedChanges, unsavedTabsClosedBy } from "./tab-close"; +import packageJson from "../package.json"; const TabbedEditor = dynamic(() => import("./files-workspace/TabbedEditor"), { ssr: false, @@ -403,9 +404,7 @@ function Chat({ tab }: { tab: Extract }) {
{!tab.messages.length && (
-
- -
+