Skip to content

fix(site): refresh landing demos and mobile scroll trigger#499

Open
TheEverests wants to merge 3 commits into
TouchAI-org:mainfrom
TheEverests:codex/site-scroll-demo-fixes-pr
Open

fix(site): refresh landing demos and mobile scroll trigger#499
TheEverests wants to merge 3 commits into
TouchAI-org:mainfrom
TheEverests:codex/site-scroll-demo-fixes-pr

Conversation

@TheEverests

Copy link
Copy Markdown
Contributor

Summary

Refreshes the landing page demos and repository stats, then adjusts the mobile ScrollTrigger start point so sections animate at a better viewport position on narrow screens.

Related issue or RFC

N/A. Focused site polish and lockfile sync.

AI assistance disclosure

  • Tool(s) used: OpenAI Codex
  • Scope of assistance: code review, validation, commit preparation, and PR drafting
  • Human review or rewrite performed: reviewed the changed files, command results, and PR text before submission
  • Architecture or boundary impact: none

Testing evidence

corepack pnpm install --frozen-lockfile
corepack pnpm test:pr
.\node_modules\.bin\commitlint.cmd --from origin/main --to HEAD

Results:

  • corepack pnpm test:pr passed.
  • commitlint passed.
  • Existing Vue lint warnings and unused Rust try_lock_mutex warnings were reported; no errors failed the run.

Risk notes

  • AgentService, runtime, MCP, or schema impact: none
  • database baseline or migration impact: none
  • release or packaging impact: apps/desktop/src-tauri/Cargo.lock now matches the existing Cargo.toml package version

Screenshots or recordings

N/A.

Checklist

  • The PR title follows Conventional Commits and is valid for squash merge.
  • This PR is either ready for review or explicitly marked as a Draft PR.
  • I did not use [WIP] or similar title prefixes.
  • If AI materially assisted this PR, I disclosed the tools and scope and I personally reviewed every affected change.
  • I can explain the why, what, and how of this change without relying on an AI tool.
  • If this touches AgentService, runtime, MCP, or schema boundaries, there is an accepted RFC.
  • If this changes architecture or adds a new cross-boundary abstraction, there is an accepted RFC.
  • I ran pnpm test:pr for this code PR, or this is a docs-only change.
  • If I changed Rust behavior or tests, I reviewed pnpm test:coverage:rust or relied on CI coverage evidence.
  • If I changed desktop startup/window/search/popup/settings/E2E paths, I ran pnpm test:e2e locally or documented why CI is the first valid proof.
  • I added tests or explained why tests are not appropriate.
  • I updated docs when behavior changed.

@github-actions github-actions Bot added area:tauri Tauri shell or desktop runtime changes area:frontend Frontend UI or view-layer changes labels Jun 30, 2026
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a new quick-start guide and 404 help page for easier navigation.
    • Introduced lightweight in-browser rendering for markdown, math, and demo content.
  • Bug Fixes

    • Improved scrolling, typing, and answer playback behavior in demo pages.
    • Enhanced accessibility and layout handling across chat-based demos.
    • Updated site routing and crawl settings for more reliable page handling.
  • Documentation

    • Added sitemap/robot guidance and PWA metadata for the site.

Walkthrough

This PR introduces two shared client-side utilities (touchai-lite-renderer.js, touchai-lite-math.js), refactors eight embedded demo HTML components to a unified scroll-driven/postMessage pattern, updates the component demo host runtime with lazy loading and bounded message queuing, adds analytics tracking and dataset-driven GitHub stats to the homepage, and adds PWA manifest, robots.txt, and Starlight docs pages.

Scroll-Driven Demo Infrastructure, Analytics, and Site Config

Layer / File(s) Summary
Shared lite renderer and math utilities
apps/site/public/demo-utils/touchai-lite-renderer.js, apps/site/public/demo-utils/touchai-lite-math.js
window.TouchAILiteRenderer provides HTML escaping, inline rendering, and markdown-to-HTML; window.TouchAILiteMathRenderer provides KaTeX/fallback formula rendering and a markdown-with-math pipeline.
Demo HTML components — CSS and markup
apps/site/public/touchai-intro/..., apps/site/public/touchai-intro-en/..., apps/site/public/feature-solver/..., apps/site/public/feature-solver-en/..., apps/site/public/feature-reminder/..., apps/site/public/feature-reminder-en/..., apps/site/public/feature-work-organizer/..., apps/site/public/feature-work-organizer-en/...
All eight demo HTML files gain scroll-driven CSS state rules (is-scroll-driven, is-complete, is-answering) for .chat-panel, .conversation-content, and .composer; <article> gets aria-busy/aria-atomic; logo images gain explicit dimensions; markdown-it is replaced by touchai-lite-renderer.js.
Demo HTML components — scroll-driven JS logic
(same eight files)
Adds getMessagePayload/notifyParent helpers, resetDemoState/setScrollDrivenState, scheduleScrollAffordanceUpdate, forwardWheelToPage, syncMathLineGeometry caching, renderScrollProgress with hasReceivedScrollProgress thresholding, aria-busy toggling across typewriter lifecycle, and typed postMessage dispatch for all control message types.
ComponentDemo host — lazy loading, pending queue, ScopedResizeObserver
apps/site/src/scripts/component-demo-runtime.ts, apps/site/src/components/ComponentDemo.astro
TouchAiHost gains a bounded pending message queue and guarded startLoad(); dispatchMessage updates is-scroll-driven host state and flushes queued messages post-load; ScopedResizeObserver scopes observers to the host; ComponentDemo.astro adds conditional lazy/inline rendering with placeholder shimmer, role="region", and aria-label.
Homepage — analytics, GitHub stats, scroll-driven orchestration
apps/site/src/pages/index.astro, apps/site/.env.example
Adds PUBLIC_ANALYTICS_ENDPOINT, pageview/click analytics via sendBeacon/fetch, dataset-driven GitHub stats with localStorage caching and periodic refresh, snapped/queued per-frame demo progress management, updated ScrollTrigger start/end computations, and aspect-ratio frame CSS scaling.
Site config and static assets
apps/site/astro.config.mjs, apps/site/src/content.config.ts, apps/site/src/content/docs/*, apps/site/public/site.webmanifest, apps/site/public/robots.txt
Adds disable404Route: true, wires Starlight docs collection, adds 404 and getting-started Markdown pages, creates PWA site.webmanifest, and adds robots.txt with sitemap.

Sequence Diagram(s)

sequenceDiagram
  participant Page as index.astro (ScrollTrigger)
  participant Host as component-demo-runtime
  participant Demo as touchai-components.html

  Page->>Host: send({ type: "touchai-set-scroll-progress", progress })
  Host->>Host: dispatchMessage — set is-scroll-driven on host dataset
  Host->>Demo: postMessage(touchai-set-scroll-progress, progress)
  Demo->>Demo: renderScrollProgress — scrub prompt, toggle aria-busy
  Demo->>Demo: forwardWheelToPage (wheel → window.scrollBy)
  Demo-->>Host: postMessage(touchai-*-ready)
  Host-->>Page: touchai message event

  Page->>Host: send({ type: "touchai-clear-scroll-driven" })
  Host->>Demo: postMessage(touchai-clear-scroll-driven)
  Demo->>Demo: setScrollDrivenState(false) — remove is-scroll-driven class
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • TouchAI-org/TouchAI#373: Modifies component-demo-runtime.ts host message queuing and mount behavior, directly overlapping with the runtime changes in this PR.
  • TouchAI-org/TouchAI#374: Changes embedded demo component-frame/.stage/.chat-panel responsive sizing and index.astro ScrollTrigger logic, the same areas refactored here.
  • TouchAI-org/TouchAI#432: Introduces the same touchai-lite-renderer.js/touchai-lite-math.js integration and scroll-driven demo refactor in the same HTML pages.

Suggested reviewers

  • hiqiancheng

🐇 A scroll-driven stage, eight demos aligned,
The lite renderer hops where markdown-it resigned,
aria-busy toggles as the typewriter types,
Analytics beacons bounce through the pipes,
And the rabbit grins — the queue never skips!


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Description check ❌ Error The description is mostly complete, but the required related issue or RFC link is missing for this code change. Add the tracking issue or RFC link under Related issue or RFC, or explicitly mark the PR as docs-only if no link is required.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is Conventional Commits-style, concise, and accurately reflects the landing demo and mobile scroll-trigger changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot requested a review from hiqiancheng June 30, 2026 08:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/site/src/pages/index.astro (1)

614-625: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Do not let the retained Glimm canvas intercept the page.

ensureGlimmController() appends .glimm-click-layer once, and completion only removes is-glimm-transitioning; with pointer-events: auto and z-index: 9999, the retained canvas can block all subsequent clicks.

Proposed fix
             .glimm-click-layer {
                 position: fixed;
                 inset: 0;
                 z-index: 9999;
                 width: 100vw;
                 height: 100vh;
-                pointer-events: auto;
+                pointer-events: none;
             }

Also applies to: 3053-3056, 3111-3114

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/site/src/pages/index.astro` around lines 614 - 625, The retained
.glimm-click-layer is still intercepting the page because
ensureGlimmController() leaves it mounted after the transition and the CSS keeps
pointer-events enabled at z-index 9999. Update the transition cleanup so the
Glimm layer is removed or made non-interactive once the animation completes, and
keep body.is-glimm-transitioning .interactive-click as the only temporary
pointer-events override. Use the .glimm-click-layer and ensureGlimmController()
hooks to locate the cleanup path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/site/.env.example`:
- Around line 15-17: The analytics endpoint value is emitted into the page by
index.astro, so the .env.example entry should explicitly warn that
PUBLIC_ANALYTICS_ENDPOINT is client-visible and must not contain secrets or
tokens. Update the comment near PUBLIC_ANALYTICS_ENDPOINT to call out that
anything embedded in the URL will be exposed to visitors, and keep the guidance
aligned with how index.astro consumes this setting.

In `@apps/site/astro.config.mjs`:
- Line 13: The site config is disabling the only available 404 route, since
`apps/site/src/content/docs/404.md` is just the Starlight-injected 404 and there
is no real `src/pages/404.*` page. Update `astro.config.mjs` by removing
`disable404Route` from the Starlight config, or alternatively add an actual
Astro 404 page if you want to keep the flag; use the `disable404Route` setting
and the existing `404.md` customization as the key places to fix.

In `@apps/site/public/demo-utils/touchai-lite-math.js`:
- Line 29: The KaTeX fallback path has an empty catch block with an unused error
binding, which triggers the lint warning. Update the catch in the lightweight
renderer fallback so it uses a binding-free catch form, and keep the fallback
behavior in place within the same KaTeX rendering logic.
- Around line 4-6: The escapeHtml helper currently falls back to raw
String(value) when renderer is unavailable, which bypasses HTML escaping in
touchai-lite-math.js. Update escapeHtml so the no-renderer path still safely
escapes the value instead of returning unescaped text, and keep the
renderer.escapeHtml branch unchanged; use the escapeHtml function in
touchai-lite-math.js as the target for the fix.

In `@apps/site/public/demo-utils/touchai-lite-renderer.js`:
- Around line 26-29: Prettier is flagging the wrapped replace expressions in the
renderer, so update the formatting in touchai-lite-renderer’s markdown handling
to match the project’s style without changing behavior. Reflow the chained
escapeHtml(...).replace(...) call and the similar wrapped expression later in
the file so they satisfy Prettier, keeping the same regex and replacement logic.

In `@apps/site/public/feature-reminder-en/touchai-components.html`:
- Around line 1577-1580: The wheel bridge in forwardWheelToPage is suppressing
browser zoom gestures by calling preventDefault on Ctrl/Meta+wheel before any
forwarding logic runs. Update forwardWheelToPage so it returns immediately for
event.ctrlKey or event.metaKey without preventing default, and keep
preventDefault only on normal wheel events that are actually forwarded to the
page.

In `@apps/site/public/feature-reminder/touchai-components.html`:
- Around line 1823-1854: The message handler in touchai-components.html is
accepting control actions from any sender, so add a same-origin parent check
before processing touchai-reminder-start, touchai-clear-scroll-driven,
touchai-reset-demo, and touchai-set-scroll-progress. Update the
window.addEventListener('message', ...) flow to verify the event source/origin
with the existing getMessagePayload-based logic, and only apply these control
paths when the message comes from the parent on the same origin; leave the
touchai-page-bg path unchanged if it is meant to remain allowed.

In `@apps/site/public/feature-solver-en/touchai-components.html`:
- Line 2: The page is marked as English via the html lang attribute, but some
remaining ARIA labels/alt text are still Chinese, so update the affected
accessibility text in the touchai-components demo to English or explicitly scope
any Chinese-only strings with lang="zh-CN". Use the html element and the
relevant labeled UI elements in touchai-components.html to locate and fix the
remaining non-English labels.
- Around line 1968-1971: The wheel bridge in forwardWheelToPage is blocking
browser zoom gestures by calling preventDefault() for Ctrl/Meta+wheel before any
scroll forwarding logic runs. Update forwardWheelToPage so it returns
immediately for Ctrl/Meta-modified wheel events without preventing default, and
only call preventDefault() when handling a normal wheel delta that is actually
being forwarded to the page.

In `@apps/site/public/feature-solver/touchai-components.html`:
- Around line 1968-1971: In forwardWheelToPage, the early Ctrl/Meta+wheel branch
is suppressing browser zoom gestures by calling preventDefault before the
scroll-bridging logic runs. Update the wheel bridge so the modifier-key case
returns immediately without preventing default, and only call preventDefault for
normal wheel events that are actually forwarded to the page. Keep the fix
localized to forwardWheelToPage and its wheel forwarding/gating path.

In `@apps/site/public/feature-work-organizer-en/touchai-components.html`:
- Around line 1471-1474: The wheel bridge in forwardWheelToPage is swallowing
browser zoom gestures by calling preventDefault on Ctrl/Meta+wheel before any
scroll forwarding logic runs. Update forwardWheelToPage so it returns
immediately for modifier-based zoom gestures without preventing default, and
reserve preventDefault only for normal wheel events that are actually forwarded
to the page.

In `@apps/site/public/feature-work-organizer/touchai-components.html`:
- Around line 1469-1472: The wheel bridge in forwardWheelToPage is suppressing
browser zoom gestures by calling preventDefault for Ctrl/Meta+wheel before any
forwarding logic runs. Update forwardWheelToPage so it returns immediately for
event.ctrlKey or event.metaKey without preventing default, and only call
preventDefault when handling a normal wheel delta that should be forwarded to
the page. Use the existing forwardWheelToPage function and its wheel-gating
logic as the place to make this change.

In `@apps/site/public/touchai-intro-en/touchai-components.html`:
- Around line 1729-1755: The window message handler currently applies
`touchai-page-bg`, `touchai-clear-scroll-driven`, `touchai-reset-demo`, and
`touchai-set-progress` from any origin; update the
`window.addEventListener('message', ...)` logic to verify the sender matches the
expected same-origin parent before mutating state. Use the existing
`getMessagePayload` flow as the entry point, add an origin/source check before
handling any TouchAI control message, and keep the state updates
(`resetDemoState`, `setScrollDrivenState`, `scheduleScrollProgress`) gated
behind that validation.

In `@apps/site/public/touchai-intro/touchai-components.html`:
- Around line 1798-1824: The `window.addEventListener('message', ...)` handler
in the TouchAI demo accepts control messages from any sender, so restrict it
before calling `getMessagePayload(event)` or applying `touchai-page-bg`,
`touchai-clear-scroll-driven`, `touchai-reset-demo`, or `touchai-set-progress`.
Add an origin/source check at the start of the message handler to allow only the
expected parent window and same-origin messages, then ignore everything else.
Keep the validation close to the existing `message` handler logic so the
controls in `touchai-components.html` are only processed from trusted
`postMessage` senders.

In `@apps/site/src/pages/index.astro`:
- Around line 1889-1891: The hidden heading uses data-i18n="hero.heading" in the
hero section, but the translation key is missing from both language
dictionaries. Add hero.heading to the existing i18n dictionaries used by the
page (the same structures that define the other hero strings in index.astro) so
the screen-reader <h1> resolves correctly in both languages instead of falling
back to Chinese.
- Around line 2515-2519: The demo cleanup path is sending
touchai-clear-scroll-driven, but the host runtime only clears the scroll-driven
state through its reset contract for *-start and touchai-reset-demo. Update the
cleanup flow in queueDemoScrollIdle and the related sendToDemo callers so the
host receives the same reset signal it already understands, and make the
matching host-side handler clear is-scroll-driven/data state there instead of
relying on the unsupported clear event.
- Around line 2718-2749: The analytics payload built in sendAnalytics currently
includes full window.location.href and document.referrer values, which may
expose query strings, fragments, or sensitive paths. Update the payload
construction to use minimized location/referrer data instead of the full URL
fields, keeping the rest of the sendAnalytics logic unchanged and preserving the
same event metadata shape where possible.

In `@apps/site/src/scripts/component-demo-runtime.ts`:
- Around line 880-883: The lazy-load guard in startLoad currently leaves
host.dataset.touchaiLoadStarted set to true even when load() rejects, which
blocks later host.load?.() retries. Update the startLoad flow in
component-demo-runtime so it either tracks the in-flight promise and reuses it,
or resets touchaiLoadStarted in the failure path before rethrowing, ensuring the
lazy demo can retry after a transient load error.
- Around line 490-498: The pending-message dedupe logic in
component-demo-runtime’s message queue is replacing an older item in place,
which can change the final flush order. Update the branch that uses
getMessageType, pendingMessages.findIndex, and pendingMessages[existingIndex] so
that when a matching messageType is found, the old entry is removed and the new
data is appended to the end of pendingMessages instead of overwriting in place.
Keep the existing push behavior for new types so the queue preserves latest
chronology.

---

Outside diff comments:
In `@apps/site/src/pages/index.astro`:
- Around line 614-625: The retained .glimm-click-layer is still intercepting the
page because ensureGlimmController() leaves it mounted after the transition and
the CSS keeps pointer-events enabled at z-index 9999. Update the transition
cleanup so the Glimm layer is removed or made non-interactive once the animation
completes, and keep body.is-glimm-transitioning .interactive-click as the only
temporary pointer-events override. Use the .glimm-click-layer and
ensureGlimmController() hooks to locate the cleanup path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 91719302-df14-43ba-a521-aaf35046f372

📥 Commits

Reviewing files that changed from the base of the PR and between 72a6e3c and 00e8b7f.

⛔ Files ignored due to path filters (5)
  • apps/desktop/src-tauri/Cargo.lock is excluded by !**/*.lock
  • apps/site/public/apple-touch-icon.png is excluded by !**/*.png
  • apps/site/public/icon-192.png is excluded by !**/*.png
  • apps/site/public/icon-512.png is excluded by !**/*.png
  • apps/site/public/seo-card.png is excluded by !**/*.png
