From c8bbfab08aa470215ca9da5442b198042b9766c4 Mon Sep 17 00:00:00 2001 From: "xuan.huang" <5563315+Huxpro@users.noreply.github.com> Date: Wed, 15 Jul 2026 02:06:15 +0300 Subject: [PATCH] chore: remove Elk PR development artifacts --- ...2026-07-14-elk-sheet-gesture-refinement.md | 280 ------------------ ...2026-07-14-example-benchmark-navigation.md | 126 -------- .../2026-07-13-elk-navigation-sheet-design.md | 102 ------- ...-14-example-benchmark-navigation-design.md | 38 --- examples/ai-chat/.impeccable.md | 35 --- examples/elk/.impeccable.md | 21 -- website/package.json | 1 - website/scripts/benchmark-navigation.test.mjs | 53 ---- 8 files changed, 656 deletions(-) delete mode 100644 docs/superpowers/plans/2026-07-14-elk-sheet-gesture-refinement.md delete mode 100644 docs/superpowers/plans/2026-07-14-example-benchmark-navigation.md delete mode 100644 docs/superpowers/specs/2026-07-13-elk-navigation-sheet-design.md delete mode 100644 docs/superpowers/specs/2026-07-14-example-benchmark-navigation-design.md delete mode 100644 examples/ai-chat/.impeccable.md delete mode 100644 examples/elk/.impeccable.md delete mode 100644 website/scripts/benchmark-navigation.test.mjs diff --git a/docs/superpowers/plans/2026-07-14-elk-sheet-gesture-refinement.md b/docs/superpowers/plans/2026-07-14-elk-sheet-gesture-refinement.md deleted file mode 100644 index 4e340137..00000000 --- a/docs/superpowers/plans/2026-07-14-elk-sheet-gesture-refinement.md +++ /dev/null @@ -1,280 +0,0 @@ -# Elk Sheet Gesture Refinement Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Make the Elk navigation Sheet feel native on iOS by porting the current `lynx-ui` direction lock, velocity projection, rubber resistance, progress-linked backdrop, and spring settle behavior into Vue Lynx. - -**Architecture:** Keep gesture state and rendering mutations in main-thread worklets inside `Sheet.vue`. Keep angle, physics, progress, and dismissal decisions as pure functions in `gesture.ts` so Node tests exercise the same calculations. Use a hybrid input policy: the dedicated handle always owns vertical drags, while the scrollable panel claims only downward drags that begin at `scrollTop === 0`. - -**Tech Stack:** Vue Lynx SFCs, Main Thread Script worklets, `useMainThreadRef`, requestAnimationFrame, Node test runner, Rspeedy, Lynx DevTool. - ---- - -## File map - -- `examples/elk/src/components/sheet/gesture.ts`: pure gesture ownership, rubber resistance, velocity projection, progress, and spring-step calculations. -- `examples/elk/src/components/sheet/Sheet.vue`: hybrid touch surfaces, main-thread state machine, direct manipulation, spring settle, and visual structure. -- `examples/elk/src/components/NavBottom.vue`: spacing adjustment for the new grabber without changing navigation behavior. -- `examples/elk/scripts/native-compat.test.mjs`: behavioral and source-contract regression coverage. - -### Task 1: Specify gesture physics with failing tests - -**Files:** - -- Modify: `examples/elk/scripts/native-compat.test.mjs` -- Test: `examples/elk/scripts/native-compat.test.mjs` - -- [ ] **Step 1: Replace the legacy fixed-distance test with behavior tests** - -Add assertions that demonstrate the desired pure API: - -```js -test('sheet claims handle drags vertically and content drags only downward at scroll top', () => { - assert.equal(sheetGesture.shouldClaimSheetGesture?.('handle', 2, -12, 40), true); - assert.equal(sheetGesture.shouldClaimSheetGesture?.('handle', 20, 10, 0), false); - assert.equal(sheetGesture.shouldClaimSheetGesture?.('content', 2, 12, 0), true); - assert.equal(sheetGesture.shouldClaimSheetGesture?.('content', 2, -12, 0), false); - assert.equal(sheetGesture.shouldClaimSheetGesture?.('content', 2, 12, 1), false); -}); - -test('sheet uses bounded rubber resistance above the open position', () => { - assert.equal(sheetGesture.resolveSheetDrag?.(30), 30); - assert.equal(sheetGesture.resolveSheetDrag?.(-40, 80, 0.5), -16); - assert.ok(sheetGesture.resolveSheetDrag?.(-10000, 80, 0.5) > -80); -}); - -test('sheet release uses distance or projected fling travel to dismiss', () => { - assert.equal(sheetGesture.shouldDismissSheet?.(120, 0, 120), true); - assert.equal(sheetGesture.shouldDismissSheet?.(119, 0, 120), false); - assert.equal(sheetGesture.shouldDismissSheet?.(24, 900, 120), true); - assert.equal(sheetGesture.shouldDismissSheet?.(10, 1200, 120), false); - assert.equal(sheetGesture.shouldDismissSheet?.(60, -900, 120), false); -}); - -test('sheet backdrop progress follows downward travel', () => { - assert.equal(sheetGesture.sheetOpenProgress?.(-20, 600), 1); - assert.equal(sheetGesture.sheetOpenProgress?.(150, 600), 0.75); - assert.equal(sheetGesture.sheetOpenProgress?.(800, 600), 0); -}); - -test('sheet filters noisy velocity samples and integrates a stable spring step', () => { - assert.equal(sheetGesture.smoothSheetVelocity?.(0, 9, 10, 0.25), 225); - const next = sheetGesture.stepSheetSpring?.(100, 0, 0, 1 / 60); - assert.ok(next.value < 100); - assert.ok(next.velocity < 0); -}); -``` - -- [ ] **Step 2: Add a source-contract test for the hybrid surfaces** - -Require `.sheet-handle`, separate handle/content start handlers, backdrop and surface refs, layout measurement, and requestAnimationFrame settling. Also require that drag mutation remains main-thread-bound and does not use `transition: all`. - -- [ ] **Step 3: Run the native compatibility test and verify RED** - -Run: - -```bash -pnpm --dir examples/elk test:native-compat -``` - -Expected: the new gesture tests fail because the new pure helpers and hybrid Sheet structure do not exist. - -### Task 2: Implement the pure gesture model - -**Files:** - -- Modify: `examples/elk/src/components/sheet/gesture.ts` -- Test: `examples/elk/scripts/native-compat.test.mjs` - -- [ ] **Step 1: Implement angle and ownership classification** - -Keep the 8 px lock threshold and use vertical angle ranges equivalent to `lynx-ui`: - -```ts -export type SheetGestureSource = 'handle' | 'content'; - -export function shouldClaimSheetGesture( - source: SheetGestureSource, - deltaX: number, - deltaY: number, - scrollTop: number, -): boolean { - 'main thread'; - const displacement = Math.sqrt(deltaX * deltaX + deltaY * deltaY); - if (displacement < SHEET_GESTURE_LOCK_DISTANCE) - return false; - const vertical = Math.abs(deltaY) > Math.abs(deltaX); - if (!vertical) - return false; - return source === 'handle' || (deltaY > 0 && scrollTop <= 0); -} -``` - -- [ ] **Step 2: Implement upstream-compatible rubber resistance** - -```ts -export function rubberEffect(original: number, max: number, coeff = 0.5): number { - 'main thread'; - if (original === 0 || max === 0) - return 0; - return (1 - 1 / ((Math.abs(original) * coeff) / max + 1)) * max; -} - -export function resolveSheetDrag(deltaY: number, rubberMax = 80, coeff = 0.5): number { - 'main thread'; - return deltaY >= 0 ? deltaY : -rubberEffect(-deltaY, rubberMax, coeff); -} -``` - -- [ ] **Step 3: Implement progress, velocity smoothing, projection, and release decisions** - -Use `travel = velocity² / (2 × 2000)` from `lynx-ui`, require 24 px of intentional downward travel for a fling dismissal, and retain the existing 120 px distance threshold as the non-fling path. - -- [ ] **Step 4: Implement one deterministic spring integration step** - -Expose `stepSheetSpring(value, velocity, target, deltaSeconds, stiffness = 300, damping = 30)` returning the next `{ value, velocity }`. Clamp `deltaSeconds` to `1 / 30` so a delayed frame cannot explode the simulation. - -- [ ] **Step 5: Run tests and verify the pure behavior is GREEN** - -Run: - -```bash -pnpm --dir examples/elk test:native-compat -``` - -Expected: physics assertions pass; the source-contract test remains red until `Sheet.vue` is updated. - -### Task 3: Implement hybrid main-thread dragging and motion - -**Files:** - -- Modify: `examples/elk/src/components/sheet/Sheet.vue` -- Modify: `examples/elk/src/components/NavBottom.vue` -- Test: `examples/elk/scripts/native-compat.test.mjs` - -- [ ] **Step 1: Replace the single panel ref with coordinated visual refs** - -Create main-thread refs for the surface, backdrop, measured surface height, gesture source, direction state, last touch sample, smoothed velocity, animation frame, and current translation. Keep all writes inside `'main thread'` functions. - -- [ ] **Step 2: Add the hybrid touch surfaces** - -Render this structure: - -```vue - - - - - - - - - - -``` - -The surface height remains capped by `topInset`; the fixed layer remains inset above the bottom bar and Sparkling safe area. - -- [ ] **Step 3: Implement direction locking and direct manipulation** - -On touch start, record coordinates/source without stopping an existing animation. On the first move beyond 8 px, call `shouldClaimSheetGesture`; reject horizontal/content-scroll gestures permanently, or cancel the current spring and capture the valid vertical gesture. During captured moves, use `resolveSheetDrag`, set `translateY`, and update backdrop opacity from `sheetOpenProgress`. - -- [ ] **Step 4: Implement spring settle and dismissal** - -Drive requestAnimationFrame with `stepSheetSpring`. Snap-back targets `0`; dismissal targets the measured surface height plus 32 px. Stop when distance and velocity are both below `0.5`, then call `runOnBackground(requestClose)()` only for dismissal. Cancelled gestures always spring to `0`. - -- [ ] **Step 5: Reset inline motion after leave** - -Add an `after-leave` handler that invokes a main-thread reset, restoring translation `0`, backdrop opacity `1`, and idle gesture state so reopening never inherits an off-screen inline transform. - -- [ ] **Step 6: Style the grabber and connected rubber fill** - -Use a 28 px hit area, a centered 36 × 4 px pill, the existing themed surface color, and a surface-attached 80 px fill below the panel so upward rubber travel never reveals the backdrop between the Sheet and bottom bar. Reduce `nav-sheet-content` top padding to preserve the current first-row rhythm. - -- [ ] **Step 7: Run the focused suite and verify GREEN** - -Run: - -```bash -pnpm --dir examples/elk test:native-compat -``` - -Expected: every native compatibility test passes. - -### Task 4: Verify native behavior and publish the updated bundle - -**Files:** - -- Verify: `examples/elk/dist/main.lynx.bundle` -- Verify: PR #196 checks and Vercel artifacts - -- [ ] **Step 1: Clear the Elk cache and restart the dev server** - -Run: - -```bash -rm -rf examples/elk/node_modules/.cache -pnpm --dir examples/elk dev -``` - -- [ ] **Step 2: Build both targets** - -Run: - -```bash -pnpm --dir examples/elk build -``` - -Expected: `dist/main.lynx.bundle` and `dist/main.web.bundle` both build successfully. - -- [ ] **Step 3: Verify three native gesture paths** - -Using Lynx DevTool on the Sparkling iOS simulator: - -1. Slow downward drag under the threshold springs open and restores the backdrop. -2. Short fast downward fling closes the Sheet. -3. Upward handle pull resists asymptotically and returns without exposing a gap; upward content swipe still scrolls. - -Capture the final open and post-settle states with `take-screenshot`. - -- [ ] **Step 4: Run repository-level validation** - -Run: - -```bash -pnpm lint -pnpm test:dev-smoke -``` - -Expected: both commands exit successfully. - -- [ ] **Step 5: Commit and push PR #196** - -Stage only the Sheet refinement files, commit with `feat(examples/elk): refine native sheet gestures`, and push `HEAD` to `origin/claude/elk-vue-lynx-port-rh9gpd`. - -- [ ] **Step 6: Confirm deployment artifacts** - -Wait for every PR check and Vercel deployment to pass, then confirm HTTP 200 for: - -```text -https://vue-lynx-git-claude-elk-vue-lynx-port-rh9gpd-huxpros-projects.vercel.app/examples/elk/dist/main.lynx.bundle -``` diff --git a/docs/superpowers/plans/2026-07-14-example-benchmark-navigation.md b/docs/superpowers/plans/2026-07-14-example-benchmark-navigation.md deleted file mode 100644 index 02412c35..00000000 --- a/docs/superpowers/plans/2026-07-14-example-benchmark-navigation.md +++ /dev/null @@ -1,126 +0,0 @@ -# Example Benchmark Navigation Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Move Elk under the website Benchmark section after AI Chat and relocate both examples' AI design context into their owning example directories. - -**Architecture:** Integrate current `origin/main` into the published PR #196 branch with a merge commit, preserving AI Chat while keeping the PR history stable. A small Node source-contract test owns the bilingual sidebar ordering and example-local `.impeccable.md` invariants; Rspress and Elk production builds provide integration coverage. - -**Tech Stack:** Git, Node.js test runner, TypeScript Rspress config, pnpm, Rspeedy. - ---- - -## File Map - -- `website/scripts/benchmark-navigation.test.mjs`: regression contract for bilingual ordering, Showcase removal, and example-local design files. -- `website/package.json`: exposes the focused navigation test command. -- `website/rspress.config.ts`: bilingual Benchmark sidebar entries. -- `examples/elk/.impeccable.md`: Elk-specific design context moved from the repository root. -- `examples/ai-chat/.impeccable.md`: AI Chat-specific design context moved from the repository root after merging `main`. -- `docs/superpowers/plans/2026-07-14-example-benchmark-navigation.md`: this execution record. - -### Task 1: Lock the ownership and navigation contract - -**Files:** -- Create: `website/scripts/benchmark-navigation.test.mjs` -- Modify: `website/package.json` - -- [ ] **Step 1: Add the focused test command** - -Add `"test:navigation": "node --test scripts/benchmark-navigation.test.mjs"` to `website/package.json`. - -- [ ] **Step 2: Write the failing source-contract test** - -The test reads `website/rspress.config.ts`, asserts the English and Chinese sequences contain `HackerNews`, `AI Chat`, then Elk; asserts no Showcase section remains; and verifies `examples/elk/.impeccable.md` plus `examples/ai-chat/.impeccable.md` exist while the root `.impeccable.md` does not. - -- [ ] **Step 3: Run the test and verify RED** - -Run: `pnpm --dir website test:navigation` - -Expected: FAIL because both example-local context paths and the merged AI Chat + Elk sidebar order do not yet exist. - -### Task 2: Move Elk context before integrating main - -**Files:** -- Delete: `.impeccable.md` -- Create: `examples/elk/.impeccable.md` - -- [ ] **Step 1: Move the Elk design context without changing its contents** - -Use `apply_patch` to add the existing content at `examples/elk/.impeccable.md` and delete the root file. - -- [ ] **Step 2: Commit the conflict-prevention move and test harness** - -```bash -git add website/package.json website/scripts/benchmark-navigation.test.mjs .impeccable.md examples/elk/.impeccable.md docs/superpowers/plans/2026-07-14-example-benchmark-navigation.md -git commit -m "test(website): lock benchmark navigation ownership" -``` - -### Task 3: Integrate main and localize AI Chat context - -**Files:** -- Merge: `origin/main` -- Delete: `.impeccable.md` -- Create: `examples/ai-chat/.impeccable.md` - -- [ ] **Step 1: Merge the current main branch** - -Run: `git merge --no-edit origin/main` - -Expected: merge succeeds because Elk's conflicting root context was moved first; AI Chat and current website dependencies enter the PR branch. - -- [ ] **Step 2: Move AI Chat's design context without changing its contents** - -Use `apply_patch` to add the merged root content at `examples/ai-chat/.impeccable.md` and delete the root file. - -### Task 4: Put Elk after AI Chat in both sidebars - -**Files:** -- Modify: `website/rspress.config.ts` - -- [ ] **Step 1: Update the English Benchmark list** - -Keep `AI Chat` and add `{ text: 'Elk (Mastodon Client)', link: '/guide/elk' }` immediately after it. Remove the Showcase divider/header block. - -- [ ] **Step 2: Update the Chinese Benchmark list** - -Keep `AI Chat` and add `{ text: 'Elk(Mastodon 客户端)', link: '/zh/guide/elk' }` immediately after it. Remove the 案例展示 divider/header block. - -- [ ] **Step 3: Run the focused test and verify GREEN** - -Run: `pnpm --dir website test:navigation` - -Expected: all navigation and ownership assertions pass. - -- [ ] **Step 4: Commit the integrated result** - -```bash -git add . website/rspress.config.ts examples/ai-chat/.impeccable.md -git commit -m "feat(website): classify Elk as a benchmark" -``` - -### Task 5: Verify and publish PR #196 - -**Files:** -- Verify: `website/dist/` -- Verify: `examples/elk/dist/main.lynx.bundle` - -- [ ] **Step 1: Run focused and production verification** - -```bash -pnpm --dir website test:navigation -pnpm --dir website build -pnpm --dir examples/elk test:native-compat -pnpm --dir examples/elk build -git diff --check -``` - -Expected: all commands exit 0; the website and Lynx bundle build successfully. - -- [ ] **Step 2: Push the existing PR head** - -Run: `git push origin HEAD:claude/elk-vue-lynx-port-rh9gpd` - -- [ ] **Step 3: Verify remote deployment** - -Wait for `gh pr checks 196 --repo Huxpro/vue-lynx --watch --interval 10`, then request the Vercel `/guide/elk` page and `/examples/elk/dist/main.lynx.bundle`. Confirm both return HTTP 200 and the PR is mergeable against `main`. diff --git a/docs/superpowers/specs/2026-07-13-elk-navigation-sheet-design.md b/docs/superpowers/specs/2026-07-13-elk-navigation-sheet-design.md deleted file mode 100644 index aaeb9423..00000000 --- a/docs/superpowers/specs/2026-07-13-elk-navigation-sheet-design.md +++ /dev/null @@ -1,102 +0,0 @@ -# Elk Navigation Sheet Design - -## Goal - -Bring the Vue Lynx Elk mobile navigation to behavioral and visual parity with -the upstream `elk-zone/elk` bottom navigation and More sheet on Lynx Web and -native iOS Lynx Explorer. - -## Reference behavior - -The reference is upstream Elk commit `d444a59`, run locally with the mocked -development data at a 390 × 844 viewport. Guest navigation contains Explore, -Local, Federated, and More. Opening More leaves the bottom bar fixed, changes -the More icon to the primary-colored close icon, dims the timeline, and raises -a rounded sheet above the bar. The sheet height is capped so roughly 200 px of -the viewport remains outside the panel. Its content scrolls independently and -can be dismissed through the backdrop, the close button, navigation, or a -downward drag. - -The gesture refinement also follows the current `lynx-ui` Sheet implementation -from `origin/main` (package version 3.134.0). Its relevant behaviors are an 8 px -direction-lock threshold, angle-based gesture claiming, a nonlinear rubber-band -at the fully-open boundary, velocity-projected dismissal, motion that stays on -the main thread, and backdrop opacity derived from sheet progress. - -## Considered approaches - -1. Copy only the upstream CSS transition into `NavBottom.vue`. This is small, - but couples panel state, navigation, and drag behavior into one component - and makes native gesture work happen on the background thread. -2. Depend directly on `@lynx-js/lynx-ui-sheet`. This preserves the official - primitive, but it is a ReactLynx package whose hooks and context cannot be - consumed by Vue Lynx. -3. Port the official primitive's state and gesture model into a reusable Vue - Lynx component, then style its content for Elk. This keeps the sheet generic, - gives native drag work to main-thread worklets, and lets Elk own its exact - navigation appearance. This is the selected approach. - -## Architecture - -`components/sheet/Sheet.vue` owns controlled visibility, backdrop dismissal, -mount/unmount transitions, and drag-to-dismiss. It exposes a default slot and -keeps visual surface choices configurable through classes and CSS. A dedicated -28 px handle is always draggable. The scrollable content also accepts a -downward drag only while its scroll position is at the top; upward gestures and -content gestures that start while scrolled remain owned by the scroll view. - -Main-thread touch handlers direction-lock after 8 px, sample drag velocity, -update the panel, handle, rubber fill, and backdrop together, and settle using -a short requestAnimationFrame spring. The background thread is contacted only -after a dismiss animation finishes. A small pure helper module owns angle -classification, nonlinear rubber resistance, velocity filtering, progress, -and dismissal decisions so the behavior can be covered by Node tests without -rendering Lynx. - -`NavBottom.vue` owns Elk-specific destinations, active/disabled state, menu -rows, theme and Zen Mode actions, and route navigation. It renders the sheet as -a root-level fixed overlay whose bottom inset includes the persistent bar and -the Sparkling safe area. Guest and authenticated tab sets mirror upstream Elk -where the current port has matching routes. - -## Interaction and visual details - -- Backdrop opacity enters with the sheet and uses Elk's black 50% scrim. -- Panel entry is 250 ms ease-out; direct backdrop/route exit is 188 ms ease-in. -- A visible 36 × 4 px grabber sits inside a 28 px touch target at the top edge. -- The panel has Elk's 8 px top radius, 1 px top border, translucent themed - surface, and independent vertical scrolling. -- Rows use 20 px horizontal padding, 40 px minimum height, 20 px icons, and the - upstream active, disabled, and primary colors. -- Pressed rows and bottom tabs use short transform/opacity feedback. -- The handle claims vertical drags in either direction after 8 px. Content only - claims a downward drag when `scrollTop` is zero, preserving natural scrolling. -- Pulling upward past the fully-open position uses the same asymptotic - rubber-band equation as `lynx-ui` with coefficient 0.5 and an 80 px cap. -- Pulling downward follows the finger. Release dismisses after the distance - threshold or after a deliberate minimum-distance fast fling; otherwise it - springs back to rest without a background-thread round trip. -- Backdrop opacity follows open progress during drag and settle, rather than - remaining fully dark until dismissal completes. -- A cancelled gesture always springs to the open position and clears captured - direction/velocity state. -- Route changes, backdrop taps, and enabled row taps close the sheet. Disabled - items do not navigate or close it. -- The existing Sparkling safe-area spacers remain outside the overlay, so the - fixed bar and panel share the same safe content on iOS. - -## Testing and verification - -Tests first cover direction locking, hybrid content/handle ownership, -rubber-band limits, velocity filtering, distance/fling dismissal, backdrop -progress, menu structure, main-thread bindings, and the persistent More/close -state. Verification then runs the Elk native compatibility suite, the example -build, the repository checks affected by Vue SFC worklets, the Vercel mobile -preview, and real slow/fast/cancelled drags on the native bundle in iOS Lynx -Explorer with cache and app restarts. - -## Scope - -This change ports the Sheet primitive locally for the Elk example. It does not -publish a new public `vue-lynx` UI package or add unsupported Elk pages merely -to make disabled menu entries clickable. diff --git a/docs/superpowers/specs/2026-07-14-example-benchmark-navigation-design.md b/docs/superpowers/specs/2026-07-14-example-benchmark-navigation-design.md deleted file mode 100644 index 9a71e516..00000000 --- a/docs/superpowers/specs/2026-07-14-example-benchmark-navigation-design.md +++ /dev/null @@ -1,38 +0,0 @@ -# Example Benchmark Navigation Design - -## Goal - -Classify Elk as a Vue Lynx benchmark beside the other production-scale ports, -and keep AI-facing design context owned by the example it describes. - -## Website Navigation - -- Keep the existing Benchmark order: TodoMVC, 7GUIs, HackerNews, AI Chat. -- Add Elk immediately after AI Chat in both English and Chinese sidebars. -- Remove the now-empty Showcase sidebar section. -- Keep the Elk card in the home-page “Try it for yourself” area because that - area is an interactive entry point rather than the sidebar classification. - -## Design Context Ownership - -- Move Elk's root `.impeccable.md` to `examples/elk/.impeccable.md`. -- Move AI Chat's root `.impeccable.md` from current `main` to - `examples/ai-chat/.impeccable.md` while integrating `main` into PR #196. -- Leave no example-specific `.impeccable.md` at the repository root. -- Preserve each file's contents; only its ownership and location change. - -## Integration Strategy - -Merge current `origin/main` into the PR #196 branch instead of rebasing its -published history. Resolve the add/add root `.impeccable.md` conflict by placing -both versions in their owning example directories. Preserve AI Chat and its -website entry from `main`, then place Elk after it. - -## Verification - -- Add a source-level regression check for both localized sidebar orders and the - absence of a Showcase section. -- Assert that each example owns its `.impeccable.md` and the root file is gone. -- Build the website and Elk native bundle. -- Push the merge and navigation changes to PR #196, then verify Vercel Preview - and the deployed native bundle. diff --git a/examples/ai-chat/.impeccable.md b/examples/ai-chat/.impeccable.md deleted file mode 100644 index 9528ad59..00000000 --- a/examples/ai-chat/.impeccable.md +++ /dev/null @@ -1,35 +0,0 @@ -## Design Context - -### Users - -Vue and Lynx developers evaluating a production-style, touch-first AI chat on -Lynx for Web and Native LynxExplorer. Their primary job is to understand and -trust the assistant's current state while composing prompts, reading streamed -reasoning and answers, and using the surrounding chat controls. - -### Brand Personality - -Calm, precise, and native. The experience should retain the official Nuxt AI -Chat template's restrained product tone while demonstrating that Vue Lynx can -match its behavior without drawing attention to platform adaptations. - -### Aesthetic Direction - -Preserve the original Nuxt AI Chat visual language: Public Sans, zinc -neutrals, blue accents, compact controls, clear message hierarchy, and both -light and dark themes. Adapt mechanics only where Lynx requires it. Avoid -decorative motion, generic AI gradients/glows, oversized spacing, and any -change that makes the port look like a redesign. - -### Design Principles - -1. Treat original Nuxt behavior and screenshots as the visual and interaction - baseline; deviations require a concrete Lynx constraint. -2. Design touch-first for mobile Native Lynx while keeping Web behavior - consistent and all important controls visible. -3. Use compact, deliberate spacing and fixed geometry for animated status - elements so streaming never causes layout movement. -4. Motion communicates state only, stays lightweight, and provides a stable - `prefers-reduced-motion` alternative where the platform exposes it. -5. Validate UI changes in LynxExplorer's native renderer, not only in a web - browser or through source-level tests. diff --git a/examples/elk/.impeccable.md b/examples/elk/.impeccable.md deleted file mode 100644 index daa07b02..00000000 --- a/examples/elk/.impeccable.md +++ /dev/null @@ -1,21 +0,0 @@ -## Design Context - -### Users - -Vue Lynx contributors and mobile developers use the Elk example to evaluate whether a Vue application can feel credible on both Lynx Web and native Lynx hosts. The interface should support familiar Mastodon navigation without asking users to relearn the upstream Elk interaction model. - -### Brand Personality - -Faithful, calm, and finely crafted. The example should feel like Elk itself rather than a generic demo, while native interactions should feel direct and physically coherent on iOS. - -### Aesthetic Direction - -Treat upstream `elk-zone/elk` as the visual and interaction reference for mobile web, and current Lynx UI primitives as the native-behavior reference. Support both light and dark themes. Prefer restrained surfaces, precise spacing, clear iconography, and subtle state transitions over decorative effects. - -### Design Principles - -- Preserve upstream Elk hierarchy, navigation semantics, spacing, and theme behavior wherever Vue Lynx supports them. -- Keep direct manipulation responsive on the main thread; motion should explain state changes rather than decorate them. -- Adapt deliberately for native safe areas, scrolling, and touch ownership without changing the recognizable Elk experience. -- Favor small, testable primitives and measurable interaction rules over one-off visual approximations. -- Verify fidelity on mobile web and real Lynx native hosts, including intermediate gesture states—not only final screenshots. diff --git a/website/package.json b/website/package.json index 5101b9fc..6e1c221e 100644 --- a/website/package.json +++ b/website/package.json @@ -6,7 +6,6 @@ "scripts": { "prepare-examples": "node scripts/prepare-examples.mjs", "prepare:docs": "pnpm prepare-examples && tsx scripts/generate-api.ts", - "test:navigation": "node --test scripts/benchmark-navigation.test.mjs", "dev": "pnpm prepare:docs && rspress dev", "dev:fast": "rspress dev", "build": "pnpm prepare:docs && rspress build", diff --git a/website/scripts/benchmark-navigation.test.mjs b/website/scripts/benchmark-navigation.test.mjs deleted file mode 100644 index 12b28774..00000000 --- a/website/scripts/benchmark-navigation.test.mjs +++ /dev/null @@ -1,53 +0,0 @@ -import assert from 'node:assert/strict'; -import { existsSync } from 'node:fs'; -import { readFile } from 'node:fs/promises'; -import test from 'node:test'; - -const configUrl = new URL('../rspress.config.ts', import.meta.url); -const rootContextUrl = new URL('../../.impeccable.md', import.meta.url); -const elkContextUrl = new URL( - '../../examples/elk/.impeccable.md', - import.meta.url, -); -const aiChatContextUrl = new URL( - '../../examples/ai-chat/.impeccable.md', - import.meta.url, -); - -function sliceBetween(source, start, end) { - const startIndex = source.indexOf(start); - const endIndex = source.indexOf(end, startIndex + start.length); - - assert.notEqual(startIndex, -1, `missing sidebar marker: ${start}`); - assert.notEqual(endIndex, -1, `missing sidebar marker: ${end}`); - return source.slice(startIndex, endIndex); -} - -test('Elk follows AI Chat in both Benchmark sidebars', async () => { - const config = await readFile(configUrl, 'utf8'); - const english = sliceBetween(config, "'/guide/': [", "'/zh/guide/': ["); - const chinese = sliceBetween(config, "'/zh/guide/': [", 'llmsUI: true'); - - assert.match( - english, - /HackerNews[\s\S]*AI Chat[\s\S]*Elk \(Mastodon Client\)/, - ); - assert.match( - chinese, - /HackerNews[\s\S]*AI Chat[\s\S]*Elk(Mastodon 客户端)/, - ); - assert.doesNotMatch(config, /sectionHeaderText:\s*'(?:Showcase|案例展示)'/); -}); - -test('example design context lives with its owning example', async () => { - assert.equal(existsSync(rootContextUrl), false); - assert.equal(existsSync(elkContextUrl), true); - assert.equal(existsSync(aiChatContextUrl), true); - - const [elkContext, aiChatContext] = await Promise.all([ - readFile(elkContextUrl, 'utf8'), - readFile(aiChatContextUrl, 'utf8'), - ]); - assert.match(elkContext, /Elk example/); - assert.match(aiChatContext, /AI chat/); -});