feat(typography): establish three-font contract (#5918)
* feat(typography): establish three-font contract Assisted-by: Hermes Agent:gpt-5.6-sol Assisted-by: OpenCode:gpt-5.3-codex-spark Assisted-by: Claude Code:claude-opus-4-8 Assisted-by: Codex:gpt-5.3-codex-spark * fix(terminal): refit after live font changes Assisted-by: Hermes Agent:gpt-5.6-sol Assisted-by: OpenCode:gpt-5.3-codex-spark Assisted-by: Claude Code:claude-opus-4-8 * docs(typography): preserve global serif guardrail Assisted-by: Hermes Agent:gpt-5.6-sol Assisted-by: Claude Code:claude-opus-4-8 * fix(typography): enforce font tokens in rendered editors Assisted-by: Hermes Agent:gpt-5.6-sol Assisted-by: Codex:gpt-5.3-codex-spark * fix(terminal): resync after stylesheet font changes Observe live extension stylesheet changes and completed stylesheet loads so open terminals refresh font metrics and backend dimensions. Assisted-by: Hermes Agent:gpt-5.6-sol Assisted-by: Codex:gpt-5.3-codex-spark Assisted-by: Greptile:undisclosed * test(typography): remove dynamic harness execution Assisted-by: Hermes Agent:gpt-5.6-sol Assisted-by: Codex:gpt-5.3-codex-spark Assisted-by: nesquena-hermes:undisclosed * test(typography): skip missing Playwright browser Assisted-by: Hermes Agent:gpt-5.6-sol Assisted-by: Codex:gpt-5.6-luna Assisted-by: nesquena-hermes:undisclosed * fix(terminal): refit after web fonts load Assisted-by: Hermes Agent:gpt-5.6-sol Assisted-by: Codex:gpt-5.6-luna Assisted-by: Greptile:undisclosed --------- Co-authored-by: nesquena-hermes <nesquena+hermes@gmail.com>
This commit is contained in:
1 parent
84f9be2088
commit
1480718767
11 files changed
+1083
-75
No files matched your search
@@ -144,6 +144,36 @@ font size. Persists alongside theme and skin.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Typography Tokens
|
||||||
|
|
||||||
|
The three font tokens are declared in `static/style.css`:
|
||||||
|
|
||||||
|
- `--font-ui`: user interface chrome, controls, composer chrome, labels, and
|
||||||
|
ordinary non-code UI text
|
||||||
|
- `--font-conversation`: user/assistant message prose
|
||||||
|
- `--font-mono`: code, tool details, logs, identifiers, and terminal text
|
||||||
|
|
||||||
|
By default:
|
||||||
|
|
||||||
|
```css
|
||||||
|
--font-conversation: var(--font-ui);
|
||||||
|
--font-mono: ui-monospace,"SFMono-Regular","SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
|
||||||
|
```
|
||||||
|
|
||||||
|
For CSS-based custom skins and extension stylesheets/scripts, override the three
|
||||||
|
font tokens directly (`--font-ui`, `--font-conversation`, `--font-mono`) on the
|
||||||
|
skin root (`:root[data-skin="..."]`), including any mode-specific variant.
|
||||||
|
`window.registerHermesSkin()` currently accepts only the documented design-token
|
||||||
|
allowlist (no `--font-*` tokens), so custom font families should be supplied via
|
||||||
|
CSS rather than skin registration.
|
||||||
|
|
||||||
|
Leaving `--font-conversation` untouched keeps prose coupled to `--font-ui`;
|
||||||
|
override it only when a different reading face is explicitly required. Avoid
|
||||||
|
hard-coded selector-level font declarations when one of these tokens already
|
||||||
|
expresses the intent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## How It Works Internally
|
## How It Works Internally
|
||||||
|
|
||||||
1. **Theme:** `document.documentElement.classList.toggle('dark', isDark)` —
|
1. **Theme:** `document.documentElement.classList.toggle('dark', isDark)` —
|
||||||
|
|||||||
+18
-9
@@ -96,16 +96,25 @@ unless the code and tests prove it is implemented.
|
|||||||
|
|
||||||
## Typography and content
|
## Typography and content
|
||||||
|
|
||||||
Use split typography intentionally:
|
Use three explicit font tokens:
|
||||||
|
|
||||||
- assistant prose uses the same system sans stack as the rest of the UI by
|
- `--font-ui`: shell chrome, controls, composer, labels, and ordinary UI text
|
||||||
default (`--font-ui` in `static/style.css`),
|
- `--font-conversation`: user/assistant message prose; by default this is
|
||||||
- editorial serif assistant prose is historical/proposal or skin-scoped only;
|
`var(--font-ui)` in `static/style.css`
|
||||||
do not reintroduce a global assistant serif without explicit design approval
|
- `--font-mono`: code, file paths, command lines, tool payloads, technical logs,
|
||||||
plus code and test evidence,
|
and terminal output
|
||||||
- user bubbles and functional UI also use the system sans stack unless a scoped
|
|
||||||
skin intentionally overrides typography,
|
Use semantic tokens for typography. Keep prose on `--font-conversation` by
|
||||||
- monospace is for code, file paths, commands, tool names, and compact metadata.
|
default so it tracks `--font-ui` whenever a skin intentionally retunes UI type.
|
||||||
|
Override `--font-conversation` only for a skin that intentionally wants a
|
||||||
|
distinct prose face.
|
||||||
|
Conversation prose must remain the system sans by default. Do not introduce a
|
||||||
|
global conversation serif through the `--font-conversation` token or
|
||||||
|
selector-level overrides without explicit design approval plus code and test
|
||||||
|
evidence; keep distinct editorial prose typography explicitly opt-in or
|
||||||
|
skin-scoped.
|
||||||
|
Avoid hard-coding selector-level font stacks when a token already carries the
|
||||||
|
intent.
|
||||||
|
|
||||||
Keep scale tight. Avoid introducing near-duplicate one-off font sizes, colors,
|
Keep scale tight. Avoid introducing near-duplicate one-off font sizes, colors,
|
||||||
radius values, or spacing values when an existing token works.
|
radius values, or spacing values when an existing token works.
|
||||||
|
|||||||
+1
-1
@@ -3871,5 +3871,5 @@ async function shutdownServer() {
|
|||||||
|
|
||||||
function _showServerStopped() {
|
function _showServerStopped() {
|
||||||
var stoppedMsg = (typeof t === 'function' ? t('settings_shutdown_stopped_message') : 'Server stopped. You can close this tab.');
|
var stoppedMsg = (typeof t === 'function' ? t('settings_shutdown_stopped_message') : 'Server stopped. You can close this tab.');
|
||||||
document.body.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100vh;color:var(--muted);font-family:system-ui,ui-sans-serif;font-size:14px"><p>' + stoppedMsg + '</p></div>';
|
document.body.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100vh;color:var(--muted);font-family:var(--font-ui);font-size:14px"><p>' + stoppedMsg + '</p></div>';
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1718,7 +1718,7 @@
|
|||||||
<div class="preview-html-wrap" id="previewHtmlWrap" style="display:none;flex:1;border-radius:8px;overflow:hidden;border:1px solid var(--border2)">
|
<div class="preview-html-wrap" id="previewHtmlWrap" style="display:none;flex:1;border-radius:8px;overflow:hidden;border:1px solid var(--border2)">
|
||||||
<iframe id="previewHtmlIframe" style="width:100%;height:100%;border:none;background:#fff" sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox" title="HTML preview"></iframe>
|
<iframe id="previewHtmlIframe" style="width:100%;height:100%;border:none;background:#fff" sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox" title="HTML preview"></iframe>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="previewEditArea" style="display:none;flex:1;width:100%;background:var(--code-bg);color:var(--pre-text);border:1px solid var(--border2);border-radius:8px;padding:12px;font-family:'SF Mono',ui-monospace,monospace;font-size:12px;line-height:1.6;resize:none;outline:none" oninput="_previewDirty=true;updateEditBtn()"></textarea>
|
<textarea id="previewEditArea" style="display:none;flex:1;width:100%;background:var(--code-bg);color:var(--pre-text);border:1px solid var(--border2);border-radius:8px;padding:12px;font-family:var(--font-mono);font-size:12px;line-height:1.6;resize:none;outline:none" oninput="_previewDirty=true;updateEditBtn()"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -12312,7 +12312,7 @@ function _openAuxAdvancedOptions(taskCfg,cfg){
|
|||||||
_auxAdvancedInputHtml('auxAdvancedBaseUrl',t('settings_aux_advanced_base_url')||'Base URL',_auxAdvancedValue(cfg,'base_url'),t('settings_aux_advanced_base_url_desc')||'Optional provider endpoint override.','text','inputmode="url"')+
|
_auxAdvancedInputHtml('auxAdvancedBaseUrl',t('settings_aux_advanced_base_url')||'Base URL',_auxAdvancedValue(cfg,'base_url'),t('settings_aux_advanced_base_url_desc')||'Optional provider endpoint override.','text','inputmode="url"')+
|
||||||
serviceTierField+
|
serviceTierField+
|
||||||
timingFields+
|
timingFields+
|
||||||
`<label style="display:grid;gap:4px;font-size:12px;color:var(--text)"><span style="font-weight:600">${esc(t('settings_aux_advanced_extra_body')||'Extra body JSON')}</span><textarea id="auxAdvancedExtraBody" rows="6" style="width:100%;box-sizing:border-box;padding:7px 8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px;font-size:12px;font-family:var(--mono,monospace)">${esc(extraBody)}</textarea><span style="font-size:10px;color:var(--muted);line-height:1.35">${esc(t('settings_aux_advanced_extra_body_desc')||'Optional JSON object merged into the model request body.')}</span></label>`+
|
`<label style="display:grid;gap:4px;font-size:12px;color:var(--text)"><span style="font-weight:600">${esc(t('settings_aux_advanced_extra_body')||'Extra body JSON')}</span><textarea id="auxAdvancedExtraBody" rows="6" style="width:100%;box-sizing:border-box;padding:7px 8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px;font-size:12px;font-family:var(--font-mono)">${esc(extraBody)}</textarea><span style="font-size:10px;color:var(--muted);line-height:1.35">${esc(t('settings_aux_advanced_extra_body_desc')||'Optional JSON object merged into the model request body.')}</span></label>`+
|
||||||
_auxAdvancedInputHtml('auxAdvancedApiKey',t('settings_aux_advanced_api_key')||'API key override','',apiKeyHint,'text','autocomplete="one-time-code" inputmode="text" readonly onfocus="this.removeAttribute("readonly")"',';-webkit-text-security:disc')+
|
_auxAdvancedInputHtml('auxAdvancedApiKey',t('settings_aux_advanced_api_key')||'API key override','',apiKeyHint,'text','autocomplete="one-time-code" inputmode="text" readonly onfocus="this.removeAttribute("readonly")"',';-webkit-text-security:disc')+
|
||||||
`<label style="display:${cfg&&cfg.api_key_set?'flex':'none'};align-items:center;gap:8px;font-size:12px;color:var(--text)"><input id="auxAdvancedApiKeyClear" type="checkbox" style="width:15px;height:15px;accent-color:var(--accent)"><span>${esc(t('settings_aux_advanced_api_key_clear')||'Clear existing API key override')}</span></label>`;
|
`<label style="display:${cfg&&cfg.api_key_set?'flex':'none'};align-items:center;gap:8px;font-size:12px;color:var(--text)"><input id="auxAdvancedApiKeyClear" type="checkbox" style="width:15px;height:15px;accent-color:var(--accent)"><span>${esc(t('settings_aux_advanced_api_key_clear')||'Clear existing API key override')}</span></label>`;
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-53
@@ -1,4 +1,9 @@
|
|||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
/* ── Typography contract ──
|
||||||
|
--font-ui: shell chrome, controls, composer, labels, and ordinary UI.
|
||||||
|
--font-conversation: user/assistant message prose (defaults to --font-ui).
|
||||||
|
--font-mono: inline code, fenced code blocks, logs, tool/metadata strings, and terminal.
|
||||||
|
*/
|
||||||
/* ── Light mode (default) — warm gold-tinted palette from Hermes brand ── */
|
/* ── Light mode (default) — warm gold-tinted palette from Hermes brand ── */
|
||||||
:root {
|
:root {
|
||||||
--bg:#FEFCF7;--sidebar:#FAF7F0;--border:#E0D8C8;--border2:rgba(0,0,0,0.15);
|
--bg:#FEFCF7;--sidebar:#FAF7F0;--border:#E0D8C8;--border2:rgba(0,0,0,0.15);
|
||||||
@@ -15,6 +20,8 @@
|
|||||||
--message-body-font-size:14px;--message-body-line-height:1.75;--message-code-font-size:12.5px;--message-pre-code-font-size:13px;--message-table-font-size:12px;
|
--message-body-font-size:14px;--message-body-line-height:1.75;--message-code-font-size:12.5px;--message-pre-code-font-size:13px;--message-table-font-size:12px;
|
||||||
--file-tree-toggle-width:10px;
|
--file-tree-toggle-width:10px;
|
||||||
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;
|
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;
|
||||||
|
--font-conversation:var(--font-ui);
|
||||||
|
--font-mono:ui-monospace,"SFMono-Regular","SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
|
||||||
--surface-subtle:rgba(0,0,0,.025);--surface-subtle-hover:rgba(0,0,0,.045);
|
--surface-subtle:rgba(0,0,0,.025);--surface-subtle-hover:rgba(0,0,0,.045);
|
||||||
--border-subtle:rgba(0,0,0,.08);--border-muted:rgba(0,0,0,.12);
|
--border-subtle:rgba(0,0,0,.08);--border-muted:rgba(0,0,0,.12);
|
||||||
font-family:var(--font-ui);font-size:14px;line-height:1.6;
|
font-family:var(--font-ui);font-size:14px;line-height:1.6;
|
||||||
@@ -141,7 +148,6 @@
|
|||||||
--user-bubble-bg:#EFEFEF;--user-bubble-border:#DEDEDE;--user-bubble-text:#242424;--user-bubble-placeholder:#707070;
|
--user-bubble-bg:#EFEFEF;--user-bubble-border:#DEDEDE;--user-bubble-text:#242424;--user-bubble-placeholder:#707070;
|
||||||
--user-selection-bg:rgba(0,0,0,0.16);--user-selection-text:#111111;
|
--user-selection-bg:rgba(0,0,0,0.16);--user-selection-text:#111111;
|
||||||
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||||
--font-mono:ui-monospace,"SFMono-Regular","SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
|
|
||||||
font-family:var(--font-ui);
|
font-family:var(--font-ui);
|
||||||
}
|
}
|
||||||
:root.dark[data-skin="graphite"]{
|
:root.dark[data-skin="graphite"]{
|
||||||
@@ -203,7 +209,7 @@
|
|||||||
:root.dark[data-skin="graphite"] .composer-box{box-shadow:0 1px 2px rgba(0,0,0,0.28);}
|
:root.dark[data-skin="graphite"] .composer-box{box-shadow:0 1px 2px rgba(0,0,0,0.28);}
|
||||||
:root[data-skin="graphite"] .composer-box:focus-within{border-color:var(--accent)!important;box-shadow:0 0 0 2px var(--focus-ring)!important;}
|
:root[data-skin="graphite"] .composer-box:focus-within{border-color:var(--accent)!important;box-shadow:0 0 0 2px var(--focus-ring)!important;}
|
||||||
:root[data-skin="graphite"] textarea#msg{font-family:var(--font-ui)!important;font-size:14px;font-weight:430;letter-spacing:0;line-height:1.45;}
|
:root[data-skin="graphite"] textarea#msg{font-family:var(--font-ui)!important;font-size:14px;font-weight:430;letter-spacing:0;line-height:1.45;}
|
||||||
:root[data-skin="graphite"] .msg-body{font-family:var(--font-ui);font-size:13px;font-weight:430;letter-spacing:0;line-height:1.6;}
|
:root[data-skin="graphite"] .msg-body{font-family:var(--font-conversation);font-size:13px;font-weight:430;letter-spacing:0;line-height:1.6;}
|
||||||
:root[data-skin="graphite"][data-font-size="small"] .msg-body{font-size:11.5px;line-height:1.6;}
|
:root[data-skin="graphite"][data-font-size="small"] .msg-body{font-size:11.5px;line-height:1.6;}
|
||||||
:root[data-skin="graphite"][data-font-size="large"] .msg-body{font-size:15px;line-height:1.62;}
|
:root[data-skin="graphite"][data-font-size="large"] .msg-body{font-size:15px;line-height:1.62;}
|
||||||
:root[data-skin="graphite"][data-font-size="xlarge"] .msg-body{font-size:17px;line-height:1.62;}
|
:root[data-skin="graphite"][data-font-size="xlarge"] .msg-body{font-size:17px;line-height:1.62;}
|
||||||
@@ -251,7 +257,7 @@
|
|||||||
:root[data-skin="graphite"] .btn.primary:hover,
|
:root[data-skin="graphite"] .btn.primary:hover,
|
||||||
:root[data-skin="graphite"] .update-primary:hover,
|
:root[data-skin="graphite"] .update-primary:hover,
|
||||||
:root[data-skin="graphite"] .clarify-submit:hover{background:var(--accent-hover)!important;border-color:var(--accent-hover)!important;}
|
:root[data-skin="graphite"] .clarify-submit:hover{background:var(--accent-hover)!important;border-color:var(--accent-hover)!important;}
|
||||||
/* ── Skin: Codex (OpenAI terminal IDE) ──
|
/* ── Skin: Codex (OpenAI terminal IDE) ──
|
||||||
Full palette rewrite with minimal editor surfaces, crisp contrast,
|
Full palette rewrite with minimal editor surfaces, crisp contrast,
|
||||||
and restrained OpenAI green action chrome. Uses native system fonts so it stays local,
|
and restrained OpenAI green action chrome. Uses native system fonts so it stays local,
|
||||||
fast, and license-safe while feeling closer to modern code workbenches. */
|
fast, and license-safe while feeling closer to modern code workbenches. */
|
||||||
@@ -268,7 +274,6 @@
|
|||||||
--user-bubble-bg:#EBEBEB;--user-bubble-border:#D9D9D9;--user-bubble-text:#252523;--user-bubble-placeholder:#6A6A68;
|
--user-bubble-bg:#EBEBEB;--user-bubble-border:#D9D9D9;--user-bubble-text:#252523;--user-bubble-placeholder:#6A6A68;
|
||||||
--user-selection-bg:rgba(46,122,96,0.20);--user-selection-text:#0D0D0D;
|
--user-selection-bg:rgba(46,122,96,0.20);--user-selection-text:#0D0D0D;
|
||||||
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||||
--font-mono:ui-monospace,"SFMono-Regular","SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
|
|
||||||
font-family:var(--font-ui);
|
font-family:var(--font-ui);
|
||||||
}
|
}
|
||||||
:root.dark[data-skin="codex"]{
|
:root.dark[data-skin="codex"]{
|
||||||
@@ -330,7 +335,7 @@
|
|||||||
:root.dark[data-skin="codex"] .composer-box{box-shadow:0 1px 2px rgba(0,0,0,0.28);}
|
:root.dark[data-skin="codex"] .composer-box{box-shadow:0 1px 2px rgba(0,0,0,0.28);}
|
||||||
:root[data-skin="codex"] .composer-box:focus-within{border-color:var(--accent)!important;box-shadow:0 0 0 2px var(--focus-ring)!important;}
|
:root[data-skin="codex"] .composer-box:focus-within{border-color:var(--accent)!important;box-shadow:0 0 0 2px var(--focus-ring)!important;}
|
||||||
:root[data-skin="codex"] textarea#msg{font-family:var(--font-ui)!important;font-size:14px;font-weight:430;letter-spacing:0;line-height:1.45;}
|
:root[data-skin="codex"] textarea#msg{font-family:var(--font-ui)!important;font-size:14px;font-weight:430;letter-spacing:0;line-height:1.45;}
|
||||||
:root[data-skin="codex"] .msg-body{font-family:var(--font-ui);font-size:13px;font-weight:430;letter-spacing:0;line-height:1.6;}
|
:root[data-skin="codex"] .msg-body{font-family:var(--font-conversation);font-size:13px;font-weight:430;letter-spacing:0;line-height:1.6;}
|
||||||
:root[data-skin="codex"][data-font-size="small"] .msg-body{font-size:11.5px;line-height:1.6;}
|
:root[data-skin="codex"][data-font-size="small"] .msg-body{font-size:11.5px;line-height:1.6;}
|
||||||
:root[data-skin="codex"][data-font-size="large"] .msg-body{font-size:15px;line-height:1.62;}
|
:root[data-skin="codex"][data-font-size="large"] .msg-body{font-size:15px;line-height:1.62;}
|
||||||
:root[data-skin="codex"][data-font-size="xlarge"] .msg-body{font-size:17px;line-height:1.62;}
|
:root[data-skin="codex"][data-font-size="xlarge"] .msg-body{font-size:17px;line-height:1.62;}
|
||||||
@@ -395,7 +400,6 @@
|
|||||||
--user-bubble-bg:#F0EEE6;--user-bubble-border:#E2DED2;--user-bubble-text:#30302E;--user-bubble-placeholder:#87867F;
|
--user-bubble-bg:#F0EEE6;--user-bubble-border:#E2DED2;--user-bubble-text:#30302E;--user-bubble-placeholder:#87867F;
|
||||||
--user-selection-bg:rgba(217,119,87,0.24);--user-selection-text:#141413;
|
--user-selection-bg:rgba(217,119,87,0.24);--user-selection-text:#141413;
|
||||||
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||||
--font-mono:ui-monospace,"SFMono-Regular","SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
|
|
||||||
font-family:var(--font-ui);
|
font-family:var(--font-ui);
|
||||||
}
|
}
|
||||||
:root.dark[data-skin="terracotta"]{
|
:root.dark[data-skin="terracotta"]{
|
||||||
@@ -457,7 +461,7 @@
|
|||||||
:root.dark[data-skin="terracotta"] .composer-box{box-shadow:0 1px 2px rgba(0,0,0,0.28);}
|
:root.dark[data-skin="terracotta"] .composer-box{box-shadow:0 1px 2px rgba(0,0,0,0.28);}
|
||||||
:root[data-skin="terracotta"] .composer-box:focus-within{border-color:var(--accent)!important;box-shadow:0 0 0 2px var(--focus-ring)!important;}
|
:root[data-skin="terracotta"] .composer-box:focus-within{border-color:var(--accent)!important;box-shadow:0 0 0 2px var(--focus-ring)!important;}
|
||||||
:root[data-skin="terracotta"] textarea#msg{font-family:var(--font-ui)!important;font-size:14px;font-weight:430;letter-spacing:0;line-height:1.45;}
|
:root[data-skin="terracotta"] textarea#msg{font-family:var(--font-ui)!important;font-size:14px;font-weight:430;letter-spacing:0;line-height:1.45;}
|
||||||
:root[data-skin="terracotta"] .msg-body{font-family:var(--font-ui);font-size:13px;font-weight:430;letter-spacing:0;line-height:1.6;}
|
:root[data-skin="terracotta"] .msg-body{font-family:var(--font-conversation);font-size:13px;font-weight:430;letter-spacing:0;line-height:1.6;}
|
||||||
:root[data-skin="terracotta"][data-font-size="small"] .msg-body{font-size:11.5px;line-height:1.6;}
|
:root[data-skin="terracotta"][data-font-size="small"] .msg-body{font-size:11.5px;line-height:1.6;}
|
||||||
:root[data-skin="terracotta"][data-font-size="large"] .msg-body{font-size:15px;line-height:1.62;}
|
:root[data-skin="terracotta"][data-font-size="large"] .msg-body{font-size:15px;line-height:1.62;}
|
||||||
:root[data-skin="terracotta"][data-font-size="xlarge"] .msg-body{font-size:17px;line-height:1.62;}
|
:root[data-skin="terracotta"][data-font-size="xlarge"] .msg-body{font-size:17px;line-height:1.62;}
|
||||||
@@ -522,7 +526,6 @@
|
|||||||
--user-bubble-bg:#EFEFEF;--user-bubble-border:#DEDEDE;--user-bubble-text:#242424;--user-bubble-placeholder:#707070;
|
--user-bubble-bg:#EFEFEF;--user-bubble-border:#DEDEDE;--user-bubble-text:#242424;--user-bubble-placeholder:#707070;
|
||||||
--user-selection-bg:rgba(0,0,0,0.16);--user-selection-text:#111111;
|
--user-selection-bg:rgba(0,0,0,0.16);--user-selection-text:#111111;
|
||||||
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||||
--font-mono:ui-monospace,"SFMono-Regular","SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
|
|
||||||
font-family:var(--font-ui);
|
font-family:var(--font-ui);
|
||||||
}
|
}
|
||||||
:root.dark[data-skin="github"]{
|
:root.dark[data-skin="github"]{
|
||||||
@@ -584,7 +587,7 @@
|
|||||||
:root.dark[data-skin="github"] .composer-box{box-shadow:0 1px 2px rgba(0,0,0,0.28);}
|
:root.dark[data-skin="github"] .composer-box{box-shadow:0 1px 2px rgba(0,0,0,0.28);}
|
||||||
:root[data-skin="github"] .composer-box:focus-within{border-color:var(--accent)!important;box-shadow:0 0 0 2px var(--focus-ring)!important;}
|
:root[data-skin="github"] .composer-box:focus-within{border-color:var(--accent)!important;box-shadow:0 0 0 2px var(--focus-ring)!important;}
|
||||||
:root[data-skin="github"] textarea#msg{font-family:var(--font-ui)!important;font-size:14px;font-weight:430;letter-spacing:0;line-height:1.45;}
|
:root[data-skin="github"] textarea#msg{font-family:var(--font-ui)!important;font-size:14px;font-weight:430;letter-spacing:0;line-height:1.45;}
|
||||||
:root[data-skin="github"] .msg-body{font-family:var(--font-ui);font-size:13px;font-weight:430;letter-spacing:0;line-height:1.6;}
|
:root[data-skin="github"] .msg-body{font-family:var(--font-conversation);font-size:13px;font-weight:430;letter-spacing:0;line-height:1.6;}
|
||||||
:root[data-skin="github"][data-font-size="small"] .msg-body{font-size:11.5px;line-height:1.6;}
|
:root[data-skin="github"][data-font-size="small"] .msg-body{font-size:11.5px;line-height:1.6;}
|
||||||
:root[data-skin="github"][data-font-size="large"] .msg-body{font-size:15px;line-height:1.62;}
|
:root[data-skin="github"][data-font-size="large"] .msg-body{font-size:15px;line-height:1.62;}
|
||||||
:root[data-skin="github"][data-font-size="xlarge"] .msg-body{font-size:17px;line-height:1.62;}
|
:root[data-skin="github"][data-font-size="xlarge"] .msg-body{font-size:17px;line-height:1.62;}
|
||||||
@@ -1551,7 +1554,7 @@
|
|||||||
.system-health-foot{font-size:11px;color:var(--muted);line-height:1.45;opacity:.82;}
|
.system-health-foot{font-size:11px;color:var(--muted);line-height:1.45;opacity:.82;}
|
||||||
.app-titlebar-icon{display:inline-flex;align-items:center;color:var(--accent);}
|
.app-titlebar-icon{display:inline-flex;align-items:center;color:var(--accent);}
|
||||||
.app-titlebar-title{font-size:12px;font-weight:600;color:var(--text);letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:60vw;}
|
.app-titlebar-title{font-size:12px;font-weight:600;color:var(--text);letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:60vw;}
|
||||||
.app-titlebar-sub{font-size:10px;color:var(--muted);background:var(--hover-bg);padding:2px 7px;border-radius:4px;font-family:'SF Mono',ui-monospace,monospace;white-space:nowrap;flex-shrink:0;}
|
.app-titlebar-sub{font-size:10px;color:var(--muted);background:var(--hover-bg);padding:2px 7px;border-radius:4px;font-family:var(--font-mono);white-space:nowrap;flex-shrink:0;}
|
||||||
.app-titlebar-sub[hidden]{display:none;}
|
.app-titlebar-sub[hidden]{display:none;}
|
||||||
.app-titlebar-title-popover{position:fixed;z-index:300;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-size:13px;color:var(--text);box-shadow:0 4px 16px rgba(0,0,0,.18);word-break:break-word;line-height:1.4;max-width:calc(100vw - 16px);pointer-events:auto;}
|
.app-titlebar-title-popover{position:fixed;z-index:300;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-size:13px;color:var(--text);box-shadow:0 4px 16px rgba(0,0,0,.18);word-break:break-word;line-height:1.4;max-width:calc(100vw - 16px);pointer-events:auto;}
|
||||||
.app-titlebar-hamburger,.app-titlebar-spacer{display:none;width:44px;height:44px;flex-shrink:0;}
|
.app-titlebar-hamburger,.app-titlebar-spacer{display:none;width:44px;height:44px;flex-shrink:0;}
|
||||||
@@ -1991,7 +1994,7 @@
|
|||||||
.approval-card.collapsed .approval-header{margin-bottom:0;}
|
.approval-card.collapsed .approval-header{margin-bottom:0;}
|
||||||
.approval-card.collapsed .approval-desc,.approval-card.collapsed .approval-cmd,.approval-card.collapsed .approval-counter,.approval-card.collapsed .approval-btns{display:none;}
|
.approval-card.collapsed .approval-desc,.approval-card.collapsed .approval-cmd,.approval-card.collapsed .approval-counter,.approval-card.collapsed .approval-btns{display:none;}
|
||||||
.approval-desc{font-size:12px;color:var(--muted);margin-bottom:8px;line-height:1.5;}
|
.approval-desc{font-size:12px;color:var(--muted);margin-bottom:8px;line-height:1.5;}
|
||||||
.approval-cmd{background:var(--code-bg);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-family:"SF Mono",ui-monospace,monospace;font-size:12px;color:var(--pre-text);white-space:pre-wrap;word-break:break-all;margin-bottom:14px;max-height:120px;overflow-y:auto;}
|
.approval-cmd{background:var(--code-bg);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-family:var(--font-mono);font-size:12px;color:var(--pre-text);white-space:pre-wrap;word-break:break-all;margin-bottom:14px;max-height:120px;overflow-y:auto;}
|
||||||
.approval-counter{font-size:11px;color:var(--muted);margin:0 0 12px;font-variant-numeric:tabular-nums;letter-spacing:0.01em;}
|
.approval-counter{font-size:11px;color:var(--muted);margin:0 0 12px;font-variant-numeric:tabular-nums;letter-spacing:0.01em;}
|
||||||
.approval-btns{display:flex;gap:8px;flex-wrap:wrap;align-items:center;}
|
.approval-btns{display:flex;gap:8px;flex-wrap:wrap;align-items:center;}
|
||||||
.approval-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:8px;font-size:12px;font-weight:600;border:1px solid var(--border2);background:var(--hover-bg);color:var(--text);cursor:pointer;transition:all .15s;white-space:nowrap;}
|
.approval-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:8px;font-size:12px;font-weight:600;border:1px solid var(--border2);background:var(--hover-bg);color:var(--text);cursor:pointer;transition:all .15s;white-space:nowrap;}
|
||||||
@@ -2301,7 +2304,7 @@
|
|||||||
.role-icon{width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;flex-shrink:0;}
|
.role-icon{width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;flex-shrink:0;}
|
||||||
.role-icon.user{background:var(--accent-bg);color:var(--accent-text);border:1px solid var(--accent-bg-strong);}
|
.role-icon.user{background:var(--accent-bg);color:var(--accent-text);border:1px solid var(--accent-bg-strong);}
|
||||||
.role-icon.assistant{background:var(--accent-bg-strong);color:var(--accent-text);border:1px solid var(--accent-bg-strong);}
|
.role-icon.assistant{background:var(--accent-bg-strong);color:var(--accent-text);border:1px solid var(--accent-bg-strong);}
|
||||||
.msg-body{font-size:var(--message-body-font-size);line-height:var(--message-body-line-height);color:var(--text);padding-left:30px;max-width:680px;overflow-wrap:anywhere;}
|
.msg-body{font-family:var(--font-conversation);font-size:var(--message-body-font-size);line-height:var(--message-body-line-height);color:var(--text);padding-left:30px;max-width:680px;overflow-wrap:anywhere;}
|
||||||
.msg-body p{margin-bottom:10px;}.msg-body p:last-child{margin-bottom:0;}
|
.msg-body p{margin-bottom:10px;}.msg-body p:last-child{margin-bottom:0;}
|
||||||
.msg-body td p,.msg-body th p{margin:0;}
|
.msg-body td p,.msg-body th p{margin:0;}
|
||||||
.msg-body ul,.msg-body ol{margin:6px 0 10px 20px;}.msg-body li{margin-bottom:3px;}
|
.msg-body ul,.msg-body ol{margin:6px 0 10px 20px;}.msg-body li{margin-bottom:3px;}
|
||||||
@@ -2317,7 +2320,7 @@
|
|||||||
.msg-body > h1:first-child,.msg-body > h2:first-child,.msg-body > h3:first-child,
|
.msg-body > h1:first-child,.msg-body > h2:first-child,.msg-body > h3:first-child,
|
||||||
.msg-body > h4:first-child,.msg-body > h5:first-child,.msg-body > h6:first-child{margin-top:0;}
|
.msg-body > h4:first-child,.msg-body > h5:first-child,.msg-body > h6:first-child{margin-top:0;}
|
||||||
.msg-body strong{color:var(--strong);font-weight:600;}.msg-body em{color:var(--em);font-style:italic;}
|
.msg-body strong{color:var(--strong);font-weight:600;}.msg-body em{color:var(--em);font-style:italic;}
|
||||||
.msg-body code{font-family:"SF Mono","Fira Code",ui-monospace,monospace;font-size:var(--message-code-font-size);background:var(--code-inline-bg);padding:1px 5px;border-radius:4px;color:var(--code-text);}
|
.msg-body code{font-family:var(--font-mono);font-size:var(--message-code-font-size);background:var(--code-inline-bg);padding:1px 5px;border-radius:4px;color:var(--code-text);}
|
||||||
.msg-body pre{background:var(--code-bg);border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow-x:auto;contain:content;margin:10px 0;}
|
.msg-body pre{background:var(--code-bg);border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow-x:auto;contain:content;margin:10px 0;}
|
||||||
.msg-body pre code{background:none;padding:0;border-radius:0;color:var(--pre-text);font-size:var(--message-pre-code-font-size);line-height:1.6;}
|
.msg-body pre code{background:none;padding:0;border-radius:0;color:var(--pre-text);font-size:var(--message-pre-code-font-size);line-height:1.6;}
|
||||||
.msg-body pre.md-source-block,.preview-md pre.md-source-block{white-space:pre-wrap;overflow-x:hidden;overflow-wrap:anywhere;}
|
.msg-body pre.md-source-block,.preview-md pre.md-source-block{white-space:pre-wrap;overflow-x:hidden;overflow-wrap:anywhere;}
|
||||||
@@ -2385,7 +2388,7 @@
|
|||||||
.diff-inline-error{color:#ef4444;font-size:13px;padding:8px 12px;border:1px solid rgba(239,68,68,.2);border-radius:8px;margin:6px 0;}
|
.diff-inline-error{color:#ef4444;font-size:13px;padding:8px 12px;border:1px solid rgba(239,68,68,.2);border-radius:8px;margin:6px 0;}
|
||||||
/* JSON/YAML tree viewer */
|
/* JSON/YAML tree viewer */
|
||||||
.code-tree-wrap{position:relative;}
|
.code-tree-wrap{position:relative;}
|
||||||
.tree-view{padding:4px 0;font-family:'JetBrains Mono',monospace;font-size:13px;}
|
.tree-view{padding:4px 0;font-family:var(--font-mono);font-size:13px;}
|
||||||
.tree-hidden{display:none;}
|
.tree-hidden{display:none;}
|
||||||
.tree-toggle-btn{background:none;border:1px solid var(--border);border-radius:4px;padding:1px 8px;font-size:10px;color:var(--muted);cursor:pointer;font-weight:600;}
|
.tree-toggle-btn{background:none;border:1px solid var(--border);border-radius:4px;padding:1px 8px;font-size:10px;color:var(--muted);cursor:pointer;font-weight:600;}
|
||||||
.tree-toggle-btn:hover{color:var(--text);border-color:var(--muted);}
|
.tree-toggle-btn:hover{color:var(--text);border-color:var(--muted);}
|
||||||
@@ -2818,7 +2821,7 @@
|
|||||||
.workspace-panel-heading.workspace-panel-heading--enabled{cursor:pointer;}
|
.workspace-panel-heading.workspace-panel-heading--enabled{cursor:pointer;}
|
||||||
.workspace-panel-heading.workspace-panel-heading--enabled:hover,
|
.workspace-panel-heading.workspace-panel-heading--enabled:hover,
|
||||||
.workspace-panel-heading.workspace-panel-heading--enabled:focus-visible{color:var(--text);background:var(--hover-bg);outline:none;}
|
.workspace-panel-heading.workspace-panel-heading--enabled:focus-visible{color:var(--text);background:var(--hover-bg);outline:none;}
|
||||||
.git-badge{grid-column:1 / -1;grid-row:2;justify-self:start;font-size:9px;font-weight:600;color:var(--muted);background:var(--hover-bg);padding:2px 7px;border-radius:4px;letter-spacing:.02em;white-space:nowrap;font-family:'SF Mono',ui-monospace,monospace;overflow:hidden;min-width:0;max-width:100%;}
|
.git-badge{grid-column:1 / -1;grid-row:2;justify-self:start;font-size:9px;font-weight:600;color:var(--muted);background:var(--hover-bg);padding:2px 7px;border-radius:4px;letter-spacing:.02em;white-space:nowrap;font-family:var(--font-mono);overflow:hidden;min-width:0;max-width:100%;}
|
||||||
.topbar-source-badge{display:inline-flex;align-items:center;margin-left:6px;padding:2px 7px;border-radius:999px;background:var(--accent-bg);color:var(--accent-text);font-size:10px;font-weight:700;letter-spacing:.02em;vertical-align:middle;}
|
.topbar-source-badge{display:inline-flex;align-items:center;margin-left:6px;padding:2px 7px;border-radius:999px;background:var(--accent-bg);color:var(--accent-text);font-size:10px;font-weight:700;letter-spacing:.02em;vertical-align:middle;}
|
||||||
.git-badge.dirty{color:var(--accent-text);background:var(--accent-bg);}
|
.git-badge.dirty{color:var(--accent-text);background:var(--accent-bg);}
|
||||||
.panel-actions{grid-column:2;grid-row:1;display:flex;gap:4px;flex-shrink:0;margin-left:auto;}
|
.panel-actions{grid-column:2;grid-row:1;display:flex;gap:4px;flex-shrink:0;margin-left:auto;}
|
||||||
@@ -2897,7 +2900,7 @@
|
|||||||
.preview-area{flex:1;overflow:auto;padding:14px;flex-direction:column;gap:8px;display:none;opacity:0;transition:opacity .15s;}
|
.preview-area{flex:1;overflow:auto;padding:14px;flex-direction:column;gap:8px;display:none;opacity:0;transition:opacity .15s;}
|
||||||
.preview-area.visible{display:flex;opacity:1;}
|
.preview-area.visible{display:flex;opacity:1;}
|
||||||
.preview-path{font-size:11px;color:var(--muted);padding-bottom:8px;border-bottom:1px solid var(--border);flex-shrink:0;}
|
.preview-path{font-size:11px;color:var(--muted);padding-bottom:8px;border-bottom:1px solid var(--border);flex-shrink:0;}
|
||||||
.preview-code{font-family:"SF Mono","Fira Code",ui-monospace,monospace;font-size:12px;line-height:1.6;white-space:pre-wrap;word-break:break-word;color:var(--pre-text);}
|
.preview-code{font-family:var(--font-mono);font-size:12px;line-height:1.6;white-space:pre-wrap;word-break:break-word;color:var(--pre-text);}
|
||||||
/* Workspace preview syntax highlighting (#3337): Prism's prism-tomorrow theme
|
/* Workspace preview syntax highlighting (#3337): Prism's prism-tomorrow theme
|
||||||
styles BOTH pre[class*="language-"] and code[class*="language-"] with its own
|
styles BOTH pre[class*="language-"] and code[class*="language-"] with its own
|
||||||
gray background + padding. Prism propagates the language-* class onto the
|
gray background + padding. Prism propagates the language-* class onto the
|
||||||
@@ -2925,7 +2928,7 @@
|
|||||||
.preview-md h5{font-size:14px;margin:16px 0 6px;text-transform:uppercase;letter-spacing:0.04em;}
|
.preview-md h5{font-size:14px;margin:16px 0 6px;text-transform:uppercase;letter-spacing:0.04em;}
|
||||||
.preview-md h6{font-size:13px;margin:14px 0 6px;color:var(--muted);text-transform:uppercase;letter-spacing:0.04em;}
|
.preview-md h6{font-size:13px;margin:14px 0 6px;color:var(--muted);text-transform:uppercase;letter-spacing:0.04em;}
|
||||||
.preview-md ul,.preview-md ol{margin:4px 0 10px 18px;}.preview-md li{margin-bottom:3px;}
|
.preview-md ul,.preview-md ol{margin:4px 0 10px 18px;}.preview-md li{margin-bottom:3px;}
|
||||||
.preview-md code{font-family:"SF Mono",ui-monospace,monospace;font-size:11.5px;background:var(--code-inline-bg);padding:1px 5px;border-radius:4px;color:var(--code-text);}
|
.preview-md code{font-family:var(--font-mono);font-size:11.5px;background:var(--code-inline-bg);padding:1px 5px;border-radius:4px;color:var(--code-text);}
|
||||||
.preview-md pre{background:var(--code-bg);border:1px solid var(--border);border-radius:8px;padding:10px 12px;overflow-x:auto;margin:8px 0;}
|
.preview-md pre{background:var(--code-bg);border:1px solid var(--border);border-radius:8px;padding:10px 12px;overflow-x:auto;margin:8px 0;}
|
||||||
.preview-md pre code{background:none;padding:0;color:var(--pre-text);font-size:11.5px;line-height:1.55;}
|
.preview-md pre code{background:none;padding:0;color:var(--pre-text);font-size:11.5px;line-height:1.55;}
|
||||||
/* Keep original theme background — prevent prism-tomorrow from overriding --code-bg */
|
/* Keep original theme background — prevent prism-tomorrow from overriding --code-bg */
|
||||||
@@ -3398,7 +3401,7 @@
|
|||||||
.cmd-item-name{font-size:13px;color:var(--text);font-weight:500;}
|
.cmd-item-name{font-size:13px;color:var(--text);font-weight:500;}
|
||||||
.cmd-item-parent{color:var(--muted);font-weight:400;}
|
.cmd-item-parent{color:var(--muted);font-weight:400;}
|
||||||
.cmd-item-subarg{font-weight:600;}
|
.cmd-item-subarg{font-weight:600;}
|
||||||
.cmd-item-path-value{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;font-size:12px;}
|
.cmd-item-path-value{font-family:var(--font-mono);font-size:12px;}
|
||||||
.cmd-item-arg{color:var(--muted);font-weight:400;font-style:italic;}
|
.cmd-item-arg{color:var(--muted);font-weight:400;font-style:italic;}
|
||||||
.cmd-item-desc{font-size:11px;color:var(--muted);margin-top:1px;}
|
.cmd-item-desc{font-size:11px;color:var(--muted);margin-top:1px;}
|
||||||
.cmd-item-badge{flex-shrink:0;font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding:2px 6px;border-radius:999px;border:1px solid var(--border2);color:var(--muted);background:var(--hover-bg);}
|
.cmd-item-badge{flex-shrink:0;font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding:2px 6px;border-radius:999px;border:1px solid var(--border2);color:var(--muted);background:var(--hover-bg);}
|
||||||
@@ -3437,7 +3440,7 @@ body.tts-enabled .msg-tts-btn{display:inline-flex;align-items:center;}
|
|||||||
@keyframes tts-pulse{0%,100%{opacity:1}50%{opacity:.5}}
|
@keyframes tts-pulse{0%,100%{opacity:1}50%{opacity:.5}}
|
||||||
|
|
||||||
/* ── Edit message inline ── */
|
/* ── Edit message inline ── */
|
||||||
.msg-edit-area{width:100%;background:rgba(255,255,255,.05);border:1px solid var(--accent-bg);border-radius:8px;color:var(--text);padding:10px 12px;font-size:14px;font-family:inherit;line-height:1.6;resize:none;outline:none;min-height:60px;box-sizing:border-box;box-shadow:0 0 0 3px var(--accent-bg);margin-top:4px;}
|
.msg-edit-area{width:100%;background:rgba(255,255,255,.05);border:1px solid var(--accent-bg);border-radius:8px;color:var(--text);padding:10px 12px;font-size:14px;font-family:var(--font-conversation);line-height:1.6;resize:none;outline:none;min-height:60px;box-sizing:border-box;box-shadow:0 0 0 3px var(--accent-bg);margin-top:4px;}
|
||||||
.msg-edit-bar{display:flex;gap:8px;margin-top:8px;margin-bottom:4px;}
|
.msg-edit-bar{display:flex;gap:8px;margin-top:8px;margin-bottom:4px;}
|
||||||
.msg-edit-send{background:var(--accent);color:#fff;border:none;border-radius:8px;padding:6px 16px;font-size:13px;font-weight:600;cursor:pointer;transition:opacity .15s;}
|
.msg-edit-send{background:var(--accent);color:#fff;border:none;border-radius:8px;padding:6px 16px;font-size:13px;font-weight:600;cursor:pointer;transition:opacity .15s;}
|
||||||
.msg-edit-send:hover{opacity:.85;}
|
.msg-edit-send:hover{opacity:.85;}
|
||||||
@@ -3742,7 +3745,7 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
.tool-worklog-group[data-tool-worklog-group="1"]:not([data-run-activity-group="1"]) .as-dot{
|
.tool-worklog-group[data-tool-worklog-group="1"]:not([data-run-activity-group="1"]) .as-dot{
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
.tool-worklog-running-label{display:none;color:var(--accent-text);font-family:'SF Mono',ui-monospace,monospace;font-size:12px;font-weight:600;line-height:1.4;}
|
.tool-worklog-running-label{display:none;color:var(--accent-text);font-family:var(--font-mono);font-size:12px;font-weight:600;line-height:1.4;}
|
||||||
.tool-worklog-group[data-tool-worklog-running="1"] .tool-worklog-running-label{display:inline-flex;}
|
.tool-worklog-group[data-tool-worklog-running="1"] .tool-worklog-running-label{display:inline-flex;}
|
||||||
.tool-worklog-body{
|
.tool-worklog-body{
|
||||||
padding-left:0;
|
padding-left:0;
|
||||||
@@ -3971,7 +3974,7 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
.tool-worklog-tool-group-label,
|
.tool-worklog-tool-group-label,
|
||||||
.tg-sum{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
.tg-sum{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
||||||
.tool-worklog-tool-group-running,
|
.tool-worklog-tool-group-running,
|
||||||
.tg-run{display:none;color:var(--accent-text);font-family:'SF Mono',ui-monospace,monospace;font-size:12px;font-weight:600;line-height:1.4;white-space:nowrap;}
|
.tg-run{display:none;color:var(--accent-text);font-family:var(--font-mono);font-size:12px;font-weight:600;line-height:1.4;white-space:nowrap;}
|
||||||
.tool-worklog-tool-group-body,
|
.tool-worklog-tool-group-body,
|
||||||
.tool-group-body{overflow:hidden;max-height:0;transition:max-height .3s ease;}
|
.tool-group-body{overflow:hidden;max-height:0;transition:max-height .3s ease;}
|
||||||
.tool-worklog-tool-group.open .tool-worklog-tool-group-body,
|
.tool-worklog-tool-group.open .tool-worklog-tool-group-body,
|
||||||
@@ -4055,9 +4058,9 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
.tool-card-detail-lead pre{margin:0;color:var(--text);font-family:var(--font-mono);font-size:var(--message-code-font-size);line-height:1.55;white-space:pre-wrap;word-break:break-word;}
|
.tool-card-detail-lead pre{margin:0;color:var(--text);font-family:var(--font-mono);font-size:var(--message-code-font-size);line-height:1.55;white-space:pre-wrap;word-break:break-word;}
|
||||||
.tool-card-args{margin-bottom:6px;}
|
.tool-card-args{margin-bottom:6px;}
|
||||||
.tool-card-args div{font-size:var(--message-code-font-size);line-height:1.6;}
|
.tool-card-args div{font-size:var(--message-code-font-size);line-height:1.6;}
|
||||||
.tool-arg-key{color:var(--blue);font-family:'SF Mono',ui-monospace,monospace;font-size:var(--message-code-font-size);display:block;margin-bottom:2px;}
|
.tool-arg-key{color:var(--blue);font-family:var(--font-mono);font-size:var(--message-code-font-size);display:block;margin-bottom:2px;}
|
||||||
.tool-arg-val{color:var(--muted);font-family:'SF Mono',ui-monospace,monospace;font-size:var(--message-code-font-size);white-space:pre-wrap;word-break:break-word;display:block;overflow-x:auto;}
|
.tool-arg-val{color:var(--muted);font-family:var(--font-mono);font-size:var(--message-code-font-size);white-space:pre-wrap;word-break:break-word;display:block;overflow-x:auto;}
|
||||||
.tool-card-result pre{font-size:var(--message-code-font-size);color:var(--muted);font-family:'SF Mono',ui-monospace,monospace;white-space:pre-wrap;word-break:break-word;max-height:240px;overflow-y:auto;margin:0;line-height:1.55;width:100%;max-width:100%;box-sizing:border-box;min-width:0;}
|
.tool-card-result pre{font-size:var(--message-code-font-size);color:var(--muted);font-family:var(--font-mono);white-space:pre-wrap;word-break:break-word;max-height:240px;overflow-y:auto;margin:0;line-height:1.55;width:100%;max-width:100%;box-sizing:border-box;min-width:0;}
|
||||||
|
|
||||||
/* ── Transparent Stream event rows ── */
|
/* ── Transparent Stream event rows ── */
|
||||||
/* Inline rhythm: transparent stream events are transcript metadata inside an
|
/* Inline rhythm: transparent stream events are transcript metadata inside an
|
||||||
@@ -4623,7 +4626,7 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
color:var(--strong);
|
color:var(--strong);
|
||||||
}
|
}
|
||||||
.handoff-summary-body code{
|
.handoff-summary-body code{
|
||||||
font-family:'SF Mono',ui-monospace,monospace;
|
font-family:var(--font-mono);
|
||||||
font-size:.92em;
|
font-size:.92em;
|
||||||
background:var(--code-inline-bg);
|
background:var(--code-inline-bg);
|
||||||
color:var(--code-text);
|
color:var(--code-text);
|
||||||
@@ -4678,7 +4681,7 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
.compression-card-label{
|
.compression-card-label{
|
||||||
font-size:11px;
|
font-size:11px;
|
||||||
font-weight:700;
|
font-weight:700;
|
||||||
font-family:'SF Mono',ui-monospace,monospace;
|
font-family:var(--font-mono);
|
||||||
letter-spacing:.03em;
|
letter-spacing:.03em;
|
||||||
color:var(--muted);
|
color:var(--muted);
|
||||||
flex-shrink:0;
|
flex-shrink:0;
|
||||||
@@ -4686,7 +4689,7 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
.compression-card-command{
|
.compression-card-command{
|
||||||
font-size:11px;
|
font-size:11px;
|
||||||
color:var(--text);
|
color:var(--text);
|
||||||
font-family:'SF Mono',ui-monospace,monospace;
|
font-family:var(--font-mono);
|
||||||
overflow:hidden;
|
overflow:hidden;
|
||||||
text-overflow:ellipsis;
|
text-overflow:ellipsis;
|
||||||
white-space:nowrap;
|
white-space:nowrap;
|
||||||
@@ -4724,7 +4727,7 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
}
|
}
|
||||||
.compression-card-focus .compression-card-focus-label{
|
.compression-card-focus .compression-card-focus-label{
|
||||||
color:var(--muted);
|
color:var(--muted);
|
||||||
font-family:'SF Mono',ui-monospace,monospace;
|
font-family:var(--font-mono);
|
||||||
}
|
}
|
||||||
.compression-card-error-body{
|
.compression-card-error-body{
|
||||||
color:#fecaca;
|
color:#fecaca;
|
||||||
@@ -4760,7 +4763,7 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
.compression-reference-kind{
|
.compression-reference-kind{
|
||||||
font-size:11px;
|
font-size:11px;
|
||||||
font-weight:700;
|
font-weight:700;
|
||||||
font-family:'SF Mono',ui-monospace,monospace;
|
font-family:var(--font-mono);
|
||||||
letter-spacing:.03em;
|
letter-spacing:.03em;
|
||||||
flex-shrink:0;
|
flex-shrink:0;
|
||||||
white-space:nowrap;
|
white-space:nowrap;
|
||||||
@@ -4812,7 +4815,7 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
}
|
}
|
||||||
.compression-reference-body pre{
|
.compression-reference-body pre{
|
||||||
margin:0;
|
margin:0;
|
||||||
font-family:'SF Mono',ui-monospace,monospace;
|
font-family:var(--font-mono);
|
||||||
font-size:11px;
|
font-size:11px;
|
||||||
line-height:1.55;
|
line-height:1.55;
|
||||||
white-space:pre-wrap;
|
white-space:pre-wrap;
|
||||||
@@ -4850,7 +4853,7 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
.compression-complete-label{
|
.compression-complete-label{
|
||||||
font-size:11px;
|
font-size:11px;
|
||||||
font-weight:700;
|
font-weight:700;
|
||||||
font-family:'SF Mono',ui-monospace,monospace;
|
font-family:var(--font-mono);
|
||||||
letter-spacing:.03em;
|
letter-spacing:.03em;
|
||||||
flex-shrink:0;
|
flex-shrink:0;
|
||||||
}
|
}
|
||||||
@@ -4861,7 +4864,7 @@ body.resizing .sidebar{transition:none!important;}
|
|||||||
text-overflow:ellipsis;
|
text-overflow:ellipsis;
|
||||||
white-space:nowrap;
|
white-space:nowrap;
|
||||||
color:var(--text);
|
color:var(--text);
|
||||||
font-family:'SF Mono',ui-monospace,monospace;
|
font-family:var(--font-mono);
|
||||||
font-size:11px;
|
font-size:11px;
|
||||||
}
|
}
|
||||||
.compression-complete-toggle{
|
.compression-complete-toggle{
|
||||||
@@ -4969,7 +4972,7 @@ main.main > #mainPlugin{display:none;}
|
|||||||
.settings-section-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:20px;padding-bottom:14px;border-bottom:1px solid var(--border);}
|
.settings-section-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:20px;padding-bottom:14px;border-bottom:1px solid var(--border);}
|
||||||
.settings-section-title{font-size:18px;font-weight:600;letter-spacing:-.01em;color:var(--text);line-height:1.3;margin-bottom:4px;}
|
.settings-section-title{font-size:18px;font-weight:600;letter-spacing:-.01em;color:var(--text);line-height:1.3;margin-bottom:4px;}
|
||||||
.settings-section-meta{font-size:13px;color:var(--muted);line-height:1.55;}
|
.settings-section-meta{font-size:13px;color:var(--muted);line-height:1.55;}
|
||||||
.settings-version-badge{display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;background:var(--surface);color:var(--muted);font-size:11px;font-weight:600;font-family:'SF Mono',ui-monospace,SFMono-Regular,Menlo,monospace;flex-shrink:0;align-self:flex-start;letter-spacing:.02em;}
|
.settings-version-badge{display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;background:var(--surface);color:var(--muted);font-size:11px;font-weight:600;font-family:var(--font-mono);flex-shrink:0;align-self:flex-start;letter-spacing:.02em;}
|
||||||
#checkUpdatesBlock{display:inline-flex;align-items:center;gap:0.5em;font-size:12px;flex-shrink:0;align-self:flex-start;flex-wrap:wrap;}
|
#checkUpdatesBlock{display:inline-flex;align-items:center;gap:0.5em;font-size:12px;flex-shrink:0;align-self:flex-start;flex-wrap:wrap;}
|
||||||
#checkUpdatesBlock .btn-tiny{padding:4px 10px;border-radius:8px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:11px;font-weight:500;cursor:pointer;display:inline-flex;align-items:center;gap:5px;transition:border-color .15s,color .15s;}
|
#checkUpdatesBlock .btn-tiny{padding:4px 10px;border-radius:8px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:11px;font-weight:500;cursor:pointer;display:inline-flex;align-items:center;gap:5px;transition:border-color .15s,color .15s;}
|
||||||
#checkUpdatesBlock .btn-tiny:hover{border-color:var(--accent);color:var(--accent);}
|
#checkUpdatesBlock .btn-tiny:hover{border-color:var(--accent);color:var(--accent);}
|
||||||
@@ -5382,7 +5385,7 @@ main.main > #mainPlugin{display:none;}
|
|||||||
border:1px solid var(--border);
|
border:1px solid var(--border);
|
||||||
border-radius:8px;
|
border-radius:8px;
|
||||||
font-size:12px;
|
font-size:12px;
|
||||||
font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
|
font-family:var(--font-mono);
|
||||||
outline:none;
|
outline:none;
|
||||||
transition:border-color .15s ease, box-shadow .15s ease;
|
transition:border-color .15s ease, box-shadow .15s ease;
|
||||||
}
|
}
|
||||||
@@ -5569,7 +5572,7 @@ main.main > #mainPlugin{display:none;}
|
|||||||
.provider-card-model-tag{
|
.provider-card-model-tag{
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
padding:2px 8px;
|
padding:2px 8px;
|
||||||
font-size:10.5px;font-family:ui-monospace,SFMono-Regular,\"SF Mono\",Menlo,Consolas,monospace;
|
font-size:10.5px;font-family:var(--font-mono);
|
||||||
color:var(--muted);
|
color:var(--muted);
|
||||||
background:var(--surface);
|
background:var(--surface);
|
||||||
border:1px solid var(--border);
|
border:1px solid var(--border);
|
||||||
@@ -5640,7 +5643,7 @@ main.main > #mainPlugin{display:none;}
|
|||||||
.cron-script-badge{flex-shrink:0;font-size:12px;line-height:1;opacity:.92;}
|
.cron-script-badge{flex-shrink:0;font-size:12px;line-height:1;opacity:.92;}
|
||||||
.cron-script-job-banner{margin-bottom:12px;}
|
.cron-script-job-banner{margin-bottom:12px;}
|
||||||
.cron-script-job-banner .detail-alert-title{font-weight:600;color:rgba(245,158,11,.98);}
|
.cron-script-job-banner .detail-alert-title{font-weight:600;color:rgba(245,158,11,.98);}
|
||||||
.detail-script{background:var(--sidebar);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-size:12px;white-space:pre-wrap;line-height:1.55;color:var(--text);font-family:'SF Mono',ui-monospace,monospace;word-break:break-word;}
|
.detail-script{background:var(--sidebar);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-size:12px;white-space:pre-wrap;line-height:1.55;color:var(--text);font-family:var(--font-mono);word-break:break-word;}
|
||||||
.detail-hint{font-size:11px;color:var(--muted);line-height:1.5;}
|
.detail-hint{font-size:11px;color:var(--muted);line-height:1.5;}
|
||||||
.cron-script-card .detail-hint,.cron-script-card-hint{margin-top:8px;}
|
.cron-script-card .detail-hint,.cron-script-card-hint{margin-top:8px;}
|
||||||
.detail-badge.cron-mode-badge.script{background:rgba(148,163,184,.14);color:var(--text-secondary);}
|
.detail-badge.cron-mode-badge.script{background:rgba(148,163,184,.14);color:var(--text-secondary);}
|
||||||
@@ -5949,7 +5952,8 @@ main.main > #mainPlugin{display:none;}
|
|||||||
.msg-body { padding-left: var(--msg-rail); padding-top: 8px; max-width: var(--msg-max); }
|
.msg-body { padding-left: var(--msg-rail); padding-top: 8px; max-width: var(--msg-max); }
|
||||||
.msg-body code,
|
.msg-body code,
|
||||||
.msg-body pre,
|
.msg-body pre,
|
||||||
.msg-body table { font-family: 'SF Mono', ui-monospace, monospace; }
|
.msg-body table { font-family:var(--font-mono); }
|
||||||
|
body code[class*="language-"],body pre[class*="language-"],body pre code[class*="language-"]{font-family:var(--font-mono);}
|
||||||
.msg-body:empty { display: none; }
|
.msg-body:empty { display: none; }
|
||||||
.assistant-turn { width: 100%; }
|
.assistant-turn { width: 100%; }
|
||||||
.assistant-turn-blocks { display: flex; flex-direction: column; }
|
.assistant-turn-blocks { display: flex; flex-direction: column; }
|
||||||
@@ -6279,7 +6283,7 @@ main.main > #mainPlugin{display:none;}
|
|||||||
transition: max-height .22s ease, opacity .18s ease, padding .22s ease, border-top-color .22s ease;
|
transition: max-height .22s ease, opacity .18s ease, padding .22s ease, border-top-color .22s ease;
|
||||||
}
|
}
|
||||||
.thinking-card.open .thinking-card-body { max-height: 260px; overflow-y: auto; opacity: 1; padding: 8px 12px; border-top-color: var(--accent-bg-strong); }
|
.thinking-card.open .thinking-card-body { max-height: 260px; overflow-y: auto; opacity: 1; padding: 8px 12px; border-top-color: var(--accent-bg-strong); }
|
||||||
.thinking-card-body pre { font-family: 'SF Mono', ui-monospace, monospace; font-size: 11px; line-height: 1.6; color: var(--muted); white-space: pre-wrap; word-break: break-word; margin: 0; }
|
.thinking-card-body pre { font-family:var(--font-mono); font-size: 11px; line-height: 1.6; color: var(--muted); white-space: pre-wrap; word-break: break-word; margin: 0; }
|
||||||
|
|
||||||
/* ── Tool cards: quiet disclosure rows ── */
|
/* ── Tool cards: quiet disclosure rows ── */
|
||||||
.tool-card { background:transparent;border:0;border-left:0;border-radius:0;margin-top:0;margin-bottom:0; }
|
.tool-card { background:transparent;border:0;border-left:0;border-radius:0;margin-top:0;margin-bottom:0; }
|
||||||
@@ -6417,7 +6421,7 @@ main.main > .main-view:not([id="mainChat"]):not([id="mainSettings"]) .main-view-
|
|||||||
.skill-file-breadcrumb{display:flex;align-items:center;gap:12px;margin-bottom:16px;padding-bottom:10px;border-bottom:1px solid var(--border-subtle,var(--border));}
|
.skill-file-breadcrumb{display:flex;align-items:center;gap:12px;margin-bottom:16px;padding-bottom:10px;border-bottom:1px solid var(--border-subtle,var(--border));}
|
||||||
.skill-file-back{font-size:12px;color:var(--accent-text);text-decoration:none;font-weight:500;}
|
.skill-file-back{font-size:12px;color:var(--accent-text);text-decoration:none;font-weight:500;}
|
||||||
.skill-file-back:hover{text-decoration:underline;}
|
.skill-file-back:hover{text-decoration:underline;}
|
||||||
.skill-file-path{font-size:12px;color:var(--muted);font-family:'SF Mono',ui-monospace,monospace;}
|
.skill-file-path{font-size:12px;color:var(--muted);font-family:var(--font-mono);}
|
||||||
.skill-file-code{background:var(--sidebar);border:1px solid var(--border);border-radius:8px;padding:12px 14px;font-size:12px;overflow:auto;max-width:960px;margin:0 auto;}
|
.skill-file-code{background:var(--sidebar);border:1px solid var(--border);border-radius:8px;padding:12px 14px;font-size:12px;overflow:auto;max-width:960px;margin:0 auto;}
|
||||||
|
|
||||||
@media(max-width:700px){
|
@media(max-width:700px){
|
||||||
@@ -6432,7 +6436,7 @@ main.main > .main-view:not([id="mainChat"]):not([id="mainSettings"]) .main-view-
|
|||||||
.detail-row:last-child{border-bottom:none;}
|
.detail-row:last-child{border-bottom:none;}
|
||||||
.detail-row-label{flex:0 0 140px;color:var(--muted);font-size:12px;}
|
.detail-row-label{flex:0 0 140px;color:var(--muted);font-size:12px;}
|
||||||
.detail-row-value{flex:1;min-width:0;color:var(--text);word-break:break-word;}
|
.detail-row-value{flex:1;min-width:0;color:var(--text);word-break:break-word;}
|
||||||
.detail-row-value code{background:var(--sidebar);border:1px solid var(--border);border-radius:4px;padding:1px 6px;font-size:12px;font-family:'SF Mono',ui-monospace,monospace;}
|
.detail-row-value code{background:var(--sidebar);border:1px solid var(--border);border-radius:4px;padding:1px 6px;font-size:12px;font-family:var(--font-mono);}
|
||||||
.detail-badge{display:inline-block;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;border:1px solid var(--border);color:var(--muted);}
|
.detail-badge{display:inline-block;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;border:1px solid var(--border);color:var(--muted);}
|
||||||
.detail-badge.active{background:var(--accent-bg);color:var(--accent-text);border-color:var(--accent-bg-strong,var(--accent-bg));}
|
.detail-badge.active{background:var(--accent-bg);color:var(--accent-text);border-color:var(--accent-bg-strong,var(--accent-bg));}
|
||||||
.detail-badge.ok{background:rgba(100,200,100,.12);color:rgba(100,200,100,.95);border-color:rgba(100,200,100,.3);}
|
.detail-badge.ok{background:rgba(100,200,100,.12);color:rgba(100,200,100,.95);border-color:rgba(100,200,100,.3);}
|
||||||
@@ -6457,7 +6461,7 @@ main.main > .main-view:not([id="mainChat"]):not([id="mainSettings"]) .main-view-
|
|||||||
.detail-card-title-row{display:flex;align-items:center;justify-content:space-between;gap:8px;}
|
.detail-card-title-row{display:flex;align-items:center;justify-content:space-between;gap:8px;}
|
||||||
.detail-expand-toggle{border:1px solid var(--border);background:var(--surface);color:var(--muted);border-radius:6px;padding:3px 8px;font-size:11px;line-height:1.3;cursor:pointer;}
|
.detail-expand-toggle{border:1px solid var(--border);background:var(--surface);color:var(--muted);border-radius:6px;padding:3px 8px;font-size:11px;line-height:1.3;cursor:pointer;}
|
||||||
.detail-expand-toggle:hover{color:var(--text);border-color:var(--accent);}
|
.detail-expand-toggle:hover{color:var(--text);border-color:var(--accent);}
|
||||||
.detail-prompt{background:var(--sidebar);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-size:12px;white-space:pre-wrap;line-height:1.55;color:var(--text);font-family:'SF Mono',ui-monospace,monospace;max-height:240px;overflow-y:auto;}
|
.detail-prompt{background:var(--sidebar);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-size:12px;white-space:pre-wrap;line-height:1.55;color:var(--text);font-family:var(--font-mono);max-height:240px;overflow-y:auto;}
|
||||||
.detail-prompt.expanded{max-height:none;overflow-y:visible;}
|
.detail-prompt.expanded{max-height:none;overflow-y:visible;}
|
||||||
.detail-run-item{border-top:1px solid var(--border);padding:8px 0;}
|
.detail-run-item{border-top:1px solid var(--border);padding:8px 0;}
|
||||||
.detail-run-item:first-child{border-top:none;}
|
.detail-run-item:first-child{border-top:none;}
|
||||||
@@ -6475,11 +6479,11 @@ main.main > .main-view:not([id="mainChat"]):not([id="mainSettings"]) .main-view-
|
|||||||
.workspace-artifact-empty{padding:16px 8px;color:var(--muted);font-size:12px;line-height:1.5;text-align:center;}
|
.workspace-artifact-empty{padding:16px 8px;color:var(--muted);font-size:12px;line-height:1.5;text-align:center;}
|
||||||
.workspace-artifact-item{display:block;width:100%;text-align:left;background:var(--surface-subtle);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 10px;margin-bottom:6px;cursor:pointer;}
|
.workspace-artifact-item{display:block;width:100%;text-align:left;background:var(--surface-subtle);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 10px;margin-bottom:6px;cursor:pointer;}
|
||||||
.workspace-artifact-item:hover{border-color:var(--accent);}
|
.workspace-artifact-item:hover{border-color:var(--accent);}
|
||||||
.workspace-artifact-filename{font-family:'SF Mono',ui-monospace,monospace;font-size:12px;overflow-wrap:anywhere;}
|
.workspace-artifact-filename{font-family:var(--font-mono);font-size:12px;overflow-wrap:anywhere;}
|
||||||
.workspace-artifact-directory{display:flex;min-width:0;overflow:hidden;white-space:nowrap;font-family:'SF Mono',ui-monospace,monospace;font-size:11px;color:var(--muted);}
|
.workspace-artifact-directory{display:flex;min-width:0;overflow:hidden;white-space:nowrap;font-family:var(--font-mono);font-size:11px;color:var(--muted);}
|
||||||
.workspace-artifact-directory-head{min-width:0;flex:0 1 auto;overflow:hidden;text-overflow:ellipsis;}
|
.workspace-artifact-directory-head{min-width:0;flex:0 1 auto;overflow:hidden;text-overflow:ellipsis;}
|
||||||
.workspace-artifact-directory-tail{flex:0 0 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;}
|
.workspace-artifact-directory-tail{flex:0 0 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;}
|
||||||
.workspace-artifact-meta{display:inline-block;margin-top:4px;padding:1px 5px;border:1px solid var(--border);border-radius:999px;color:var(--muted);font-size:11px;line-height:1.3;}
|
.workspace-artifact-meta{display:inline-block;margin-top:4px;padding:1px 5px;border:1px solid var(--border);border-radius:999px;color:var(--muted);font-size:11px;line-height:1.3;}
|
||||||
.workspace-artifacts-count{opacity:.65;font-size:11px;}
|
.workspace-artifacts-count{opacity:.65;font-size:11px;}
|
||||||
.rightpanel[data-active-tab="artifacts"] .breadcrumb-bar,.rightpanel[data-active-tab="artifacts"] .file-tree,.rightpanel[data-active-tab="artifacts"] #wsEmptyState{display:none!important;}
|
.rightpanel[data-active-tab="artifacts"] .breadcrumb-bar,.rightpanel[data-active-tab="artifacts"] .file-tree,.rightpanel[data-active-tab="artifacts"] #wsEmptyState{display:none!important;}
|
||||||
.rightpanel[data-active-tab="artifacts"] .workspace-artifacts{display:block;}
|
.rightpanel[data-active-tab="artifacts"] .workspace-artifacts{display:block;}
|
||||||
@@ -6526,8 +6530,8 @@ main.main > .main-view:not([id="mainChat"]):not([id="mainSettings"]) .main-view-
|
|||||||
.cron-schedule-preset-field input[type="time"]{min-width:120px;}
|
.cron-schedule-preset-field input[type="time"]{min-width:120px;}
|
||||||
.cron-schedule-preset-field input[type="number"]{width:64px;}
|
.cron-schedule-preset-field input[type="number"]{width:64px;}
|
||||||
.cron-schedule-preset-time-hint{flex-basis:100%;margin-top:2px;}
|
.cron-schedule-preset-time-hint{flex-basis:100%;margin-top:2px;}
|
||||||
.cron-schedule-preview{font-family:'SF Mono',ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--text);opacity:.85;}
|
.cron-schedule-preview{font-family:var(--font-mono);color:var(--text);opacity:.85;}
|
||||||
.detail-form-row textarea{resize:vertical;font-family:'SF Mono',ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;}
|
.detail-form-row textarea{resize:vertical;font-family:var(--font-mono);font-size:12px;}
|
||||||
.detail-form-row .detail-form-hint{font-size:11px;color:var(--muted);line-height:1.5;}
|
.detail-form-row .detail-form-hint{font-size:11px;color:var(--muted);line-height:1.5;}
|
||||||
.detail-form-warning{font-size:11px;line-height:1.5;border:1px solid rgba(245,158,11,.35);background:rgba(245,158,11,.1);color:rgba(245,158,11,.98);border-radius:8px;padding:8px 10px;}
|
.detail-form-warning{font-size:11px;line-height:1.5;border:1px solid rgba(245,158,11,.35);background:rgba(245,158,11,.1);color:rgba(245,158,11,.98);border-radius:8px;padding:8px 10px;}
|
||||||
.cron-once-warning{margin-top:2px;}
|
.cron-once-warning{margin-top:2px;}
|
||||||
@@ -6893,7 +6897,7 @@ main.main.showing-insights > #mainInsights{display:flex;overflow-y:auto;}
|
|||||||
font-size:11px;
|
font-size:11px;
|
||||||
color:var(--muted);
|
color:var(--muted);
|
||||||
}
|
}
|
||||||
.kanban-modal-hint code{background:var(--input-bg);padding:1px 5px;border-radius:4px;font-family:'SF Mono',ui-monospace,Menlo,monospace;font-size:11px;color:var(--text);}
|
.kanban-modal-hint code{background:var(--input-bg);padding:1px 5px;border-radius:4px;font-family:var(--font-mono);font-size:11px;color:var(--text);}
|
||||||
.kanban-modal-hint em{color:var(--text);font-style:normal;font-weight:600;}
|
.kanban-modal-hint em{color:var(--text);font-style:normal;font-weight:600;}
|
||||||
.kanban-modal-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:14px;}
|
.kanban-modal-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:14px;}
|
||||||
.kanban-modal-error{
|
.kanban-modal-error{
|
||||||
@@ -7038,7 +7042,7 @@ main.main.showing-insights > #mainInsights{display:flex;overflow-y:auto;}
|
|||||||
.kanban-profile-lane-unassigned .kanban-profile-lane-head{border-bottom:1px solid color-mix(in srgb,var(--warning,#f5c542) 45%,var(--border));padding-bottom:6px}
|
.kanban-profile-lane-unassigned .kanban-profile-lane-head{border-bottom:1px solid color-mix(in srgb,var(--warning,#f5c542) 45%,var(--border));padding-bottom:6px}
|
||||||
.kanban-board-in-lane{min-height:0;overflow-x:auto}
|
.kanban-board-in-lane{min-height:0;overflow-x:auto}
|
||||||
.kanban-card-topline{display:flex;gap:6px;align-items:center;margin-bottom:4px;font-size:10px;color:var(--muted)}
|
.kanban-card-topline{display:flex;gap:6px;align-items:center;margin-bottom:4px;font-size:10px;color:var(--muted)}
|
||||||
.kanban-card-id{font-family:var(--mono);opacity:.8}
|
.kanban-card-id{font-family:var(--font-mono);opacity:.8}
|
||||||
.kanban-badge{border:1px solid var(--border);border-radius:999px;padding:1px 6px;font-size:10px;color:var(--muted)}
|
.kanban-badge{border:1px solid var(--border);border-radius:999px;padding:1px 6px;font-size:10px;color:var(--muted)}
|
||||||
.kanban-badge.priority{color:var(--accent)}
|
.kanban-badge.priority{color:var(--accent)}
|
||||||
.kanban-badge.tenant{color:var(--text)}
|
.kanban-badge.tenant{color:var(--text)}
|
||||||
@@ -7051,7 +7055,7 @@ main.main.showing-insights > #mainInsights{display:flex;overflow-y:auto;}
|
|||||||
.kanban-card-stale-amber{border-color:rgba(245,197,66,.55)}
|
.kanban-card-stale-amber{border-color:rgba(245,197,66,.55)}
|
||||||
.kanban-card-stale-red{border-color:rgba(255,95,95,.65)}
|
.kanban-card-stale-red{border-color:rgba(255,95,95,.65)}
|
||||||
.kanban-column.drop-target{outline:2px solid var(--accent);outline-offset:-2px}
|
.kanban-column.drop-target{outline:2px solid var(--accent);outline-offset:-2px}
|
||||||
.hermes-kanban-md p{margin:0 0 4px}.hermes-kanban-md code{font-family:var(--mono);font-size:.95em}.hermes-kanban-md h1,.hermes-kanban-md h2,.hermes-kanban-md h3,.hermes-kanban-md h4,.hermes-kanban-md h5,.hermes-kanban-md h6{margin:10px 0 4px;font-weight:650;color:var(--text)}.hermes-kanban-md h1{font-size:15px}.hermes-kanban-md h2{font-size:14px}.hermes-kanban-md h3{font-size:13px}.hermes-kanban-md h4,.hermes-kanban-md h5,.hermes-kanban-md h6{font-size:12px}.hermes-kanban-md ul,.hermes-kanban-md ol{margin:4px 0;padding-left:20px}.hermes-kanban-md li{margin:2px 0}.hermes-kanban-md li.checked{opacity:.6}.hermes-kanban-md li input[type=checkbox]{margin:0 4px 0 0;vertical-align:middle}.hermes-kanban-md table{border-collapse:collapse;margin:6px 0;font-size:11px;width:100%}.hermes-kanban-md td{border:1px solid var(--border);padding:4px 6px;vertical-align:top}.hermes-kanban-md blockquote{margin:4px 0;padding:2px 8px;border-left:3px solid var(--accent);color:var(--muted);font-size:12px}.hermes-kanban-md pre{background:var(--input-bg);border:1px solid var(--border);border-radius:6px;padding:8px;margin:6px 0;overflow-x:auto;font-size:11px;line-height:1.4;color:var(--text)}.hermes-kanban-md hr{border:none;border-top:1px solid var(--border);margin:8px 0}
|
.hermes-kanban-md p{margin:0 0 4px}.hermes-kanban-md code{font-family:var(--font-mono);font-size:.95em}.hermes-kanban-md h1,.hermes-kanban-md h2,.hermes-kanban-md h3,.hermes-kanban-md h4,.hermes-kanban-md h5,.hermes-kanban-md h6{margin:10px 0 4px;font-weight:650;color:var(--text)}.hermes-kanban-md h1{font-size:15px}.hermes-kanban-md h2{font-size:14px}.hermes-kanban-md h3{font-size:13px}.hermes-kanban-md h4,.hermes-kanban-md h5,.hermes-kanban-md h6{font-size:12px}.hermes-kanban-md ul,.hermes-kanban-md ol{margin:4px 0;padding-left:20px}.hermes-kanban-md li{margin:2px 0}.hermes-kanban-md li.checked{opacity:.6}.hermes-kanban-md li input[type=checkbox]{margin:0 4px 0 0;vertical-align:middle}.hermes-kanban-md table{border-collapse:collapse;margin:6px 0;font-size:11px;width:100%}.hermes-kanban-md td{border:1px solid var(--border);padding:4px 6px;vertical-align:top}.hermes-kanban-md blockquote{margin:4px 0;padding:2px 8px;border-left:3px solid var(--accent);color:var(--muted);font-size:12px}.hermes-kanban-md pre{background:var(--input-bg);border:1px solid var(--border);border-radius:6px;padding:8px;margin:6px 0;overflow-x:auto;font-size:11px;line-height:1.4;color:var(--text)}.hermes-kanban-md hr{border:none;border-top:1px solid var(--border);margin:8px 0}
|
||||||
|
|
||||||
@media (max-width: 640px){
|
@media (max-width: 640px){
|
||||||
.kanban-board{scroll-snap-type:x mandatory;}
|
.kanban-board{scroll-snap-type:x mandatory;}
|
||||||
@@ -7086,10 +7090,10 @@ main.main.showing-logs > #mainLogs{display:flex;}
|
|||||||
.logs-copy{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:1px solid var(--border);background:var(--surface);color:var(--text);border-radius:8px;padding:7px 10px;font-size:12px;font-weight:600;cursor:pointer;transition:background .15s,border-color .15s,color .15s;}
|
.logs-copy{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:1px solid var(--border);background:var(--surface);color:var(--text);border-radius:8px;padding:7px 10px;font-size:12px;font-weight:600;cursor:pointer;transition:background .15s,border-color .15s,color .15s;}
|
||||||
.logs-copy:hover{background:var(--hover-bg);border-color:var(--border2);}
|
.logs-copy:hover{background:var(--hover-bg);border-color:var(--border2);}
|
||||||
.logs-copy.compact{padding:6px 10px;white-space:nowrap;}
|
.logs-copy.compact{padding:6px 10px;white-space:nowrap;}
|
||||||
.logs-status{font-size:12px;color:var(--muted);margin-top:3px;font-family:'SF Mono',ui-monospace,monospace;}
|
.logs-status{font-size:12px;color:var(--muted);margin-top:3px;font-family:var(--font-mono);}
|
||||||
.logs-main-body{padding:18px 24px;}
|
.logs-main-body{padding:18px 24px;}
|
||||||
.logs-content{max-width:1200px;}
|
.logs-content{max-width:1200px;}
|
||||||
.logs-output{min-height:320px;max-height:calc(100vh - 170px);overflow:auto;background:var(--code-bg);border:1px solid var(--border);border-radius:12px;padding:12px 0;font-family:'SF Mono','Fira Code',ui-monospace,monospace;font-size:12px;line-height:1.55;color:var(--pre-text);white-space:pre;}
|
.logs-output{min-height:320px;max-height:calc(100vh - 170px);overflow:auto;background:var(--code-bg);border:1px solid var(--border);border-radius:12px;padding:12px 0;font-family:var(--font-mono);font-size:12px;line-height:1.55;color:var(--pre-text);white-space:pre;}
|
||||||
.logs-output.wrap{white-space:pre-wrap;overflow-wrap:anywhere;}
|
.logs-output.wrap{white-space:pre-wrap;overflow-wrap:anywhere;}
|
||||||
.log-line{padding:0 14px;min-height:1.55em;border-left:3px solid transparent;}
|
.log-line{padding:0 14px;min-height:1.55em;border-left:3px solid transparent;}
|
||||||
.log-line:hover{background:rgba(255,255,255,.04);}
|
.log-line:hover{background:rgba(255,255,255,.04);}
|
||||||
|
|||||||
+115
-6
@@ -15,6 +15,11 @@ const TERMINAL_UI={
|
|||||||
resizing:false,
|
resizing:false,
|
||||||
resizeStartY:0,
|
resizeStartY:0,
|
||||||
resizeStartHeight:0,
|
resizeStartHeight:0,
|
||||||
|
lastAppliedTheme:null,
|
||||||
|
lastAppliedFontFamily:null,
|
||||||
|
fontFitFrame:null,
|
||||||
|
fontLoadGeneration:0,
|
||||||
|
fontLoadRequest:null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const TERMINAL_HEIGHT_DEFAULT=260;
|
const TERMINAL_HEIGHT_DEFAULT=260;
|
||||||
@@ -114,8 +119,78 @@ function _terminalTheme(){
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncComposerTerminalTheme(){
|
function _terminalMonoFont(){
|
||||||
if(TERMINAL_UI.term)TERMINAL_UI.term.options.theme=_terminalTheme();
|
return _terminalCssVar(
|
||||||
|
'--font-mono',
|
||||||
|
'ui-monospace,"SFMono-Regular","SF Mono",Menlo,Consolas,"Liberation Mono",monospace'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _terminalThemesEqual(left,right){
|
||||||
|
if(left===right)return true;
|
||||||
|
if(!left||!right)return false;
|
||||||
|
const leftKeys=Object.keys(left);
|
||||||
|
const rightKeys=Object.keys(right);
|
||||||
|
return leftKeys.length===rightKeys.length&&leftKeys.every(key=>Object.prototype.hasOwnProperty.call(right,key)&&left[key]===right[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _scheduleTerminalFontFit(term){
|
||||||
|
if(TERMINAL_UI.fontFitFrame!==null)return;
|
||||||
|
TERMINAL_UI.fontFitFrame=requestAnimationFrame(()=>{
|
||||||
|
TERMINAL_UI.fontFitFrame=null;
|
||||||
|
if(TERMINAL_UI.term!==term||!TERMINAL_UI.open||TERMINAL_UI.collapsed)return;
|
||||||
|
_fitTerminal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _watchTerminalFontLoad(term,fontFamily){
|
||||||
|
const request={
|
||||||
|
generation:TERMINAL_UI.fontLoadGeneration+1,
|
||||||
|
term,
|
||||||
|
fontFamily,
|
||||||
|
};
|
||||||
|
TERMINAL_UI.fontLoadGeneration=request.generation;
|
||||||
|
TERMINAL_UI.fontLoadRequest=request;
|
||||||
|
const fontSet=document.fonts;
|
||||||
|
if(!fontSet||typeof fontSet.load!=='function')return;
|
||||||
|
const fontSize=Number(term.options&&term.options.fontSize);
|
||||||
|
const fontShorthand=(Number.isFinite(fontSize)&&fontSize>0?fontSize:13)+'px '+fontFamily;
|
||||||
|
let load;
|
||||||
|
try{
|
||||||
|
load=document.fonts.load(fontShorthand);
|
||||||
|
}catch(_){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Promise.resolve(load).then(fontFaces=>{
|
||||||
|
if(!fontFaces||fontFaces.length===0)return;
|
||||||
|
if(TERMINAL_UI.fontLoadRequest!==request
|
||||||
|
||TERMINAL_UI.fontLoadGeneration!==request.generation
|
||||||
|
||TERMINAL_UI.term!==term
|
||||||
|
||!TERMINAL_UI.open
|
||||||
|
||TERMINAL_UI.collapsed
|
||||||
|
||TERMINAL_UI.lastAppliedFontFamily!==fontFamily)return;
|
||||||
|
_scheduleTerminalFontFit(term);
|
||||||
|
},()=>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncComposerTerminalAppearance(forceFontLoad=false){
|
||||||
|
if(!TERMINAL_UI.term)return;
|
||||||
|
const term=TERMINAL_UI.term;
|
||||||
|
const theme=_terminalTheme();
|
||||||
|
const fontFamily=_terminalMonoFont();
|
||||||
|
if(!_terminalThemesEqual(TERMINAL_UI.lastAppliedTheme,theme)){
|
||||||
|
term.options.theme=theme;
|
||||||
|
TERMINAL_UI.lastAppliedTheme={...theme};
|
||||||
|
}
|
||||||
|
const fontFamilyChanged=TERMINAL_UI.lastAppliedFontFamily!==fontFamily;
|
||||||
|
if(fontFamilyChanged){
|
||||||
|
term.options.fontFamily=fontFamily;
|
||||||
|
TERMINAL_UI.lastAppliedFontFamily=fontFamily;
|
||||||
|
_scheduleTerminalFontFit(term);
|
||||||
|
}
|
||||||
|
if(fontFamilyChanged||forceFontLoad){
|
||||||
|
_watchTerminalFontLoad(term,fontFamily);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _xtermReady(){
|
function _xtermReady(){
|
||||||
@@ -130,13 +205,15 @@ function _ensureXterm(){
|
|||||||
surface.textContent='Terminal library failed to load. Check network access to cdn.jsdelivr.net.';
|
surface.textContent='Terminal library failed to load. Check network access to cdn.jsdelivr.net.';
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const theme=_terminalTheme();
|
||||||
|
const fontFamily=_terminalMonoFont();
|
||||||
const term=new window.Terminal({
|
const term=new window.Terminal({
|
||||||
cursorBlink:true,
|
cursorBlink:true,
|
||||||
fontSize:13,
|
fontSize:13,
|
||||||
fontFamily:'Menlo, Monaco, Consolas, "Liberation Mono", monospace',
|
fontFamily,
|
||||||
scrollback:1000,
|
scrollback:1000,
|
||||||
convertEol:false,
|
convertEol:false,
|
||||||
theme:_terminalTheme(),
|
theme,
|
||||||
});
|
});
|
||||||
let fitAddon=null;
|
let fitAddon=null;
|
||||||
if(window.FitAddon&&typeof window.FitAddon.FitAddon==='function'){
|
if(window.FitAddon&&typeof window.FitAddon.FitAddon==='function'){
|
||||||
@@ -162,7 +239,10 @@ function _ensureXterm(){
|
|||||||
});
|
});
|
||||||
TERMINAL_UI.term=term;
|
TERMINAL_UI.term=term;
|
||||||
TERMINAL_UI.fitAddon=fitAddon;
|
TERMINAL_UI.fitAddon=fitAddon;
|
||||||
|
TERMINAL_UI.lastAppliedTheme={...theme};
|
||||||
|
TERMINAL_UI.lastAppliedFontFamily=fontFamily;
|
||||||
_fitTerminal();
|
_fitTerminal();
|
||||||
|
_watchTerminalFontLoad(term,fontFamily);
|
||||||
return term;
|
return term;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,12 +651,20 @@ function expandComposerTerminal(opts){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _disposeXterm(){
|
function _disposeXterm(){
|
||||||
|
TERMINAL_UI.fontLoadGeneration+=1;
|
||||||
|
TERMINAL_UI.fontLoadRequest=null;
|
||||||
|
if(TERMINAL_UI.fontFitFrame!==null){
|
||||||
|
if(typeof cancelAnimationFrame==='function')cancelAnimationFrame(TERMINAL_UI.fontFitFrame);
|
||||||
|
TERMINAL_UI.fontFitFrame=null;
|
||||||
|
}
|
||||||
if(TERMINAL_UI.term){
|
if(TERMINAL_UI.term){
|
||||||
try{TERMINAL_UI.term.dispose();}catch(_){}
|
try{TERMINAL_UI.term.dispose();}catch(_){}
|
||||||
}
|
}
|
||||||
TERMINAL_UI.term=null;
|
TERMINAL_UI.term=null;
|
||||||
TERMINAL_UI.fitAddon=null;
|
TERMINAL_UI.fitAddon=null;
|
||||||
TERMINAL_UI.typedLine='';
|
TERMINAL_UI.typedLine='';
|
||||||
|
TERMINAL_UI.lastAppliedTheme=null;
|
||||||
|
TERMINAL_UI.lastAppliedFontFamily=null;
|
||||||
const {surface}= _terminalEls();
|
const {surface}= _terminalEls();
|
||||||
if(surface)surface.textContent='';
|
if(surface)surface.textContent='';
|
||||||
}
|
}
|
||||||
@@ -696,8 +784,29 @@ window.addEventListener('resize',()=>{
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(window.MutationObserver){
|
if(window.MutationObserver){
|
||||||
new MutationObserver(syncComposerTerminalTheme).observe(document.documentElement,{
|
new MutationObserver(()=>syncComposerTerminalAppearance()).observe(document.documentElement,{
|
||||||
attributes:true,
|
attributes:true,
|
||||||
attributeFilter:['class','data-skin'],
|
attributeFilter:['class','data-skin','style'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const terminalHead=document.head;
|
||||||
|
if(terminalHead){
|
||||||
|
const terminalHeadStylesheetLoadListener=(event)=>{
|
||||||
|
const target=event && event.target;
|
||||||
|
if(!target||typeof target.tagName!=='string'||target.tagName.toLowerCase()!=='link')return;
|
||||||
|
const rel=typeof target.getAttribute==='function'
|
||||||
|
? target.getAttribute('rel')
|
||||||
|
: target.rel;
|
||||||
|
if(!String(rel||'').toLowerCase().split(/\s+/).includes('stylesheet'))return;
|
||||||
|
syncComposerTerminalAppearance(true);
|
||||||
|
};
|
||||||
|
terminalHead.addEventListener('load',terminalHeadStylesheetLoadListener,true);
|
||||||
|
new MutationObserver(()=>syncComposerTerminalAppearance(true)).observe(terminalHead,{
|
||||||
|
attributes:true,
|
||||||
|
attributeFilter:['href','media','disabled'],
|
||||||
|
childList:true,
|
||||||
|
subtree:true,
|
||||||
|
characterData:true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -10140,7 +10140,7 @@ function _renderLockManualInstruction(target, res){
|
|||||||
code.style.background='rgba(0,0,0,0.05)';
|
code.style.background='rgba(0,0,0,0.05)';
|
||||||
code.style.padding='6px';
|
code.style.padding='6px';
|
||||||
code.style.margin='4px 0';
|
code.style.margin='4px 0';
|
||||||
code.style.fontFamily='ui-monospace,monospace';
|
code.style.fontFamily='var(--font-mono)';
|
||||||
code.style.borderRadius='4px';
|
code.style.borderRadius='4px';
|
||||||
code.style.whiteSpace='pre-wrap';
|
code.style.whiteSpace='pre-wrap';
|
||||||
code.style.wordBreak='break-all';
|
code.style.wordBreak='break-all';
|
||||||
|
|||||||
@@ -247,8 +247,8 @@ def test_terminal_xterm_theme_follows_appearance_tokens():
|
|||||||
assert "function _terminalTheme" in terminal_js
|
assert "function _terminalTheme" in terminal_js
|
||||||
assert "_terminalCssVar('--code-bg'" in terminal_js
|
assert "_terminalCssVar('--code-bg'" in terminal_js
|
||||||
assert "_terminalCssVar('--pre-text'" in terminal_js
|
assert "_terminalCssVar('--pre-text'" in terminal_js
|
||||||
assert "syncComposerTerminalTheme" in terminal_js
|
assert "syncComposerTerminalAppearance" in terminal_js
|
||||||
assert "attributeFilter:['class','data-skin']" in terminal_js
|
assert "attributeFilter:['class','data-skin','style']" in terminal_js
|
||||||
assert "background:var(--code-bg)" in style_css
|
assert "background:var(--code-bg)" in style_css
|
||||||
assert "color:var(--pre-text)" in style_css
|
assert "color:var(--pre-text)" in style_css
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,6 @@ def test_graphite_skin_uses_native_ui_and_mono_font_stacks():
|
|||||||
assert "-webkit-font-smoothing:antialiased" in CSS
|
assert "-webkit-font-smoothing:antialiased" in CSS
|
||||||
assert ':root[data-skin="graphite"] textarea#msg' in CSS
|
assert ':root[data-skin="graphite"] textarea#msg' in CSS
|
||||||
assert ':root[data-skin="graphite"] textarea#msg{font-family:var(--font-ui)!important;font-size:14px;' in CSS
|
assert ':root[data-skin="graphite"] textarea#msg{font-family:var(--font-ui)!important;font-size:14px;' in CSS
|
||||||
assert ':root[data-skin="graphite"] .msg-body{font-family:var(--font-ui);font-size:13px;font-weight:430;' in CSS
|
assert ':root[data-skin="graphite"] .msg-body{font-family:var(--font-conversation);font-size:13px;font-weight:430;' in CSS
|
||||||
assert ':root[data-skin="graphite"][data-font-size="large"] .msg-body{font-size:15px;' in CSS
|
assert ':root[data-skin="graphite"][data-font-size="large"] .msg-body{font-size:15px;' in CSS
|
||||||
assert ':root[data-skin="graphite"] .tool-card-name' in CSS
|
assert ':root[data-skin="graphite"] .tool-card-name' in CSS
|
||||||
@@ -0,0 +1,856 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
try:
|
||||||
|
from playwright.sync_api import Error as PlaywrightError, sync_playwright
|
||||||
|
except Exception: # pragma: no cover - dependency optional
|
||||||
|
PlaywrightError = None
|
||||||
|
sync_playwright = None
|
||||||
|
|
||||||
|
|
||||||
|
REPO = Path(__file__).parent.parent
|
||||||
|
CSS = (REPO / "static" / "style.css").read_text(encoding="utf-8")
|
||||||
|
TERMINAL_JS = (REPO / "static" / "terminal.js").read_text(encoding="utf-8")
|
||||||
|
PANEL_JS = (REPO / "static" / "panels.js").read_text(encoding="utf-8")
|
||||||
|
UI_JS = (REPO / "static" / "ui.js").read_text(encoding="utf-8")
|
||||||
|
BOOT_JS = (REPO / "static" / "boot.js").read_text(encoding="utf-8")
|
||||||
|
INDEX_HTML = (REPO / "static" / "index.html").read_text(encoding="utf-8")
|
||||||
|
NODE = shutil.which("node")
|
||||||
|
|
||||||
|
|
||||||
|
def _iter_root_skin_blocks(css):
|
||||||
|
selector_re = re.compile(r'(:root(?:\.dark)?\[data-skin="[^"]+"\][^{]*?)\{')
|
||||||
|
for selector_match in selector_re.finditer(css):
|
||||||
|
selector = selector_match.group(1).strip()
|
||||||
|
idx = selector_match.end()
|
||||||
|
depth = 1
|
||||||
|
end = idx
|
||||||
|
while end < len(css) and depth:
|
||||||
|
ch = css[end]
|
||||||
|
if ch == "{":
|
||||||
|
depth += 1
|
||||||
|
elif ch == "}":
|
||||||
|
depth -= 1
|
||||||
|
end += 1
|
||||||
|
if depth:
|
||||||
|
continue
|
||||||
|
yield selector, css[idx : end - 1]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_root_skin_block(css, skin):
|
||||||
|
target = f':root[data-skin="{skin}"]'
|
||||||
|
for selector, block in _iter_root_skin_blocks(css):
|
||||||
|
if selector == target:
|
||||||
|
return block
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _font_stack_offenders(css):
|
||||||
|
cleaned = re.sub(r"/\*.*?\*/", "", css, flags=re.S)
|
||||||
|
pattern = re.compile(r"(?i)\b(font-family|font)\s*:\s*([^;]+);")
|
||||||
|
offenders = []
|
||||||
|
for _, declaration in pattern.findall(cleaned):
|
||||||
|
value = declaration.strip().lower()
|
||||||
|
if ("monospace" not in value and "ui-monospace" not in value):
|
||||||
|
continue
|
||||||
|
if value == "inherit":
|
||||||
|
continue
|
||||||
|
offenders.append(declaration)
|
||||||
|
return offenders
|
||||||
|
|
||||||
|
|
||||||
|
def test_typography_root_tokens_are_defined():
|
||||||
|
assert '--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;' in CSS
|
||||||
|
assert '--font-conversation:var(--font-ui);' in CSS
|
||||||
|
assert '--font-mono:ui-monospace,"SFMono-Regular","SF Mono",Menlo,Consolas,"Liberation Mono",monospace;' in CSS
|
||||||
|
|
||||||
|
|
||||||
|
def test_msg_body_uses_conversation_font():
|
||||||
|
assert '.msg-body{font-family:var(--font-conversation);font-size:var(--message-body-font-size);line-height:var(--message-body-line-height);' in CSS
|
||||||
|
|
||||||
|
|
||||||
|
def test_builtin_skin_msg_body_rules_use_conversation_font():
|
||||||
|
for skin in ("graphite", "codex", "terracotta", "github"):
|
||||||
|
selector = (
|
||||||
|
f':root[data-skin="{skin}"] .msg-body'
|
||||||
|
'{font-family:var(--font-conversation);font-size:13px;font-weight:430;letter-spacing:0;line-height:1.6;}'
|
||||||
|
)
|
||||||
|
assert selector in CSS
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_skin_msg_body_rules_use_root_font_ui_token():
|
||||||
|
assert not re.search(
|
||||||
|
r':root(?:\.dark)?\[data-skin="[^"]+"\]\s*\.msg-body\s*\{[^{}]*\bfont-family\s*:\s*var\(--font-ui\)\s*;[^{}]*\}',
|
||||||
|
CSS,
|
||||||
|
re.S,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_skin_redefines_font_conversation():
|
||||||
|
for selector, block in _iter_root_skin_blocks(CSS):
|
||||||
|
if '--font-conversation:' in block:
|
||||||
|
raise AssertionError(f'Unexpected skin-level --font-conversation: {selector}')
|
||||||
|
|
||||||
|
|
||||||
|
def test_nous_skin_keeps_monospace_ui_default():
|
||||||
|
assert _get_root_skin_block(CSS, "nous")
|
||||||
|
assert re.search(
|
||||||
|
r'--font-ui:"SF Mono","Roboto Mono","Courier New",monospace;',
|
||||||
|
_get_root_skin_block(CSS, "nous"),
|
||||||
|
re.S,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_geist_and_neon_skins_set_expected_font_ui_tokens():
|
||||||
|
geist_block = _get_root_skin_block(CSS, "geist-contrast")
|
||||||
|
assert geist_block
|
||||||
|
assert "--font-ui:\"Geist\",\"Geist Sans\",-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif;" in geist_block
|
||||||
|
for skin in ("neon", "neon-soft", "neon-paint"):
|
||||||
|
block = _get_root_skin_block(CSS, skin)
|
||||||
|
assert block, f"Missing {skin} root skin block"
|
||||||
|
assert "--font-ui:system-ui,-apple-system,sans-serif;" in block
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_stale_var_mono_usage_or_literal_monospace_font_family_stacks():
|
||||||
|
assert 'var(--mono' not in CSS
|
||||||
|
offending_lines = _font_stack_offenders(CSS)
|
||||||
|
assert not offending_lines, f"Unexpected literal monospace font stacks: {offending_lines[:4]}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_edit_surfaces_use_font_contract_tokens_structurally():
|
||||||
|
assert re.search(
|
||||||
|
r"\.msg-edit-area\{[^}]*font-family:var\(--font-conversation\)",
|
||||||
|
CSS,
|
||||||
|
re.S,
|
||||||
|
), ".msg-edit-area must use var(--font-conversation)"
|
||||||
|
assert re.search(
|
||||||
|
r'<textarea id="previewEditArea"[^>]*style="[^"]*font-family:var\(--font-mono\)',
|
||||||
|
INDEX_HTML,
|
||||||
|
), "#previewEditArea must use var(--font-mono)"
|
||||||
|
|
||||||
|
|
||||||
|
def test_playwright_regression_ensures_font_contract_for_syntax_and_edit_surfaces():
|
||||||
|
preview_style_match = re.search(
|
||||||
|
r'<textarea id="previewEditArea"[^>]*style="([^"]*)"',
|
||||||
|
INDEX_HTML,
|
||||||
|
)
|
||||||
|
assert preview_style_match, "Could not locate #previewEditArea inline style in index.html"
|
||||||
|
|
||||||
|
fixture_html = f"""
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<style>{CSS}</style>
|
||||||
|
<style>
|
||||||
|
:root{{
|
||||||
|
--font-ui:"UiFontSentinel";
|
||||||
|
--font-conversation:"ConvoFontSentinel";
|
||||||
|
--font-mono:"MonoFontSentinel";
|
||||||
|
}}
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
code[class*="language-"],pre[class*="language-"]{{font-family: "PrismIntruder", sans-serif;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg-body">
|
||||||
|
<p>inline <code id="inlineCode" class="language-js">token</code> sample.</p>
|
||||||
|
<pre id="fencedPre" class="language-js"><code id="fencedCode" class="language-js">fenced token</code></pre>
|
||||||
|
</div>
|
||||||
|
<textarea class="msg-edit-area" id="msgEditArea"></textarea>
|
||||||
|
<textarea id="previewEditArea" style="{preview_style_match.group(1)}"></textarea>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
if sync_playwright is None:
|
||||||
|
pytest.skip("playwright is unavailable; run `playwright install chromium`")
|
||||||
|
|
||||||
|
with sync_playwright() as playwright:
|
||||||
|
browser = None
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
browser = playwright.chromium.launch(
|
||||||
|
headless=True,
|
||||||
|
args=["--no-sandbox", "--disable-dev-shm-usage"],
|
||||||
|
)
|
||||||
|
except PlaywrightError as exc:
|
||||||
|
if "Executable doesn't exist at" in str(exc):
|
||||||
|
pytest.skip("playwright chromium executable is unavailable; run `playwright install chromium`")
|
||||||
|
raise
|
||||||
|
page = browser.new_page(viewport={"width": 1024, "height": 768})
|
||||||
|
page.set_content(fixture_html)
|
||||||
|
|
||||||
|
inline_code_font = page.eval_on_selector(
|
||||||
|
"#inlineCode",
|
||||||
|
"el => getComputedStyle(el).fontFamily",
|
||||||
|
)
|
||||||
|
fenced_pre_font = page.eval_on_selector(
|
||||||
|
"#fencedPre",
|
||||||
|
"el => getComputedStyle(el).fontFamily",
|
||||||
|
)
|
||||||
|
fenced_code_font = page.eval_on_selector(
|
||||||
|
"#fencedCode",
|
||||||
|
"el => getComputedStyle(el).fontFamily",
|
||||||
|
)
|
||||||
|
message_edit_font = page.eval_on_selector(
|
||||||
|
"#msgEditArea",
|
||||||
|
"el => getComputedStyle(el).fontFamily",
|
||||||
|
)
|
||||||
|
preview_edit_font = page.eval_on_selector(
|
||||||
|
"#previewEditArea",
|
||||||
|
"el => getComputedStyle(el).fontFamily",
|
||||||
|
)
|
||||||
|
runtime_token_font = page.evaluate("""
|
||||||
|
() => {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.id = 'runtime-font-sentinel';
|
||||||
|
style.textContent = ':root { --font-mono: "RuntimeMono", monospace; }';
|
||||||
|
document.head.appendChild(style);
|
||||||
|
return getComputedStyle(document.documentElement).getPropertyValue('--font-mono');
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
edited_runtime_token_font = page.evaluate("""
|
||||||
|
() => {
|
||||||
|
const style = document.getElementById('runtime-font-sentinel');
|
||||||
|
style.textContent = ':root { --font-mono: "EditedRuntimeMono", monospace; }';
|
||||||
|
return getComputedStyle(document.documentElement).getPropertyValue('--font-mono');
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
linked_stylesheet_font = page.evaluate("""
|
||||||
|
async () => {
|
||||||
|
const link = document.createElement('link');
|
||||||
|
const encodedCss = encodeURIComponent(':root { --font-mono: "LinkedRuntimeMono", monospace; }');
|
||||||
|
link.rel = 'stylesheet';
|
||||||
|
link.href = 'data:text/css,' + encodedCss;
|
||||||
|
return await new Promise((resolve) => {
|
||||||
|
link.onload = () => {
|
||||||
|
resolve(getComputedStyle(document.documentElement).getPropertyValue('--font-mono'));
|
||||||
|
};
|
||||||
|
link.onerror = () => {
|
||||||
|
resolve(getComputedStyle(document.documentElement).getPropertyValue('--font-mono'));
|
||||||
|
};
|
||||||
|
document.head.appendChild(link);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
finally:
|
||||||
|
if browser is not None:
|
||||||
|
browser.close()
|
||||||
|
|
||||||
|
assert "MonoFontSentinel" in inline_code_font
|
||||||
|
assert "MonoFontSentinel" in fenced_pre_font
|
||||||
|
assert "MonoFontSentinel" in fenced_code_font
|
||||||
|
assert "ConvoFontSentinel" in message_edit_font
|
||||||
|
assert "MonoFontSentinel" in preview_edit_font
|
||||||
|
assert "RuntimeMono" in runtime_token_font
|
||||||
|
assert "EditedRuntimeMono" in edited_runtime_token_font
|
||||||
|
assert "LinkedRuntimeMono" in linked_stylesheet_font
|
||||||
|
|
||||||
|
|
||||||
|
def test_terminal_sync_tracks_applied_appearance_and_observes_relevant_root_attributes():
|
||||||
|
block_match = re.search(
|
||||||
|
r"function\s+syncComposerTerminalAppearance\([^)]*\)\s*\{.*?\n\}\s*\n\s*function\s+_xtermReady\(\)",
|
||||||
|
TERMINAL_JS,
|
||||||
|
re.S,
|
||||||
|
)
|
||||||
|
assert block_match
|
||||||
|
sync_block = block_match.group(0)
|
||||||
|
assert "function _terminalThemesEqual" in TERMINAL_JS
|
||||||
|
assert "lastAppliedTheme" in sync_block
|
||||||
|
assert "lastAppliedFontFamily" in sync_block
|
||||||
|
assert re.search(
|
||||||
|
r"new\s+MutationObserver\(\(\)\s*=>\s*syncComposerTerminalAppearance\(\)\)\.observe\(document\.documentElement,\{.*?attributeFilter:\s*\[\s*['\"]class['\"],\s*['\"]data-skin['\"],\s*['\"]style['\"],?\s*\]",
|
||||||
|
TERMINAL_JS,
|
||||||
|
re.S,
|
||||||
|
)
|
||||||
|
assert "const terminalHead=document.head" in TERMINAL_JS
|
||||||
|
assert "if(terminalHead){" in TERMINAL_JS
|
||||||
|
assert "terminalHeadStylesheetLoadListener" in TERMINAL_JS
|
||||||
|
assert re.search(
|
||||||
|
r"new\s+MutationObserver\(\(\)\s*=>\s*syncComposerTerminalAppearance\(true\)\)\.observe\(terminalHead,\{\s*attributes:\s*true,\s*attributeFilter:\s*\[\s*['\"]href['\"],\s*['\"]media['\"],\s*['\"]disabled['\"]\s*\],\s*childList:\s*true,\s*subtree:\s*true,\s*characterData:\s*true,\s*\}\)",
|
||||||
|
TERMINAL_JS,
|
||||||
|
re.S,
|
||||||
|
)
|
||||||
|
head_decl=TERMINAL_JS.index("const terminalHead=document.head")
|
||||||
|
head_guard=TERMINAL_JS.index("if(terminalHead){")
|
||||||
|
head_load_listener=TERMINAL_JS.index("terminalHead.addEventListener('load',terminalHeadStylesheetLoadListener,true)")
|
||||||
|
head_observer=TERMINAL_JS.index("new MutationObserver(()=>syncComposerTerminalAppearance(true)).observe(terminalHead,{")
|
||||||
|
assert head_decl < head_guard < head_load_listener < head_observer
|
||||||
|
assert re.search(
|
||||||
|
r"terminalHead\.addEventListener\(\s*['\"]load['\"],\s*terminalHeadStylesheetLoadListener,\s*true\s*\)",
|
||||||
|
TERMINAL_JS,
|
||||||
|
re.S,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(NODE is None, reason="node not on PATH")
|
||||||
|
def test_terminal_appearance_sync_is_behaviorally_idempotent_and_lifecycle_safe():
|
||||||
|
script = (
|
||||||
|
"""
|
||||||
|
const styles={
|
||||||
|
'--font-mono':'"Active Mono",monospace',
|
||||||
|
'--code-bg':'#111111',
|
||||||
|
};
|
||||||
|
const rootInline={};
|
||||||
|
let dark=false;
|
||||||
|
let observerCallbacks={};
|
||||||
|
let observedTargets=[];
|
||||||
|
let observedOptions={};
|
||||||
|
let fitCalls=0;
|
||||||
|
let resizeSchedules=0;
|
||||||
|
let terminalHeadLoadListener=null;
|
||||||
|
let terminalHeadLoadListenerOptions=null;
|
||||||
|
let unhandledFontLoadRejections=0;
|
||||||
|
let nextAnimationFrameId=1;
|
||||||
|
const animationFrames=new Map();
|
||||||
|
const writes=[];
|
||||||
|
const createdAppearances=[];
|
||||||
|
const fontLoadCalls=[];
|
||||||
|
const pendingFontLoads=[];
|
||||||
|
const surface={textContent:''};
|
||||||
|
|
||||||
|
process.on('unhandledRejection',()=>{unhandledFontLoadRejections+=1;});
|
||||||
|
|
||||||
|
const terminalFonts={
|
||||||
|
load(shorthand){
|
||||||
|
let resolve;
|
||||||
|
let reject;
|
||||||
|
const promise=new Promise((resolvePromise,rejectPromise)=>{
|
||||||
|
resolve=resolvePromise;
|
||||||
|
reject=rejectPromise;
|
||||||
|
});
|
||||||
|
const request={shorthand,resolve,reject};
|
||||||
|
fontLoadCalls.push(shorthand);
|
||||||
|
pendingFontLoads.push(request);
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function pendingAnimationFrames(){
|
||||||
|
return animationFrames.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flushAnimationFrames(){
|
||||||
|
const pending=[...animationFrames.values()];
|
||||||
|
animationFrames.clear();
|
||||||
|
pending.forEach(callback=>callback());
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeMutationObserver {
|
||||||
|
constructor(callback){ this.callback=callback; }
|
||||||
|
observe(target,options){
|
||||||
|
const targetName = target===document.documentElement ? 'documentElement' : target===document.head ? 'head' : 'other';
|
||||||
|
observedTargets.push(targetName);
|
||||||
|
observedOptions[targetName]=options;
|
||||||
|
observerCallbacks[targetName]=this.callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeTerminal {
|
||||||
|
constructor(options){
|
||||||
|
createdAppearances.push({
|
||||||
|
fontFamily:options.fontFamily,
|
||||||
|
fontSize:options.fontSize,
|
||||||
|
theme:{...options.theme},
|
||||||
|
});
|
||||||
|
const current={
|
||||||
|
fontFamily:options.fontFamily,
|
||||||
|
fontSize:options.fontSize,
|
||||||
|
theme:options.theme,
|
||||||
|
};
|
||||||
|
this.options=new Proxy(current,{
|
||||||
|
set(target,key,value){
|
||||||
|
writes.push(String(key));
|
||||||
|
target[key]=value;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.cols=80;
|
||||||
|
this.rows=24;
|
||||||
|
}
|
||||||
|
loadAddon(){}
|
||||||
|
open(){}
|
||||||
|
onData(){}
|
||||||
|
dispose(){}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeFitAddon {
|
||||||
|
fit(){ fitCalls+=1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
const root={
|
||||||
|
classList:{contains(name){ return name==='dark'&&dark; }},
|
||||||
|
style:{setProperty(name,value){ rootInline[name]=value; }},
|
||||||
|
};
|
||||||
|
global.window={
|
||||||
|
addEventListener(){},
|
||||||
|
MutationObserver:FakeMutationObserver,
|
||||||
|
Terminal:FakeTerminal,
|
||||||
|
FitAddon:{FitAddon:FakeFitAddon},
|
||||||
|
};
|
||||||
|
global.MutationObserver=FakeMutationObserver;
|
||||||
|
global.document={
|
||||||
|
documentElement:root,
|
||||||
|
fonts:terminalFonts,
|
||||||
|
head:{
|
||||||
|
addEventListener(type,listener,options){
|
||||||
|
if(type==='load'){
|
||||||
|
terminalHeadLoadListener=listener;
|
||||||
|
terminalHeadLoadListenerOptions=options;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeEventListener(){
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getElementById(){ return null; },
|
||||||
|
};
|
||||||
|
global.getComputedStyle=()=>({
|
||||||
|
getPropertyValue(name){ return styles[name]||rootInline[name]||''; },
|
||||||
|
});
|
||||||
|
global.$=(id)=>id==='terminalSurface'?surface:null;
|
||||||
|
global.requestAnimationFrame=(callback)=>{
|
||||||
|
const id=nextAnimationFrameId++;
|
||||||
|
animationFrames.set(id,callback);
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
global.cancelAnimationFrame=(id)=>animationFrames.delete(id);
|
||||||
|
global.setTimeout=(_callback,delay)=>{
|
||||||
|
if(delay===120)resizeSchedules+=1;
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
global.clearTimeout=()=>{};
|
||||||
|
"""
|
||||||
|
+ TERMINAL_JS
|
||||||
|
+ """
|
||||||
|
async function settleFontLoad(){
|
||||||
|
await Promise.resolve();
|
||||||
|
await Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
(async()=>{
|
||||||
|
const first=_ensureXterm();
|
||||||
|
const initialFontLoad=pendingFontLoads[0];
|
||||||
|
TERMINAL_UI.open=true;
|
||||||
|
TERMINAL_UI.sessionId='session-1';
|
||||||
|
const initialFitCalls=fitCalls;
|
||||||
|
resizeSchedules=0;
|
||||||
|
initialFontLoad.resolve([]);
|
||||||
|
await settleFontLoad();
|
||||||
|
const initialDelayedPendingFrames=pendingAnimationFrames();
|
||||||
|
flushAnimationFrames();
|
||||||
|
const initialDelayedFitDelta=fitCalls-initialFitCalls;
|
||||||
|
const initialDelayedResizeSchedules=resizeSchedules;
|
||||||
|
|
||||||
|
writes.length=0;
|
||||||
|
resizeSchedules=0;
|
||||||
|
const fitsBeforeLateSameFamily=fitCalls;
|
||||||
|
const fontLoadsBeforeLateSameFamily=fontLoadCalls.length;
|
||||||
|
observerCallbacks.head([{type:'childList'},{type:'characterData'}]);
|
||||||
|
const lateSameFamilyFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
const lateSameFamilyWrites=[...writes];
|
||||||
|
const lateSameFamilyLoadCount=fontLoadCalls.length-fontLoadsBeforeLateSameFamily;
|
||||||
|
const lateSameFamilyPendingFramesBeforeCompletion=pendingAnimationFrames();
|
||||||
|
flushAnimationFrames();
|
||||||
|
const lateSameFamilyFitDeltaBeforeCompletion=fitCalls-fitsBeforeLateSameFamily;
|
||||||
|
lateSameFamilyFontLoad.resolve([{}]);
|
||||||
|
await settleFontLoad();
|
||||||
|
const lateSameFamilyPendingFramesAfterLoad=pendingAnimationFrames();
|
||||||
|
const fitsBeforeLateSameFamilyFit=fitCalls;
|
||||||
|
flushAnimationFrames();
|
||||||
|
const lateSameFamilyFitDelta=fitCalls-fitsBeforeLateSameFamilyFit;
|
||||||
|
const lateSameFamilyResizeSchedules=resizeSchedules;
|
||||||
|
|
||||||
|
writes.length=0;
|
||||||
|
const fitsBeforeLateStylesheetLoad=fitCalls;
|
||||||
|
const fontLoadsBeforeLateStylesheetLoad=fontLoadCalls.length;
|
||||||
|
terminalHeadLoadListener&&terminalHeadLoadListener({target:{
|
||||||
|
tagName:'LINK',
|
||||||
|
getAttribute(name){ return name==='rel'?'stylesheet':null; },
|
||||||
|
}});
|
||||||
|
const lateStylesheetFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
const lateStylesheetLoadWrites=[...writes];
|
||||||
|
const lateStylesheetLoadCount=fontLoadCalls.length-fontLoadsBeforeLateStylesheetLoad;
|
||||||
|
const lateStylesheetPendingFrames=pendingAnimationFrames();
|
||||||
|
flushAnimationFrames();
|
||||||
|
lateStylesheetFontLoad.resolve([]);
|
||||||
|
await settleFontLoad();
|
||||||
|
const lateStylesheetFitDelta=fitCalls-fitsBeforeLateStylesheetLoad;
|
||||||
|
const lateStylesheetPendingFramesAfterLoad=pendingAnimationFrames();
|
||||||
|
|
||||||
|
writes.length=0;
|
||||||
|
const fitsBeforeUnchanged=fitCalls;
|
||||||
|
syncComposerTerminalAppearance();
|
||||||
|
const unchangedWrites=[...writes];
|
||||||
|
const unchangedFitDelta=fitCalls-fitsBeforeUnchanged;
|
||||||
|
const unchangedPendingFrames=pendingAnimationFrames();
|
||||||
|
|
||||||
|
writes.length=0;
|
||||||
|
const fitsBeforeUnrelated=fitCalls;
|
||||||
|
document.documentElement.style.setProperty('--unrelated-outline-offset','3px');
|
||||||
|
observerCallbacks.documentElement([{attributeName:'style'}]);
|
||||||
|
const unrelatedStyleWrites=[...writes];
|
||||||
|
const unrelatedFitDelta=fitCalls-fitsBeforeUnrelated;
|
||||||
|
const unrelatedPendingFrames=pendingAnimationFrames();
|
||||||
|
|
||||||
|
writes.length=0;
|
||||||
|
const fitsBeforeTheme=fitCalls;
|
||||||
|
styles['--code-bg']='#222222';
|
||||||
|
observerCallbacks.documentElement([{attributeName:'style'}]);
|
||||||
|
const themeOnlyWrites=[...writes];
|
||||||
|
const backgroundAfterChange=first.options.theme.background;
|
||||||
|
const themeOnlyFitDelta=fitCalls-fitsBeforeTheme;
|
||||||
|
const themeOnlyPendingFrames=pendingAnimationFrames();
|
||||||
|
|
||||||
|
writes.length=0;
|
||||||
|
resizeSchedules=0;
|
||||||
|
const fitsBeforeFont=fitCalls;
|
||||||
|
const fontLoadsBeforeExtension=fontLoadCalls.length;
|
||||||
|
styles['--font-mono']='"Extension Mono",monospace';
|
||||||
|
observerCallbacks.head([{type:'childList'},{type:'characterData'}]);
|
||||||
|
const extensionFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
const extensionFontLoadCount=fontLoadCalls.length-fontLoadsBeforeExtension;
|
||||||
|
const fontOnlyWrites=[...writes];
|
||||||
|
const fontAfterChange=first.options.fontFamily;
|
||||||
|
const fontFitDeltaBeforeFrame=fitCalls-fitsBeforeFont;
|
||||||
|
const fontPendingFramesBeforeFlush=pendingAnimationFrames();
|
||||||
|
flushAnimationFrames();
|
||||||
|
const fontFitDeltaAfterFrame=fitCalls-fitsBeforeFont;
|
||||||
|
const fontResizeSchedules=resizeSchedules;
|
||||||
|
extensionFontLoad.resolve([{}]);
|
||||||
|
await settleFontLoad();
|
||||||
|
const fontPendingFramesAfterLoad=pendingAnimationFrames();
|
||||||
|
const fitsBeforeDelayedFontFit=fitCalls;
|
||||||
|
flushAnimationFrames();
|
||||||
|
const fontFitDeltaAfterLoad=fitCalls-fitsBeforeDelayedFontFit;
|
||||||
|
const fontResizeSchedulesAfterLoad=resizeSchedules;
|
||||||
|
|
||||||
|
writes.length=0;
|
||||||
|
resizeSchedules=0;
|
||||||
|
const fitsBeforeNonStylesheetLoad=fitCalls;
|
||||||
|
styles['--font-mono']='\"Non Stylesheet Mono\",monospace';
|
||||||
|
terminalHeadLoadListener&&terminalHeadLoadListener({target:{
|
||||||
|
tagName:'SCRIPT',
|
||||||
|
getAttribute:()=>null,
|
||||||
|
}});
|
||||||
|
const nonStylesheetLoadWrites=[...writes];
|
||||||
|
const nonStylesheetLoadFitDelta=fitCalls-fitsBeforeNonStylesheetLoad;
|
||||||
|
const nonStylesheetLoadPendingFrames=pendingAnimationFrames();
|
||||||
|
|
||||||
|
writes.length=0;
|
||||||
|
resizeSchedules=0;
|
||||||
|
const fitsBeforeEmptyFont=fitCalls;
|
||||||
|
styles['--font-mono']='ui-monospace,monospace';
|
||||||
|
observerCallbacks.head([{type:'childList'}]);
|
||||||
|
const emptyFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
const emptyFontWrites=[...writes];
|
||||||
|
const emptyFontPendingFramesBeforeFlush=pendingAnimationFrames();
|
||||||
|
flushAnimationFrames();
|
||||||
|
const emptyFontImmediateFitDelta=fitCalls-fitsBeforeEmptyFont;
|
||||||
|
const emptyFontResizeSchedules=resizeSchedules;
|
||||||
|
const fitsBeforeEmptyFontCompletion=fitCalls;
|
||||||
|
emptyFontLoad.resolve([]);
|
||||||
|
await settleFontLoad();
|
||||||
|
const emptyFontPendingFramesAfterLoad=pendingAnimationFrames();
|
||||||
|
const emptyFontDelayedFitDelta=fitCalls-fitsBeforeEmptyFontCompletion;
|
||||||
|
|
||||||
|
writes.length=0;
|
||||||
|
resizeSchedules=0;
|
||||||
|
const fitsBeforeStylesheetLoad=fitCalls;
|
||||||
|
styles['--font-mono']='\"Stylesheet Load Mono\",monospace';
|
||||||
|
terminalHeadLoadListener&&terminalHeadLoadListener({target:{
|
||||||
|
tagName:'LINK',
|
||||||
|
getAttribute(name){ return name==='rel'?'stylesheet':null; },
|
||||||
|
}});
|
||||||
|
const stylesheetFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
const stylesheetLoadWrites=[...writes];
|
||||||
|
const stylesheetLoadFontAfterChange=first.options.fontFamily;
|
||||||
|
const stylesheetLoadFitDeltaBeforeFrame=fitCalls-fitsBeforeStylesheetLoad;
|
||||||
|
const stylesheetLoadPendingFramesBeforeFlush=pendingAnimationFrames();
|
||||||
|
flushAnimationFrames();
|
||||||
|
const stylesheetLoadFitDeltaAfterFrame=fitCalls-fitsBeforeStylesheetLoad;
|
||||||
|
const stylesheetLoadResizeSchedules=resizeSchedules;
|
||||||
|
stylesheetFontLoad.resolve([]);
|
||||||
|
await settleFontLoad();
|
||||||
|
|
||||||
|
resizeSchedules=0;
|
||||||
|
styles['--font-mono']='"Stale Prior Mono",monospace';
|
||||||
|
observerCallbacks.head([{type:'childList'}]);
|
||||||
|
const staleFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
flushAnimationFrames();
|
||||||
|
styles['--font-mono']='"Replacement Mono",monospace';
|
||||||
|
observerCallbacks.head([{type:'childList'}]);
|
||||||
|
const replacementFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
flushAnimationFrames();
|
||||||
|
const fitsBeforeStaleCompletion=fitCalls;
|
||||||
|
staleFontLoad.resolve([{}]);
|
||||||
|
await settleFontLoad();
|
||||||
|
const staleCompletionFitDelta=fitCalls-fitsBeforeStaleCompletion;
|
||||||
|
const staleCompletionPendingFrames=pendingAnimationFrames();
|
||||||
|
replacementFontLoad.resolve([]);
|
||||||
|
await settleFontLoad();
|
||||||
|
|
||||||
|
styles['--font-mono']='"Rejecting Mono",monospace';
|
||||||
|
observerCallbacks.head([{type:'childList'}]);
|
||||||
|
const rejectedFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
flushAnimationFrames();
|
||||||
|
const fitsBeforeRejectedCompletion=fitCalls;
|
||||||
|
rejectedFontLoad.reject(new Error('optional font failed'));
|
||||||
|
await settleFontLoad();
|
||||||
|
const rejectedCompletionFitDelta=fitCalls-fitsBeforeRejectedCompletion;
|
||||||
|
const rejectedCompletionPendingFrames=pendingAnimationFrames();
|
||||||
|
|
||||||
|
styles['--font-mono']='"Disposed Mono",monospace';
|
||||||
|
observerCallbacks.head([{type:'childList'}]);
|
||||||
|
const disposedFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
const oldTerm=TERMINAL_UI.term;
|
||||||
|
const generationBeforeDispose=TERMINAL_UI.fontLoadGeneration;
|
||||||
|
const pendingFitBeforeDispose=pendingAnimationFrames();
|
||||||
|
_disposeXterm();
|
||||||
|
const pendingFitAfterDispose=pendingAnimationFrames();
|
||||||
|
const cacheReset=TERMINAL_UI.lastAppliedTheme===null
|
||||||
|
&&TERMINAL_UI.lastAppliedFontFamily===null
|
||||||
|
&&TERMINAL_UI.fontFitFrame===null
|
||||||
|
&&TERMINAL_UI.fontLoadRequest===null
|
||||||
|
&&TERMINAL_UI.fontLoadGeneration===generationBeforeDispose+1;
|
||||||
|
styles['--font-mono']='"Recreated Mono",monospace';
|
||||||
|
styles['--code-bg']='#333333';
|
||||||
|
const fitsBeforeRecreate=fitCalls;
|
||||||
|
const recreated=_ensureXterm();
|
||||||
|
const recreatedFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
const recreateFitDelta=fitCalls-fitsBeforeRecreate;
|
||||||
|
const recreatePendingFrames=pendingAnimationFrames();
|
||||||
|
const fitsBeforeDisposedCompletion=fitCalls;
|
||||||
|
disposedFontLoad.resolve([{}]);
|
||||||
|
await settleFontLoad();
|
||||||
|
const disposedCompletionFitDelta=fitCalls-fitsBeforeDisposedCompletion;
|
||||||
|
const disposedCompletionPendingFrames=pendingAnimationFrames();
|
||||||
|
recreatedFontLoad.resolve([]);
|
||||||
|
await settleFontLoad();
|
||||||
|
|
||||||
|
TERMINAL_UI.open=true;
|
||||||
|
styles['--font-mono']='"Closed Terminal Mono",monospace';
|
||||||
|
observerCallbacks.head([{type:'childList'}]);
|
||||||
|
const closedFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
flushAnimationFrames();
|
||||||
|
const fitsBeforeClosedCompletion=fitCalls;
|
||||||
|
TERMINAL_UI.open=false;
|
||||||
|
closedFontLoad.resolve([{}]);
|
||||||
|
await settleFontLoad();
|
||||||
|
const closedCompletionFitDelta=fitCalls-fitsBeforeClosedCompletion;
|
||||||
|
const closedCompletionPendingFrames=pendingAnimationFrames();
|
||||||
|
TERMINAL_UI.open=true;
|
||||||
|
|
||||||
|
styles['--font-mono']='"Collapsed Terminal Mono",monospace';
|
||||||
|
observerCallbacks.head([{type:'childList'}]);
|
||||||
|
const collapsedFontLoad=pendingFontLoads[pendingFontLoads.length-1];
|
||||||
|
flushAnimationFrames();
|
||||||
|
const fitsBeforeCollapsedCompletion=fitCalls;
|
||||||
|
TERMINAL_UI.collapsed=true;
|
||||||
|
collapsedFontLoad.resolve([{}]);
|
||||||
|
await settleFontLoad();
|
||||||
|
const collapsedCompletionFitDelta=fitCalls-fitsBeforeCollapsedCompletion;
|
||||||
|
const collapsedCompletionPendingFrames=pendingAnimationFrames();
|
||||||
|
TERMINAL_UI.collapsed=false;
|
||||||
|
|
||||||
|
process.stdout.write(JSON.stringify({
|
||||||
|
initial:createdAppearances[0],
|
||||||
|
initialFontLoadShorthand:fontLoadCalls[0],
|
||||||
|
initialFitCalls,
|
||||||
|
initialDelayedPendingFrames,
|
||||||
|
initialDelayedFitDelta,
|
||||||
|
initialDelayedResizeSchedules,
|
||||||
|
lateSameFamilyWrites,
|
||||||
|
lateSameFamilyLoadCount,
|
||||||
|
lateSameFamilyPendingFramesBeforeCompletion,
|
||||||
|
lateSameFamilyFitDeltaBeforeCompletion,
|
||||||
|
lateSameFamilyPendingFramesAfterLoad,
|
||||||
|
lateSameFamilyFitDelta,
|
||||||
|
lateSameFamilyResizeSchedules,
|
||||||
|
lateStylesheetLoadWrites,
|
||||||
|
lateStylesheetLoadCount,
|
||||||
|
lateStylesheetPendingFrames,
|
||||||
|
lateStylesheetFitDelta,
|
||||||
|
lateStylesheetPendingFramesAfterLoad,
|
||||||
|
unchangedWrites,
|
||||||
|
unchangedFitDelta,
|
||||||
|
unchangedPendingFrames,
|
||||||
|
unrelatedStyleWrites,
|
||||||
|
unrelatedFitDelta,
|
||||||
|
unrelatedPendingFrames,
|
||||||
|
themeOnlyWrites,
|
||||||
|
backgroundAfterChange,
|
||||||
|
themeOnlyFitDelta,
|
||||||
|
themeOnlyPendingFrames,
|
||||||
|
fontOnlyWrites,
|
||||||
|
fontAfterChange,
|
||||||
|
fontFitDeltaBeforeFrame,
|
||||||
|
fontPendingFramesBeforeFlush,
|
||||||
|
fontFitDeltaAfterFrame,
|
||||||
|
fontResizeSchedules,
|
||||||
|
extensionFontLoadCount,
|
||||||
|
fontPendingFramesAfterLoad,
|
||||||
|
fontFitDeltaAfterLoad,
|
||||||
|
fontResizeSchedulesAfterLoad,
|
||||||
|
nonStylesheetLoadWrites,
|
||||||
|
nonStylesheetLoadFitDelta,
|
||||||
|
nonStylesheetLoadPendingFrames,
|
||||||
|
emptyFontWrites,
|
||||||
|
emptyFontPendingFramesBeforeFlush,
|
||||||
|
emptyFontImmediateFitDelta,
|
||||||
|
emptyFontResizeSchedules,
|
||||||
|
emptyFontPendingFramesAfterLoad,
|
||||||
|
emptyFontDelayedFitDelta,
|
||||||
|
stylesheetLoadWrites,
|
||||||
|
stylesheetLoadFontAfterChange,
|
||||||
|
stylesheetLoadFitDeltaBeforeFrame,
|
||||||
|
stylesheetLoadPendingFramesBeforeFlush,
|
||||||
|
stylesheetLoadFitDeltaAfterFrame,
|
||||||
|
stylesheetLoadResizeSchedules,
|
||||||
|
staleCompletionFitDelta,
|
||||||
|
staleCompletionPendingFrames,
|
||||||
|
rejectedCompletionFitDelta,
|
||||||
|
rejectedCompletionPendingFrames,
|
||||||
|
unhandledFontLoadRejections,
|
||||||
|
terminalHeadLoadListenerRegistered: typeof terminalHeadLoadListener === 'function',
|
||||||
|
terminalHeadLoadListenerOptions,
|
||||||
|
observerTargets:observedTargets,
|
||||||
|
observerCallbacks:Object.keys(observerCallbacks),
|
||||||
|
observedOptions,
|
||||||
|
pendingFitBeforeDispose,
|
||||||
|
pendingFitAfterDispose,
|
||||||
|
cacheReset,
|
||||||
|
oldTermIsReplaced:oldTerm!==recreated,
|
||||||
|
recreated:createdAppearances[1],
|
||||||
|
recreateFitDelta,
|
||||||
|
recreatePendingFrames,
|
||||||
|
disposedCompletionFitDelta,
|
||||||
|
disposedCompletionPendingFrames,
|
||||||
|
closedCompletionFitDelta,
|
||||||
|
closedCompletionPendingFrames,
|
||||||
|
collapsedCompletionFitDelta,
|
||||||
|
collapsedCompletionPendingFrames,
|
||||||
|
}));
|
||||||
|
})().catch((error)=>{
|
||||||
|
process.exitCode=1;
|
||||||
|
process.stderr.write((error&&error.stack?error.stack:String(error))+"\\n");
|
||||||
|
});
|
||||||
|
""")
|
||||||
|
result = subprocess.run(
|
||||||
|
[NODE, "-e", script],
|
||||||
|
cwd=REPO,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=20,
|
||||||
|
)
|
||||||
|
assert result.returncode == 0, result.stderr
|
||||||
|
observed = json.loads(result.stdout)
|
||||||
|
|
||||||
|
assert observed["initial"]["fontFamily"] == '"Active Mono",monospace'
|
||||||
|
assert observed["initial"]["fontSize"] == 13
|
||||||
|
assert observed["initial"]["theme"]["background"] == "#111111"
|
||||||
|
assert observed["initialFontLoadShorthand"] == '13px "Active Mono",monospace'
|
||||||
|
assert observed["initialFitCalls"] == 1
|
||||||
|
assert observed["initialDelayedPendingFrames"] == 0
|
||||||
|
assert observed["initialDelayedFitDelta"] == 0
|
||||||
|
assert observed["initialDelayedResizeSchedules"] == 0
|
||||||
|
assert observed["lateSameFamilyWrites"] == []
|
||||||
|
assert observed["lateSameFamilyLoadCount"] == 1
|
||||||
|
assert observed["lateSameFamilyPendingFramesBeforeCompletion"] == 0
|
||||||
|
assert observed["lateSameFamilyFitDeltaBeforeCompletion"] == 0
|
||||||
|
assert observed["lateSameFamilyPendingFramesAfterLoad"] == 1
|
||||||
|
assert observed["lateSameFamilyFitDelta"] == 1
|
||||||
|
assert observed["lateSameFamilyResizeSchedules"] == 1
|
||||||
|
assert observed["lateStylesheetLoadWrites"] == []
|
||||||
|
assert observed["lateStylesheetLoadCount"] == 1
|
||||||
|
assert observed["lateStylesheetPendingFrames"] == 0
|
||||||
|
assert observed["lateStylesheetFitDelta"] == 0
|
||||||
|
assert observed["lateStylesheetPendingFramesAfterLoad"] == 0
|
||||||
|
assert observed["unchangedWrites"] == []
|
||||||
|
assert observed["unchangedFitDelta"] == 0
|
||||||
|
assert observed["unchangedPendingFrames"] == 0
|
||||||
|
assert observed["unrelatedStyleWrites"] == []
|
||||||
|
assert observed["unrelatedFitDelta"] == 0
|
||||||
|
assert observed["unrelatedPendingFrames"] == 0
|
||||||
|
assert observed["themeOnlyWrites"] == ["theme"]
|
||||||
|
assert observed["backgroundAfterChange"] == "#222222"
|
||||||
|
assert observed["themeOnlyFitDelta"] == 0
|
||||||
|
assert observed["themeOnlyPendingFrames"] == 0
|
||||||
|
assert observed["fontOnlyWrites"] == ["fontFamily"]
|
||||||
|
assert observed["fontAfterChange"] == '"Extension Mono",monospace'
|
||||||
|
assert observed["fontFitDeltaBeforeFrame"] == 0
|
||||||
|
assert observed["fontPendingFramesBeforeFlush"] == 1
|
||||||
|
assert observed["fontFitDeltaAfterFrame"] == 1
|
||||||
|
assert observed["fontResizeSchedules"] == 1
|
||||||
|
assert observed["extensionFontLoadCount"] == 1
|
||||||
|
assert observed["fontPendingFramesAfterLoad"] == 1
|
||||||
|
assert observed["fontFitDeltaAfterLoad"] == 1
|
||||||
|
assert observed["fontResizeSchedulesAfterLoad"] == 2
|
||||||
|
assert observed["terminalHeadLoadListenerRegistered"] is True
|
||||||
|
assert observed["terminalHeadLoadListenerOptions"] is True
|
||||||
|
assert observed["nonStylesheetLoadWrites"] == []
|
||||||
|
assert observed["nonStylesheetLoadFitDelta"] == 0
|
||||||
|
assert observed["nonStylesheetLoadPendingFrames"] == 0
|
||||||
|
assert observed["emptyFontWrites"] == ["fontFamily"]
|
||||||
|
assert observed["emptyFontPendingFramesBeforeFlush"] == 1
|
||||||
|
assert observed["emptyFontImmediateFitDelta"] == 1
|
||||||
|
assert observed["emptyFontResizeSchedules"] == 1
|
||||||
|
assert observed["emptyFontPendingFramesAfterLoad"] == 0
|
||||||
|
assert observed["emptyFontDelayedFitDelta"] == 0
|
||||||
|
assert observed["stylesheetLoadWrites"] == ["fontFamily"]
|
||||||
|
assert observed["stylesheetLoadFontAfterChange"] == '"Stylesheet Load Mono",monospace'
|
||||||
|
assert observed["stylesheetLoadFitDeltaBeforeFrame"] == 0
|
||||||
|
assert observed["stylesheetLoadPendingFramesBeforeFlush"] == 1
|
||||||
|
assert observed["stylesheetLoadFitDeltaAfterFrame"] == 1
|
||||||
|
assert observed["stylesheetLoadResizeSchedules"] == 1
|
||||||
|
assert observed["staleCompletionFitDelta"] == 0
|
||||||
|
assert observed["staleCompletionPendingFrames"] == 0
|
||||||
|
assert observed["rejectedCompletionFitDelta"] == 0
|
||||||
|
assert observed["rejectedCompletionPendingFrames"] == 0
|
||||||
|
assert observed["unhandledFontLoadRejections"] == 0
|
||||||
|
assert observed["observerTargets"] == ["documentElement", "head"]
|
||||||
|
assert observed["observerCallbacks"] == ["documentElement", "head"]
|
||||||
|
assert observed["observedOptions"]["documentElement"] == {
|
||||||
|
"attributes": True,
|
||||||
|
"attributeFilter": ["class", "data-skin", "style"],
|
||||||
|
}
|
||||||
|
assert observed["observedOptions"]["head"] == {
|
||||||
|
"attributes": True,
|
||||||
|
"attributeFilter": ["href", "media", "disabled"],
|
||||||
|
"childList": True,
|
||||||
|
"subtree": True,
|
||||||
|
"characterData": True,
|
||||||
|
}
|
||||||
|
assert observed["pendingFitBeforeDispose"] == 1
|
||||||
|
assert observed["pendingFitAfterDispose"] == 0
|
||||||
|
assert observed["cacheReset"] is True
|
||||||
|
assert observed["oldTermIsReplaced"] is True
|
||||||
|
assert observed["recreated"]["fontFamily"] == '"Recreated Mono",monospace'
|
||||||
|
assert observed["recreated"]["theme"]["background"] == "#333333"
|
||||||
|
assert observed["recreateFitDelta"] == 1
|
||||||
|
assert observed["recreatePendingFrames"] == 0
|
||||||
|
assert observed["disposedCompletionFitDelta"] == 0
|
||||||
|
assert observed["disposedCompletionPendingFrames"] == 0
|
||||||
|
assert observed["closedCompletionFitDelta"] == 0
|
||||||
|
assert observed["closedCompletionPendingFrames"] == 0
|
||||||
|
assert observed["collapsedCompletionFitDelta"] == 0
|
||||||
|
assert observed["collapsedCompletionPendingFrames"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_first_party_technical_js_uses_font_mono_contract():
|
||||||
|
assert 'function _terminalMonoFont()' in TERMINAL_JS
|
||||||
|
assert "'ui-monospace,\"SFMono-Regular\",\"SF Mono\",Menlo,Consolas,\"Liberation Mono\",monospace'" in TERMINAL_JS
|
||||||
|
assert 'font-family:var(--font-mono)' in PANEL_JS
|
||||||
|
assert "fontFamily='var(--font-mono)'" in UI_JS
|
||||||
|
assert "font-family:var(--font-ui)" in BOOT_JS
|
||||||
|
|
||||||
|
|
||||||
|
def test_terminal_contract_updates_on_root_style_changes():
|
||||||
|
assert "attributeFilter:['class','data-skin','style']" in TERMINAL_JS
|
||||||
|
assert 'function syncComposerTerminalAppearance' in TERMINAL_JS
|
||||||
|
assert 'function _terminalMonoFont' in TERMINAL_JS
|
||||||
Reference in new issue
Block a user