📒 Files selected for processing (20)
  • apps/site/.env.example
  • apps/site/astro.config.mjs
  • apps/site/public/demo-utils/touchai-lite-math.js
  • apps/site/public/demo-utils/touchai-lite-renderer.js
  • apps/site/public/feature-reminder-en/touchai-components.html
  • apps/site/public/feature-reminder/touchai-components.html
  • apps/site/public/feature-solver-en/touchai-components.html
  • apps/site/public/feature-solver/touchai-components.html
  • apps/site/public/feature-work-organizer-en/touchai-components.html
  • apps/site/public/feature-work-organizer/touchai-components.html
  • apps/site/public/robots.txt
  • apps/site/public/site.webmanifest
  • apps/site/public/touchai-intro-en/touchai-components.html
  • apps/site/public/touchai-intro/touchai-components.html
  • apps/site/src/components/ComponentDemo.astro
  • apps/site/src/content.config.ts
  • apps/site/src/content/docs/404.md
  • apps/site/src/content/docs/getting-started.md
  • apps/site/src/pages/index.astro
  • apps/site/src/scripts/component-demo-runtime.ts
📜 Review details
🧰 Additional context used
🪛 ast-grep (0.44.0)
apps/site/public/demo-utils/touchai-lite-renderer.js

[warning] 2-6: Avoid hand-rolled HTML escaping (replacing characters with HTML entities); use a vetted encoder/sanitizer such as DOMPurify or sanitize-html.
Context: String(value)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(manual-sanitization)


