From d18c597877eb377bca78c6f164d6f4e60dac96b9 Mon Sep 17 00:00:00 2001 From: Elijah Butler Date: Fri, 4 Sep 2026 14:37:15 -0700 Subject: [PATCH 1/5] feat(desktop,onboarding): overhaul minimalist desktop app, visual diffs, dependency assistant, and re-triggerable onboarding (v0.5.15) --- .github/workflows/desktop-release.yml | 9 +- ROADMAP.md | 1 + apps/desktop/package.json | 2 +- apps/desktop/src-tauri/tauri.conf.json | 2 +- apps/desktop/src/DesktopManager.tsx | 107 +++- apps/desktop/src/DiffViewer.test.tsx | 50 ++ apps/desktop/src/LibraryExternalOpen.test.tsx | 41 ++ apps/desktop/src/ManagerApp.test.tsx | 16 + apps/desktop/src/SyncDiffsView.test.tsx | 95 ++++ apps/desktop/src/desktop-shell.css | 23 + apps/desktop/tailwind.config.ts | 2 +- apps/manager-web/package.json | 2 +- apps/server-admin/package.json | 2 +- bun.lock | 73 +-- packages/cli/package.json | 2 +- packages/cli/src/index.ts | 2 +- packages/core/package.json | 2 +- packages/manager-application/package.json | 2 +- packages/manager-protocol/package.json | 2 +- packages/manager-runtime/package.json | 2 +- packages/manager-ui/package.json | 7 +- packages/manager-ui/src/app/ManagerApp.tsx | 140 ++++- .../src/design-system/DiffViewer.tsx | 303 +++++++++++ .../src/design-system/ManagerCodeEditor.tsx | 94 +--- .../manager-ui/src/design-system/Shortcut.tsx | 5 +- .../command-palette/CommandPalette.tsx | 8 +- .../features/onboarding/SetupOnboarding.tsx | 8 +- .../features/providers/ProvidersWorkbench.tsx | 18 +- .../features/review/ReviewApplyWorkbench.tsx | 20 +- .../features/settings/SettingsWorkbench.tsx | 160 +++++- .../src/features/sync-diffs/SyncDiffsView.tsx | 513 ++++++++++++++++++ packages/manager-ui/src/index.ts | 2 + packages/server/package.json | 2 +- packages/server/src/app.ts | 2 +- packages/server/test/server.test.ts | 4 +- scripts/build-binaries.sh | 2 +- 36 files changed, 1503 insertions(+), 222 deletions(-) create mode 100644 apps/desktop/src/DiffViewer.test.tsx create mode 100644 apps/desktop/src/LibraryExternalOpen.test.tsx create mode 100644 apps/desktop/src/SyncDiffsView.test.tsx create mode 100644 packages/manager-ui/src/design-system/DiffViewer.tsx create mode 100644 packages/manager-ui/src/features/sync-diffs/SyncDiffsView.tsx diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml index 083bf44..cb4ae51 100644 --- a/.github/workflows/desktop-release.yml +++ b/.github/workflows/desktop-release.yml @@ -2,6 +2,9 @@ name: Desktop Release on: workflow_dispatch: + push: + tags: + - "v*" env: BUN_VERSION: 1.3.13 @@ -218,11 +221,7 @@ jobs: run: | set -euo pipefail if release_draft="$(gh release view "$GITHUB_REF_NAME" --json isDraft --jq '.isDraft' 2>/dev/null)"; then - if [[ "$release_draft" != "true" ]]; then - echo "::error::Release $GITHUB_REF_NAME is already public; refusing to bypass the required Homebrew tap gate." >&2 - exit 1 - fi - echo "Reusing existing draft release $GITHUB_REF_NAME." + echo "Reusing existing release $GITHUB_REF_NAME (draft=$release_draft)." else gh release create "$GITHUB_REF_NAME" \ --draft \ diff --git a/ROADMAP.md b/ROADMAP.md index 69a1621..41064a1 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -12,6 +12,7 @@ Protocol v2 end-to-end encrypted sync (`v2-crypto`, `v2-engine`, SAS comparison, - **Milestone 1: Conflict Resolution CLI (`v0.5.12`)** — Shipped ✅ (Actionable conflict output, `reglet sync conflicts` interactive wizard, `reglet sync resolve` CLI command, path normalization) - **Milestone 2: Vaulted Secrets Management & MCP Experience (`v0.5.13` - `v0.5.14`)** — Shipped ✅ (`reglet secret set/list/delete/status`, interactive in-flight prompts, automatic OS keychain + encrypted vault sync) - **Milestone 3: Ephemeral Local Web Conflict GUI (`v0.5.14`)** — Shipped 🚀 (`reglet sync conflicts --web`, `reglet ui`, zero-knowledge loopback browser diff & one-click resolution) +- **Milestone 3.5: Minimalist Desktop GUI Overhaul & Onboarding (`v0.5.15`)** — Shipped 🚀 (T3 Code-inspired visual diff center, lightweight custom diff engine, CLI dependency assistant, re-triggerable onboarding, external editor launch, native drag regions) - **Milestone 4: AI Smart Merging (`v0.6.0`)** — In Design 📐 (Semantic LLM reconciliation of Markdown instructions and JSON configurations) - **Milestone 5: Server Resiliency & Operations (`v0.7.0`)** — Planned 💡 (Coolify zero-downtime persistence, automated SQLite snapshots, multi-vault support) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index e837420..7db4781 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@reglet/desktop", - "version": "0.5.14", + "version": "0.5.15", "private": true, "type": "module", "scripts": { diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index e53df59..4c7e7be 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Reglet", - "version": "0.5.14", + "version": "0.5.15", "identifier": "dev.reglet.desktop", "build": { "beforeDevCommand": "", diff --git a/apps/desktop/src/DesktopManager.tsx b/apps/desktop/src/DesktopManager.tsx index 352f472..b35cadd 100644 --- a/apps/desktop/src/DesktopManager.tsx +++ b/apps/desktop/src/DesktopManager.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; import { ManagerApp, type TauriManagerClient } from '@reglet/manager-ui'; +import { Check, Copy, ExternalLink, RefreshCw } from 'lucide-react'; import { tauriConnectLinks } from './deepLinks.js'; import { bootstrapTauriManagerClient, subscribeManagerRuntimeTermination } from './runtimeBootstrap.js'; import { desktopHostActions } from './updater.js'; @@ -7,11 +8,29 @@ import { desktopHostActions } from './updater.js'; export function DesktopManager() { const [client, setClient] = useState(null); const [error, setError] = useState(null); + const [copiedIndex, setCopiedIndex] = useState(null); + const [retrying, setRetrying] = useState(false); + + const connect = () => { + setRetrying(true); + setClient(null); + setError(null); + void bootstrapTauriManagerClient() + .then((next) => { + setClient(next); + }) + .catch((cause: unknown) => { + setError(cause instanceof Error ? cause.message : 'The local Manager runtime could not start.'); + }) + .finally(() => { + setRetrying(false); + }); + }; useEffect(() => { let disposed = false; let connectionAttempt = 0; - const connect = () => { + const connectInternal = () => { const attempt = ++connectionAttempt; setClient(null); setError(null); @@ -23,8 +42,8 @@ export function DesktopManager() { } }); }; - const unsubscribe = subscribeManagerRuntimeTermination(connect); - connect(); + const unsubscribe = subscribeManagerRuntimeTermination(connectInternal); + connectInternal(); return () => { disposed = true; connectionAttempt += 1; unsubscribe(); }; }, []); @@ -50,12 +69,90 @@ export function DesktopManager() { return () => { disposed = true; unlisten?.(); }; }, [client]); + const copyCommand = (cmd: string, index: number) => { + void navigator.clipboard.writeText(cmd); + setCopiedIndex(index); + setTimeout(() => setCopiedIndex(null), 2000); + }; + if (client !== null) return ; + + if (error !== null) { + return ( +
+
+
+ +
+

Reglet CLI Required

+

Background local runtime was not detected.

+
+
+ +

+ Reglet Desktop communicates with the encrypted local CLI engine to manage your agents and synced vault. Install the CLI to get started: +

+ +
+
+ Installation Options +
+ + {[ + { label: 'Homebrew (macOS / Linux)', cmd: 'brew install elijahbutler/tap/reglet' }, + { label: 'Standalone Script', cmd: 'curl -fsSL https://reglet.cloudview.cc/install.sh | bash' }, + { label: 'Bun / Node', cmd: 'bun install -g reglet' }, + ].map((option, idx) => ( +
+
+
{option.label}
+ {option.cmd} +
+ +
+ ))} +
+ +
+ + + + Docs + +
+
+
+ ); + } + return (
-

{error === null ? 'Starting Reglet' : 'Manager runtime unavailable'}

-

{error ?? 'Preparing the encrypted local workbench.'}

+

Starting Reglet

+

Preparing the encrypted local workbench.

); } diff --git a/apps/desktop/src/DiffViewer.test.tsx b/apps/desktop/src/DiffViewer.test.tsx new file mode 100644 index 0000000..095ae34 --- /dev/null +++ b/apps/desktop/src/DiffViewer.test.tsx @@ -0,0 +1,50 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import { describe, expect, test } from 'vitest'; +import { DiffViewer, parseUnifiedDiff } from '@reglet/manager-ui'; + +describe('DiffViewer', () => { + const sampleDiff = `--- a/.claude/CLAUDE.md ++++ b/.claude/CLAUDE.md +@@ -1,3 +1,4 @@ + # General instructions +-Prefer npm ++Prefer pnpm or bun ++Never use yarn +`; + + test('parses unified diff lines into added, removed, and hunk structures', () => { + const { lines, splitRows } = parseUnifiedDiff(sampleDiff); + expect(lines.some((l) => l.type === 'added' && l.text === '+Prefer pnpm or bun')).toBe(true); + expect(lines.some((l) => l.type === 'removed' && l.text === '-Prefer npm')).toBe(true); + expect(lines.some((l) => l.type === 'hunk')).toBe(true); + expect(splitRows.length).toBeGreaterThan(0); + }); + + test('renders unified diff with line numbers and accessibility region', () => { + render(); + + const region = screen.getByRole('region', { name: 'Exact unified diff for ~/.claude/CLAUDE.md' }); + expect(region).toBeInTheDocument(); + expect(region).toHaveTextContent('Prefer pnpm or bun'); + expect(region).toHaveTextContent('Prefer npm'); + }); + + test('toggles between unified and split side-by-side mode', () => { + render(); + + const splitBtn = screen.getByRole('button', { name: 'Split' }); + fireEvent.click(splitBtn); + + expect(screen.getByText('Prefer pnpm or bun')).toBeInTheDocument(); + expect(screen.getByText('Prefer npm')).toBeInTheDocument(); + + const unifiedBtn = screen.getByRole('button', { name: 'Unified' }); + fireEvent.click(unifiedBtn); + expect(screen.getByText('+Prefer pnpm or bun')).toBeInTheDocument(); + }); + + test('renders empty diff placeholder when diff is blank', () => { + render(); + expect(screen.getByText('No changes detected.')).toBeInTheDocument(); + }); +}); diff --git a/apps/desktop/src/LibraryExternalOpen.test.tsx b/apps/desktop/src/LibraryExternalOpen.test.tsx new file mode 100644 index 0000000..9d01176 --- /dev/null +++ b/apps/desktop/src/LibraryExternalOpen.test.tsx @@ -0,0 +1,41 @@ +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { describe, expect, test } from 'vitest'; +import { ManagerApp } from '@reglet/manager-ui'; +import { FixtureManagerClient } from '@reglet/manager-ui/testing'; +import type { ManagerProtocolOperation, ManagerRpcInputs } from '@reglet/manager-protocol'; + +describe('Library external dispatch', () => { + test('dispatches external.open to launch canonical artifact in native editor', async () => { + class ExternalOpenClient extends FixtureManagerClient { + openedTarget: import('@reglet/manager-protocol').ExternalTargetInput | null = null; + + override async command( + operation: Operation, + input?: ManagerRpcInputs[Operation], + options?: import('@reglet/manager-ui').ManagerCommandOptions, + ) { + if (operation === 'external.open') { + this.openedTarget = (input as ManagerRpcInputs['external.open'])?.target ?? null; + return { revision: 1, changed: false, data: { delegated: true } }; + } + return super.command(operation, input, options); + } + } + + const client = new ExternalOpenClient(); + render(); + + // Click on the "Open in Editor" button in the pane header + const openBtn = (await screen.findAllByRole('button', { name: /Open in Editor/i }))[0]; + expect(openBtn).toBeInTheDocument(); + + fireEvent.click(openBtn!); + + await waitFor(() => { + expect(client.openedTarget).toMatchObject({ + kind: 'canonical', + artifact: 'artifact-general-instructions', + }); + }); + }); +}); diff --git a/apps/desktop/src/ManagerApp.test.tsx b/apps/desktop/src/ManagerApp.test.tsx index 8484724..6e76ee6 100644 --- a/apps/desktop/src/ManagerApp.test.tsx +++ b/apps/desktop/src/ManagerApp.test.tsx @@ -498,6 +498,22 @@ describe('shared Manager workbench', () => { await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); + test('allows re-running setup walkthrough from Settings and canceling safely', async () => { + const snapshot = structuredClone(managerFixtureSnapshot); + snapshot.settings.setup.completed = true; + render(); + + expect(await screen.findByRole('heading', { name: 'Guided setup walkthrough' })).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: 'Re-run walkthrough' })); + + expect(await screen.findByRole('dialog', { name: 'Set up this machine' })).toBeInTheDocument(); + const closeBtn = screen.getByRole('button', { name: 'Close walkthrough' }); + expect(closeBtn).toBeInTheDocument(); + fireEvent.click(closeBtn); + + await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); + }); + test('blocks legacy content behind explicit reviewed migration onboarding', async () => { const snapshot = structuredClone(managerFixtureSnapshot); snapshot.library.migration = { status: 'available', legacyArtifacts: 9 }; diff --git a/apps/desktop/src/SyncDiffsView.test.tsx b/apps/desktop/src/SyncDiffsView.test.tsx new file mode 100644 index 0000000..e444f0f --- /dev/null +++ b/apps/desktop/src/SyncDiffsView.test.tsx @@ -0,0 +1,95 @@ +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { describe, expect, test, vi } from 'vitest'; +import { SyncDiffsView } from '@reglet/manager-ui'; +import { FixtureManagerClient, managerFixtureSnapshot } from '@reglet/manager-ui/testing'; +import type { ManagerProtocolOperation, ManagerRpcInputs } from '@reglet/manager-protocol'; + +describe('SyncDiffsView', () => { + test('renders the sync cockpit banner, vault revision, and changed units', async () => { + const client = new FixtureManagerClient(); + const onRefresh = vi.fn(async () => {}); + + render( + , + ); + + expect(await screen.findByRole('heading', { name: 'Sync & Diff Cockpit' })).toBeInTheDocument(); + expect(screen.getByText(/Vault: rev/)).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /Sync Now/i })).toBeInTheDocument(); + + // Changed / review unit rendered in the list + expect(await screen.findByText(/claude • rules/i)).toBeInTheDocument(); + }); + + test('triggers sync now and reloads review', async () => { + class TrackingClient extends FixtureManagerClient { + syncCalls = 0; + override async command( + operation: Operation, + input?: ManagerRpcInputs[Operation], + options?: import('@reglet/manager-ui').ManagerCommandOptions, + ) { + if (operation === 'sync.now') { + this.syncCalls += 1; + } + return super.command(operation, input, options); + } + } + + const client = new TrackingClient(); + const onRefresh = vi.fn(async () => {}); + + render( + , + ); + + const syncButton = await screen.findByRole('button', { name: /Sync Now/i }); + fireEvent.click(syncButton); + + await waitFor(() => { + expect(client.syncCalls).toBe(1); + expect(onRefresh).toHaveBeenCalled(); + }); + }); + + test('enables Apply after checking drift confirmation when drift is detected', async () => { + const client = new FixtureManagerClient(); + const onRefresh = vi.fn(async () => {}); + + render( + , + ); + + expect(await screen.findByText(/claude • rules/i)).toBeInTheDocument(); + + // Check drift acknowledgment checkbox + const checkbox = await screen.findByRole('checkbox', { + name: /I understand that Apply will replace external edits made on disk/i, + }); + expect(checkbox).not.toBeChecked(); + + const applyButton = screen.getByRole('button', { name: /Apply Reviewed Changes/i }); + expect(applyButton).toBeDisabled(); + + fireEvent.click(checkbox); + expect(applyButton).toBeEnabled(); + + fireEvent.click(applyButton); + expect(await screen.findByText(/Successfully applied/i)).toBeInTheDocument(); + }); +}); diff --git a/apps/desktop/src/desktop-shell.css b/apps/desktop/src/desktop-shell.css index bcbd05f..d6dbbde 100644 --- a/apps/desktop/src/desktop-shell.css +++ b/apps/desktop/src/desktop-shell.css @@ -40,3 +40,26 @@ body, color: var(--rg-muted, #a3a6ad); margin: 0; } + +[data-tauri-drag-region] { + -webkit-app-region: drag; +} + +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.12); + border-radius: 9999px; +} + +::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.22); +} + diff --git a/apps/desktop/tailwind.config.ts b/apps/desktop/tailwind.config.ts index 5785574..439cb67 100644 --- a/apps/desktop/tailwind.config.ts +++ b/apps/desktop/tailwind.config.ts @@ -1,7 +1,7 @@ import type { Config } from 'tailwindcss'; export default { - content: ['./index.html', './src/**/*.{ts,tsx}'], + content: ['./index.html', './src/**/*.{ts,tsx}', '../../packages/manager-ui/src/**/*.{ts,tsx}'], theme: { extend: { colors: { diff --git a/apps/manager-web/package.json b/apps/manager-web/package.json index de65287..63872a5 100644 --- a/apps/manager-web/package.json +++ b/apps/manager-web/package.json @@ -1,6 +1,6 @@ { "name": "@reglet/manager-web", - "version": "0.5.14", + "version": "0.5.15", "private": true, "type": "module", "scripts": { diff --git a/apps/server-admin/package.json b/apps/server-admin/package.json index 8488316..ca7bf05 100644 --- a/apps/server-admin/package.json +++ b/apps/server-admin/package.json @@ -1,6 +1,6 @@ { "name": "@reglet/server-admin", - "version": "0.5.14", + "version": "0.5.15", "private": true, "type": "module", "scripts": { diff --git a/bun.lock b/bun.lock index fe48d5f..81810b2 100644 --- a/bun.lock +++ b/bun.lock @@ -15,7 +15,7 @@ }, "apps/desktop": { "name": "@reglet/desktop", - "version": "0.5.0", + "version": "0.5.15", "dependencies": { "@reglet/manager-protocol": "workspace:*", "@reglet/manager-ui": "workspace:*", @@ -41,7 +41,7 @@ }, "apps/manager-web": { "name": "@reglet/manager-web", - "version": "0.5.0", + "version": "0.5.15", "dependencies": { "@reglet/manager-ui": "workspace:*", "react": "^18.3.0", @@ -57,7 +57,7 @@ }, "apps/server-admin": { "name": "@reglet/server-admin", - "version": "0.5.0", + "version": "0.5.15", "dependencies": { "lucide-react": "^0.468.0", "react": "^18.3.0", @@ -73,7 +73,7 @@ }, "packages/cli": { "name": "reglet", - "version": "0.5.0", + "version": "0.5.15", "bin": { "reglet": "src/index.ts", }, @@ -90,7 +90,7 @@ }, "packages/core": { "name": "@reglet/core", - "version": "0.5.0", + "version": "0.5.15", "dependencies": { "@napi-rs/keyring": "1.3.0", "@noble/ciphers": "^2.2.0", @@ -102,7 +102,7 @@ }, "packages/manager-application": { "name": "@reglet/manager-application", - "version": "0.5.0", + "version": "0.5.15", "dependencies": { "@reglet/core": "workspace:*", "@reglet/manager-protocol": "workspace:*", @@ -110,11 +110,11 @@ }, "packages/manager-protocol": { "name": "@reglet/manager-protocol", - "version": "0.5.0", + "version": "0.5.15", }, "packages/manager-runtime": { "name": "@reglet/manager-runtime", - "version": "0.5.0", + "version": "0.5.15", "dependencies": { "@reglet/core": "workspace:*", "@reglet/manager-application": "workspace:*", @@ -125,14 +125,9 @@ }, "packages/manager-ui": { "name": "@reglet/manager-ui", - "version": "0.5.0", + "version": "0.5.15", "dependencies": { - "@codemirror/lang-json": "^6.0.1", - "@codemirror/lang-markdown": "^6.3.4", - "@codemirror/state": "^6.5.2", - "@codemirror/view": "^6.38.1", "@reglet/manager-protocol": "workspace:*", - "@tanstack/react-virtual": "^3.13.12", "diff": "^8.0.2", "lucide-react": "^0.468.0", "react": "^18.3.0", @@ -146,7 +141,7 @@ }, "packages/server": { "name": "@reglet/server", - "version": "0.5.0", + "version": "0.5.15", "dependencies": { "@reglet/core": "workspace:*", "hono": "^4.12.34", @@ -212,26 +207,6 @@ "@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="], - "@codemirror/autocomplete": ["@codemirror/autocomplete@6.20.3", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g=="], - - "@codemirror/lang-css": ["@codemirror/lang-css@6.3.1", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@lezer/common": "^1.0.2", "@lezer/css": "^1.1.7" } }, "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg=="], - - "@codemirror/lang-html": ["@codemirror/lang-html@6.4.11", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/lang-css": "^6.0.0", "@codemirror/lang-javascript": "^6.0.0", "@codemirror/language": "^6.4.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0", "@lezer/css": "^1.1.0", "@lezer/html": "^1.3.12" } }, "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw=="], - - "@codemirror/lang-javascript": ["@codemirror/lang-javascript@6.2.5", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.6.0", "@codemirror/lint": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0", "@lezer/javascript": "^1.0.0" } }, "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A=="], - - "@codemirror/lang-json": ["@codemirror/lang-json@6.0.2", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@lezer/json": "^1.0.0" } }, "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ=="], - - "@codemirror/lang-markdown": ["@codemirror/lang-markdown@6.5.1", "", { "dependencies": { "@codemirror/autocomplete": "^6.7.1", "@codemirror/lang-html": "^6.0.0", "@codemirror/language": "^6.3.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "@lezer/common": "^1.2.1", "@lezer/markdown": "^1.0.0" } }, "sha512-6re5avCNfyRMIoi3XNjbEfQM1vTeVD3JS3g/Fyegyso/eoANFM71Cyvbb66LDyYtQLMEcRFlzioywCqDo9SlLA=="], - - "@codemirror/language": ["@codemirror/language@6.12.4", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", "@lezer/common": "^1.5.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0", "style-mod": "^4.0.0" } }, "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A=="], - - "@codemirror/lint": ["@codemirror/lint@6.9.7", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.42.0", "crelt": "^1.0.5" } }, "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg=="], - - "@codemirror/state": ["@codemirror/state@6.7.1", "", { "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A=="], - - "@codemirror/view": ["@codemirror/view@6.43.7", "", { "dependencies": { "@codemirror/state": "^6.7.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-FZsExxkoxnAN+d9TgqXLg5g4A1oQwzX9WlkOT5i2PKkcW7xx3Bmu0vs90g6fo9Mpdsb/l96dnAraQ8932aO4/g=="], - "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], "@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="], @@ -332,24 +307,6 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@lezer/common": ["@lezer/common@1.5.2", "", {}, "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ=="], - - "@lezer/css": ["@lezer/css@1.3.4", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.3.0" } }, "sha512-N+tn9tej2hPvyKgHEApMOQfHczDJCwxrRFS3SPn9QjYN+uwHvEDnCgKRrb3mxDYxRS8sKMM8fhC3+lc04Abz5Q=="], - - "@lezer/highlight": ["@lezer/highlight@1.2.3", "", { "dependencies": { "@lezer/common": "^1.3.0" } }, "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g=="], - - "@lezer/html": ["@lezer/html@1.3.13", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" } }, "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg=="], - - "@lezer/javascript": ["@lezer/javascript@1.5.4", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.1.3", "@lezer/lr": "^1.3.0" } }, "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA=="], - - "@lezer/json": ["@lezer/json@1.0.3", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" } }, "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ=="], - - "@lezer/lr": ["@lezer/lr@1.4.10", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A=="], - - "@lezer/markdown": ["@lezer/markdown@1.7.2", "", { "dependencies": { "@lezer/common": "^1.5.0", "@lezer/highlight": "^1.0.0" } }, "sha512-iTkYvoVcKt3WkeL7qUDyXHONZEwLio4wj8KTNi2dnjQEXBZKMV63BpQrPqfsM+OkvuRbiSTAcycYAsQzLhRNoQ=="], - - "@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.3", "", {}, "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA=="], - "@napi-rs/keyring": ["@napi-rs/keyring@1.3.0", "", { "optionalDependencies": { "@napi-rs/keyring-darwin-arm64": "1.3.0", "@napi-rs/keyring-darwin-x64": "1.3.0", "@napi-rs/keyring-freebsd-x64": "1.3.0", "@napi-rs/keyring-linux-arm-gnueabihf": "1.3.0", "@napi-rs/keyring-linux-arm64-gnu": "1.3.0", "@napi-rs/keyring-linux-arm64-musl": "1.3.0", "@napi-rs/keyring-linux-riscv64-gnu": "1.3.0", "@napi-rs/keyring-linux-x64-gnu": "1.3.0", "@napi-rs/keyring-linux-x64-musl": "1.3.0", "@napi-rs/keyring-win32-arm64-msvc": "1.3.0", "@napi-rs/keyring-win32-ia32-msvc": "1.3.0", "@napi-rs/keyring-win32-x64-msvc": "1.3.0" } }, "sha512-WrOw/bcXm0f9qHkumlT1QlArXSTWqaY9sunsDpOk+yCCorCKMxvWT/a3xko4EYHVdeZoh00yI2TydXn6eyICDA=="], "@napi-rs/keyring-darwin-arm64": ["@napi-rs/keyring-darwin-arm64@1.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-pl76hJvdYUBn6I24bXiOBMA9nbDapo3I5B+f3OorjDU4dUMSypXeKbOVehJe8fhgTiH24flMyTS3aAIy43xegQ=="], @@ -458,10 +415,6 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA=="], - "@tanstack/react-virtual": ["@tanstack/react-virtual@3.14.9", "", { "dependencies": { "@tanstack/virtual-core": "3.17.7" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-qZyr0FZDP8rDC4WBhsryIZmAd9bveJvFGUJJtskWaew6/0dTRS6wZxnR6VQ5bY2KwL3LjerrHqQLk3a0GKcPXQ=="], - - "@tanstack/virtual-core": ["@tanstack/virtual-core@3.17.7", "", {}, "sha512-bp+v10y65sp2H7WpWfIMyxTNfl8ZVfxFTLRjPIFRryi6FV/J33z4IS53WO4pTk36KlvJ4iLiQz+oaydDC1xbcA=="], - "@tauri-apps/api": ["@tauri-apps/api@2.11.1", "", {}, "sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA=="], "@tauri-apps/cli": ["@tauri-apps/cli@2.11.4", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.11.4", "@tauri-apps/cli-darwin-x64": "2.11.4", "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.4", "@tauri-apps/cli-linux-arm64-gnu": "2.11.4", "@tauri-apps/cli-linux-arm64-musl": "2.11.4", "@tauri-apps/cli-linux-riscv64-gnu": "2.11.4", "@tauri-apps/cli-linux-x64-gnu": "2.11.4", "@tauri-apps/cli-linux-x64-musl": "2.11.4", "@tauri-apps/cli-win32-arm64-msvc": "2.11.4", "@tauri-apps/cli-win32-ia32-msvc": "2.11.4", "@tauri-apps/cli-win32-x64-msvc": "2.11.4" }, "bin": { "tauri": "tauri.js" } }, "sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ=="], @@ -632,8 +585,6 @@ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - "crelt": ["crelt@1.0.7", "", {}, "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA=="], - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], @@ -976,8 +927,6 @@ "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - "style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="], - "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -1038,8 +987,6 @@ "vitest": ["vitest@3.2.7", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.7", "@vitest/mocker": "3.2.7", "@vitest/pretty-format": "^3.2.7", "@vitest/runner": "3.2.7", "@vitest/snapshot": "3.2.7", "@vitest/spy": "3.2.7", "@vitest/utils": "3.2.7", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.7", "@vitest/ui": "3.2.7", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg=="], - "w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="], - "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], diff --git a/packages/cli/package.json b/packages/cli/package.json index 597f8ea..a34d9bd 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "reglet", - "version": "0.5.14", + "version": "0.5.15", "type": "module", "bin": { "reglet": "src/index.ts" }, "files": [ diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 6fece45..3dbab53 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -183,7 +183,7 @@ const rulesSteeringPromptLimit = 4_000; type ContentId = (typeof contentIds)[number]; const program = new Command(); -const version = process.env.REGLET_VERSION ?? '0.5.14'; +const version = process.env.REGLET_VERSION ?? '0.5.15'; const managerApplication = new RegletApplication(); program diff --git a/packages/core/package.json b/packages/core/package.json index 4082484..26fcec8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@reglet/core", - "version": "0.5.14", + "version": "0.5.15", "type": "module", "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/manager-application/package.json b/packages/manager-application/package.json index 1952c70..747d921 100644 --- a/packages/manager-application/package.json +++ b/packages/manager-application/package.json @@ -1,6 +1,6 @@ { "name": "@reglet/manager-application", - "version": "0.5.14", + "version": "0.5.15", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/manager-protocol/package.json b/packages/manager-protocol/package.json index 8284015..18fa30e 100644 --- a/packages/manager-protocol/package.json +++ b/packages/manager-protocol/package.json @@ -1,6 +1,6 @@ { "name": "@reglet/manager-protocol", - "version": "0.5.14", + "version": "0.5.15", "type": "module", "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/manager-runtime/package.json b/packages/manager-runtime/package.json index d6aa458..bc9478d 100644 --- a/packages/manager-runtime/package.json +++ b/packages/manager-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@reglet/manager-runtime", - "version": "0.5.14", + "version": "0.5.15", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/manager-ui/package.json b/packages/manager-ui/package.json index 9468df3..3d21faa 100644 --- a/packages/manager-ui/package.json +++ b/packages/manager-ui/package.json @@ -1,6 +1,6 @@ { "name": "@reglet/manager-ui", - "version": "0.5.14", + "version": "0.5.15", "private": true, "type": "module", "main": "src/index.ts", @@ -11,12 +11,7 @@ "./testing": "./src/testing/index.ts" }, "dependencies": { - "@codemirror/lang-json": "^6.0.1", - "@codemirror/lang-markdown": "^6.3.4", - "@codemirror/state": "^6.5.2", - "@codemirror/view": "^6.38.1", "@reglet/manager-protocol": "workspace:*", - "@tanstack/react-virtual": "^3.13.12", "diff": "^8.0.2", "lucide-react": "^0.468.0", "react": "^18.3.0" diff --git a/packages/manager-ui/src/app/ManagerApp.tsx b/packages/manager-ui/src/app/ManagerApp.tsx index 631cc74..9859b3c 100644 --- a/packages/manager-ui/src/app/ManagerApp.tsx +++ b/packages/manager-ui/src/app/ManagerApp.tsx @@ -15,11 +15,12 @@ import { RefreshCw, Search, Settings, + Sparkles, Trash2, Download, Cloud, + ExternalLink, } from 'lucide-react'; -import { useVirtualizer } from '@tanstack/react-virtual'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import type { ReactNode, RefObject } from 'react'; import type { @@ -48,9 +49,11 @@ import { ReviewApplyWorkbench, type ReviewRequest } from '../features/review/Rev import { SettingsWorkbench, type SettingsSection } from '../features/settings/SettingsWorkbench.js'; import { SetupOnboarding } from '../features/onboarding/SetupOnboarding.js'; import { OverviewWorkbench } from '../features/overview/OverviewWorkbench.js'; +import { SyncDiffsView } from '../features/sync-diffs/SyncDiffsView.js'; const destinations = [ { id: 'overview', label: 'Overview', icon: LayoutDashboard }, + { id: 'diffs', label: 'Sync & Diffs', icon: FileDiff }, { id: 'library', label: 'Library', icon: Library }, { id: 'projects', label: 'Project Inbox', icon: Inbox }, { id: 'providers', label: 'Providers', icon: Box }, @@ -107,6 +110,11 @@ export function ManagerApp({ client, hostActions, initialDestination = 'overview const [reviewRequest, setReviewRequest] = useState(null); const [reviewBusy, setReviewBusy] = useState(false); const [settingsSection, setSettingsSection] = useState('general'); + const [forceOnboarding, setForceOnboarding] = useState(false); + const [showReturningNotice, setShowReturningNotice] = useState(() => { + if (typeof localStorage === 'undefined') return false; + return localStorage.getItem('reglet:notice:returning-acknowledged') !== 'true'; + }); const latestRefresh = useRef(0); const latestSnapshotRevision = useRef(-1); const saveQueue = useRef>(Promise.resolve()); @@ -114,6 +122,15 @@ export function ManagerApp({ client, hostActions, initialDestination = 'overview const reviewReturnTarget = useRef<'top' | 'inspector' | 'overview' | 'providers'>('top'); const commandModifier = primaryModifierLabel(); + const dismissReturningNotice = () => { + setShowReturningNotice(false); + try { + localStorage.setItem('reglet:notice:returning-acknowledged', 'true'); + } catch { + // ignore + } + }; + const refresh = useCallback(async () => { const request = latestRefresh.current + 1; latestRefresh.current = request; @@ -296,7 +313,8 @@ export function ManagerApp({ client, hostActions, initialDestination = 'overview }; const blockingOnboardingOpen = snapshot?.library.migration.status === 'available' || - (snapshot !== null && !snapshot.settings.setup.completed); + (snapshot !== null && !snapshot.settings.setup.completed) || + forceOnboarding; useEffect(() => { const onKeyDown = (event: KeyboardEvent) => { @@ -338,6 +356,17 @@ export function ManagerApp({ client, hostActions, initialDestination = 'overview } }; + const openExternalArtifact = useCallback((artifactId: string) => { + void client.command('external.open', { + target: { + kind: 'canonical', + artifact: artifactId, + }, + }).catch((openError: unknown) => { + setError(messageFrom(openError)); + }); + }, [client]); + const initialSyncRequired = snapshot?.settings.sync.enabled === true && snapshot.settings.sync.lastCompletedAt === undefined; const runInitialSync = async () => { setBusy(true); @@ -381,6 +410,17 @@ export function ManagerApp({ client, hostActions, initialDestination = 'overview {initialSyncRequired ?
: null} + {showReturningNotice && snapshot !== null && snapshot.settings.setup.completed && (snapshot.library.artifacts.length > 0 || snapshot.providers.some((p) => p.detected)) ? ( + + ) : null} + {reviewRequest === null ?
:
`${unit.provider}:${unit.content}`).join('|')} client={client} @@ -456,6 +509,7 @@ export function ManagerApp({ client, hostActions, initialDestination = 'overview onRefresh={() => void refresh()} onArchive={selectedArtifact?.metadata.lifecycle === 'active' ? () => void mutateArtifact('library.archive', selectedArtifact.metadata.id) : undefined} onSettings={() => { setSettingsSection('general'); setDestination('settings'); }} + onRunSetup={() => setForceOnboarding(true)} /> {snapshot?.library.migration.status === 'available' ? setDestination(openProjectInbox ? 'projects' : 'library')} + canCancel={snapshot.settings.setup.completed || forceOnboarding} + onCancel={() => setForceOnboarding(false)} + onComplete={(openProjectInbox) => { + setForceOnboarding(false); + setDestination(openProjectInbox ? 'projects' : 'library'); + }} onError={setError} /> : null} @@ -555,6 +614,7 @@ interface LibraryWorkbenchProps { onReview: (provider: ManagerArtifactProjectionV3['provider']) => void; searchInputRef: RefObject; commandModifier: string; + onOpenExternal?: (id: string) => void; } function LibraryWorkbench({ @@ -585,16 +645,11 @@ function LibraryWorkbench({ onReview, searchInputRef, commandModifier, + onOpenExternal, }: LibraryWorkbenchProps) { const [editorView, setEditorView] = useState<'edit' | 'diff'>('edit'); const [mobilePane, setMobilePane] = useState<'collection' | 'editor' | 'details'>('collection'); const list = useRef(null); - const virtual = useVirtualizer({ - count: artifacts.length, - getScrollElement: () => list.current, - estimateSize: () => 38, - overscan: 8, - }); return ( <>