refactor(desktop): move the bot-chat cluster onto the Astryx settings kit - #1993

Merged
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx
Aug 3, 2026
Merged

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit#1993
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx

Conversation

@jackwener

Copy link
Copy Markdown
Member

Closes out the settings convergence: 远程接入 (overview + detail) was the last page group on the pre-#1972 dialect — #1972 didn't list it in its rewrite scope, so opening it felt like a different product from every other settings page.

Overview (bot-chat-overview.tsx)

  • Bespoke page container / section-header dialect / hand-rolled list grids → SettingsPageSettingsSection → the kit's hairline rows.
  • SettingsSection gains a titleId prop (Heading id + section aria-labelledby) so both sections keep the ids the a11y wiring names them by.
  • Readiness Badge → the shared StatusDot + text idiom; the title span's aria-label keeps each row's accessible name byte-identical for settings.spec.ts.
  • EmptyState drops its padding-override class for isCompact.

Detail (bot-chat-detail.tsx)

  • The runtime block was a full-width tinted card used as page structure (the named cards-in-page anti-pattern) → open SettingsSection with the test/connect/restart cluster in its action slot.
  • The 4→2→1-column <dl> status grid → MetadataList, which owns the responsive collapse the two hand-rolled media queries used to carry.
  • The configuration-header dialect → bare-variant SettingsSection wrapping the mode toggle + credential form (e2e's heading '接入方式' stays a heading).
  • The hand-tinted quick-setup plate → Astryx Card (a genuine callout — the one legitimate Card use in a settings page).
  • Header readiness Badge → StatusDot + text.

Deliberately untouched

.settingsBotDetailHeader's pinned grid (settings.spec.ts asserts its back → switch → doc link → connect focus order), the brand plate, both QR frames, and the onboarding glyphs — documented art, not layout debt.

Verification

typecheck ✅ · check-dead-css ✅ · check-a11y/copy/console ✅ · product Storybook smoke 71 renders × 3 viewports ✅ · e2e heading-name/focus-order/accessible-name contracts re-checked against the new DOM (roles and names unchanged).

Net: 4 files, +114/−257 — bot.css sheds ~200 lines of retired dialect while keeping the attention wash, planned-opacity, and narrow-viewport row behaviors.

… kit
远程接入 was the last page group speaking the pre-#1972 dialect — #1972
did not list it in its rewrite scope, so a user opening it saw a
different product than every other settings page.
- Overview: bespoke page container + section-header dialect + hand-
rolled list grids become SettingsPage → SettingsSection → the kit's
hairline rows. SettingsSection gains a `titleId` prop so the two
sections keep the heading ids their aria-labelledby wiring names
them by. The readiness Badge becomes the shared StatusDot + text
idiom (the title span's aria-label keeps the row's accessible name
byte-identical for the e2e). EmptyState drops its padding-override
class for `isCompact`.
- Detail: the runtime block was a full-width tinted card used as page
structure (the named anti-pattern) — now an open SettingsSection
whose action slot carries the test/connect/restart cluster. The
4→2→1-column <dl> status grid becomes MetadataList, which owns the
responsive collapse the two media queries hand-rolled. The
configuration header dialect becomes a bare-variant SettingsSection
wrapping the mode toggle + credential form; the hand-tinted
quick-setup plate becomes an Astryx Card (a genuine callout).
- Deliberately untouched: `.settingsBotDetailHeader`'s pinned grid
(settings.spec.ts asserts its back → switch → doc link → connect
focus order), the brand plate, both QR frames, and the onboarding
glyphs — documented art, not layout debt.
- bot.css drops ~200 lines of retired dialect; the attention wash,
planned-opacity, and 620px chevron-hiding row behaviors stay.
Verified: desktop typecheck, check-dead-css, check-a11y/copy/console,
product Storybook smoke (71 catalog renders x 3 viewports); e2e
heading-name and focus-order contracts re-checked against the new DOM.
@jackwener
jackwener merged commit 23d8c70 into mainAug 3, 2026
jackwener added a commit that referenced this pull request Aug 3, 2026
… seam (#1997)
Adversarial review of #1984/#1991/#1993 (two independent lenses,
findings verified before fixing) plus one user-reported shell defect.
Shell:
- The content column is a rounded floating plate flush to the sidebar;
the theme's full-height sidenav hairline ran on the same seam and
read as a doubled border beside the plate's corner radius. Dropped
in the expanded state too (collapsed already did) — separation is
tonal, per DESIGN.md's One Working Plane rule.
Regressions from #1993, confirmed and fixed:
- bot-chat-detail lost all inter-section rhythm: its wrapper pair kept
a gap on a display:block section (inert) and a class with no CSS
rule. The page is a SettingsPage now, like the overview next door;
the back button keeps only its optical inline outdent.
- bot.css had the file-header comment replace-all'd into the middle of
`.settingsBotDetailHeader > .settingsBotLogo`. Parsed by luck;
removed.
- MetadataList destructures a closed prop list, so the runtime grid's
aria-label was silently dropped — the group name rides a real
role="group" wrapper now.
- The kit section header now wraps: at the 480px window floor a
multi-button action cluster drops under the title instead of
crushing it (replaces the deleted bot-runtime media query).
- Overview rows go flush-inline (the 10px inset was old card-grid
residue); the WeChat QR dialog gets its height floor back (224px)
so state swaps stop resizing the modal.
Convergence completions the review surfaced:
- claude-subscription-card drops its SectionHeader + full-width Card
and repeated title: it renders under ProvidersPanel's RouteHeader
like its sibling OAuth panels, as a bare VStack with the shared
StatusDot + text status idiom.
- ProvidersPanel connection status Badge -> StatusDot + text; health's
hand-rolled five-arm dot mapping -> the shared statusDotVariant.
- subagent-presets panel: Card-wrapped .settingsRows + a lead Item
posing as a header -> SettingsSection; settings-surface fallback
drops its Card wrap.
- About hero pills referenced two classNames with no CSS rule —
they are the Badges about.css's comment always described.
- `.settingsPage .settingsPageStack` (rename slip) -> nested
`.settingsPageStack .settingsPageStack`; quota labels round.
Verified: typecheck, check-dead-css, check-a11y/copy/console,
format:check, product Storybook smoke (71 renders x 3 viewports).
Sign up for freeto 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

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit - #1993

Merged
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx
Aug 3, 2026
Merged

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit#1993
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx

Conversation

@jackwener

Copy link
Copy Markdown
Member

Closes out the settings convergence: 远程接入 (overview + detail) was the last page group on the pre-#1972 dialect — #1972 didn't list it in its rewrite scope, so opening it felt like a different product from every other settings page.

Overview (bot-chat-overview.tsx)

  • Bespoke page container / section-header dialect / hand-rolled list grids → SettingsPageSettingsSection → the kit's hairline rows.
  • SettingsSection gains a titleId prop (Heading id + section aria-labelledby) so both sections keep the ids the a11y wiring names them by.
  • Readiness Badge → the shared StatusDot + text idiom; the title span's aria-label keeps each row's accessible name byte-identical for settings.spec.ts.
  • EmptyState drops its padding-override class for isCompact.

Detail (bot-chat-detail.tsx)

  • The runtime block was a full-width tinted card used as page structure (the named cards-in-page anti-pattern) → open SettingsSection with the test/connect/restart cluster in its action slot.
  • The 4→2→1-column <dl> status grid → MetadataList, which owns the responsive collapse the two hand-rolled media queries used to carry.
  • The configuration-header dialect → bare-variant SettingsSection wrapping the mode toggle + credential form (e2e's heading '接入方式' stays a heading).
  • The hand-tinted quick-setup plate → Astryx Card (a genuine callout — the one legitimate Card use in a settings page).
  • Header readiness Badge → StatusDot + text.

Deliberately untouched

.settingsBotDetailHeader's pinned grid (settings.spec.ts asserts its back → switch → doc link → connect focus order), the brand plate, both QR frames, and the onboarding glyphs — documented art, not layout debt.

Verification

typecheck ✅ · check-dead-css ✅ · check-a11y/copy/console ✅ · product Storybook smoke 71 renders × 3 viewports ✅ · e2e heading-name/focus-order/accessible-name contracts re-checked against the new DOM (roles and names unchanged).

Net: 4 files, +114/−257 — bot.css sheds ~200 lines of retired dialect while keeping the attention wash, planned-opacity, and narrow-viewport row behaviors.

… kit
远程接入 was the last page group speaking the pre-#1972 dialect — #1972
did not list it in its rewrite scope, so a user opening it saw a
different product than every other settings page.
- Overview: bespoke page container + section-header dialect + hand-
rolled list grids become SettingsPage → SettingsSection → the kit's
hairline rows. SettingsSection gains a `titleId` prop so the two
sections keep the heading ids their aria-labelledby wiring names
them by. The readiness Badge becomes the shared StatusDot + text
idiom (the title span's aria-label keeps the row's accessible name
byte-identical for the e2e). EmptyState drops its padding-override
class for `isCompact`.
- Detail: the runtime block was a full-width tinted card used as page
structure (the named anti-pattern) — now an open SettingsSection
whose action slot carries the test/connect/restart cluster. The
4→2→1-column <dl> status grid becomes MetadataList, which owns the
responsive collapse the two media queries hand-rolled. The
configuration header dialect becomes a bare-variant SettingsSection
wrapping the mode toggle + credential form; the hand-tinted
quick-setup plate becomes an Astryx Card (a genuine callout).
- Deliberately untouched: `.settingsBotDetailHeader`'s pinned grid
(settings.spec.ts asserts its back → switch → doc link → connect
focus order), the brand plate, both QR frames, and the onboarding
glyphs — documented art, not layout debt.
- bot.css drops ~200 lines of retired dialect; the attention wash,
planned-opacity, and 620px chevron-hiding row behaviors stay.
Verified: desktop typecheck, check-dead-css, check-a11y/copy/console,
product Storybook smoke (71 catalog renders x 3 viewports); e2e
heading-name and focus-order contracts re-checked against the new DOM.
@jackwener
jackwener merged commit 23d8c70 into mainAug 3, 2026
jackwener added a commit that referenced this pull request Aug 3, 2026
… seam (#1997)
Adversarial review of #1984/#1991/#1993 (two independent lenses,
findings verified before fixing) plus one user-reported shell defect.
Shell:
- The content column is a rounded floating plate flush to the sidebar;
the theme's full-height sidenav hairline ran on the same seam and
read as a doubled border beside the plate's corner radius. Dropped
in the expanded state too (collapsed already did) — separation is
tonal, per DESIGN.md's One Working Plane rule.
Regressions from #1993, confirmed and fixed:
- bot-chat-detail lost all inter-section rhythm: its wrapper pair kept
a gap on a display:block section (inert) and a class with no CSS
rule. The page is a SettingsPage now, like the overview next door;
the back button keeps only its optical inline outdent.
- bot.css had the file-header comment replace-all'd into the middle of
`.settingsBotDetailHeader > .settingsBotLogo`. Parsed by luck;
removed.
- MetadataList destructures a closed prop list, so the runtime grid's
aria-label was silently dropped — the group name rides a real
role="group" wrapper now.
- The kit section header now wraps: at the 480px window floor a
multi-button action cluster drops under the title instead of
crushing it (replaces the deleted bot-runtime media query).
- Overview rows go flush-inline (the 10px inset was old card-grid
residue); the WeChat QR dialog gets its height floor back (224px)
so state swaps stop resizing the modal.
Convergence completions the review surfaced:
- claude-subscription-card drops its SectionHeader + full-width Card
and repeated title: it renders under ProvidersPanel's RouteHeader
like its sibling OAuth panels, as a bare VStack with the shared
StatusDot + text status idiom.
- ProvidersPanel connection status Badge -> StatusDot + text; health's
hand-rolled five-arm dot mapping -> the shared statusDotVariant.
- subagent-presets panel: Card-wrapped .settingsRows + a lead Item
posing as a header -> SettingsSection; settings-surface fallback
drops its Card wrap.
- About hero pills referenced two classNames with no CSS rule —
they are the Badges about.css's comment always described.
- `.settingsPage .settingsPageStack` (rename slip) -> nested
`.settingsPageStack .settingsPageStack`; quota labels round.
Verified: typecheck, check-dead-css, check-a11y/copy/console,
format:check, product Storybook smoke (71 renders x 3 viewports).
Sign up for freeto 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

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit - #1993

Merged
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx
Aug 3, 2026
Merged

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit#1993
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx

Conversation

@jackwener

Copy link
Copy Markdown
Member

Closes out the settings convergence: 远程接入 (overview + detail) was the last page group on the pre-#1972 dialect — #1972 didn't list it in its rewrite scope, so opening it felt like a different product from every other settings page.

Overview (bot-chat-overview.tsx)

  • Bespoke page container / section-header dialect / hand-rolled list grids → SettingsPageSettingsSection → the kit's hairline rows.
  • SettingsSection gains a titleId prop (Heading id + section aria-labelledby) so both sections keep the ids the a11y wiring names them by.
  • Readiness Badge → the shared StatusDot + text idiom; the title span's aria-label keeps each row's accessible name byte-identical for settings.spec.ts.
  • EmptyState drops its padding-override class for isCompact.

Detail (bot-chat-detail.tsx)

  • The runtime block was a full-width tinted card used as page structure (the named cards-in-page anti-pattern) → open SettingsSection with the test/connect/restart cluster in its action slot.
  • The 4→2→1-column <dl> status grid → MetadataList, which owns the responsive collapse the two hand-rolled media queries used to carry.
  • The configuration-header dialect → bare-variant SettingsSection wrapping the mode toggle + credential form (e2e's heading '接入方式' stays a heading).
  • The hand-tinted quick-setup plate → Astryx Card (a genuine callout — the one legitimate Card use in a settings page).
  • Header readiness Badge → StatusDot + text.

Deliberately untouched

.settingsBotDetailHeader's pinned grid (settings.spec.ts asserts its back → switch → doc link → connect focus order), the brand plate, both QR frames, and the onboarding glyphs — documented art, not layout debt.

Verification

typecheck ✅ · check-dead-css ✅ · check-a11y/copy/console ✅ · product Storybook smoke 71 renders × 3 viewports ✅ · e2e heading-name/focus-order/accessible-name contracts re-checked against the new DOM (roles and names unchanged).

Net: 4 files, +114/−257 — bot.css sheds ~200 lines of retired dialect while keeping the attention wash, planned-opacity, and narrow-viewport row behaviors.

… kit
远程接入 was the last page group speaking the pre-#1972 dialect — #1972
did not list it in its rewrite scope, so a user opening it saw a
different product than every other settings page.
- Overview: bespoke page container + section-header dialect + hand-
rolled list grids become SettingsPage → SettingsSection → the kit's
hairline rows. SettingsSection gains a `titleId` prop so the two
sections keep the heading ids their aria-labelledby wiring names
them by. The readiness Badge becomes the shared StatusDot + text
idiom (the title span's aria-label keeps the row's accessible name
byte-identical for the e2e). EmptyState drops its padding-override
class for `isCompact`.
- Detail: the runtime block was a full-width tinted card used as page
structure (the named anti-pattern) — now an open SettingsSection
whose action slot carries the test/connect/restart cluster. The
4→2→1-column <dl> status grid becomes MetadataList, which owns the
responsive collapse the two media queries hand-rolled. The
configuration header dialect becomes a bare-variant SettingsSection
wrapping the mode toggle + credential form; the hand-tinted
quick-setup plate becomes an Astryx Card (a genuine callout).
- Deliberately untouched: `.settingsBotDetailHeader`'s pinned grid
(settings.spec.ts asserts its back → switch → doc link → connect
focus order), the brand plate, both QR frames, and the onboarding
glyphs — documented art, not layout debt.
- bot.css drops ~200 lines of retired dialect; the attention wash,
planned-opacity, and 620px chevron-hiding row behaviors stay.
Verified: desktop typecheck, check-dead-css, check-a11y/copy/console,
product Storybook smoke (71 catalog renders x 3 viewports); e2e
heading-name and focus-order contracts re-checked against the new DOM.
@jackwener
jackwener merged commit 23d8c70 into mainAug 3, 2026
jackwener added a commit that referenced this pull request Aug 3, 2026
… seam (#1997)
Adversarial review of #1984/#1991/#1993 (two independent lenses,
findings verified before fixing) plus one user-reported shell defect.
Shell:
- The content column is a rounded floating plate flush to the sidebar;
the theme's full-height sidenav hairline ran on the same seam and
read as a doubled border beside the plate's corner radius. Dropped
in the expanded state too (collapsed already did) — separation is
tonal, per DESIGN.md's One Working Plane rule.
Regressions from #1993, confirmed and fixed:
- bot-chat-detail lost all inter-section rhythm: its wrapper pair kept
a gap on a display:block section (inert) and a class with no CSS
rule. The page is a SettingsPage now, like the overview next door;
the back button keeps only its optical inline outdent.
- bot.css had the file-header comment replace-all'd into the middle of
`.settingsBotDetailHeader > .settingsBotLogo`. Parsed by luck;
removed.
- MetadataList destructures a closed prop list, so the runtime grid's
aria-label was silently dropped — the group name rides a real
role="group" wrapper now.
- The kit section header now wraps: at the 480px window floor a
multi-button action cluster drops under the title instead of
crushing it (replaces the deleted bot-runtime media query).
- Overview rows go flush-inline (the 10px inset was old card-grid
residue); the WeChat QR dialog gets its height floor back (224px)
so state swaps stop resizing the modal.
Convergence completions the review surfaced:
- claude-subscription-card drops its SectionHeader + full-width Card
and repeated title: it renders under ProvidersPanel's RouteHeader
like its sibling OAuth panels, as a bare VStack with the shared
StatusDot + text status idiom.
- ProvidersPanel connection status Badge -> StatusDot + text; health's
hand-rolled five-arm dot mapping -> the shared statusDotVariant.
- subagent-presets panel: Card-wrapped .settingsRows + a lead Item
posing as a header -> SettingsSection; settings-surface fallback
drops its Card wrap.
- About hero pills referenced two classNames with no CSS rule —
they are the Badges about.css's comment always described.
- `.settingsPage .settingsPageStack` (rename slip) -> nested
`.settingsPageStack .settingsPageStack`; quota labels round.
Verified: typecheck, check-dead-css, check-a11y/copy/console,
format:check, product Storybook smoke (71 renders x 3 viewports).
Sign up for freeto 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

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit - #1993

Merged
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx
Aug 3, 2026
Merged

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit#1993
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx

Conversation

@jackwener

Copy link
Copy Markdown
Member

Closes out the settings convergence: 远程接入 (overview + detail) was the last page group on the pre-#1972 dialect — #1972 didn't list it in its rewrite scope, so opening it felt like a different product from every other settings page.

Overview (bot-chat-overview.tsx)

  • Bespoke page container / section-header dialect / hand-rolled list grids → SettingsPageSettingsSection → the kit's hairline rows.
  • SettingsSection gains a titleId prop (Heading id + section aria-labelledby) so both sections keep the ids the a11y wiring names them by.
  • Readiness Badge → the shared StatusDot + text idiom; the title span's aria-label keeps each row's accessible name byte-identical for settings.spec.ts.
  • EmptyState drops its padding-override class for isCompact.

Detail (bot-chat-detail.tsx)

  • The runtime block was a full-width tinted card used as page structure (the named cards-in-page anti-pattern) → open SettingsSection with the test/connect/restart cluster in its action slot.
  • The 4→2→1-column <dl> status grid → MetadataList, which owns the responsive collapse the two hand-rolled media queries used to carry.
  • The configuration-header dialect → bare-variant SettingsSection wrapping the mode toggle + credential form (e2e's heading '接入方式' stays a heading).
  • The hand-tinted quick-setup plate → Astryx Card (a genuine callout — the one legitimate Card use in a settings page).
  • Header readiness Badge → StatusDot + text.

Deliberately untouched

.settingsBotDetailHeader's pinned grid (settings.spec.ts asserts its back → switch → doc link → connect focus order), the brand plate, both QR frames, and the onboarding glyphs — documented art, not layout debt.

Verification

typecheck ✅ · check-dead-css ✅ · check-a11y/copy/console ✅ · product Storybook smoke 71 renders × 3 viewports ✅ · e2e heading-name/focus-order/accessible-name contracts re-checked against the new DOM (roles and names unchanged).

Net: 4 files, +114/−257 — bot.css sheds ~200 lines of retired dialect while keeping the attention wash, planned-opacity, and narrow-viewport row behaviors.

… kit
远程接入 was the last page group speaking the pre-#1972 dialect — #1972
did not list it in its rewrite scope, so a user opening it saw a
different product than every other settings page.
- Overview: bespoke page container + section-header dialect + hand-
rolled list grids become SettingsPage → SettingsSection → the kit's
hairline rows. SettingsSection gains a `titleId` prop so the two
sections keep the heading ids their aria-labelledby wiring names
them by. The readiness Badge becomes the shared StatusDot + text
idiom (the title span's aria-label keeps the row's accessible name
byte-identical for the e2e). EmptyState drops its padding-override
class for `isCompact`.
- Detail: the runtime block was a full-width tinted card used as page
structure (the named anti-pattern) — now an open SettingsSection
whose action slot carries the test/connect/restart cluster. The
4→2→1-column <dl> status grid becomes MetadataList, which owns the
responsive collapse the two media queries hand-rolled. The
configuration header dialect becomes a bare-variant SettingsSection
wrapping the mode toggle + credential form; the hand-tinted
quick-setup plate becomes an Astryx Card (a genuine callout).
- Deliberately untouched: `.settingsBotDetailHeader`'s pinned grid
(settings.spec.ts asserts its back → switch → doc link → connect
focus order), the brand plate, both QR frames, and the onboarding
glyphs — documented art, not layout debt.
- bot.css drops ~200 lines of retired dialect; the attention wash,
planned-opacity, and 620px chevron-hiding row behaviors stay.
Verified: desktop typecheck, check-dead-css, check-a11y/copy/console,
product Storybook smoke (71 catalog renders x 3 viewports); e2e
heading-name and focus-order contracts re-checked against the new DOM.
@jackwener
jackwener merged commit 23d8c70 into mainAug 3, 2026
jackwener added a commit that referenced this pull request Aug 3, 2026
… seam (#1997)
Adversarial review of #1984/#1991/#1993 (two independent lenses,
findings verified before fixing) plus one user-reported shell defect.
Shell:
- The content column is a rounded floating plate flush to the sidebar;
the theme's full-height sidenav hairline ran on the same seam and
read as a doubled border beside the plate's corner radius. Dropped
in the expanded state too (collapsed already did) — separation is
tonal, per DESIGN.md's One Working Plane rule.
Regressions from #1993, confirmed and fixed:
- bot-chat-detail lost all inter-section rhythm: its wrapper pair kept
a gap on a display:block section (inert) and a class with no CSS
rule. The page is a SettingsPage now, like the overview next door;
the back button keeps only its optical inline outdent.
- bot.css had the file-header comment replace-all'd into the middle of
`.settingsBotDetailHeader > .settingsBotLogo`. Parsed by luck;
removed.
- MetadataList destructures a closed prop list, so the runtime grid's
aria-label was silently dropped — the group name rides a real
role="group" wrapper now.
- The kit section header now wraps: at the 480px window floor a
multi-button action cluster drops under the title instead of
crushing it (replaces the deleted bot-runtime media query).
- Overview rows go flush-inline (the 10px inset was old card-grid
residue); the WeChat QR dialog gets its height floor back (224px)
so state swaps stop resizing the modal.
Convergence completions the review surfaced:
- claude-subscription-card drops its SectionHeader + full-width Card
and repeated title: it renders under ProvidersPanel's RouteHeader
like its sibling OAuth panels, as a bare VStack with the shared
StatusDot + text status idiom.
- ProvidersPanel connection status Badge -> StatusDot + text; health's
hand-rolled five-arm dot mapping -> the shared statusDotVariant.
- subagent-presets panel: Card-wrapped .settingsRows + a lead Item
posing as a header -> SettingsSection; settings-surface fallback
drops its Card wrap.
- About hero pills referenced two classNames with no CSS rule —
they are the Badges about.css's comment always described.
- `.settingsPage .settingsPageStack` (rename slip) -> nested
`.settingsPageStack .settingsPageStack`; quota labels round.
Verified: typecheck, check-dead-css, check-a11y/copy/console,
format:check, product Storybook smoke (71 renders x 3 viewports).
Sign up for freeto 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

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit - #1993

Merged
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx
Aug 3, 2026
Merged

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit#1993
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx

Conversation

@jackwener

Copy link
Copy Markdown
Member

Closes out the settings convergence: 远程接入 (overview + detail) was the last page group on the pre-#1972 dialect — #1972 didn't list it in its rewrite scope, so opening it felt like a different product from every other settings page.

Overview (bot-chat-overview.tsx)

  • Bespoke page container / section-header dialect / hand-rolled list grids → SettingsPageSettingsSection → the kit's hairline rows.
  • SettingsSection gains a titleId prop (Heading id + section aria-labelledby) so both sections keep the ids the a11y wiring names them by.
  • Readiness Badge → the shared StatusDot + text idiom; the title span's aria-label keeps each row's accessible name byte-identical for settings.spec.ts.
  • EmptyState drops its padding-override class for isCompact.

Detail (bot-chat-detail.tsx)

  • The runtime block was a full-width tinted card used as page structure (the named cards-in-page anti-pattern) → open SettingsSection with the test/connect/restart cluster in its action slot.
  • The 4→2→1-column <dl> status grid → MetadataList, which owns the responsive collapse the two hand-rolled media queries used to carry.
  • The configuration-header dialect → bare-variant SettingsSection wrapping the mode toggle + credential form (e2e's heading '接入方式' stays a heading).
  • The hand-tinted quick-setup plate → Astryx Card (a genuine callout — the one legitimate Card use in a settings page).
  • Header readiness Badge → StatusDot + text.

Deliberately untouched

.settingsBotDetailHeader's pinned grid (settings.spec.ts asserts its back → switch → doc link → connect focus order), the brand plate, both QR frames, and the onboarding glyphs — documented art, not layout debt.

Verification

typecheck ✅ · check-dead-css ✅ · check-a11y/copy/console ✅ · product Storybook smoke 71 renders × 3 viewports ✅ · e2e heading-name/focus-order/accessible-name contracts re-checked against the new DOM (roles and names unchanged).

Net: 4 files, +114/−257 — bot.css sheds ~200 lines of retired dialect while keeping the attention wash, planned-opacity, and narrow-viewport row behaviors.

… kit
远程接入 was the last page group speaking the pre-#1972 dialect — #1972
did not list it in its rewrite scope, so a user opening it saw a
different product than every other settings page.
- Overview: bespoke page container + section-header dialect + hand-
rolled list grids become SettingsPage → SettingsSection → the kit's
hairline rows. SettingsSection gains a `titleId` prop so the two
sections keep the heading ids their aria-labelledby wiring names
them by. The readiness Badge becomes the shared StatusDot + text
idiom (the title span's aria-label keeps the row's accessible name
byte-identical for the e2e). EmptyState drops its padding-override
class for `isCompact`.
- Detail: the runtime block was a full-width tinted card used as page
structure (the named anti-pattern) — now an open SettingsSection
whose action slot carries the test/connect/restart cluster. The
4→2→1-column <dl> status grid becomes MetadataList, which owns the
responsive collapse the two media queries hand-rolled. The
configuration header dialect becomes a bare-variant SettingsSection
wrapping the mode toggle + credential form; the hand-tinted
quick-setup plate becomes an Astryx Card (a genuine callout).
- Deliberately untouched: `.settingsBotDetailHeader`'s pinned grid
(settings.spec.ts asserts its back → switch → doc link → connect
focus order), the brand plate, both QR frames, and the onboarding
glyphs — documented art, not layout debt.
- bot.css drops ~200 lines of retired dialect; the attention wash,
planned-opacity, and 620px chevron-hiding row behaviors stay.
Verified: desktop typecheck, check-dead-css, check-a11y/copy/console,
product Storybook smoke (71 catalog renders x 3 viewports); e2e
heading-name and focus-order contracts re-checked against the new DOM.
@jackwener
jackwener merged commit 23d8c70 into mainAug 3, 2026
jackwener added a commit that referenced this pull request Aug 3, 2026
… seam (#1997)
Adversarial review of #1984/#1991/#1993 (two independent lenses,
findings verified before fixing) plus one user-reported shell defect.
Shell:
- The content column is a rounded floating plate flush to the sidebar;
the theme's full-height sidenav hairline ran on the same seam and
read as a doubled border beside the plate's corner radius. Dropped
in the expanded state too (collapsed already did) — separation is
tonal, per DESIGN.md's One Working Plane rule.
Regressions from #1993, confirmed and fixed:
- bot-chat-detail lost all inter-section rhythm: its wrapper pair kept
a gap on a display:block section (inert) and a class with no CSS
rule. The page is a SettingsPage now, like the overview next door;
the back button keeps only its optical inline outdent.
- bot.css had the file-header comment replace-all'd into the middle of
`.settingsBotDetailHeader > .settingsBotLogo`. Parsed by luck;
removed.
- MetadataList destructures a closed prop list, so the runtime grid's
aria-label was silently dropped — the group name rides a real
role="group" wrapper now.
- The kit section header now wraps: at the 480px window floor a
multi-button action cluster drops under the title instead of
crushing it (replaces the deleted bot-runtime media query).
- Overview rows go flush-inline (the 10px inset was old card-grid
residue); the WeChat QR dialog gets its height floor back (224px)
so state swaps stop resizing the modal.
Convergence completions the review surfaced:
- claude-subscription-card drops its SectionHeader + full-width Card
and repeated title: it renders under ProvidersPanel's RouteHeader
like its sibling OAuth panels, as a bare VStack with the shared
StatusDot + text status idiom.
- ProvidersPanel connection status Badge -> StatusDot + text; health's
hand-rolled five-arm dot mapping -> the shared statusDotVariant.
- subagent-presets panel: Card-wrapped .settingsRows + a lead Item
posing as a header -> SettingsSection; settings-surface fallback
drops its Card wrap.
- About hero pills referenced two classNames with no CSS rule —
they are the Badges about.css's comment always described.
- `.settingsPage .settingsPageStack` (rename slip) -> nested
`.settingsPageStack .settingsPageStack`; quota labels round.
Verified: typecheck, check-dead-css, check-a11y/copy/console,
format:check, product Storybook smoke (71 renders x 3 viewports).
Sign up for freeto 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

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit - #1993

Merged
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx
Aug 3, 2026
Merged

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit#1993
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx

Conversation

@jackwener

Copy link
Copy Markdown
Member

Closes out the settings convergence: 远程接入 (overview + detail) was the last page group on the pre-#1972 dialect — #1972 didn't list it in its rewrite scope, so opening it felt like a different product from every other settings page.

Overview (bot-chat-overview.tsx)

  • Bespoke page container / section-header dialect / hand-rolled list grids → SettingsPageSettingsSection → the kit's hairline rows.
  • SettingsSection gains a titleId prop (Heading id + section aria-labelledby) so both sections keep the ids the a11y wiring names them by.
  • Readiness Badge → the shared StatusDot + text idiom; the title span's aria-label keeps each row's accessible name byte-identical for settings.spec.ts.
  • EmptyState drops its padding-override class for isCompact.

Detail (bot-chat-detail.tsx)

  • The runtime block was a full-width tinted card used as page structure (the named cards-in-page anti-pattern) → open SettingsSection with the test/connect/restart cluster in its action slot.
  • The 4→2→1-column <dl> status grid → MetadataList, which owns the responsive collapse the two hand-rolled media queries used to carry.
  • The configuration-header dialect → bare-variant SettingsSection wrapping the mode toggle + credential form (e2e's heading '接入方式' stays a heading).
  • The hand-tinted quick-setup plate → Astryx Card (a genuine callout — the one legitimate Card use in a settings page).
  • Header readiness Badge → StatusDot + text.

Deliberately untouched

.settingsBotDetailHeader's pinned grid (settings.spec.ts asserts its back → switch → doc link → connect focus order), the brand plate, both QR frames, and the onboarding glyphs — documented art, not layout debt.

Verification

typecheck ✅ · check-dead-css ✅ · check-a11y/copy/console ✅ · product Storybook smoke 71 renders × 3 viewports ✅ · e2e heading-name/focus-order/accessible-name contracts re-checked against the new DOM (roles and names unchanged).

Net: 4 files, +114/−257 — bot.css sheds ~200 lines of retired dialect while keeping the attention wash, planned-opacity, and narrow-viewport row behaviors.

… kit
远程接入 was the last page group speaking the pre-#1972 dialect — #1972
did not list it in its rewrite scope, so a user opening it saw a
different product than every other settings page.
- Overview: bespoke page container + section-header dialect + hand-
rolled list grids become SettingsPage → SettingsSection → the kit's
hairline rows. SettingsSection gains a `titleId` prop so the two
sections keep the heading ids their aria-labelledby wiring names
them by. The readiness Badge becomes the shared StatusDot + text
idiom (the title span's aria-label keeps the row's accessible name
byte-identical for the e2e). EmptyState drops its padding-override
class for `isCompact`.
- Detail: the runtime block was a full-width tinted card used as page
structure (the named anti-pattern) — now an open SettingsSection
whose action slot carries the test/connect/restart cluster. The
4→2→1-column <dl> status grid becomes MetadataList, which owns the
responsive collapse the two media queries hand-rolled. The
configuration header dialect becomes a bare-variant SettingsSection
wrapping the mode toggle + credential form; the hand-tinted
quick-setup plate becomes an Astryx Card (a genuine callout).
- Deliberately untouched: `.settingsBotDetailHeader`'s pinned grid
(settings.spec.ts asserts its back → switch → doc link → connect
focus order), the brand plate, both QR frames, and the onboarding
glyphs — documented art, not layout debt.
- bot.css drops ~200 lines of retired dialect; the attention wash,
planned-opacity, and 620px chevron-hiding row behaviors stay.
Verified: desktop typecheck, check-dead-css, check-a11y/copy/console,
product Storybook smoke (71 catalog renders x 3 viewports); e2e
heading-name and focus-order contracts re-checked against the new DOM.
@jackwener
jackwener merged commit 23d8c70 into mainAug 3, 2026
jackwener added a commit that referenced this pull request Aug 3, 2026
… seam (#1997)
Adversarial review of #1984/#1991/#1993 (two independent lenses,
findings verified before fixing) plus one user-reported shell defect.
Shell:
- The content column is a rounded floating plate flush to the sidebar;
the theme's full-height sidenav hairline ran on the same seam and
read as a doubled border beside the plate's corner radius. Dropped
in the expanded state too (collapsed already did) — separation is
tonal, per DESIGN.md's One Working Plane rule.
Regressions from #1993, confirmed and fixed:
- bot-chat-detail lost all inter-section rhythm: its wrapper pair kept
a gap on a display:block section (inert) and a class with no CSS
rule. The page is a SettingsPage now, like the overview next door;
the back button keeps only its optical inline outdent.
- bot.css had the file-header comment replace-all'd into the middle of
`.settingsBotDetailHeader > .settingsBotLogo`. Parsed by luck;
removed.
- MetadataList destructures a closed prop list, so the runtime grid's
aria-label was silently dropped — the group name rides a real
role="group" wrapper now.
- The kit section header now wraps: at the 480px window floor a
multi-button action cluster drops under the title instead of
crushing it (replaces the deleted bot-runtime media query).
- Overview rows go flush-inline (the 10px inset was old card-grid
residue); the WeChat QR dialog gets its height floor back (224px)
so state swaps stop resizing the modal.
Convergence completions the review surfaced:
- claude-subscription-card drops its SectionHeader + full-width Card
and repeated title: it renders under ProvidersPanel's RouteHeader
like its sibling OAuth panels, as a bare VStack with the shared
StatusDot + text status idiom.
- ProvidersPanel connection status Badge -> StatusDot + text; health's
hand-rolled five-arm dot mapping -> the shared statusDotVariant.
- subagent-presets panel: Card-wrapped .settingsRows + a lead Item
posing as a header -> SettingsSection; settings-surface fallback
drops its Card wrap.
- About hero pills referenced two classNames with no CSS rule —
they are the Badges about.css's comment always described.
- `.settingsPage .settingsPageStack` (rename slip) -> nested
`.settingsPageStack .settingsPageStack`; quota labels round.
Verified: typecheck, check-dead-css, check-a11y/copy/console,
format:check, product Storybook smoke (71 renders x 3 viewports).
Sign up for freeto 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

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit - #1993

Merged
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx
Aug 3, 2026
Merged

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit#1993
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx

Conversation

@jackwener

Copy link
Copy Markdown
Member

Closes out the settings convergence: 远程接入 (overview + detail) was the last page group on the pre-#1972 dialect — #1972 didn't list it in its rewrite scope, so opening it felt like a different product from every other settings page.

Overview (bot-chat-overview.tsx)

  • Bespoke page container / section-header dialect / hand-rolled list grids → SettingsPageSettingsSection → the kit's hairline rows.
  • SettingsSection gains a titleId prop (Heading id + section aria-labelledby) so both sections keep the ids the a11y wiring names them by.
  • Readiness Badge → the shared StatusDot + text idiom; the title span's aria-label keeps each row's accessible name byte-identical for settings.spec.ts.
  • EmptyState drops its padding-override class for isCompact.

Detail (bot-chat-detail.tsx)

  • The runtime block was a full-width tinted card used as page structure (the named cards-in-page anti-pattern) → open SettingsSection with the test/connect/restart cluster in its action slot.
  • The 4→2→1-column <dl> status grid → MetadataList, which owns the responsive collapse the two hand-rolled media queries used to carry.
  • The configuration-header dialect → bare-variant SettingsSection wrapping the mode toggle + credential form (e2e's heading '接入方式' stays a heading).
  • The hand-tinted quick-setup plate → Astryx Card (a genuine callout — the one legitimate Card use in a settings page).
  • Header readiness Badge → StatusDot + text.

Deliberately untouched

.settingsBotDetailHeader's pinned grid (settings.spec.ts asserts its back → switch → doc link → connect focus order), the brand plate, both QR frames, and the onboarding glyphs — documented art, not layout debt.

Verification

typecheck ✅ · check-dead-css ✅ · check-a11y/copy/console ✅ · product Storybook smoke 71 renders × 3 viewports ✅ · e2e heading-name/focus-order/accessible-name contracts re-checked against the new DOM (roles and names unchanged).

Net: 4 files, +114/−257 — bot.css sheds ~200 lines of retired dialect while keeping the attention wash, planned-opacity, and narrow-viewport row behaviors.

… kit
远程接入 was the last page group speaking the pre-#1972 dialect — #1972
did not list it in its rewrite scope, so a user opening it saw a
different product than every other settings page.
- Overview: bespoke page container + section-header dialect + hand-
rolled list grids become SettingsPage → SettingsSection → the kit's
hairline rows. SettingsSection gains a `titleId` prop so the two
sections keep the heading ids their aria-labelledby wiring names
them by. The readiness Badge becomes the shared StatusDot + text
idiom (the title span's aria-label keeps the row's accessible name
byte-identical for the e2e). EmptyState drops its padding-override
class for `isCompact`.
- Detail: the runtime block was a full-width tinted card used as page
structure (the named anti-pattern) — now an open SettingsSection
whose action slot carries the test/connect/restart cluster. The
4→2→1-column <dl> status grid becomes MetadataList, which owns the
responsive collapse the two media queries hand-rolled. The
configuration header dialect becomes a bare-variant SettingsSection
wrapping the mode toggle + credential form; the hand-tinted
quick-setup plate becomes an Astryx Card (a genuine callout).
- Deliberately untouched: `.settingsBotDetailHeader`'s pinned grid
(settings.spec.ts asserts its back → switch → doc link → connect
focus order), the brand plate, both QR frames, and the onboarding
glyphs — documented art, not layout debt.
- bot.css drops ~200 lines of retired dialect; the attention wash,
planned-opacity, and 620px chevron-hiding row behaviors stay.
Verified: desktop typecheck, check-dead-css, check-a11y/copy/console,
product Storybook smoke (71 catalog renders x 3 viewports); e2e
heading-name and focus-order contracts re-checked against the new DOM.
@jackwener
jackwener merged commit 23d8c70 into mainAug 3, 2026
jackwener added a commit that referenced this pull request Aug 3, 2026
… seam (#1997)
Adversarial review of #1984/#1991/#1993 (two independent lenses,
findings verified before fixing) plus one user-reported shell defect.
Shell:
- The content column is a rounded floating plate flush to the sidebar;
the theme's full-height sidenav hairline ran on the same seam and
read as a doubled border beside the plate's corner radius. Dropped
in the expanded state too (collapsed already did) — separation is
tonal, per DESIGN.md's One Working Plane rule.
Regressions from #1993, confirmed and fixed:
- bot-chat-detail lost all inter-section rhythm: its wrapper pair kept
a gap on a display:block section (inert) and a class with no CSS
rule. The page is a SettingsPage now, like the overview next door;
the back button keeps only its optical inline outdent.
- bot.css had the file-header comment replace-all'd into the middle of
`.settingsBotDetailHeader > .settingsBotLogo`. Parsed by luck;
removed.
- MetadataList destructures a closed prop list, so the runtime grid's
aria-label was silently dropped — the group name rides a real
role="group" wrapper now.
- The kit section header now wraps: at the 480px window floor a
multi-button action cluster drops under the title instead of
crushing it (replaces the deleted bot-runtime media query).
- Overview rows go flush-inline (the 10px inset was old card-grid
residue); the WeChat QR dialog gets its height floor back (224px)
so state swaps stop resizing the modal.
Convergence completions the review surfaced:
- claude-subscription-card drops its SectionHeader + full-width Card
and repeated title: it renders under ProvidersPanel's RouteHeader
like its sibling OAuth panels, as a bare VStack with the shared
StatusDot + text status idiom.
- ProvidersPanel connection status Badge -> StatusDot + text; health's
hand-rolled five-arm dot mapping -> the shared statusDotVariant.
- subagent-presets panel: Card-wrapped .settingsRows + a lead Item
posing as a header -> SettingsSection; settings-surface fallback
drops its Card wrap.
- About hero pills referenced two classNames with no CSS rule —
they are the Badges about.css's comment always described.
- `.settingsPage .settingsPageStack` (rename slip) -> nested
`.settingsPageStack .settingsPageStack`; quota labels round.
Verified: typecheck, check-dead-css, check-a11y/copy/console,
format:check, product Storybook smoke (71 renders x 3 viewports).
Sign up for freeto 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

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit - #1993

Merged
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx
Aug 3, 2026
Merged

refactor(desktop): move the bot-chat cluster onto the Astryx settings kit#1993
jackwener merged 1 commit into
mainfrom
refactor/bot-chat-astryx

Conversation

@jackwener

Copy link
Copy Markdown
Member

Closes out the settings convergence: 远程接入 (overview + detail) was the last page group on the pre-#1972 dialect — #1972 didn't list it in its rewrite scope, so opening it felt like a different product from every other settings page.

Overview (bot-chat-overview.tsx)

  • Bespoke page container / section-header dialect / hand-rolled list grids → SettingsPageSettingsSection → the kit's hairline rows.
  • SettingsSection gains a titleId prop (Heading id + section aria-labelledby) so both sections keep the ids the a11y wiring names them by.
  • Readiness Badge → the shared StatusDot + text idiom; the title span's aria-label keeps each row's accessible name byte-identical for settings.spec.ts.
  • EmptyState drops its padding-override class for isCompact.

Detail (bot-chat-detail.tsx)

  • The runtime block was a full-width tinted card used as page structure (the named cards-in-page anti-pattern) → open SettingsSection with the test/connect/restart cluster in its action slot.
  • The 4→2→1-column <dl> status grid → MetadataList, which owns the responsive collapse the two hand-rolled media queries used to carry.
  • The configuration-header dialect → bare-variant SettingsSection wrapping the mode toggle + credential form (e2e's heading '接入方式' stays a heading).
  • The hand-tinted quick-setup plate → Astryx Card (a genuine callout — the one legitimate Card use in a settings page).
  • Header readiness Badge → StatusDot + text.

Deliberately untouched

.settingsBotDetailHeader's pinned grid (settings.spec.ts asserts its back → switch → doc link → connect focus order), the brand plate, both QR frames, and the onboarding glyphs — documented art, not layout debt.

Verification

typecheck ✅ · check-dead-css ✅ · check-a11y/copy/console ✅ · product Storybook smoke 71 renders × 3 viewports ✅ · e2e heading-name/focus-order/accessible-name contracts re-checked against the new DOM (roles and names unchanged).

Net: 4 files, +114/−257 — bot.css sheds ~200 lines of retired dialect while keeping the attention wash, planned-opacity, and narrow-viewport row behaviors.

… kit
远程接入 was the last page group speaking the pre-#1972 dialect — #1972
did not list it in its rewrite scope, so a user opening it saw a
different product than every other settings page.
- Overview: bespoke page container + section-header dialect + hand-
rolled list grids become SettingsPage → SettingsSection → the kit's
hairline rows. SettingsSection gains a `titleId` prop so the two
sections keep the heading ids their aria-labelledby wiring names
them by. The readiness Badge becomes the shared StatusDot + text
idiom (the title span's aria-label keeps the row's accessible name
byte-identical for the e2e). EmptyState drops its padding-override
class for `isCompact`.
- Detail: the runtime block was a full-width tinted card used as page
structure (the named anti-pattern) — now an open SettingsSection
whose action slot carries the test/connect/restart cluster. The
4→2→1-column <dl> status grid becomes MetadataList, which owns the
responsive collapse the two media queries hand-rolled. The
configuration header dialect becomes a bare-variant SettingsSection
wrapping the mode toggle + credential form; the hand-tinted
quick-setup plate becomes an Astryx Card (a genuine callout).
- Deliberately untouched: `.settingsBotDetailHeader`'s pinned grid
(settings.spec.ts asserts its back → switch → doc link → connect
focus order), the brand plate, both QR frames, and the onboarding
glyphs — documented art, not layout debt.
- bot.css drops ~200 lines of retired dialect; the attention wash,
planned-opacity, and 620px chevron-hiding row behaviors stay.
Verified: desktop typecheck, check-dead-css, check-a11y/copy/console,
product Storybook smoke (71 catalog renders x 3 viewports); e2e
heading-name and focus-order contracts re-checked against the new DOM.
@jackwener
jackwener merged commit 23d8c70 into mainAug 3, 2026
jackwener added a commit that referenced this pull request Aug 3, 2026
… seam (#1997)
Adversarial review of #1984/#1991/#1993 (two independent lenses,
findings verified before fixing) plus one user-reported shell defect.
Shell:
- The content column is a rounded floating plate flush to the sidebar;
the theme's full-height sidenav hairline ran on the same seam and
read as a doubled border beside the plate's corner radius. Dropped
in the expanded state too (collapsed already did) — separation is
tonal, per DESIGN.md's One Working Plane rule.
Regressions from #1993, confirmed and fixed:
- bot-chat-detail lost all inter-section rhythm: its wrapper pair kept
a gap on a display:block section (inert) and a class with no CSS
rule. The page is a SettingsPage now, like the overview next door;
the back button keeps only its optical inline outdent.
- bot.css had the file-header comment replace-all'd into the middle of
`.settingsBotDetailHeader > .settingsBotLogo`. Parsed by luck;
removed.
- MetadataList destructures a closed prop list, so the runtime grid's
aria-label was silently dropped — the group name rides a real
role="group" wrapper now.
- The kit section header now wraps: at the 480px window floor a
multi-button action cluster drops under the title instead of
crushing it (replaces the deleted bot-runtime media query).
- Overview rows go flush-inline (the 10px inset was old card-grid
residue); the WeChat QR dialog gets its height floor back (224px)
so state swaps stop resizing the modal.
Convergence completions the review surfaced:
- claude-subscription-card drops its SectionHeader + full-width Card
and repeated title: it renders under ProvidersPanel's RouteHeader
like its sibling OAuth panels, as a bare VStack with the shared
StatusDot + text status idiom.
- ProvidersPanel connection status Badge -> StatusDot + text; health's
hand-rolled five-arm dot mapping -> the shared statusDotVariant.
- subagent-presets panel: Card-wrapped .settingsRows + a lead Item
posing as a header -> SettingsSection; settings-surface fallback
drops its Card wrap.
- About hero pills referenced two classNames with no CSS rule —
they are the Badges about.css's comment always described.
- `.settingsPage .settingsPageStack` (rename slip) -> nested
`.settingsPageStack .settingsPageStack`; quota labels round.
Verified: typecheck, check-dead-css, check-a11y/copy/console,
format:check, product Storybook smoke (71 renders x 3 viewports).
Sign up for freeto 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

@jackwener