[warning] 2-5: Avoid hand-rolled HTML escaping (replacing characters with HTML entities); use a vetted encoder/sanitizer such as DOMPurify or sanitize-html.
Context: String(value)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(manual-sanitization)


[warning] 2-4: Avoid hand-rolled HTML escaping (replacing characters with HTML entities); use a vetted encoder/sanitizer such as DOMPurify or sanitize-html.
Context: String(value)
.replace(/&/g, '&')
.replace(/</g, '<')
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(manual-sanitization)


[warning] 2-3: Avoid hand-rolled HTML escaping (replacing characters with HTML entities); use a vetted encoder/sanitizer such as DOMPurify or sanitize-html.
Context: String(value)
.replace(/&/g, '&')
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(manual-sanitization)

apps/site/public/demo-utils/touchai-lite-math.js

[warning] 239-239: Detects non-literal values in regular expressions
Context: new RegExp(<p class="paragraph-node">${token}</p>, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity (ReDoS via non-literal RegExp).

(detect-non-literal-regexp)


[warning] 242-242: Detects non-literal values in regular expressions
Context: new RegExp(token, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity (ReDoS via non-literal RegExp).

(detect-non-literal-regexp)

apps/site/public/touchai-intro/touchai-components.html

[warning] 1631-1631: Avoid using the initial state variable in setState
Context: setScrollDrivenState(hasReceivedScrollProgress)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)


[warning] 1797-1824: Check origin of events
Context: window.addEventListener('message', (event) => {
const message = getMessagePayload(event);
if (message.type === 'touchai-page-bg') {
document.documentElement.style.setProperty(
'--page-bg',
typeof message.background === 'string' ? message.background : 'transparent'
);
return;
}

                if (message.type === 'touchai-clear-scroll-driven') {
                    hasReceivedScrollProgress = false;
                    setScrollDrivenState(false);
                    updateScrollAffordance();
                    return;
                }

                if (message.type === 'touchai-reset-demo') {
                    resetDemoState(false);
                    return;
                }

                if (message.type !== 'touchai-set-progress') {
                    return;
                }

                scheduleScrollProgress(message.progress);
            })

Note: [CWE-346] Origin Validation Error (postMessage handler without origin check).

(event-check-origin)

apps/site/public/feature-solver-en/touchai-components.html

[warning] 2035-2035: Avoid using the initial state variable in setState
Context: setScrollDrivenState(hasReceivedScrollProgress)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)

