Skip to content

feat(codeintel): config settings panel for ts/jsconfig + rich tooltip component#3006

Merged
abose merged 3 commits into
mainfrom
ai
Jul 2, 2026
Merged

feat(codeintel): config settings panel for ts/jsconfig + rich tooltip component#3006
abose merged 3 commits into
mainfrom
ai

Conversation

@abose

@abose abose commented Jul 2, 2026

Copy link
Copy Markdown
Member

Two pieces, the second built on the first:

  1. NotificationUI.attachRichTooltip - a reusable, theme-aware, HTML-capable
    hover tooltip any feature can attach to its elements. Body-attached
    singleton (scroll containers can't clip it), viewport-clamped with
    below->above flipping, hover-friendly reading (grace period lets the
    pointer travel onto the tooltip; text selectable; mousedown dismisses),
    light+dark themed in core (.phoenix-rich-tooltip) with structured-content
    helpers (ph-tip-title/-sub/-rows/-term/-def/-foot/-example) so feature
    tooltips share one typography. Returns { detach }; hideRichTooltip()
    exported. Content is TRUSTED html - callers escape untrusted parts.
    Unit-tested in the NotificationUI suite (hover show/hide, function content,
    mousedown/detach, viewport clamping).

  2. The Code Intelligence settings panel - a friendly UI over the generated
    config, auto-shown as a bottom panel when the project-root
    tsconfig.json/jsconfig.json is the file being viewed, hidden on navigation:

  • Six setting cards in ONE grid with uniform column counts (3/2/1 via a
    ResizeObserver setting cols-N classes; 6 divides evenly by every count so
    no layout ever has orphan cards - the bundled less v3 has no @container,
    so pure-CSS container queries were not an option). Card typography
    calibrated against sibling panels (14px labels / 13px subtexts).
  • Toggles are instant-apply switches (checkJs, typeAcquisition.enable, and
    the autoManage contract made visible as "Managed by Phoenix Code");
    selects for target/module/jsx with a "(current: x)" escape so unknown
    values are never clobbered. Each card's (i) icon opens a rich tooltip
    with concrete, example-driven explanations (console.lgo -> "Did you mean
    'log'?"; per-version Target cheat sheet; per-option Module/JSX meanings).
  • Panel edits ARE file edits: mutate -> doc.setText -> auto-save (visible +
    undoable in the editor above; tsserver watches config files natively so
    changes re-scope with no explicit restart); hand-edits to the JSON flow
    back into the controls. Files that aren't strict JSON (comments/JSONC)
    get a read-only notice instead of a destructive stringify round-trip.
  • Footer: origin note whose "Problems panel" link opens that panel and
    pulses the status-bar indicator on hover (teaching where it lives), plus
    the new codeIntel.autoCreateConfig preference (default true; description
    documents the Problems-panel re-enable path) which gates auto-creation in
    CodeIntelligence._autoEnable - managing existing configs is unaffected.
  • Styles live in src/styles/Extn-TypeScriptSupport.less (imported by
    brackets.less like other panels, so core theme variables apply); panel
    tab icon added; all strings i18n'd with {APP_NAME}.

Tests: NotificationUI 17/17; integration:TypeScript LSP 20/20 including
three new panel specs (auto-show/hide lifecycle, checkJs toggle writes and
saves the file, JSONC read-only mode).

Other changes see commit details.

abose added 3 commits July 2, 2026 19:10
The generated ts/jsconfig used one fixed template (module esnext +
moduleResolution bundler + jsx react). Research across the TS docs/release
notes, TS wiki and VS Code docs shows JS intelligence for every mainstream
flavor - Node CommonJS, ES modules, bundler-style, UMD consumption, plain
browser scripts - does not need per-flavor detection; it needs one
permissive base plus automatic type acquisition:

- module "preserve" (TS 5.4+; we bundle 5.9.3): resolves BOTH import and
  require(), never demands file extensions on relative imports (nodenext
  would, breaking go-to-def in extensionless ESM JS), and implies bundler
  resolution + esModuleInterop + resolveJsonModule. Per-file CJS-vs-ESM
  classification stays driven by file extension + nearest package.json
  "type" regardless, so every flavor resolves under this one setting.
  moduleResolution dropped from the template (implied).
- target esnext with no `lib` already includes DOM - browser globals work
  with zero configuration.
- jsx "react-jsx" (automatic runtime - no "Cannot find name 'React'");
  allowUmdGlobalAccess (module files may use UMD/browser globals without
  imports - editor-only looseness); exclude gains bower_components.
- typeAcquisition { enable: true } top-level on BOTH file types - the
  critical line. ATA (tsserver auto-fetching @types/node and package.json
  dependency types via npm, even with no node_modules) defaults ON for
  jsconfig but OFF for tsconfig, so the jsconfig->tsconfig upgrade would
  otherwise silently kill Node builtin/require("pkg") IntelliSense.
  Degrades gracefully to locally installed @types when npm is unavailable.

Known limitation, documented in the template comment: tsserver cannot infer
AMD/RequireJS define() modules cross-file (Salsa never supported AMD; no
config changes that) - parity with VS Code. Users' own paths/baseUrl
survive rewrites via the existing compilerOptions preserve-merge.

Verified live: fresh Node project auto-generates the new template and,
with NO node_modules, fs.readFileSync hovers with full typed signatures
(ATA fetched @types/node through the vtsls process). Tests: contract test
updated (new defaults, typeAcquisition on both file types, user overrides
preserved) plus three flavor demo tests generating real projects with the
generated config - CommonJS require() cross-file resolution, extensionless
ESM import under "type":"module", and DOM intelligence in plain scripts -
using per-test randomized temp dirs (fixed temp paths collide with
per-project persisted working sets, popping "Error Opening File" restores).
integration:TypeScript LSP green 17/17 twice.
… component

Two pieces, the second built on the first:

1. NotificationUI.attachRichTooltip - a reusable, theme-aware, HTML-capable
hover tooltip any feature can attach to its elements. Body-attached
singleton (scroll containers can't clip it), viewport-clamped with
below->above flipping, hover-friendly reading (grace period lets the
pointer travel onto the tooltip; text selectable; mousedown dismisses),
light+dark themed in core (.phoenix-rich-tooltip) with structured-content
helpers (ph-tip-title/-sub/-rows/-term/-def/-foot/-example) so feature
tooltips share one typography. Returns { detach }; hideRichTooltip()
exported. Content is TRUSTED html - callers escape untrusted parts.
Unit-tested in the NotificationUI suite (hover show/hide, function content,
mousedown/detach, viewport clamping).

2. The Code Intelligence settings panel - a friendly UI over the generated
config, auto-shown as a bottom panel when the project-root
tsconfig.json/jsconfig.json is the file being viewed, hidden on navigation:

- Six setting cards in ONE grid with uniform column counts (3/2/1 via a
  ResizeObserver setting cols-N classes; 6 divides evenly by every count so
  no layout ever has orphan cards - the bundled less v3 has no @container,
  so pure-CSS container queries were not an option). Card typography
  calibrated against sibling panels (14px labels / 13px subtexts).
- Toggles are instant-apply switches (checkJs, typeAcquisition.enable, and
  the autoManage contract made visible as "Managed by Phoenix Code");
  selects for target/module/jsx with a "(current: x)" escape so unknown
  values are never clobbered. Each card's (i) icon opens a rich tooltip
  with concrete, example-driven explanations (console.lgo -> "Did you mean
  'log'?"; per-version Target cheat sheet; per-option Module/JSX meanings).
- Panel edits ARE file edits: mutate -> doc.setText -> auto-save (visible +
  undoable in the editor above; tsserver watches config files natively so
  changes re-scope with no explicit restart); hand-edits to the JSON flow
  back into the controls. Files that aren't strict JSON (comments/JSONC)
  get a read-only notice instead of a destructive stringify round-trip.
- Footer: origin note whose "Problems panel" link opens that panel and
  pulses the status-bar indicator on hover (teaching where it lives), plus
  the new codeIntel.autoCreateConfig preference (default true; description
  documents the Problems-panel re-enable path) which gates auto-creation in
  CodeIntelligence._autoEnable - managing existing configs is unaffected.
- Styles live in src/styles/Extn-TypeScriptSupport.less (imported by
  brackets.less like other panels, so core theme variables apply); panel
  tab icon added; all strings i18n'd with {APP_NAME}.

Tests: NotificationUI 17/17; integration:TypeScript LSP 20/20 including
three new panel specs (auto-show/hide lifecycle, checkJs toggle writes and
saves the file, JSONC read-only mode).
… tsserver

Enabling checkJs, typing, then undoing could kill both vtsls tsserver
processes (semantic and syntax) with "Debug Failure. Bad line number.
Line: 23, lineStarts.length: 23" inside applyChangesInOpenFiles - we sent
a didChange whose range referenced a line one past the end of the
server's copy of the document.

Root cause: DocumentSync's replay-verification safety net used a LENIENT
_offsetAt that clamped out-of-range positions to end-of-text. A stale
pending edit (from the documented flush-vs-change-listener race) whose
position lay beyond the document could therefore replay to exactly the
current text by coincidence and pass verification - while the raw
out-of-range line number shipped to the server. tsserver crashes on such
ranges rather than clamping.

Fix: verification is now STRICT. _offsetAt returns -1 for a line beyond
the text or a character beyond its line, _applyIncremental returns null
on any invalid position, and _contentChangesFor treats that as drift ->
full-text resync. Verified edits are now valid positions AND reproduce
the document exactly, so a conforming server can neither crash nor
diverge.

Belt and suspenders: a failed didChange notify flags the doc for full
resync (lastSentText no longer reflects the server's copy), and a failed
didOpen marks the doc not-open so the next change re-opens it fresh.

Also fix releaseProd: the extension concatenator counts literal
"define(" substrings per file; two comments saying "RequireJS define()
modules" tripped it. Reworded (CodeIntelligence.js, unittests.js).

Tests: 3 new regression specs in unit:DocumentSync (out-of-range line /
character -> null; the exact crash shape - a stale clamp-passable edit -
now falls back to full text). unit:DocumentSync 17/17,
integration:TypeScript LSP 20/20.
@abose abose changed the title feat(codeintel): universal module-flavor config template + ATA feat(codeintel): config settings panel for ts/jsconfig + rich tooltip component Jul 2, 2026
@sonarqubecloud

sonarqubecloud Bot commented Jul 2, 2026

Copy link
Copy Markdown

@abose abose merged commit 9d5561c into main Jul 2, 2026
7 of 21 checks passed
@abose abose deleted the ai branch July 2, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant