From c5d3159f4a2c2e1b9612bdb3721c43f5d039105a Mon Sep 17 00:00:00 2001
From: zhixuan <201550585+icecreamx10@users.noreply.github.com>
Date: Wed, 22 Jul 2026 11:54:59 +0800
Subject: [PATCH 1/2] docs: remove workflow notes
Remove task-specific files from docs/workflows. Future implementation context, root causes, platform caveats, and validation results belong in commit messages instead of repository workflow documents.
---
.../2026-07-21-clip-scintilla-margin.md | 65 -------------------
...7-21-disable-scintilla-line-scroll-snap.md | 36 ----------
...07-21-match-scintilla-bounce-background.md | 38 -----------
...-21-prevent-scintilla-first-frame-flash.md | 45 -------------
4 files changed, 184 deletions(-)
delete mode 100644 docs/workflows/2026-07-21-clip-scintilla-margin.md
delete mode 100644 docs/workflows/2026-07-21-disable-scintilla-line-scroll-snap.md
delete mode 100644 docs/workflows/2026-07-21-match-scintilla-bounce-background.md
delete mode 100644 docs/workflows/2026-07-21-prevent-scintilla-first-frame-flash.md
diff --git a/docs/workflows/2026-07-21-clip-scintilla-margin.md b/docs/workflows/2026-07-21-clip-scintilla-margin.md
deleted file mode 100644
index 385ac64..0000000
--- a/docs/workflows/2026-07-21-clip-scintilla-margin.md
+++ /dev/null
@@ -1,65 +0,0 @@
-# Clip Scintilla margin painting to the editor viewport
-
-## Product intent
-
-Keep the existing pixel-smooth macOS editor scrolling while preventing line
-numbers from being painted below the editor into the bottom panel.
-
-## Scope
-
-- Artifact type: Lynxtron GO showcase with a macOS native Scintilla extension.
-- Distribution type: workspace source build used by `pnpm preview`.
-- Runtime path: `lynxtron-go/scintilla-extension` on macOS Cocoa.
-- Restrict the Scintilla margin dirty rectangle and graphics context to the
- visible content viewport before painting line numbers.
-- Preserve partially visible first and last lines introduced by the existing
- Lynxtron-owned smooth-scroll content view.
-- Do not alter the Windows implementation.
-- Preserve all unrelated user changes in the dirty worktree.
-
-## Root cause
-
-`SCIMarginView` is a separate `NSRulerView`. Its Cocoa drawing path expands a
-partial invalidation to the full ruler bounds when the ruler is taller than the
-content viewport. Scintilla then paints the last partial line with an unclipped
-text operation, so its line number can cross the editor's lower boundary.
-
-## Acceptance criteria
-
-1. Margin painting cannot draw below `scrollView.contentView.bounds`.
-2. Pixel-level vertical scrolling remains enabled and partial lines remain
- supported.
-3. The legacy oversized ruler invalidation cannot expand the Scintilla margin
- paint rectangle beyond the visible content viewport.
-4. The macOS Scintilla extension builds successfully.
-5. A running Lynxtron GO editor is smoke-tested by scrolling a long file near
- the editor's lower edge; no line-number glyph appears in the bottom panel.
-6. Windows code and unrelated worktree changes remain untouched.
-
-## Verification notes
-
-- Run `pnpm --dir lynxtron-go run build:scintilla-extension`.
-- Relaunch the preview so it loads the rebuilt native extension.
-- Capture or inspect the editor after a partial-line vertical scroll.
-- Record any runtime automation limitation explicitly.
-
-## Verification result
-
-- `pnpm --dir lynxtron-go run build:scintilla-extension` passed and rebuilt
- `ScintillaView.mm`, `libscintilla.a`, and `lynx_scintilla_module.node`.
-- `pnpm --dir lynxtron-go run build` passed; Rspeedy reported only the
- pre-existing unsupported-CSS warnings and Rspack reported the two existing
- dynamic-require warnings.
-- Relaunched `dist/desktop` after quitting the stale singleton instance, so the
- running app loaded the rebuilt native module.
-- Scrolled the long `src/app/App.css` editor and inspected the native macOS
- window: line numbers stayed inside the editor boundary and no glyph appeared
- in the Console panel.
-- Lynx DevTool remained useful for session discovery, while Computer Use was
- required for the final visual check because DevTool screenshots do not
- reliably include native Scintilla subviews.
-- `pnpm --dir lynxtron-go test` completed with 183/184 tests passing. The one
- failure is the unrelated, repeatable
- `TypeScriptLanguageService > treats lynxtron-go desktop host sources as a
- Node environment` assertion, which currently receives three diagnostics at
- `src/extension-host/__tests__/typescript.test.ts:393`.
diff --git a/docs/workflows/2026-07-21-disable-scintilla-line-scroll-snap.md b/docs/workflows/2026-07-21-disable-scintilla-line-scroll-snap.md
deleted file mode 100644
index 740988c..0000000
--- a/docs/workflows/2026-07-21-disable-scintilla-line-scroll-snap.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# Disable Scintilla vertical line scroll snapping
-
-## Product intent
-
-Make trackpad and mouse-wheel scrolling in Lynxtron GO code editors feel continuous. A partially visible first or last line is acceptable; the viewport must not jump to the nearest whole line.
-
-## Scope
-
-- Artifact type: Lynxtron GO showcase with a macOS native Scintilla extension.
-- Distribution type: workspace source build used by `pnpm preview`.
-- Runtime path: `lynxtron-go/scintilla-extension` on macOS Cocoa.
-- Disable only vertical whole-line snapping performed by `SCIContentView.adjustScroll:`.
-- Preserve horizontal whole-point rounding used to avoid Retina drawing debris.
-- Prefer a Lynxtron-owned subclass/adapter in `module/scintilla_view.mm`; do not modify vendored files under `scintilla-extension/scintilla/` when the existing `contentViewClass` extension point is sufficient.
-- Do not change Windows behavior.
-
-## Root cause
-
-Vendored Cocoa `SCIContentView.adjustScroll:` rounds `proposedVisibleRect.origin.y` to the nearest multiple of `TextHeight`. This converts smooth pixel scrolling into line-sized jumps and feels sticky.
-
-## Acceptance criteria
-
-1. macOS vertical scrolling accepts fractional/partial-line `origin.y` values without rounding to line height.
-2. Horizontal scrolling retains whole-point rounding for interior document positions.
-3. Existing Scintilla content, styling, selection, and editor registration behavior remains unchanged.
-4. No generated output, build directory, or `node_modules` change is committed.
-5. `pnpm --dir lynxtron-go run build:scintilla-extension` succeeds.
-6. The narrowest practical Lynxtron GO build or runtime smoke check succeeds and is reported.
-
-## Verification notes
-
-- `pnpm --dir lynxtron-go run build:scintilla-extension` passed.
-- `pnpm --dir lynxtron-go exec rspack build` passed with the two existing dynamic-`require` warnings.
-- `pnpm preview` rebuilt the native module and launched Lynxtron GO successfully; the host registered the Scintilla editor extension.
-- The built module contains `LynxtronSCIContentView` / `LynxtronScintillaView`, and the build output matches the copy under `dist/desktop` by SHA-256.
-- Runtime smoke test used 5-pixel scroll-wheel events on a file longer than one viewport. The viewport retained partial-line offsets instead of snapping to a whole line.
diff --git a/docs/workflows/2026-07-21-match-scintilla-bounce-background.md b/docs/workflows/2026-07-21-match-scintilla-bounce-background.md
deleted file mode 100644
index 3567bda..0000000
--- a/docs/workflows/2026-07-21-match-scintilla-bounce-background.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Match Scintilla overscroll background to the editor
-
-## Product intent
-
-Keep the macOS editor visually continuous while the user pulls past the top edge. The temporary rubber-band area must use the active editor theme background instead of the native scroll view's default background.
-
-## Scope
-
-- Artifact type: Lynxtron GO showcase with a macOS native Scintilla extension.
-- Distribution type: workspace source build used by `pnpm preview`.
-- Runtime path: `lynxtron-go/scintilla-extension` on macOS Cocoa.
-- Fix the background exposed by vertical elastic overscroll.
-- Keep the color synchronized with both the compiled-in default theme and `ApplyTheme` dark/light changes.
-- Implement in the Lynxtron-owned adapter; do not add this product-specific behavior to vendored Scintilla.
-- Do not change Windows behavior or disable native rubber-band scrolling.
-
-## Root cause
-
-Scintilla paints the document background itself, but elastic overscroll temporarily exposes the enclosing `NSScrollView` / `NSClipView`. Those native views retain their default AppKit background instead of the Scintilla `STYLE_DEFAULT` color, so the pulled-open area does not match the editor.
-
-## Acceptance criteria
-
-1. Pulling down past the first line reveals the same color as the active editor content background.
-2. The background follows both dark and light theme changes.
-3. Continuous partial-line scrolling from the preceding fix remains enabled.
-4. No vendored Scintilla or Windows source is changed by this task.
-5. The native extension and Lynxtron GO desktop bundle build successfully.
-6. A runtime smoke check confirms the Scintilla extension registers and the editor remains usable.
-
-## Verification notes
-
-- `pnpm --dir lynxtron-go run build:scintilla-extension` passed.
-- `pnpm --dir lynxtron-go exec rspack build` passed with the two existing dynamic-`require` warnings.
-- The built module contains `-[LynxtronScintillaView syncBounceBackgroundWithStyleDefault]`, and the build output matches the copy under `dist/desktop` by SHA-256.
-- `run-dev` restarted successfully and registered the Scintilla editor extension.
-- Switching Settings → Appearance from Dark to Light and back updated every native editor immediately, exercising both `ApplyTheme` background synchronization branches; the original Dark setting was restored afterwards.
-- AppKit did not enter physical trackpad rubber-band state from synthesized `CGEvent` scroll phases, so the exact peak-frame visual assertion still requires a real trackpad gesture. The native container colors are nevertheless derived from the same live `STYLE_DEFAULT` value exposed during that state.
-- For parallel GUI verification, launch with `LYNXTRON_ALLOW_MULTI=1` to skip the singleton lock and avoid interfering with another Lynxtron GO instance.
diff --git a/docs/workflows/2026-07-21-prevent-scintilla-first-frame-flash.md b/docs/workflows/2026-07-21-prevent-scintilla-first-frame-flash.md
deleted file mode 100644
index 618c168..0000000
--- a/docs/workflows/2026-07-21-prevent-scintilla-first-frame-flash.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Prevent Scintilla first-frame theme flash
-
-## Scope
-
-- Artifact type: Lynxtron GO showcase with a macOS native Scintilla extension.
-- Distribution type: workspace source build consumed through `dist/desktop` / `pnpm preview`.
-- Runtime path: `lynxtron-go/scintilla-extension/module/scintilla_view.mm` on macOS Cocoa.
-- Keep the native editor on its configured background and font size from its first visible frame.
-- Do not change Windows or vendored Scintilla sources.
-
-## Reproduction
-
-Hide a visible Fiddle editor pane, start a 60 fps screen recording, then reopen the same file from the sidebar. Before the fix, recorded frames show this sequence:
-
-1. the pane toolbar appears over a black native canvas;
-2. the first line number appears on the black canvas at the compiled default size;
-3. the canvas switches to the Fiddle theme background and configured 13pt size.
-
-## Root cause
-
-The macOS constructor enqueues compiled default styling with `dispatch_async`. If properties arrive on the main thread, `ApplyTheme` runs first, then the queued constructor defaults overwrite it with the 14pt fallback. The editor can attach and paint that intermediate state until the JS post-mount theme call corrects it. Off-main `ApplyTheme` is also asynchronous, so layout attachment is not guaranteed to wait for theme application.
-
-## Implementation
-
-- Run the constructor's Scintilla default setup inline on the main thread, or synchronously dispatch it there when construction starts off-main.
-- Apply `theme-dark` and `font-size` synchronously on the main thread before returning from `ApplyTheme`.
-- Leave the existing palette, continuous scrolling, and overscroll background behavior unchanged.
-
-## Verification
-
-- Captured the failure before the change at 60 fps: the new Preload pane painted a black canvas, then a large line number, and only afterward changed to the configured background and font size.
-- Rebuilt `lynxtron-scintilla-editor`, then rebuilt the desktop bundle. The source build and copied `dist/desktop` native modules both have SHA-256 `412e18115ea2cec90c88381aaeaad8301644aefd0cb7a134e4e13fee0a3d030f`.
-- Restarted the source runtime with `LYNXTRON_ALLOW_MULTI=1` and captured the same close/reopen interaction at 120 fps. The Preload pane's first visible frame already has the configured background and 13pt text; subsequent frames show no background or font-size transition.
-
-## Windows follow-up
-
-Windows remains to be fixed and verified on a Windows runtime. Its implementation has a different ordering bug with the same likely visual result: `OnPropertiesChanged` may receive `theme-dark` and `font-size` before the Scintilla `HWND` exists, but `ApplyTheme` returns without caching them. `OnLayoutChanged` later creates a visible control with the hard-coded dark 14pt defaults, so a later JS theme call can still cause a background and font-size jump. The Windows fix should cache theme properties before the `HWND` exists, create the control hidden, apply the cached theme after `ConfigureScintilla`, and only then reveal it.
-
-## Acceptance criteria
-
-1. Constructor defaults are fully installed before the native element can receive theme properties.
-2. `theme-dark` and `font-size` property application completes before `OnPropertiesChanged` returns.
-3. Reopening a pane shows no black frame and no visible font-size jump.
-4. Existing continuous scrolling and overscroll background synchronization remain unchanged.
-5. The native extension and desktop bundle build successfully.
From 833519a06317e1b1e26332d3ce6fb9d062cd0a10 Mon Sep 17 00:00:00 2001
From: zhixuan <201550585+icecreamx10@users.noreply.github.com>
Date: Wed, 22 Jul 2026 11:55:19 +0800
Subject: [PATCH 2/2] fix(types): expose Lynx runtime globals in editor
Inject a minimal virtual declaration file for Lynx UI projects that explicitly use @lynx-js/types with an ES-only lib. This exposes console and the timeout/interval globals without enabling DOM or Node APIs.
The installed @lynx-js/types package declares timer functions outside declare global and exports Console without a global console variable, so Lynxtron GO reported valid runtime APIs as unresolved names.
Validated with the TypeScript language-service regression test and a production desktop rspack build. The broader language-service suite still has the pre-existing desktop Response diagnostic failure.
---
.../__tests__/typescript.test.ts | 8 ++++
.../language-server/typescript.ts | 48 +++++++++++++++++++
2 files changed, 56 insertions(+)
diff --git a/lynxtron-go/src/extension-host/__tests__/typescript.test.ts b/lynxtron-go/src/extension-host/__tests__/typescript.test.ts
index 6af4f43..38f9204 100644
--- a/lynxtron-go/src/extension-host/__tests__/typescript.test.ts
+++ b/lynxtron-go/src/extension-host/__tests__/typescript.test.ts
@@ -304,6 +304,9 @@ import { useState } from '@lynx-js/react';
export function App() {
const [count] = useState(1);
+ const interval = setInterval(() => console.log(count), 1000);
+ const timeout = setTimeout(() => clearInterval(interval), 1000);
+ clearTimeout(timeout);
return {count};
}
`);
@@ -320,6 +323,11 @@ root.render();
expect(markers.some(m => m.code === 2875)).toBe(false);
expect(markers.some(m => m.code === 2307)).toBe(false);
+ expect(markers.some(m => m.message.includes("Cannot find name 'setInterval'"))).toBe(false);
+ expect(markers.some(m => m.message.includes("Cannot find name 'clearInterval'"))).toBe(false);
+ expect(markers.some(m => m.message.includes("Cannot find name 'setTimeout'"))).toBe(false);
+ expect(markers.some(m => m.message.includes("Cannot find name 'clearTimeout'"))).toBe(false);
+ expect(markers.some(m => m.message.includes("Cannot find name 'console'"))).toBe(false);
expect(markers.filter(m => m.severity === 'error')).toHaveLength(0);
} finally {
fs.rmSync(tempRoot, { recursive: true, force: true });
diff --git a/lynxtron-go/src/extension-host/language-server/typescript.ts b/lynxtron-go/src/extension-host/language-server/typescript.ts
index e65b8d6..b00a2c4 100644
--- a/lynxtron-go/src/extension-host/language-server/typescript.ts
+++ b/lynxtron-go/src/extension-host/language-server/typescript.ts
@@ -10,6 +10,31 @@ interface FileEntry {
content: string;
}
+const LYNX_GLOBALS_WORKAROUND_FILE = '.lynxtron-go-lynx-globals.d.ts';
+const LYNX_GLOBALS_WORKAROUND_SOURCE = `
+export {};
+
+declare global {
+ interface Console {
+ debug(...args: any[]): void;
+ error(...args: any[]): void;
+ group(label?: string): void;
+ groupEnd(): void;
+ info(...args: any[]): void;
+ log(...args: any[]): void;
+ alog(...args: any[]): void;
+ warn(...args: any[]): void;
+ }
+
+ var console: Console;
+
+ function setTimeout(callback: (...args: unknown[]) => unknown, delay: number): number;
+ function setInterval(callback: (...args: unknown[]) => unknown, delay: number): number;
+ function clearTimeout(timeoutId: number): void;
+ function clearInterval(timeoutId: number): void;
+}
+`;
+
interface ProjectConfig {
key: string;
projectRoot: string;
@@ -236,6 +261,16 @@ function shouldUseBundledModuleFallback(
return /\.(?:[cm]?js|jsx)$/.test(resolved.resolvedFileName);
}
+function needsLynxGlobalsWorkaround(options: ts.CompilerOptions): boolean {
+ if (!options.types?.includes('@lynx-js/types')) return false;
+
+ // An omitted lib list uses TypeScript's defaults, which already include DOM.
+ // Lynx app configs explicitly choose an ES-only lib to avoid browser globals.
+ if (!options.lib) return false;
+
+ return !options.lib.some((libPath) => path.basename(libPath).toLowerCase() === 'lib.dom.d.ts');
+}
+
class LanguageServiceHost implements ts.LanguageServiceHost {
private files = new Map();
private rootFiles: string[];
@@ -253,6 +288,16 @@ class LanguageServiceHost implements ts.LanguageServiceHost {
this.options = options;
this.rootFiles = [...new Set(rootFiles)];
this.bundledFallback = bundledFallback;
+
+ // @lynx-js/types@3.8 declares the timer functions outside declare global
+ // and exports Console without declaring the global console variable. Keep
+ // Lynx projects free of DOM types while making the runtime globals visible.
+ if (needsLynxGlobalsWorkaround(options)) {
+ this.files.set(path.join(projectRoot, LYNX_GLOBALS_WORKAROUND_FILE), {
+ version: 0,
+ content: LYNX_GLOBALS_WORKAROUND_SOURCE,
+ });
+ }
}
updateFile(filePath: string, content: string, version: number) {
@@ -294,10 +339,13 @@ class LanguageServiceHost implements ts.LanguageServiceHost {
}
fileExists(path: string): boolean {
+ if (this.files.has(path)) return true;
return ts.sys.fileExists(path);
}
readFile(path: string): string | undefined {
+ const entry = this.files.get(path);
+ if (entry) return entry.content;
return ts.sys.readFile(path);
}