apps/site/public/touchai-intro-en/touchai-components.html

[warning] 1560-1560: Avoid using the initial state variable in setState
Context: setScrollDrivenState(hasReceivedScrollProgress)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)


[warning] 1728-1755: Check origin of events
Context: window.addEventListener('message', (event) => {
const message = getMessagePayload(event);
if (message.type === 'touchai-page-bg') {
document.documentElement.style.setProperty(
'--page-bg',
typeof message.background === 'string' ? message.background : 'transparent'
);
return;
}

                if (message.type === 'touchai-clear-scroll-driven') {
                    hasReceivedScrollProgress = false;
                    setScrollDrivenState(false);
                    updateScrollAffordance();
                    return;
                }

                if (message.type === 'touchai-reset-demo') {
                    resetDemoState(false);
                    return;
                }

                if (message.type !== 'touchai-set-progress') {
                    return;
                }

                scheduleScrollProgress(message.progress);
            })

Note: [CWE-346] Origin Validation Error (postMessage handler without origin check).

(event-check-origin)

apps/site/public/feature-reminder/touchai-components.html

[warning] 1638-1638: Avoid using the initial state variable in setState
Context: setScrollDrivenState(hasReceivedScrollProgress)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)

apps/site/public/feature-solver/touchai-components.html

[warning] 2031-2031: Avoid using the initial state variable in setState
Context: setScrollDrivenState(hasReceivedScrollProgress)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)

apps/site/public/feature-reminder-en/touchai-components.html

[warning] 1644-1644: Avoid using the initial state variable in setState
Context: setScrollDrivenState(hasReceivedScrollProgress)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)

apps/site/public/feature-work-organizer/touchai-components.html

[warning] 1532-1532: Avoid using the initial state variable in setState
Context: setScrollDrivenState(hasReceivedScrollProgress)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)

apps/site/public/feature-work-organizer-en/touchai-components.html

[warning] 1538-1538: Avoid using the initial state variable in setState
Context: setScrollDrivenState(hasReceivedScrollProgress)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)

🪛 ESLint
apps/site/src/content.config.ts

[error] 1-3: Run autofix to sort these imports!

(simple-import-sort/imports)

apps/site/public/demo-utils/touchai-lite-renderer.js

[error] 26-29: Replace ⏎····················/\*\*([^*]+)\*\*/g,⏎····················'<strong>$1</strong>'⏎················ with /\*\*([^*]+)\*\*/g,·'<strong>$1</strong>'

(prettier/prettier)


[error] 51-53: Replace ⏎····················.map((item)·=>·

  • ${renderInline(item)}
  • )⏎···················· with .map((item)·=>·
  • ${renderInline(item)}
  • )

    (prettier/prettier)

    apps/site/public/demo-utils/touchai-lite-math.js

    [error] 29-29: 'error' is defined but never used.

    (@typescript-eslint/no-unused-vars)


    [error] 29-29: Empty block statement.

    (no-empty)


    [error] 208-210: Replace ⏎············?·'formula-module'⏎··········· with ·?·'formula-module'

    (prettier/prettier)


    [error] 239-242: Replace ⏎····················new·RegExp(<p·class="paragraph-node">${token}

    ,·'g'),⏎····················replacement⏎················ with new·RegExp(<p·class="paragraph-node">${token}

    ,·'g'),·replacement

    (prettier/prettier)

    🔇 Additional comments (11)
    apps/site/src/content.config.ts (1)

    1-10: LGTM!

    apps/site/src/content/docs/getting-started.md (1)

    1-29: LGTM!

    apps/site/public/site.webmanifest (1)

    11-27: 🎯 Functional Correctness

    Manifest icons are present. The referenced /icon-192.png and /icon-512.png files exist under apps/site/public/.

    apps/site/src/pages/index.astro (1)

    172-190: 📐 Maintainability & Code Quality

    Keep is:inline on these scripts. They’re intentional raw inline payloads, so removing the opt-out would change Astro’s script handling.

    			> Likely an incorrect or invalid review comment.
    
    apps/site/src/components/ComponentDemo.astro (1)

    13-13: LGTM!

    Also applies to: 173-182, 208-219, 262-271, 283-348, 366-426, 439-441

    apps/site/public/feature-reminder-en/touchai-components.html (1)

    86-89: LGTM!

    Also applies to: 155-168, 470-516, 760-760, 1197-1197, 1211-1211, 1229-1548, 1569-1576, 1581-1882

    apps/site/public/feature-solver/touchai-components.html (1)

    88-91: LGTM!

    Also applies to: 157-170, 400-446, 1048-1048, 1591-1652, 1739-1939, 1960-1967, 1972-2257

    apps/site/public/feature-solver-en/touchai-components.html (1)

    87-90: LGTM!

    Also applies to: 156-169, 400-446, 1048-1048, 1591-1652, 1739-1939, 1960-1967, 1972-2260

    apps/site/public/feature-work-organizer-en/touchai-components.html (1)

    86-89: LGTM!

    Also applies to: 155-168, 470-516, 760-760, 1118-1231, 1304-1440, 1463-1470, 1475-1774

    apps/site/public/feature-work-organizer/touchai-components.html (1)

    86-89: LGTM!

    Also applies to: 155-168, 470-516, 760-760, 1116-1229, 1302-1438, 1461-1468, 1473-1769

    apps/site/src/scripts/component-demo-runtime.ts (1)

    11-13: LGTM!

    Also applies to: 49-263, 466-489, 499-549, 689-715, 734-800, 820-822, 887-946

    Comment thread apps/site/.env.example
    Comment on lines +15 to +17
    # Generic analytics collector endpoint that accepts JSON POST payloads.
    # Leave empty to disable the built-in pageview and click tracking.
    PUBLIC_ANALYTICS_ENDPOINT=

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

    Warn that the analytics endpoint is client-visible.

    index.astro emits this value into the page, so tokens embedded in the URL would be exposed to every visitor.

    Proposed doc tweak
     # Generic analytics collector endpoint that accepts JSON POST payloads.
    +# This value is exposed to browsers; do not include secrets or private write tokens.
     # Leave empty to disable the built-in pageview and click tracking.
     PUBLIC_ANALYTICS_ENDPOINT=
    📝 Committable suggestion

    ‼️ IMPORTANT
    Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    Suggested change
    # Generic analytics collector endpoint that accepts JSON POST payloads.
    # Leave empty to disable the built-in pageview and click tracking.
    PUBLIC_ANALYTICS_ENDPOINT=
    # Generic analytics collector endpoint that accepts JSON POST payloads.
    # This value is exposed to browsers; do not include secrets or private write tokens.
    # Leave empty to disable the built-in pageview and click tracking.
    PUBLIC_ANALYTICS_ENDPOINT=
    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@apps/site/.env.example` around lines 15 - 17, The analytics endpoint value is
    emitted into the page by index.astro, so the .env.example entry should
    explicitly warn that PUBLIC_ANALYTICS_ENDPOINT is client-visible and must not
    contain secrets or tokens. Update the comment near PUBLIC_ANALYTICS_ENDPOINT to
    call out that anything embedded in the URL will be exposed to visitors, and keep
    the guidance aligned with how index.astro consumes this setting.
    

    react(),
    starlight({
    title: 'TouchAI',
    disable404Route: true,

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

    🧩 Analysis chain

    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    echo "Astro custom 404 route candidates:"
    fd -a '^404\.(astro|md|mdx)$' apps/site/src/pages || true
    
    echo
    echo "Starlight docs 404 candidates:"
    fd -a '^404\.(md|mdx)$' apps/site/src/content/docs || true

    Repository: TouchAI-org/TouchAI

    Length of output: 277


    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    echo "Astro/Starlight config and 404-related files:"
    git ls-files 'apps/site/**' | rg '(^|/)(astro\.config\.mjs|404\.(astro|md|mdx)|starlight|pages/404|content/docs/404)' || true
    
    echo
    echo "Search for disable404Route usage:"
    rg -n "disable404Route|404\\.md|404\\.astro" apps/site || true
    
    echo
    echo "Inspect the site astro config:"
    cat -n apps/site/astro.config.mjs

    Repository: TouchAI-org/TouchAI

    Length of output: 972


    Keep Starlight’s 404 route enabled
    apps/site/src/content/docs/404.md only customizes the injected Starlight 404; there’s no src/pages/404.* in this tree, so disable404Route: true removes the only 404 route. Remove this flag or add a real Astro 404 page.

    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@apps/site/astro.config.mjs` at line 13, The site config is disabling the only
    available 404 route, since `apps/site/src/content/docs/404.md` is just the
    Starlight-injected 404 and there is no real `src/pages/404.*` page. Update
    `astro.config.mjs` by removing `disable404Route` from the Starlight config, or
    alternatively add an actual Astro 404 page if you want to keep the flag; use the
    `disable404Route` setting and the existing `404.md` customization as the key
    places to fix.
    

    Comment on lines +4 to +6
    function escapeHtml(value) {
    return renderer ? renderer.escapeHtml(value) : String(value);
    }

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

    Keep escaping safe when the base renderer is unavailable.

    The fallback currently returns raw String(value), so loading this utility before touchai-lite-renderer.js turns formula/text rendering into raw HTML insertion.

    🛡️ Proposed fix
         function escapeHtml(value) {
    -        return renderer ? renderer.escapeHtml(value) : String(value);
    +        if (renderer) return renderer.escapeHtml(value);
    +        return String(value)
    +            .replace(/&/g, '&amp;')
    +            .replace(/</g, '&lt;')
    +            .replace(/>/g, '&gt;')
    +            .replace(/"/g, '&quot;');
         }
    📝 Committable suggestion

    ‼️ IMPORTANT
    Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    Suggested change
    function escapeHtml(value) {
    return renderer ? renderer.escapeHtml(value) : String(value);
    }
    function escapeHtml(value) {
    if (renderer) return renderer.escapeHtml(value);
    return String(value)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
    }
    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@apps/site/public/demo-utils/touchai-lite-math.js` around lines 4 - 6, The
    escapeHtml helper currently falls back to raw String(value) when renderer is
    unavailable, which bypasses HTML escaping in touchai-lite-math.js. Update
    escapeHtml so the no-renderer path still safely escapes the value instead of
    returning unescaped text, and keep the renderer.escapeHtml branch unchanged; use
    the escapeHtml function in touchai-lite-math.js as the target for the fix.
    

    strict: 'ignore',
    trust: false,
    });
    } catch (error) {}

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

    Make the KaTeX fallback catch lint-clean.

    The empty catch with an unused error binding is reported by ESLint. Prefer catch { /* fall back to lightweight renderer */ }.

    🧰 Tools
    🪛 ESLint

    [error] 29-29: 'error' is defined but never used.

    (@typescript-eslint/no-unused-vars)


    [error] 29-29: Empty block statement.

    (no-empty)

    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@apps/site/public/demo-utils/touchai-lite-math.js` at line 29, The KaTeX
    fallback path has an empty catch block with an unused error binding, which
    triggers the lint warning. Update the catch in the lightweight renderer fallback
    so it uses a binding-free catch form, and keep the fallback behavior in place
    within the same KaTeX rendering logic.
    

    Source: Linters/SAST tools

    Comment on lines +26 to +29
    return escapeHtml(segment).replace(
    /\*\*([^*]+)\*\*/g,
    '<strong>$1</strong>'
    );

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

    Apply the formatter to satisfy Prettier.

    Static analysis reports Prettier errors in these two wrapped expressions; this can fail lint even though behavior is correct.

    Also applies to: 51-53

    🧰 Tools
    🪛 ESLint

    [error] 26-29: Replace ⏎····················/\*\*([^*]+)\*\*/g,⏎····················'<strong>$1</strong>'⏎················ with /\*\*([^*]+)\*\*/g,·'<strong>$1</strong>'

    (prettier/prettier)

    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@apps/site/public/demo-utils/touchai-lite-renderer.js` around lines 26 - 29,
    Prettier is flagging the wrapped replace expressions in the renderer, so update
    the formatting in touchai-lite-renderer’s markdown handling to match the
    project’s style without changing behavior. Reflow the chained
    escapeHtml(...).replace(...) call and the similar wrapped expression later in
    the file so they satisfy Prettier, keeping the same regex and replacement logic.
    

    Source: Linters/SAST tools

    Comment on lines +1889 to +1891
    <span class="sr-only" data-i18n="hero.heading">
    TouchAI:一触即达的桌面效率 Agent
    </span>

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

    Add the missing hero.heading translation.

    The hidden <h1> text has data-i18n="hero.heading", but neither language dictionary defines that key, so switching to English leaves the screen-reader heading in Chinese.

    Proposed fix
                         zh: {
    +                        'hero.heading': 'TouchAI:一触即达的桌面效率 Agent',
                             'nav.features': '功能',
    @@
                         en: {
    +                        'hero.heading': 'TouchAI: A Desktop Efficiency Agent at Your Fingertips',
                             'nav.features': 'Features',

    Also applies to: 2333-2454, 2619-2623

    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@apps/site/src/pages/index.astro` around lines 1889 - 1891, The hidden heading
    uses data-i18n="hero.heading" in the hero section, but the translation key is
    missing from both language dictionaries. Add hero.heading to the existing i18n
    dictionaries used by the page (the same structures that define the other hero
    strings in index.astro) so the screen-reader <h1> resolves correctly in both
    languages instead of falling back to Chinese.
    

    Comment on lines +2515 to +2519
    const queueDemoScrollIdle = (frame) => {
    if (!frame) return;
    requestAnimationFrame(() => {
    sendToDemo(frame, { type: 'touchai-clear-scroll-driven' });
    });

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

    Align touchai-clear-scroll-driven with the host reset contract.

    This page sends touchai-clear-scroll-driven, but the host runtime only clears is-scroll-driven for *-start and touchai-reset-demo. Cleanup after a mid-progress trigger can leave the host scroll-driven class/data state stuck.

    Proposed cross-file fix
     const isScrollDrivenResetType = (messageType: string) =>
    -    messageType.endsWith('-start') || messageType === 'touchai-reset-demo';
    +    messageType.endsWith('-start') ||
    +    messageType === 'touchai-clear-scroll-driven' ||
    +    messageType === 'touchai-reset-demo';

    Also applies to: 3511-3512, 3581-3586

    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@apps/site/src/pages/index.astro` around lines 2515 - 2519, The demo cleanup
    path is sending touchai-clear-scroll-driven, but the host runtime only clears
    the scroll-driven state through its reset contract for *-start and
    touchai-reset-demo. Update the cleanup flow in queueDemoScrollIdle and the
    related sendToDemo callers so the host receives the same reset signal it already
    understands, and make the matching host-side handler clear is-scroll-driven/data
    state there instead of relying on the unsupported clear event.
    

    Comment on lines +2718 to +2749
    const sendAnalytics = (type, detail = {}) => {
    if (!analyticsEndpoint) return;
    const payload = JSON.stringify({
    type,
    href: window.location.href,
    path: window.location.pathname,
    title: document.title,
    lang: document.documentElement.lang,
    referrer: document.referrer || '',
    viewport: `${window.innerWidth}x${window.innerHeight}`,
    ts: new Date().toISOString(),
    ...detail,
    });

    try {
    if (navigator.sendBeacon) {
    const sent = navigator.sendBeacon(
    analyticsEndpoint,
    new Blob([payload], { type: 'application/json' })
    );
    if (sent) return;
    }
    } catch (_error) {
    // Fall through to fetch.
    }

    fetch(analyticsEndpoint, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: payload,
    keepalive: true,
    }).catch(() => {});

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

    Avoid sending full URLs and referrers to analytics.

    window.location.href and document.referrer can include query strings, fragments, or same-origin sensitive paths. Send only minimized fields unless the collector explicitly needs more.

    Proposed fix
                     const sendAnalytics = (type, detail = {}) => {
                         if (!analyticsEndpoint) return;
    +                    const pageUrl = new URL(window.location.href);
    +                    const referrerOrigin = (() => {
    +                        try {
    +                            return document.referrer ? new URL(document.referrer).origin : '';
    +                        } catch (_error) {
    +                            return '';
    +                        }
    +                    })();
                         const payload = JSON.stringify({
                             type,
    -                        href: window.location.href,
    -                        path: window.location.pathname,
    +                        origin: pageUrl.origin,
    +                        path: pageUrl.pathname,
                             title: document.title,
                             lang: document.documentElement.lang,
    -                        referrer: document.referrer || '',
    +                        referrerOrigin,
                             viewport: `${window.innerWidth}x${window.innerHeight}`,
    📝 Committable suggestion

    ‼️ IMPORTANT
    Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    Suggested change
    const sendAnalytics = (type, detail = {}) => {
    if (!analyticsEndpoint) return;
    const payload = JSON.stringify({
    type,
    href: window.location.href,
    path: window.location.pathname,
    title: document.title,
    lang: document.documentElement.lang,
    referrer: document.referrer || '',
    viewport: `${window.innerWidth}x${window.innerHeight}`,
    ts: new Date().toISOString(),
    ...detail,
    });
    try {
    if (navigator.sendBeacon) {
    const sent = navigator.sendBeacon(
    analyticsEndpoint,
    new Blob([payload], { type: 'application/json' })
    );
    if (sent) return;
    }
    } catch (_error) {
    // Fall through to fetch.
    }
    fetch(analyticsEndpoint, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: payload,
    keepalive: true,
    }).catch(() => {});
    const sendAnalytics = (type, detail = {}) => {
    if (!analyticsEndpoint) return;
    const pageUrl = new URL(window.location.href);
    const referrerOrigin = (() => {
    try {
    return document.referrer ? new URL(document.referrer).origin : '';
    } catch (_error) {
    return '';
    }
    })();
    const payload = JSON.stringify({
    type,
    origin: pageUrl.origin,
    path: pageUrl.pathname,
    title: document.title,
    lang: document.documentElement.lang,
    referrerOrigin,
    viewport: `${window.innerWidth}x${window.innerHeight}`,
    ts: new Date().toISOString(),
    ...detail,
    });
    try {
    if (navigator.sendBeacon) {
    const sent = navigator.sendBeacon(
    analyticsEndpoint,
    new Blob([payload], { type: 'application/json' })
    );
    if (sent) return;
    }
    } catch (_error) {
    // Fall through to fetch.
    }
    fetch(analyticsEndpoint, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: payload,
    keepalive: true,
    }).catch(() => {});
    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@apps/site/src/pages/index.astro` around lines 2718 - 2749, The analytics
    payload built in sendAnalytics currently includes full window.location.href and
    document.referrer values, which may expose query strings, fragments, or
    sensitive paths. Update the payload construction to use minimized
    location/referrer data instead of the full URL fields, keeping the rest of the
    sendAnalytics logic unchanged and preserving the same event metadata shape where
    possible.
    

    Comment on lines +490 to +498
    if (messageType) {
    const existingIndex = pendingMessages.findIndex(
    (pendingMessage) => getMessageType(pendingMessage) === messageType
    );
    if (existingIndex >= 0) {
    pendingMessages[existingIndex] = data;
    } else {
    pendingMessages.push(data);
    }

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

    Preserve pending-message order when deduping by type.

    Replacing an earlier queued message in place can invert the final order. For example: progress → clear → progress flushes as latest progress then stale clear. Move replaced messages to the tail so the queue reflects the latest chronology.

    Proposed fix
     if (messageType) {
         const existingIndex = pendingMessages.findIndex(
             (pendingMessage) => getMessageType(pendingMessage) === messageType
         );
         if (existingIndex >= 0) {
    -        pendingMessages[existingIndex] = data;
    +        pendingMessages.splice(existingIndex, 1);
    +        pendingMessages.push(data);
         } else {
             pendingMessages.push(data);
         }
    📝 Committable suggestion

    ‼️ IMPORTANT
    Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    Suggested change
    if (messageType) {
    const existingIndex = pendingMessages.findIndex(
    (pendingMessage) => getMessageType(pendingMessage) === messageType
    );
    if (existingIndex >= 0) {
    pendingMessages[existingIndex] = data;
    } else {
    pendingMessages.push(data);
    }
    if (messageType) {
    const existingIndex = pendingMessages.findIndex(
    (pendingMessage) => getMessageType(pendingMessage) === messageType
    );
    if (existingIndex >= 0) {
    pendingMessages.splice(existingIndex, 1);
    pendingMessages.push(data);
    } else {
    pendingMessages.push(data);
    }
    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@apps/site/src/scripts/component-demo-runtime.ts` around lines 490 - 498, The
    pending-message dedupe logic in component-demo-runtime’s message queue is
    replacing an older item in place, which can change the final flush order. Update
    the branch that uses getMessageType, pendingMessages.findIndex, and
    pendingMessages[existingIndex] so that when a matching messageType is found, the
    old entry is removed and the new data is appended to the end of pendingMessages
    instead of overwriting in place. Keep the existing push behavior for new types
    so the queue preserves latest chronology.
    

    Comment on lines +880 to +883
    const startLoad = () => {
    if (host.dataset.touchaiLoadStarted === 'true') return Promise.resolve();
    host.dataset.touchaiLoadStarted = 'true';
    return load();

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

    Allow lazy-load retries after a failed first load.

    touchaiLoadStarted stays true even if load() fails, so future host.load?.() calls no-op and the lazy demo cannot recover from a transient fetch error. Track an in-flight promise or clear the flag on failure.

    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@apps/site/src/scripts/component-demo-runtime.ts` around lines 880 - 883, The
    lazy-load guard in startLoad currently leaves host.dataset.touchaiLoadStarted
    set to true even when load() rejects, which blocks later host.load?.() retries.
    Update the startLoad flow in component-demo-runtime so it either tracks the
    in-flight promise and reuses it, or resets touchaiLoadStarted in the failure
    path before rethrowing, ensuring the lazy demo can retry after a transient load
    error.
    

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Labels

    area:frontend Frontend UI or view-layer changes area:tauri Tauri shell or desktop runtime changes

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    1 participant