toolroll 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -24
- package/dist/accent-colors.d.ts +6 -3
- package/dist/accent-colors.js +9 -4
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +23 -23
- package/dist/browser-workspace.d.ts +46 -0
- package/dist/builder.d.ts +18 -1
- package/dist/builder.js +177 -5
- package/dist/chat-delivery.js +6 -4
- package/dist/claim.d.ts +15 -1
- package/dist/claim.js +30 -2
- package/dist/control-ui.js +1 -1
- package/dist/daemon.d.ts +9 -0
- package/dist/daemon.js +21 -0
- package/dist/decision.d.ts +6 -0
- package/dist/decision.js +12 -0
- package/dist/desktop-update.d.ts +1 -1
- package/dist/dispatch.d.ts +3 -1
- package/dist/dispatch.js +13 -0
- package/dist/dispose.js +11 -7
- package/dist/exhaustion.d.ts +14 -0
- package/dist/exhaustion.js +35 -0
- package/dist/install-method.d.ts +17 -0
- package/dist/install-method.js +61 -0
- package/dist/invoke.js +26 -0
- package/dist/lead-status.d.ts +7 -0
- package/dist/lead-status.js +15 -2
- package/dist/mate.js +5 -2
- package/dist/operate.d.ts +5 -0
- package/dist/operate.js +109 -15
- package/dist/provider-auth.d.ts +138 -0
- package/dist/provider-auth.js +291 -0
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +8 -1
- package/dist/releases.d.ts +76 -0
- package/dist/releases.js +225 -0
- package/dist/serve.d.ts +4 -0
- package/dist/serve.js +207 -76
- package/dist/setup-guide.d.ts +2 -2
- package/dist/setup-guide.js +2 -0
- package/dist/skills.d.ts +6 -0
- package/dist/skills.js +8 -0
- package/dist/store.d.ts +2 -2
- package/dist/store.js +13 -4
- package/dist/subscription-chat.js +3 -0
- package/dist/update-notice.d.ts +5 -0
- package/dist/update-notice.js +4 -0
- package/dist/work-index.js +22 -4
- package/dist/worktree.d.ts +13 -0
- package/dist/worktree.js +55 -29
- package/package.json +1 -1
package/dist/serve.js
CHANGED
|
@@ -120,10 +120,12 @@ import { projectAuthority } from "./project-access.js";
|
|
|
120
120
|
* repo, only runs whose task belongs to that repo (or to no repo yet).
|
|
121
121
|
*/
|
|
122
122
|
import { createConnectionChecker } from "./provider-connection.js";
|
|
123
|
+
import { liftAuthPause, noteSignInProbe, signInNotices } from "./provider-auth.js";
|
|
123
124
|
import { openRouterModelsCache, openRouterPickerScript } from "./openrouter-models.js";
|
|
124
125
|
import { checkModels, isNewModel, livePin, modelOptions, modelWords, RUNTIME_TOOLS, runtimeStates, seenModels, setWatch, updateRuntime, watchState } from "./model-catalog.js";
|
|
125
126
|
import { MODELS_CSS, modelsHtml, modelsScript } from "./models-ui.js";
|
|
126
127
|
import { ASSISTANTS, modelChoices, detectPreparation, previewProjectInstructions, addProjectInstructions } from "./setup-guide.js";
|
|
128
|
+
import { projectSkillInstalled } from "./skills.js";
|
|
127
129
|
import { previewSetup, approveSetup } from "./control-setup.js";
|
|
128
130
|
import { controlSetupHtml, setupPreviewHtml, connectionHtml, connectionWords, hiddenFields } from "./control-ui.js";
|
|
129
131
|
import { composerSchedule, scheduleEditorHtml, scheduleEditorScript } from "./task-composer.js";
|
|
@@ -229,6 +231,10 @@ import { verifyApproverByPassword, verifyApproverStanding } from "./principal.js
|
|
|
229
231
|
import { runMateTurn, MATE_MESSAGE_MAX_CHARS } from "./mate.js";
|
|
230
232
|
import { confirmCoordinatorProposal, confirmMateProposal, dismissCoordinatorProposal, dismissMateProposal } from "./mate-doors.js";
|
|
231
233
|
import { envValue } from "./names.js";
|
|
234
|
+
import { cachedRelease, isNewer, runnerVersions, setUpdateChecks, updateChecksOff } from "./releases.js";
|
|
235
|
+
import { installMethod } from "./install-method.js";
|
|
236
|
+
import { PACKAGE_VERSION } from "./version.js";
|
|
237
|
+
import { updateNoticeWords } from "./update-notice.js";
|
|
232
238
|
const SESSION_COOKIE = "standing-orders_session";
|
|
233
239
|
const BODY_CAP = 16 * 1024;
|
|
234
240
|
// URL encoding can triple UTF-8 bytes. Admit the existing bounded task
|
|
@@ -384,11 +390,46 @@ export function createDecisionServer(options) {
|
|
|
384
390
|
}
|
|
385
391
|
}
|
|
386
392
|
const clock = options.clock ?? (() => new Date());
|
|
393
|
+
const install = installMethod(options.installBin);
|
|
394
|
+
/** What the last daily check found (the cache only, never the network), or nothing while checks are off. */
|
|
395
|
+
const updateFacts = () => {
|
|
396
|
+
if (options.configDir === undefined)
|
|
397
|
+
return null;
|
|
398
|
+
const switched = updateChecksOff(process.env, options.configDir);
|
|
399
|
+
const release = switched.off ? null : cachedRelease(options.configDir);
|
|
400
|
+
return { release, newer: release !== null && isNewer(release.version, PACKAGE_VERSION), on: !switched.off, byEnv: switched.byEnv };
|
|
401
|
+
};
|
|
402
|
+
/** Settings → Updates, for everyone who can open Settings; only an operator may flip the switch. */
|
|
403
|
+
const settingsUpdates = (actor, csrf) => {
|
|
404
|
+
const facts = updateFacts();
|
|
405
|
+
if (facts === null)
|
|
406
|
+
return null;
|
|
407
|
+
const known = new Map(runnerVersions(options.configDir).map(one => [one.runner, one.version]));
|
|
408
|
+
return {
|
|
409
|
+
current: PACKAGE_VERSION,
|
|
410
|
+
latest: facts.release === null ? null : { version: facts.release.version, newer: facts.newer, security: facts.release.security, notes: facts.release.notes, url: facts.release.url },
|
|
411
|
+
updateCommand: install.updateCommand,
|
|
412
|
+
check: { on: facts.on, byEnv: facts.byEnv, canManage: csrf !== "" && store.isInstanceOperator(actor) },
|
|
413
|
+
workers: store.listRunners().filter(one => one.retiredAt === null).map(one => {
|
|
414
|
+
const version = known.get(one.name) ?? null;
|
|
415
|
+
return { name: one.name, version, older: version !== null && isNewer(PACKAGE_VERSION, version) };
|
|
416
|
+
}),
|
|
417
|
+
};
|
|
418
|
+
};
|
|
387
419
|
const workspaceRevision = prepareWorkspaceRevision(store);
|
|
388
420
|
const workspaceIncarnation = randomBytes(16).toString('hex');
|
|
389
421
|
const workspaceValidators = new WorkspaceValidatorCache();
|
|
390
422
|
const providerHome = options.connectionHome ?? homedir();
|
|
391
|
-
const
|
|
423
|
+
const checkConnection = createConnectionChecker({ home: providerHome, clock, ...(options.connectionProbe === undefined ? {} : { probe: options.connectionProbe }) });
|
|
424
|
+
// Every sign-in check the console makes also answers a sign-in pause.
|
|
425
|
+
const connectionCheck = async (provider, fresh) => {
|
|
426
|
+
const value = await checkConnection(provider, fresh);
|
|
427
|
+
try {
|
|
428
|
+
noteSignInProbe(store, provider, value.state, clock());
|
|
429
|
+
}
|
|
430
|
+
catch { /* the pause keeps its own state */ }
|
|
431
|
+
return value;
|
|
432
|
+
};
|
|
392
433
|
const modelCatalog = openRouterModelsCache(options.modelCatalogFetcher);
|
|
393
434
|
const modelSeams = {
|
|
394
435
|
home: providerHome,
|
|
@@ -1338,6 +1379,7 @@ export function createDecisionServer(options) {
|
|
|
1338
1379
|
const requestFacts = {
|
|
1339
1380
|
theme: pinnedTheme(request.headers.cookie),
|
|
1340
1381
|
accent: pinnedAccent(request.headers.cookie),
|
|
1382
|
+
updateSeen: /(?:^|;\s*)so-update-seen=([0-9A-Za-z.-]{1,60})(?:;|$)/.exec(request.headers.cookie ?? "")?.[1] ?? null,
|
|
1341
1383
|
actor: who.name,
|
|
1342
1384
|
csrf: who.via === "cookie" ? who.session.csrf : "",
|
|
1343
1385
|
returnTo: safeReturn(url.pathname + url.search),
|
|
@@ -3752,7 +3794,7 @@ export function createDecisionServer(options) {
|
|
|
3752
3794
|
const googleView = { connected: googleConnected(options.configDir ?? null)?.address ?? null, clientId: google?.clientId ?? "", redirect: origin === null ? null : `${origin}${GOOGLE_CALLBACK}` };
|
|
3753
3795
|
return email === null ? { set: false, host: "", port: 587, secure: false, user: "", from: "", imapHost: "", imapPort: 993, google: googleView }
|
|
3754
3796
|
: { set: true, host: email.host, port: email.port, secure: email.secure, user: email.user, from: email.from, imapHost: email.imap?.host ?? "", imapPort: email.imap?.port ?? 993, google: googleView };
|
|
3755
|
-
})() : null, telegramDeliveryWords(store, loadBotToken(process.env, options.telegramTokenFile)), settingsWorkers(store, now)));
|
|
3797
|
+
})() : null, telegramDeliveryWords(store, loadBotToken(process.env, options.telegramTokenFile)), settingsWorkers(store, now), settingsUpdates(who.name, csrf)));
|
|
3756
3798
|
}
|
|
3757
3799
|
if (url.pathname === "/push/key") {
|
|
3758
3800
|
// Session-gated: the key is not secret, but strangers get nothing.
|
|
@@ -3804,7 +3846,7 @@ export function createDecisionServer(options) {
|
|
|
3804
3846
|
return null;
|
|
3805
3847
|
const repos = admissionList() ?? [];
|
|
3806
3848
|
const setupDone = repos.filter(one => store.liveWorktreeSetup(one) !== null).length;
|
|
3807
|
-
const skillDone = repos.filter(
|
|
3849
|
+
const skillDone = repos.filter(projectSkillInstalled).length;
|
|
3808
3850
|
const counted = (done) => repos.length <= 1 ? "" : ` (${done} of ${repos.length} repos)`;
|
|
3809
3851
|
return [
|
|
3810
3852
|
{
|
|
@@ -4429,6 +4471,8 @@ export function createDecisionServer(options) {
|
|
|
4429
4471
|
navigation: [...browserNavigationOf(currentPath, s.chrome.project, needsYou), { label: 'Workspace tools', href: '/menu', active: path.pathname === '/menu' }],
|
|
4430
4472
|
chats,
|
|
4431
4473
|
...(s.refreshSeconds === undefined ? {} : { refreshSeconds: Math.max(5, Math.floor(s.refreshSeconds)) }),
|
|
4474
|
+
...(s.chrome.signIn === undefined ? {} : { signIn: s.chrome.signIn }),
|
|
4475
|
+
...(s.chrome.update === undefined ? {} : { update: s.chrome.update }),
|
|
4432
4476
|
};
|
|
4433
4477
|
if (requestFacts.workspaceRead) {
|
|
4434
4478
|
const validator = requestFacts.workspaceValidator;
|
|
@@ -4544,6 +4588,16 @@ export function createDecisionServer(options) {
|
|
|
4544
4588
|
inboxSaturated: badge.saturated,
|
|
4545
4589
|
settings: true,
|
|
4546
4590
|
...(store.isDemo() ? { demo: true } : {}),
|
|
4591
|
+
...(() => { const signIn = signInNotices(store); return signIn.length === 0 ? {} : { signIn }; })(),
|
|
4592
|
+
...(() => {
|
|
4593
|
+
// Operators only (only someone at this computer can update it), and gone once this version is dismissed.
|
|
4594
|
+
if (!facts?.csrf || !actor || !store.isInstanceOperator(actor))
|
|
4595
|
+
return {};
|
|
4596
|
+
const update = updateFacts();
|
|
4597
|
+
if (update === null || !update.newer || update.release === null || facts.updateSeen === update.release.version)
|
|
4598
|
+
return {};
|
|
4599
|
+
return { update: { version: update.release.version, security: update.release.security, href: "/settings#updates", dismissHref: "/settings/updates/dismiss" } };
|
|
4600
|
+
})(),
|
|
4547
4601
|
...(liveMode === null || liveModeTerms === null
|
|
4548
4602
|
? {}
|
|
4549
4603
|
: {
|
|
@@ -5729,6 +5783,41 @@ export function createDecisionServer(options) {
|
|
|
5729
5783
|
const denied = authorizeMutation(request, who, body);
|
|
5730
5784
|
if (denied !== null)
|
|
5731
5785
|
return refuse(response, who, denied.status, denied.message);
|
|
5786
|
+
// A person says a provider's sign-in works again: its sign-in pause
|
|
5787
|
+
// lifts, its tasks may start, and one short message says so.
|
|
5788
|
+
const resumeProvider = /^\/providers\/([a-z]+)\/resume$/.exec(url.pathname);
|
|
5789
|
+
if (resumeProvider !== null) {
|
|
5790
|
+
const provider = resumeProvider[1];
|
|
5791
|
+
if (!isProviderId(provider))
|
|
5792
|
+
return refuse(response, who, 400, "unknown provider", "/work");
|
|
5793
|
+
if (who.role !== "approver")
|
|
5794
|
+
return refuse(response, who, 403, "Only an operator can resume an agent's work.", "/work");
|
|
5795
|
+
liftAuthPause(store, provider, "person", who.name, clock());
|
|
5796
|
+
return redirect(response, "/work");
|
|
5797
|
+
}
|
|
5798
|
+
// The update notice is dismissed per browser and per version: a newer release shows it again.
|
|
5799
|
+
if (url.pathname === "/settings/updates/dismiss") {
|
|
5800
|
+
const version = body.get("version") ?? "";
|
|
5801
|
+
if (!/^[0-9A-Za-z.-]{1,60}$/.test(version))
|
|
5802
|
+
return refuse(response, who, 400, "Choose a version to dismiss.", "/settings#updates");
|
|
5803
|
+
response.setHeader("Set-Cookie", `so-update-seen=${version}; SameSite=Lax; Path=/; Max-Age=31536000`);
|
|
5804
|
+
if (body.get("quiet") === "1") {
|
|
5805
|
+
response.writeHead(204);
|
|
5806
|
+
response.end();
|
|
5807
|
+
return;
|
|
5808
|
+
}
|
|
5809
|
+
return redirect(response, safeReturn(body.get("return") ?? "/settings#updates"));
|
|
5810
|
+
}
|
|
5811
|
+
// The daily check's switch: an installation setting, so an operator's.
|
|
5812
|
+
if (url.pathname === "/settings/updates") {
|
|
5813
|
+
if (options.configDir === undefined)
|
|
5814
|
+
return refuse(response, who, 404, "Updates are not set up on this console.", "/settings");
|
|
5815
|
+
if (!store.isInstanceOperator(who.name))
|
|
5816
|
+
return refuse(response, who, 403, "Only an operator can change update checks.", "/settings#updates");
|
|
5817
|
+
const on = body.get("check") === "on";
|
|
5818
|
+
setUpdateChecks(options.configDir, on);
|
|
5819
|
+
return redirect(response, `/settings?said=${encodeURIComponent(on ? "Toolroll looks for a newer version once a day." : "Update checks are off.")}#updates`);
|
|
5820
|
+
}
|
|
5732
5821
|
if (url.pathname === '/code' || url.pathname.startsWith('/code/')) {
|
|
5733
5822
|
const wantsJson = request.headers.accept?.includes('application/json') === true;
|
|
5734
5823
|
const fail = (status, message, delivery = 'rejected', sessionId) => wantsJson
|
|
@@ -6634,7 +6723,7 @@ export function createDecisionServer(options) {
|
|
|
6634
6723
|
if (accentField !== null) {
|
|
6635
6724
|
const accent = normalHex(accentField);
|
|
6636
6725
|
if (accent === null)
|
|
6637
|
-
return refuse(response, who, 400, "Choose a colour as six hex digits, like #
|
|
6726
|
+
return refuse(response, who, 400, "Choose a colour as six hex digits, like #6e56cf.", "/settings");
|
|
6638
6727
|
response.setHeader("Set-Cookie", accent === DEFAULT_ACCENT
|
|
6639
6728
|
? "so-accent=; SameSite=Lax; Path=/; Max-Age=0"
|
|
6640
6729
|
: `so-accent=${accent.slice(1)}; SameSite=Lax; Path=/; Max-Age=31536000`);
|
|
@@ -10641,7 +10730,7 @@ function page(response, status, html, nonce, fetches) {
|
|
|
10641
10730
|
* can start a sign-in and have someone else finish it into their project.
|
|
10642
10731
|
*/
|
|
10643
10732
|
/** A moment's page on the way to or back from another site's sign-in: no stylesheet may load there, so the palette rides inline. */
|
|
10644
|
-
const HANDOFF_STYLE = `<style>body{font:15px/1.5 "Geist",system-ui,sans-serif;margin:2rem;background:#efefef;color:#171717}a{color:#171717;text-decoration-color:#
|
|
10733
|
+
const HANDOFF_STYLE = `<style>body{font:15px/1.5 "Geist",system-ui,sans-serif;margin:2rem;background:#efefef;color:#171717}a{color:#171717;text-decoration-color:#8f8f8f;text-underline-offset:3px}@media(prefers-color-scheme:dark){body{background:#0b0b0b;color:#ededed}a{color:#ededed;text-decoration-color:#ff6fb5}}</style>`;
|
|
10645
10734
|
const SIGN_IN_COOKIE = "so-sign-in";
|
|
10646
10735
|
function startedHere(request, state) {
|
|
10647
10736
|
const held = new RegExp(`(?:^|;\\s*)${SIGN_IN_COOKIE}=([A-Za-z0-9_-]{16,128})`).exec(request.headers.cookie ?? "")?.[1];
|
|
@@ -11255,7 +11344,7 @@ const THEME_LIGHT = `
|
|
|
11255
11344
|
--so-ink: #171717; --so-muted: #666666; --so-line: #e6e6e6; --so-input-line: #d4d4d4;
|
|
11256
11345
|
--so-accent: #171717; --so-accent-hover: #383838; --so-accent-text: #171717; --so-on-accent: #ffffff; --so-soft: #f2f2f2;
|
|
11257
11346
|
--so-nav-ink: #525252; --so-nav-hover: #e4e4e4; --so-nav-current: #ffffff; --so-nav-current-ink: #171717;
|
|
11258
|
-
--so-signal: #
|
|
11347
|
+
--so-signal: #171717; --so-signal-hover: #383838; --so-on-signal: #ffffff; --so-signal-soft: #e8e8e8;
|
|
11259
11348
|
--so-danger: #c4320a; --so-danger-soft: #feebe7; --so-success: #218358; --so-success-soft: #e6f6eb;
|
|
11260
11349
|
--so-warning: #ab6400; --so-warning-soft: #fff4d5; --so-info: #0d74ce; --so-info-soft: #e6f4fe;
|
|
11261
11350
|
--so-neutral-ink: #525252; --so-neutral-soft: #f0f0f0; --so-live: #0d74ce;
|
|
@@ -11263,13 +11352,13 @@ const THEME_LIGHT = `
|
|
|
11263
11352
|
--so-sheet-shadow: 0 0 0 1px rgb(0 0 0 / .06), 0 1px 2px rgb(0 0 0 / .04), 0 4px 12px -6px rgb(0 0 0 / .06);
|
|
11264
11353
|
--so-pill-shadow: 0 0 0 1px rgb(0 0 0 / .06), 0 1px 2px rgb(0 0 0 / .06);
|
|
11265
11354
|
--so-overlay: rgb(0 0 0 / .32); --so-shadow-overlay: 0 0 0 1px rgb(0 0 0 / .08), 0 24px 48px -12px rgb(0 0 0 / .22);
|
|
11266
|
-
--so-selection: #
|
|
11355
|
+
--so-selection: #d4d4d4; --so-scroll: #cfcfcf; --so-code-bg: #fafafa; --so-user-bubble: #f2f2f2;`;
|
|
11267
11356
|
const THEME_DARK = `
|
|
11268
11357
|
--so-ground: #0b0b0b; --so-paper: #161616; --so-sidebar: #0b0b0b; --so-raised: #1c1c1c;
|
|
11269
11358
|
--so-ink: #ededed; --so-muted: #a1a1a1; --so-line: #262626; --so-input-line: #363636;
|
|
11270
11359
|
--so-accent: #ededed; --so-accent-hover: #ffffff; --so-accent-text: #ededed; --so-on-accent: #0a0a0a; --so-soft: #1f1f1f;
|
|
11271
11360
|
--so-nav-ink: #a1a1a1; --so-nav-hover: #171717; --so-nav-current: #1f1f1f; --so-nav-current-ink: #ededed;
|
|
11272
|
-
--so-signal: #
|
|
11361
|
+
--so-signal: #ededed; --so-signal-hover: #ffffff; --so-on-signal: #0a0a0a; --so-signal-soft: #2e2e2e;
|
|
11273
11362
|
--so-danger: #ff977d; --so-danger-soft: rgb(255 151 125 / .12); --so-success: #3dd68c; --so-success-soft: rgb(61 214 140 / .12);
|
|
11274
11363
|
--so-warning: #ffca16; --so-warning-soft: rgb(255 202 22 / .12); --so-info: #70b8ff; --so-info-soft: rgb(112 184 255 / .12);
|
|
11275
11364
|
--so-neutral-ink: #b4b4b4; --so-neutral-soft: #1f1f1f; --so-live: #70b8ff;
|
|
@@ -11277,7 +11366,7 @@ const THEME_DARK = `
|
|
|
11277
11366
|
--so-sheet-shadow: 0 0 0 1px #262626, 0 1px 2px rgb(0 0 0 / .4);
|
|
11278
11367
|
--so-pill-shadow: 0 0 0 1px #2a2a2a, 0 1px 2px rgb(0 0 0 / .5);
|
|
11279
11368
|
--so-overlay: rgb(0 0 0 / .6); --so-shadow-overlay: 0 0 0 1px #2e2e2e, 0 24px 48px -12px rgb(0 0 0 / .7);
|
|
11280
|
-
--so-selection: #
|
|
11369
|
+
--so-selection: #3a3a3a; --so-scroll: #333333; --so-code-bg: #111111; --so-user-bubble: #202020;`;
|
|
11281
11370
|
/** The console's original token names, now views onto the palette. */
|
|
11282
11371
|
const THEME_MAPPING = `
|
|
11283
11372
|
--background: var(--so-ground); --foreground: var(--so-ink); --card: var(--so-paper);
|
|
@@ -11296,7 +11385,7 @@ const STYLE = `
|
|
|
11296
11385
|
/* The Console — the design system, v4 "Signal" (2026-09-27). A neutral
|
|
11297
11386
|
frame with paper sheets inset into it (Arc, Linear), compact Raycast
|
|
11298
11387
|
density, Geist for words and Geist Mono for machine facts. Ink is every
|
|
11299
|
-
act a person can take;
|
|
11388
|
+
act a person can take; the accent (ink by default) means "waits on you" and nothing
|
|
11300
11389
|
else; blue means live. Flat surfaces: hairlines and one soft sheet
|
|
11301
11390
|
shadow, no glass. Zero dependencies, zero page JS beyond the nonce'd
|
|
11302
11391
|
chrome layer. */
|
|
@@ -11334,6 +11423,7 @@ const STYLE = `
|
|
|
11334
11423
|
${THEME_LIGHT}
|
|
11335
11424
|
${THEME_MAPPING}
|
|
11336
11425
|
--radius: 0.625rem;
|
|
11426
|
+
--so-ease-out: cubic-bezier(.23, 1, .32, 1);
|
|
11337
11427
|
--font-sans: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
11338
11428
|
--font-mono: "Geist Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
|
|
11339
11429
|
}
|
|
@@ -11348,6 +11438,11 @@ ${THEME_DARK}
|
|
|
11348
11438
|
${THEME_DARK}
|
|
11349
11439
|
}
|
|
11350
11440
|
* { box-sizing: border-box; }
|
|
11441
|
+
/* Touch: no grey flash on a tap, no double-tap zoom wait on a control,
|
|
11442
|
+
and a long press never selects a control's label. Text stays selectable. */
|
|
11443
|
+
html { -webkit-tap-highlight-color: transparent; }
|
|
11444
|
+
a, button, summary, [role=button], [role=tab] { touch-action: manipulation; }
|
|
11445
|
+
button, [role=button], [role=tab], .task-context-chip, .approval-chip, .verdict-chip { -webkit-user-select: none; user-select: none; }
|
|
11351
11446
|
::selection { background: var(--so-selection); color: var(--so-ink); }
|
|
11352
11447
|
::placeholder { color: var(--muted-foreground); }
|
|
11353
11448
|
body {
|
|
@@ -11378,7 +11473,7 @@ ${THEME_DARK}
|
|
|
11378
11473
|
color: var(--muted-foreground); text-decoration: none; font-size: 0.8125rem; font-weight: 500;
|
|
11379
11474
|
display: flex; align-items: center; padding: 0 .625rem; transition: color .15s;
|
|
11380
11475
|
}
|
|
11381
|
-
.topbar nav a:hover { color: var(--foreground); }
|
|
11476
|
+
@media (hover: hover) and (pointer: fine) { .topbar nav a:hover { color: var(--foreground); } }
|
|
11382
11477
|
main { max-width: 44rem; margin-inline: auto; padding: 1.75rem 1.25rem 4rem; }
|
|
11383
11478
|
h1 { font-size: 1.25rem; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 .25rem; line-height: 1.3; }
|
|
11384
11479
|
h1 .meta { font-weight: 400; letter-spacing: 0; }
|
|
@@ -11389,7 +11484,7 @@ ${THEME_DARK}
|
|
|
11389
11484
|
color: var(--muted-foreground); margin: 2rem 0 .5rem; font-family: var(--font-sans);
|
|
11390
11485
|
}
|
|
11391
11486
|
a { color: var(--foreground); text-decoration: underline; text-decoration-color: var(--border); text-underline-offset: 3px; }
|
|
11392
|
-
a:hover { text-decoration-color: var(--muted-foreground); }
|
|
11487
|
+
@media (hover: hover) and (pointer: fine) { a:hover { text-decoration-color: var(--muted-foreground); } }
|
|
11393
11488
|
p { margin: .4rem 0; }
|
|
11394
11489
|
code { background: var(--muted); border-radius: .3rem; padding: .1rem .35rem; font-family: var(--font-mono); font-size: .8125rem; }
|
|
11395
11490
|
.mono { font-family: var(--font-mono); font-size: .8125rem; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
|
|
@@ -11497,7 +11592,7 @@ ${THEME_DARK}
|
|
|
11497
11592
|
.row:last-of-type { border-bottom: none; }
|
|
11498
11593
|
.row .right { margin-left: auto; }
|
|
11499
11594
|
a.row { text-decoration: none; }
|
|
11500
|
-
a.row:hover { background: var(--muted); }
|
|
11595
|
+
@media (hover: hover) and (pointer: fine) { a.row:hover { background: var(--muted); } }
|
|
11501
11596
|
|
|
11502
11597
|
/* A parked decision: the question in full weight, the whole card the
|
|
11503
11598
|
tap target, the neutral border — the "needs you" header above it
|
|
@@ -11508,38 +11603,38 @@ ${THEME_DARK}
|
|
|
11508
11603
|
background: var(--card); padding: .875rem 1.125rem; margin: .625rem 0;
|
|
11509
11604
|
text-decoration: none; transition: border-color .15s;
|
|
11510
11605
|
}
|
|
11511
|
-
.decide-card:hover { border-color: color-mix(in srgb, var(--border) 55%, var(--muted-foreground)); }
|
|
11606
|
+
@media (hover: hover) and (pointer: fine) { .decide-card:hover { border-color: color-mix(in srgb, var(--border) 55%, var(--muted-foreground)); } }
|
|
11512
11607
|
.decide-card .q { font-weight: 600; margin: 0 0 .25rem; }
|
|
11513
11608
|
|
|
11514
11609
|
/* Buttons: secondary by default (paper + hairline); a form's one
|
|
11515
11610
|
submit is primary (ink on paper, paper on ink); the approve act is
|
|
11516
|
-
|
|
11611
|
+
the accent; danger is red and outlined. 2.125rem at a desk, 2.75rem to a thumb. */
|
|
11517
11612
|
button {
|
|
11518
11613
|
font: 500 0.8125rem/1.4 var(--font-sans); cursor: pointer; border-radius: calc(var(--radius) - 3px);
|
|
11519
11614
|
border: 1px solid var(--input); background: var(--card); color: var(--foreground);
|
|
11520
11615
|
padding: .35rem .75rem; min-height: 2.125rem;
|
|
11521
|
-
transition: background .12s ease-out, border-color .12s ease-out, color .12s ease-out;
|
|
11616
|
+
transition: background .12s var(--so-ease-out), border-color .12s var(--so-ease-out), color .12s var(--so-ease-out);
|
|
11522
11617
|
}
|
|
11523
|
-
button:hover { background: var(--so-soft); }
|
|
11618
|
+
@media (hover: hover) and (pointer: fine) { button:hover { background: var(--so-soft); } }
|
|
11524
11619
|
button:active { background: var(--so-nav-hover); }
|
|
11525
11620
|
form.card > button[type=submit], .sticky-actions button[type=submit], form.card .sticky-actions button {
|
|
11526
11621
|
background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); font-weight: 600;
|
|
11527
11622
|
}
|
|
11528
|
-
form.card > button[type=submit]:hover, .sticky-actions button[type=submit]:hover {
|
|
11623
|
+
@media (hover: hover) and (pointer: fine) { form.card > button[type=submit]:hover, .sticky-actions button[type=submit]:hover {
|
|
11529
11624
|
background: color-mix(in srgb, var(--primary) 85%, var(--background)); border-color: color-mix(in srgb, var(--primary) 85%, var(--background));
|
|
11530
|
-
}
|
|
11625
|
+
} }
|
|
11531
11626
|
/* The approve act is the one magenta verb: it resolves what waits on you.
|
|
11532
11627
|
The ceremony's frame is neutral so the button is the only coloured thing
|
|
11533
11628
|
in it; a danger act stays red even inside one. */
|
|
11534
11629
|
.approve-form button[type=submit], .approve-form .sticky-actions button[type=submit] {
|
|
11535
11630
|
background: var(--brand); color: var(--brand-foreground); border-color: var(--brand); font-weight: 600;
|
|
11536
11631
|
}
|
|
11537
|
-
.approve-form button[type=submit]:hover, .approve-form .sticky-actions button[type=submit]:hover { background: color-mix(in srgb, var(--brand) 85%, var(--foreground)); border-color: color-mix(in srgb, var(--brand) 85%, var(--foreground)); }
|
|
11632
|
+
@media (hover: hover) and (pointer: fine) { .approve-form button[type=submit]:hover, .approve-form .sticky-actions button[type=submit]:hover { background: color-mix(in srgb, var(--brand) 85%, var(--foreground)); border-color: color-mix(in srgb, var(--brand) 85%, var(--foreground)); } }
|
|
11538
11633
|
button.danger, .approve-form button[type=submit].danger {
|
|
11539
11634
|
color: var(--destructive); border-color: color-mix(in srgb, var(--destructive) 50%, transparent);
|
|
11540
11635
|
background: transparent; font-weight: 500;
|
|
11541
11636
|
}
|
|
11542
|
-
button.danger:hover, .approve-form button[type=submit].danger:hover { background: var(--destructive-soft); }
|
|
11637
|
+
@media (hover: hover) and (pointer: fine) { button.danger:hover, .approve-form button[type=submit].danger:hover { background: var(--destructive-soft); } }
|
|
11543
11638
|
/* Removing a credential takes a second, deliberate tap. */
|
|
11544
11639
|
details.confirm-remove { display: inline-block; margin: 0 0 0 .5rem; vertical-align: top; }
|
|
11545
11640
|
details.confirm-remove > summary { cursor: pointer; color: var(--destructive); font-size: .8125rem; min-height: 2.5rem; display: inline-flex; align-items: center; }
|
|
@@ -11553,7 +11648,7 @@ ${THEME_DARK}
|
|
|
11553
11648
|
border: 1px solid var(--input); border-radius: calc(var(--radius) - 3px);
|
|
11554
11649
|
transition: border-color .15s, box-shadow .15s;
|
|
11555
11650
|
}
|
|
11556
|
-
input:hover, textarea:hover, select:hover { border-color: var(--muted-foreground); }
|
|
11651
|
+
@media (hover: hover) and (pointer: fine) { input:hover, textarea:hover, select:hover { border-color: var(--muted-foreground); } }
|
|
11557
11652
|
input[type=number] { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
|
|
11558
11653
|
input[type=radio], input[type=checkbox] { accent-color: var(--ring); }
|
|
11559
11654
|
.permission-field { border: 0; padding: 0; margin: 1rem 0 0; min-width: 0; }
|
|
@@ -11565,7 +11660,7 @@ ${THEME_DARK}
|
|
|
11565
11660
|
border-radius: calc(var(--radius) - 3px); background: color-mix(in srgb, var(--card) 66%, transparent);
|
|
11566
11661
|
cursor: pointer; transition: border-color .15s, background .15s, box-shadow .15s;
|
|
11567
11662
|
}
|
|
11568
|
-
.permission-choice:hover { background: var(--muted); }
|
|
11663
|
+
@media (hover: hover) and (pointer: fine) { .permission-choice:hover { background: var(--muted); } }
|
|
11569
11664
|
.permission-choice:has(input:checked) {
|
|
11570
11665
|
border-color: color-mix(in srgb, var(--foreground) 42%, var(--border));
|
|
11571
11666
|
background: var(--card); box-shadow: 0 0 0 1px color-mix(in srgb, var(--foreground) 8%, transparent), var(--shadow);
|
|
@@ -11597,7 +11692,8 @@ ${THEME_DARK}
|
|
|
11597
11692
|
min-height: 8.5rem; max-height: 18rem; margin: 0; padding: .9rem 1rem; resize: vertical;
|
|
11598
11693
|
border: 0; background: transparent; box-shadow: none; font-size: 1.05rem; line-height: 1.55;
|
|
11599
11694
|
}
|
|
11600
|
-
.task-prompt textarea:
|
|
11695
|
+
.task-prompt textarea:focus-visible { border: 0; box-shadow: none; }
|
|
11696
|
+
@media (hover: hover) and (pointer: fine) { .task-prompt textarea:hover { border: 0; box-shadow: none; } }
|
|
11601
11697
|
.task-repo { margin: .25rem .45rem .7rem; }
|
|
11602
11698
|
.task-composer-footer { display: flex; align-items: center; gap: .5rem; padding: .25rem; }
|
|
11603
11699
|
.task-context { display: flex; align-items: center; gap: .4rem; flex: 1 1 auto; min-width: 0; }
|
|
@@ -11616,7 +11712,7 @@ ${THEME_DARK}
|
|
|
11616
11712
|
flex: none; min-height: 2.45rem; padding: .45rem .95rem; border-radius: 999px;
|
|
11617
11713
|
background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); font-weight: 600;
|
|
11618
11714
|
}
|
|
11619
|
-
.task-submit:hover { background: color-mix(in srgb, var(--primary) 85%, var(--background)); border-color: transparent; }
|
|
11715
|
+
@media (hover: hover) and (pointer: fine) { .task-submit:hover { background: color-mix(in srgb, var(--primary) 85%, var(--background)); border-color: transparent; } }
|
|
11620
11716
|
details.task-options {
|
|
11621
11717
|
margin: .65rem .25rem 0; padding: .15rem .5rem 0; border: 0; border-top: 1px solid var(--glass-border);
|
|
11622
11718
|
border-radius: 0; background: transparent;
|
|
@@ -11703,7 +11799,7 @@ ${THEME_DARK}
|
|
|
11703
11799
|
.receipt-visuals { display: grid; grid-template-columns: repeat(auto-fill, minmax(8rem, 14rem)); gap: .55rem; margin-top: .75rem; }
|
|
11704
11800
|
.receipt-shot { display: grid; gap: .35rem; color: var(--muted-foreground); font-size: .7rem; text-decoration: none; }
|
|
11705
11801
|
.receipt-shot img { display: block; width: 100%; aspect-ratio: 16 / 10; object-fit: cover; border: 1px solid var(--glass-border); border-radius: calc(var(--radius) - 3px); background: var(--muted); }
|
|
11706
|
-
.receipt-shot:hover { color: var(--foreground); }
|
|
11802
|
+
@media (hover: hover) and (pointer: fine) { .receipt-shot:hover { color: var(--foreground); } }
|
|
11707
11803
|
.receipt-caveats, .receipt-coverage { margin-top: .8rem; padding: .7rem .8rem; border-left: 1px solid var(--border); border-radius: 0 calc(var(--radius) - 3px) calc(var(--radius) - 3px) 0; background: color-mix(in srgb, var(--muted) 62%, transparent); font-size: .78rem; }
|
|
11708
11804
|
.receipt-caveats ul, .receipt-coverage ul { margin: .3rem 0 0; padding-left: 1.15rem; }
|
|
11709
11805
|
/* Secondary receipt detail (concise pass, 2026-09-13): native
|
|
@@ -11735,7 +11831,7 @@ ${THEME_DARK}
|
|
|
11735
11831
|
display: grid; gap: .2rem; padding: .55rem .65rem; margin-bottom: .2rem; min-width: 0;
|
|
11736
11832
|
border: 1px solid transparent; border-radius: calc(var(--radius) - 3px); text-decoration: none; color: var(--foreground);
|
|
11737
11833
|
}
|
|
11738
|
-
.cockpit-row:hover { background: var(--card); }
|
|
11834
|
+
@media (hover: hover) and (pointer: fine) { .cockpit-row:hover { background: var(--card); } }
|
|
11739
11835
|
.cockpit-row.current { background: var(--muted); border-color: color-mix(in srgb, var(--foreground) 12%, var(--border)); }
|
|
11740
11836
|
.cockpit-row-head { display: flex; align-items: flex-start; justify-content: space-between; gap: .5rem; min-width: 0; }
|
|
11741
11837
|
.cockpit-row-head strong { min-width: 0; overflow-wrap: anywhere; font-size: .8125rem; font-weight: 550; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
|
|
@@ -11812,7 +11908,7 @@ ${THEME_DARK}
|
|
|
11812
11908
|
min-height: 1.75rem; padding: .2rem .7rem; border: 0; border-radius: 999px;
|
|
11813
11909
|
background: transparent; box-shadow: none; color: var(--muted-foreground); font-size: .7rem;
|
|
11814
11910
|
}
|
|
11815
|
-
.diff-modes button:hover { transform: none; color: var(--foreground); }
|
|
11911
|
+
@media (hover: hover) and (pointer: fine) { .diff-modes button:hover { transform: none; color: var(--foreground); } }
|
|
11816
11912
|
.diff-modes button[aria-pressed="true"] { background: var(--card); color: var(--foreground); box-shadow: 0 1px 3px color-mix(in srgb, var(--background) 20%, transparent); }
|
|
11817
11913
|
.diff-review-help {
|
|
11818
11914
|
margin: 0; padding: .62rem .85rem; border-bottom: 1px solid var(--glass-border);
|
|
@@ -11849,7 +11945,7 @@ ${THEME_DARK}
|
|
|
11849
11945
|
display: grid; grid-template-columns: 2rem 3.2rem 3.2rem minmax(max-content, 1fr); align-items: stretch;
|
|
11850
11946
|
min-width: max-content; min-height: 1.8rem; font: 400 .72rem/1.55 var(--font-mono);
|
|
11851
11947
|
}
|
|
11852
|
-
.diff-line:hover { background: color-mix(in srgb, var(--foreground) 4%, transparent); }
|
|
11948
|
+
@media (hover: hover) and (pointer: fine) { .diff-line:hover { background: color-mix(in srgb, var(--foreground) 4%, transparent); } }
|
|
11853
11949
|
.diff-line code { display: flex; min-width: 0; padding: .3rem .75rem .3rem .6rem; color: inherit; white-space: pre; }
|
|
11854
11950
|
.diff-line code b { display: inline-block; width: 1rem; flex: none; font-weight: 500; opacity: .72; }
|
|
11855
11951
|
.diff-gutter {
|
|
@@ -11865,7 +11961,7 @@ ${THEME_DARK}
|
|
|
11865
11961
|
}
|
|
11866
11962
|
.diff-annotate { padding: 0; background: color-mix(in srgb, var(--card) 94%, transparent); color: var(--muted-foreground); box-shadow: none; opacity: .35; }
|
|
11867
11963
|
.diff-annotate svg { width: .78rem; height: .78rem; }
|
|
11868
|
-
.diff-annotate:hover { transform: none; background: color-mix(in srgb, var(--running) 15%, var(--card)); color: var(--running); opacity: 1; }
|
|
11964
|
+
@media (hover: hover) and (pointer: fine) { .diff-annotate:hover { transform: none; background: color-mix(in srgb, var(--running) 15%, var(--card)); color: var(--running); opacity: 1; } }
|
|
11869
11965
|
.diff-annotate-space { background: color-mix(in srgb, var(--card) 94%, transparent); }
|
|
11870
11966
|
.diff-review[data-mode="view"] .diff-line { grid-template-columns: 0 3.2rem 3.2rem minmax(max-content, 1fr); }
|
|
11871
11967
|
.diff-review[data-mode="view"] .diff-annotate,
|
|
@@ -11935,11 +12031,11 @@ ${THEME_DARK}
|
|
|
11935
12031
|
details[open] { padding-bottom: .875rem; }
|
|
11936
12032
|
details.arm-danger { border-color: color-mix(in srgb, var(--destructive) 30%, transparent); }
|
|
11937
12033
|
summary { padding: .5rem 0; cursor: pointer; font-weight: 500; font-size: 0.8125rem; color: var(--muted-foreground); min-height: 2.25rem; }
|
|
11938
|
-
summary:hover { color: var(--foreground); }
|
|
12034
|
+
@media (hover: hover) and (pointer: fine) { summary:hover { color: var(--foreground); } }
|
|
11939
12035
|
details form.option { border: none; padding: .25rem 0 0; margin: 0; }
|
|
11940
12036
|
.evidence { margin-top: 1.5rem; font-size: 0.8125rem; }
|
|
11941
12037
|
.evidence a { display: block; padding: .55rem 0; border-bottom: 1px solid var(--border); text-decoration: none; }
|
|
11942
|
-
.evidence a:hover { color: var(--muted-foreground); }
|
|
12038
|
+
@media (hover: hover) and (pointer: fine) { .evidence a:hover { color: var(--muted-foreground); } }
|
|
11943
12039
|
.evidence strong { display: block; font-size: 0.6875rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted-foreground); font-family: var(--font-mono); }
|
|
11944
12040
|
|
|
11945
12041
|
.filters { font-size: 0.8125rem; color: var(--muted-foreground); }
|
|
@@ -11948,7 +12044,7 @@ ${THEME_DARK}
|
|
|
11948
12044
|
color: var(--muted-foreground); font-weight: 500;
|
|
11949
12045
|
}
|
|
11950
12046
|
.filters strong { background: var(--muted); color: var(--foreground); }
|
|
11951
|
-
.filters a:hover { color: var(--foreground); }
|
|
12047
|
+
@media (hover: hover) and (pointer: fine) { .filters a:hover { color: var(--foreground); } }
|
|
11952
12048
|
|
|
11953
12049
|
/* A picked tournament result: marked in the built green, not magenta. */
|
|
11954
12050
|
.card.picked { border-color: color-mix(in srgb, var(--success) 40%, var(--border)); }
|
|
@@ -11976,7 +12072,7 @@ ${THEME_DARK}
|
|
|
11976
12072
|
display: grid; place-items: center; flex: 0 0 2rem; width: 2rem; min-height: 2rem; padding: 0;
|
|
11977
12073
|
border-color: transparent; background: transparent; color: var(--muted-foreground); box-shadow: none;
|
|
11978
12074
|
}
|
|
11979
|
-
.side-toggle:hover { background: var(--glass); color: var(--foreground); transform: none; }
|
|
12075
|
+
@media (hover: hover) and (pointer: fine) { .side-toggle:hover { background: var(--glass); color: var(--foreground); transform: none; } }
|
|
11980
12076
|
.side-toggle svg { width: 1rem; height: 1rem; }
|
|
11981
12077
|
/* The scope bar: one hairline row, the single scope truth on every
|
|
11982
12078
|
* screen; its name is the switcher. Magenta never appears here except
|
|
@@ -12012,7 +12108,7 @@ ${THEME_DARK}
|
|
|
12012
12108
|
border-right: 1.75px solid var(--foreground); border-bottom: 1.75px solid var(--foreground);
|
|
12013
12109
|
transform: rotate(45deg) translateY(-.125rem);
|
|
12014
12110
|
}
|
|
12015
|
-
.switcher-menu button:hover { background: var(--muted); }
|
|
12111
|
+
@media (hover: hover) and (pointer: fine) { .switcher-menu button:hover { background: var(--muted); } }
|
|
12016
12112
|
.switcher-menu .manage {
|
|
12017
12113
|
display: block; margin-top: .25rem; padding: .625rem .75rem; border-top: 1px solid var(--border);
|
|
12018
12114
|
font-size: .75rem; color: var(--muted-foreground); text-decoration: none;
|
|
@@ -12024,14 +12120,14 @@ ${THEME_DARK}
|
|
|
12024
12120
|
}
|
|
12025
12121
|
.scope-status .hot { color: var(--brand); font-weight: 500; }
|
|
12026
12122
|
.scope-status a { color: inherit; text-decoration: none; }
|
|
12027
|
-
.scope-status a:hover { text-decoration: underline; }
|
|
12123
|
+
@media (hover: hover) and (pointer: fine) { .scope-status a:hover { text-decoration: underline; } }
|
|
12028
12124
|
/* A project card's name and counts are forms or links dressed as text and chips. */
|
|
12029
12125
|
.project-card button.project-name, .project-card a.project-name {
|
|
12030
12126
|
all: unset; cursor: pointer; font-weight: 600; color: var(--foreground);
|
|
12031
12127
|
}
|
|
12032
|
-
.project-card a.project-name:hover, .project-card button.project-name:hover { text-decoration: underline; }
|
|
12128
|
+
@media (hover: hover) and (pointer: fine) { .project-card a.project-name:hover, .project-card button.project-name:hover { text-decoration: underline; } }
|
|
12033
12129
|
.project-card button.badge { min-height: auto; box-shadow: none; cursor: pointer; }
|
|
12034
|
-
.project-card button.badge:hover, .project-card a.badge:hover { border-color: var(--input); }
|
|
12130
|
+
@media (hover: hover) and (pointer: fine) { .project-card button.badge:hover, .project-card a.badge:hover { border-color: var(--input); } }
|
|
12035
12131
|
/* Adding a project is the page's primary job, not badge-sized metadata.
|
|
12036
12132
|
* Two roomy action tiles make both roads obvious on a desk and give each
|
|
12037
12133
|
* one a generous thumb target on a phone. The manual path stays tertiary. */
|
|
@@ -12054,11 +12150,11 @@ ${THEME_DARK}
|
|
|
12054
12150
|
|
|
12055
12151
|
transition: transform .15s ease, background .15s, border-color .15s, box-shadow .15s;
|
|
12056
12152
|
}
|
|
12057
|
-
.project-add-action:hover {
|
|
12153
|
+
@media (hover: hover) and (pointer: fine) { .project-add-action:hover {
|
|
12058
12154
|
border-color: color-mix(in srgb, var(--border) 50%, var(--muted-foreground));
|
|
12059
12155
|
background: var(--card); text-decoration: none;
|
|
12060
12156
|
box-shadow: var(--shadow);
|
|
12061
|
-
}
|
|
12157
|
+
} }
|
|
12062
12158
|
.project-add-action:active { transform: translateY(0); }
|
|
12063
12159
|
.project-add-icon {
|
|
12064
12160
|
display: grid; place-items: center; width: 2.75rem; height: 2.75rem;
|
|
@@ -12078,7 +12174,7 @@ ${THEME_DARK}
|
|
|
12078
12174
|
display: flex; align-items: center; min-height: 2.75rem; width: fit-content;
|
|
12079
12175
|
color: var(--muted-foreground); cursor: pointer; font-size: .8125rem; font-weight: 500;
|
|
12080
12176
|
}
|
|
12081
|
-
.project-add-more > summary:hover { color: var(--foreground); }
|
|
12177
|
+
@media (hover: hover) and (pointer: fine) { .project-add-more > summary:hover { color: var(--foreground); } }
|
|
12082
12178
|
.project-add-more > .card { margin: 0 0 .25rem; }
|
|
12083
12179
|
.side nav { display: flex; flex-direction: column; gap: .125rem; }
|
|
12084
12180
|
/* Inline decision options: neutral buttons — the card's magenta outline is
|
|
@@ -12101,7 +12197,7 @@ ${THEME_DARK}
|
|
|
12101
12197
|
font-family: var(--font-sans);
|
|
12102
12198
|
}
|
|
12103
12199
|
.nav-group > summary::-webkit-details-marker { display: none; }
|
|
12104
|
-
.nav-group > summary:hover { background: var(--glass); color: var(--foreground); }
|
|
12200
|
+
@media (hover: hover) and (pointer: fine) { .nav-group > summary:hover { background: var(--glass); color: var(--foreground); } }
|
|
12105
12201
|
.nav-group > summary .chevron { width: .875rem; height: .875rem; flex: none; transition: transform .15s; }
|
|
12106
12202
|
.nav-group[open] > summary .chevron { transform: rotate(180deg); }
|
|
12107
12203
|
.nav-group .nav-group-items { display: flex; flex-direction: column; gap: .125rem; margin: .125rem 0 .25rem; }
|
|
@@ -12115,7 +12211,8 @@ ${THEME_DARK}
|
|
|
12115
12211
|
}
|
|
12116
12212
|
.side nav a .glyph { display: inline-flex; width: 1rem; height: 1rem; color: var(--muted-foreground); flex: none; }
|
|
12117
12213
|
.side nav a .glyph svg { width: 1rem; height: 1rem; }
|
|
12118
|
-
.side nav a:hover { background: var(--glass); color: var(--foreground); transform: translateX(2px); }
|
|
12214
|
+
@media (hover: hover) and (pointer: fine) { .side nav a:hover { background: var(--glass); color: var(--foreground); transform: translateX(2px); } }
|
|
12215
|
+
.side nav a:active { background: var(--glass); color: var(--foreground); }
|
|
12119
12216
|
.app.sidebar-collapsed { grid-template-columns: 64px minmax(0, 1fr); }
|
|
12120
12217
|
.app.sidebar-collapsed .side { padding-inline: .625rem; }
|
|
12121
12218
|
.app.sidebar-collapsed .side-head { flex-direction: column; gap: .2rem; margin-bottom: .55rem; }
|
|
@@ -12148,14 +12245,14 @@ ${THEME_DARK}
|
|
|
12148
12245
|
border-radius: calc(var(--radius) - 3px); padding: .525rem; margin: .75rem 0 .125rem;
|
|
12149
12246
|
box-shadow: 0 10px 28px -18px rgb(255 255 255 / .5);
|
|
12150
12247
|
}
|
|
12151
|
-
.side .new-task:hover { background: color-mix(in srgb, var(--primary) 85%, var(--background)); }
|
|
12248
|
+
@media (hover: hover) and (pointer: fine) { .side .new-task:hover { background: color-mix(in srgb, var(--primary) 85%, var(--background)); } }
|
|
12152
12249
|
/* The same action link outside the sidebar reads as a real button. */
|
|
12153
12250
|
.content .new-task {
|
|
12154
12251
|
display: inline-block; text-decoration: none; font-weight: 500; font-size: .8125rem;
|
|
12155
12252
|
background: var(--secondary); color: var(--foreground); border: 1px solid var(--border);
|
|
12156
12253
|
border-radius: calc(var(--radius) - 2px); padding: .5rem .875rem;
|
|
12157
12254
|
}
|
|
12158
|
-
.content .new-task:hover { background: color-mix(in srgb, var(--secondary) 70%, var(--border)); }
|
|
12255
|
+
@media (hover: hover) and (pointer: fine) { .content .new-task:hover { background: color-mix(in srgb, var(--secondary) 70%, var(--border)); } }
|
|
12159
12256
|
.content { min-width: 0; }
|
|
12160
12257
|
.content > main { max-width: 54rem; margin: 0; padding: 2rem 2.5rem 4rem; }
|
|
12161
12258
|
|
|
@@ -12166,6 +12263,7 @@ ${THEME_DARK}
|
|
|
12166
12263
|
}
|
|
12167
12264
|
.banner .badge { margin-right: .5rem; }
|
|
12168
12265
|
.banner a { color: var(--muted-foreground); }
|
|
12266
|
+
.sign-in-banner form { display: inline; margin: 0 0 0 .5rem; padding: 0; border: 0; background: transparent; }
|
|
12169
12267
|
|
|
12170
12268
|
.split { display: grid; grid-template-columns: minmax(250px, 320px) minmax(0, 1fr); min-height: 100vh; }
|
|
12171
12269
|
.list-pane {
|
|
@@ -12177,7 +12275,7 @@ ${THEME_DARK}
|
|
|
12177
12275
|
display: block; padding: .5rem .625rem; border-radius: calc(var(--radius) - 4px);
|
|
12178
12276
|
text-decoration: none; font-size: .8125rem; margin-bottom: .125rem;
|
|
12179
12277
|
}
|
|
12180
|
-
.list-pane a.item:hover { background: var(--card); }
|
|
12278
|
+
@media (hover: hover) and (pointer: fine) { .list-pane a.item:hover { background: var(--card); } }
|
|
12181
12279
|
.list-pane a.item.current { background: var(--muted); }
|
|
12182
12280
|
.list-pane a.item .t { display: block; font-weight: 500; color: var(--foreground);
|
|
12183
12281
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
@@ -12206,13 +12304,13 @@ ${THEME_DARK}
|
|
|
12206
12304
|
min-width: 4.6rem; padding: .4rem .72rem; border-radius: .52rem; color: var(--muted-foreground);
|
|
12207
12305
|
font-size: .72rem; font-weight: 550; text-align: center; text-decoration: none;
|
|
12208
12306
|
}
|
|
12209
|
-
.task-view-switch a:hover { color: var(--foreground); background: color-mix(in srgb, var(--muted) 70%, transparent); }
|
|
12307
|
+
@media (hover: hover) and (pointer: fine) { .task-view-switch a:hover { color: var(--foreground); background: color-mix(in srgb, var(--muted) 70%, transparent); } }
|
|
12210
12308
|
.task-view-switch a.active { color: var(--foreground); background: var(--glass-strong); box-shadow: 0 1px 5px rgb(0 0 0 / .1); }
|
|
12211
12309
|
.acts-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: .75rem 0 .25rem; }
|
|
12212
12310
|
.acts-bar form.inline { margin: 0; display: inline-flex; align-items: center; gap: .375rem; }
|
|
12213
12311
|
.acts-bar form.inline button { width: auto; }
|
|
12214
12312
|
.acts-bar .primary button { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); font-weight: 600; }
|
|
12215
|
-
.acts-bar .primary button:hover { background: color-mix(in srgb, var(--primary) 85%, var(--background)); }
|
|
12313
|
+
@media (hover: hover) and (pointer: fine) { .acts-bar .primary button:hover { background: color-mix(in srgb, var(--primary) 85%, var(--background)); } }
|
|
12216
12314
|
.acts-bar .act-hold input[type=text] { width: 10rem; min-height: 2.25rem; margin: 0; font-size: .8125rem; }
|
|
12217
12315
|
.acts-why { margin: 0 0 .5rem; }
|
|
12218
12316
|
.dispatch-status { padding: .8rem .9rem; border-radius: var(--radius); overflow: hidden; }
|
|
@@ -12491,7 +12589,7 @@ ${THEME_DARK}
|
|
|
12491
12589
|
transform: rotate(45deg) translateY(-.125rem); transition: transform .15s;
|
|
12492
12590
|
}
|
|
12493
12591
|
details.section[open] > summary::after { transform: rotate(225deg) translateY(-.125rem); }
|
|
12494
|
-
details.section > summary:hover h2 { color: var(--foreground); }
|
|
12592
|
+
@media (hover: hover) and (pointer: fine) { details.section > summary:hover h2 { color: var(--foreground); } }
|
|
12495
12593
|
details.more-agents { margin: .75rem 0 0; }
|
|
12496
12594
|
.task-layout > .task-main { min-width: 0; }
|
|
12497
12595
|
.task-rail { position: sticky; top: 1rem; }
|
|
@@ -12558,7 +12656,7 @@ ${THEME_DARK}
|
|
|
12558
12656
|
background: var(--card); box-shadow: var(--shadow);
|
|
12559
12657
|
}
|
|
12560
12658
|
.work-tools-menu a { display: block; padding: .5rem .625rem; border-radius: .5rem; text-decoration: none; color: var(--foreground); font-size: .875rem; }
|
|
12561
|
-
.work-tools-menu a:hover { background: var(--muted); }
|
|
12659
|
+
@media (hover: hover) and (pointer: fine) { .work-tools-menu a:hover { background: var(--muted); } }
|
|
12562
12660
|
/* The result panel (workspace package 3): one presentation of a finished
|
|
12563
12661
|
result for the run page, the review cockpit, and the chat's result
|
|
12564
12662
|
detail — the deliverable first, problems ahead of readiness words,
|
|
@@ -12584,7 +12682,7 @@ ${THEME_DARK}
|
|
|
12584
12682
|
.result-notes > summary::-webkit-details-marker, .result-details > summary::-webkit-details-marker { display: none; }
|
|
12585
12683
|
.result-notes > summary::before, .result-details > summary::before { content: "▸"; }
|
|
12586
12684
|
.result-notes[open] > summary::before, .result-details[open] > summary::before { content: "▾"; }
|
|
12587
|
-
.result-notes > summary:hover, .result-details > summary:hover { color: var(--foreground); }
|
|
12685
|
+
@media (hover: hover) and (pointer: fine) { .result-notes > summary:hover, .result-details > summary:hover { color: var(--foreground); } }
|
|
12588
12686
|
.result-details > summary .meta { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono); font-size: .68rem; font-weight: 400; }
|
|
12589
12687
|
.result-notes .recap { margin: .25rem 0 .4rem; font-size: .875rem; }
|
|
12590
12688
|
.result-attention { margin: .75rem 0 .25rem; padding: .7rem .85rem; border-left: 1px solid var(--warning); border-radius: 0 calc(var(--radius) - 3px) calc(var(--radius) - 3px) 0; background: var(--warning-soft); font-size: .8125rem; }
|
|
@@ -12695,9 +12793,9 @@ ${THEME_DARK}
|
|
|
12695
12793
|
.work-details > summary::-webkit-details-marker { display: none; }
|
|
12696
12794
|
.work-details > summary::before { content: "▸"; }
|
|
12697
12795
|
.work-details[open] > summary::before { content: "▾"; }
|
|
12698
|
-
.work-details > summary:hover { color: var(--foreground); }
|
|
12796
|
+
@media (hover: hover) and (pointer: fine) { .work-details > summary:hover { color: var(--foreground); } }
|
|
12699
12797
|
.work-title { display: block; font-weight: 600; font-size: .9375rem; line-height: 1.35; color: var(--foreground); text-decoration: none; overflow-wrap: anywhere; }
|
|
12700
|
-
.work-title:hover { text-decoration: underline; }
|
|
12798
|
+
@media (hover: hover) and (pointer: fine) { .work-title:hover { text-decoration: underline; } }
|
|
12701
12799
|
.work-meta { display: flex; flex-wrap: wrap; gap: .25rem .625rem; margin: .25rem 0 0; font-size: .75rem; color: var(--muted-foreground); }
|
|
12702
12800
|
.work-meta .mono { overflow-wrap: anywhere; }
|
|
12703
12801
|
.project-label { display: inline-block; padding: 0 .4rem; border: 1px solid var(--border); border-radius: 999px; font-size: .6875rem; line-height: 1.5; color: var(--foreground); }
|
|
@@ -12852,7 +12950,7 @@ ${THEME_DARK}
|
|
|
12852
12950
|
border: 1px solid var(--border); border-radius: var(--radius); background: var(--card);
|
|
12853
12951
|
padding: .75rem .875rem; color: var(--foreground); min-height: 44px; justify-content: center;
|
|
12854
12952
|
}
|
|
12855
|
-
.menu-row:hover { border-color: color-mix(in srgb, var(--border) 60%, var(--muted-foreground)); }
|
|
12953
|
+
@media (hover: hover) and (pointer: fine) { .menu-row:hover { border-color: color-mix(in srgb, var(--border) 60%, var(--muted-foreground)); } }
|
|
12856
12954
|
|
|
12857
12955
|
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(13.5rem, 1fr)); gap: .625rem; margin: .5rem 0; }
|
|
12858
12956
|
.stat-card {
|
|
@@ -12887,7 +12985,7 @@ ${THEME_DARK}
|
|
|
12887
12985
|
width: 100%; margin-top: 1.25rem;
|
|
12888
12986
|
background: var(--so-accent); color: var(--so-on-accent); border-color: var(--so-accent); font-weight: 600;
|
|
12889
12987
|
}
|
|
12890
|
-
.login-shell button:hover { background: var(--so-accent-hover); border-color: var(--so-accent-hover); }
|
|
12988
|
+
@media (hover: hover) and (pointer: fine) { .login-shell button:hover { background: var(--so-accent-hover); border-color: var(--so-accent-hover); } }
|
|
12891
12989
|
.login-foot { text-align: center; margin: 1.5rem 0 0; font-size: 0.75rem; color: var(--muted-foreground); line-height: 1.9; }
|
|
12892
12990
|
.login-foot code { background: none; padding: 0; color: var(--muted-foreground); white-space: nowrap; }
|
|
12893
12991
|
.so-wordmark { display: flex; align-items: center; gap: 9px; font-size: 13px; font-weight: 600; text-decoration: none; white-space: nowrap; letter-spacing: -.02em; color: var(--foreground); }
|
|
@@ -12899,7 +12997,7 @@ ${THEME_DARK}
|
|
|
12899
12997
|
.sso-step-up[data-sso-step-up="confirmed"] { color: var(--so-success); }
|
|
12900
12998
|
.sso-step-up[data-sso-step-up="confirmed"]::before { content: "✓"; margin-right: 6px; }
|
|
12901
12999
|
.login-shell .login-sso { display: flex; justify-content: center; align-items: center; min-height: 44px; border-radius: 8px; background: var(--so-accent); color: var(--so-on-accent); font-weight: 600; text-decoration: none; margin: 0 0 1rem; }
|
|
12902
|
-
.login-shell .login-sso:hover { background: var(--so-accent-hover); }
|
|
13000
|
+
@media (hover: hover) and (pointer: fine) { .login-shell .login-sso:hover { background: var(--so-accent-hover); } }
|
|
12903
13001
|
.login-shell .login-password > summary { text-align: center; font-size: .875rem; color: var(--muted-foreground); cursor: pointer; margin: 0 0 .75rem; }
|
|
12904
13002
|
.focus-page { max-width: 40rem; margin: 0 auto; padding: 2rem 1.25rem 3rem; }
|
|
12905
13003
|
.focus-page .focus-brand { display: inline-flex; margin: 0 0 2.5rem; }
|
|
@@ -12952,7 +13050,7 @@ ${THEME_DARK}
|
|
|
12952
13050
|
.lane > summary::-webkit-details-marker { display: none; }
|
|
12953
13051
|
.lane h2 { display: flex; align-items: center; gap: .4rem; margin: 0; font-size: .6875rem; }
|
|
12954
13052
|
.lane h2 a { color: inherit; text-decoration: none; }
|
|
12955
|
-
.lane h2 a:hover { text-decoration: underline; }
|
|
13053
|
+
@media (hover: hover) and (pointer: fine) { .lane h2 a:hover { text-decoration: underline; } }
|
|
12956
13054
|
.lane .hint { margin-top: 0; }
|
|
12957
13055
|
.lane-count { color: var(--muted-foreground); font-weight: 400; font-variant-numeric: tabular-nums; }
|
|
12958
13056
|
.lane h2::before {
|
|
@@ -12967,7 +13065,7 @@ ${THEME_DARK}
|
|
|
12967
13065
|
background: var(--card); border: 1px solid var(--border);
|
|
12968
13066
|
border-radius: calc(var(--radius) - 2px); padding: .5rem .625rem; margin-top: .5rem;
|
|
12969
13067
|
}
|
|
12970
|
-
.lane-card:hover { border-color: color-mix(in srgb, var(--border) 55%, var(--muted-foreground)); }
|
|
13068
|
+
@media (hover: hover) and (pointer: fine) { .lane-card:hover { border-color: color-mix(in srgb, var(--border) 55%, var(--muted-foreground)); } }
|
|
12971
13069
|
.lane-card .id { display: block; font-family: var(--font-mono); font-size: .6875rem; color: var(--muted-foreground); margin-bottom: .125rem; overflow-wrap: anywhere; }
|
|
12972
13070
|
.lane-card .t { display: block; font-size: .8125rem; font-weight: 500; line-height: 1.35; }
|
|
12973
13071
|
.lane-card .dot { margin-right: .4rem; }
|
|
@@ -13165,7 +13263,7 @@ ${THEME_DARK}
|
|
|
13165
13263
|
border: 1px solid var(--glass-border); border-radius: calc(var(--radius) - 3px);
|
|
13166
13264
|
background: color-mix(in srgb, var(--glass-strong) 64%, transparent); text-decoration: none;
|
|
13167
13265
|
}
|
|
13168
|
-
.chat-overview-stat:hover { background: var(--glass-strong); }
|
|
13266
|
+
@media (hover: hover) and (pointer: fine) { .chat-overview-stat:hover { background: var(--glass-strong); } }
|
|
13169
13267
|
.chat-overview-stat b { color: var(--foreground); font: 600 1.2rem/1.2 var(--font-mono); font-variant-numeric: tabular-nums; }
|
|
13170
13268
|
.chat-overview-stat span { overflow: hidden; color: var(--muted-foreground); font-size: .65rem; text-overflow: ellipsis; white-space: nowrap; }
|
|
13171
13269
|
.chat-overview-stat.attention b { color: var(--warning); }
|
|
@@ -13175,7 +13273,7 @@ ${THEME_DARK}
|
|
|
13175
13273
|
display: grid; grid-template-columns: 1.75rem minmax(0, 1fr) auto; align-items: center; gap: .6rem;
|
|
13176
13274
|
padding: .48rem .55rem; border-radius: calc(var(--radius) - 4px); color: inherit; text-decoration: none;
|
|
13177
13275
|
}
|
|
13178
|
-
.chat-overview-item:hover { background: color-mix(in srgb, var(--muted) 72%, transparent); }
|
|
13276
|
+
@media (hover: hover) and (pointer: fine) { .chat-overview-item:hover { background: color-mix(in srgb, var(--muted) 72%, transparent); } }
|
|
13179
13277
|
.chat-overview-icon { display: grid; place-items: center; width: 1.75rem; height: 1.75rem; border-radius: .55rem; background: var(--muted); color: var(--muted-foreground); }
|
|
13180
13278
|
.chat-overview-icon svg { width: .9rem; height: .9rem; }
|
|
13181
13279
|
.chat-overview-item.decision .chat-overview-icon { color: var(--warning); background: var(--warning-soft); }
|
|
@@ -13202,10 +13300,10 @@ ${THEME_DARK}
|
|
|
13202
13300
|
background: color-mix(in srgb, var(--muted) 58%, transparent);
|
|
13203
13301
|
transition: transform .16s ease, background .16s, border-color .16s, box-shadow .16s;
|
|
13204
13302
|
}
|
|
13205
|
-
.chat-project-card:hover {
|
|
13303
|
+
@media (hover: hover) and (pointer: fine) { .chat-project-card:hover {
|
|
13206
13304
|
border-color: var(--glass-border); background: color-mix(in srgb, var(--muted) 82%, transparent);
|
|
13207
13305
|
box-shadow: 0 12px 28px -24px rgb(0 0 0 / .8);
|
|
13208
|
-
}
|
|
13306
|
+
} }
|
|
13209
13307
|
.chat-project-name { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: .45rem; }
|
|
13210
13308
|
.chat-project-name strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .8125rem; }
|
|
13211
13309
|
.chat-project-name .mono { color: var(--running); font-size: .6875rem; }
|
|
@@ -13277,7 +13375,7 @@ ${THEME_DARK}
|
|
|
13277
13375
|
.proposal-actions form { margin: 0; }
|
|
13278
13376
|
/* The confirm is the card's one primary; dismiss stays quiet (UI polish 2026-09-13). */
|
|
13279
13377
|
.proposal-actions .acts form:first-child button[type=submit] { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); font-weight: 600; }
|
|
13280
|
-
.proposal-actions .acts form:first-child button[type=submit]:hover { background: color-mix(in srgb, var(--primary) 85%, var(--background)); border-color: color-mix(in srgb, var(--primary) 85%, var(--background)); }
|
|
13378
|
+
@media (hover: hover) and (pointer: fine) { .proposal-actions .acts form:first-child button[type=submit]:hover { background: color-mix(in srgb, var(--primary) 85%, var(--background)); border-color: color-mix(in srgb, var(--primary) 85%, var(--background)); } }
|
|
13281
13379
|
.proposal-actions .done, .proposal-actions .refused, .proposal-wait { margin: 0; padding: .55rem .65rem; border-radius: calc(var(--radius) - 5px); font-size: .72rem; }
|
|
13282
13380
|
.proposal-actions .done { color: var(--success); background: var(--success-soft); }
|
|
13283
13381
|
.proposal-actions .refused { color: var(--destructive); background: var(--destructive-soft); }
|
|
@@ -13358,7 +13456,8 @@ ${THEME_DARK}
|
|
|
13358
13456
|
width: 100%; min-height: 3.5rem; max-height: 13rem; margin: 0; padding: .75rem .85rem;
|
|
13359
13457
|
resize: vertical; border: 0; background: transparent; box-shadow: none; font-size: 1rem;
|
|
13360
13458
|
}
|
|
13361
|
-
.composer textarea:
|
|
13459
|
+
.composer textarea:focus-visible { border: 0; box-shadow: none; }
|
|
13460
|
+
@media (hover: hover) and (pointer: fine) { .composer textarea:hover { border: 0; box-shadow: none; } }
|
|
13362
13461
|
.composer button {
|
|
13363
13462
|
flex: 0 0 2.75rem; width: 2.75rem; min-height: 2.75rem; padding: 0; border-radius: 999px;
|
|
13364
13463
|
font-size: 0; box-shadow: 0 10px 24px -16px rgb(255 255 255 / .65);
|
|
@@ -13536,7 +13635,7 @@ ${THEME_DARK}
|
|
|
13536
13635
|
.mate-mint form { border-top: 1px solid var(--glass-border); padding-top: .5rem; }
|
|
13537
13636
|
/* The start act is the page's one primary (UI polish 2026-09-13). */
|
|
13538
13637
|
.mate-mint form > button[type=submit] { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); font-weight: 600; }
|
|
13539
|
-
.mate-mint form > button[type=submit]:hover { background: color-mix(in srgb, var(--primary) 85%, var(--background)); border-color: color-mix(in srgb, var(--primary) 85%, var(--background)); }
|
|
13638
|
+
@media (hover: hover) and (pointer: fine) { .mate-mint form > button[type=submit]:hover { background: color-mix(in srgb, var(--primary) 85%, var(--background)); border-color: color-mix(in srgb, var(--primary) 85%, var(--background)); } }
|
|
13540
13639
|
.mate-terms { display: flex; flex-wrap: wrap; gap: 1rem; align-items: baseline; padding: .35rem 0; }
|
|
13541
13640
|
.mate-terms .inline-field { white-space: nowrap; }
|
|
13542
13641
|
button.quiet { background: transparent; color: var(--fg-muted); border-color: var(--border); }
|
|
@@ -13587,7 +13686,7 @@ ${THEME_DARK}
|
|
|
13587
13686
|
display: block; padding: .58rem .65rem; margin: .16rem 0; border: 1px solid transparent;
|
|
13588
13687
|
border-radius: calc(var(--radius) - 4px); color: inherit; text-decoration: none;
|
|
13589
13688
|
}
|
|
13590
|
-
.wb-row:hover { background: var(--card); border-color: var(--border); }
|
|
13689
|
+
@media (hover: hover) and (pointer: fine) { .wb-row:hover { background: var(--card); border-color: var(--border); } }
|
|
13591
13690
|
.wb-row.wb-selected { background: var(--muted); border-color: var(--border); }
|
|
13592
13691
|
.wb-row .wb-title { display: block; font-size: .8125rem; font-weight: 500; line-height: 1.35; }
|
|
13593
13692
|
.wb-row .wb-meta { display: flex; align-items: center; gap: .3rem; flex-wrap: wrap; margin-top: .22rem; }
|
|
@@ -13649,7 +13748,7 @@ ${THEME_DARK}
|
|
|
13649
13748
|
.icon-button svg { width: 1rem; height: 1rem; }
|
|
13650
13749
|
.queue-card p { margin: 0; }
|
|
13651
13750
|
.queue-card a { text-decoration: none; }
|
|
13652
|
-
.queue-card a:hover { text-decoration: underline; }
|
|
13751
|
+
@media (hover: hover) and (pointer: fine) { .queue-card a:hover { text-decoration: underline; } }
|
|
13653
13752
|
.queue-card .row + .row { margin-top: .125rem; border-bottom: none; }
|
|
13654
13753
|
.queue-card p.row { padding: 0; border-bottom: none; }
|
|
13655
13754
|
.tracks { margin-top: 1.5rem; }
|
|
@@ -13658,7 +13757,7 @@ ${THEME_DARK}
|
|
|
13658
13757
|
.track-row p { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
|
|
13659
13758
|
.track-row p .right { margin-left: auto; }
|
|
13660
13759
|
.track-row a { text-decoration: none; }
|
|
13661
|
-
.track-row a:hover { text-decoration: underline; }
|
|
13760
|
+
@media (hover: hover) and (pointer: fine) { .track-row a:hover { text-decoration: underline; } }
|
|
13662
13761
|
.track-strip { display: inline-flex; gap: .3rem; align-items: center; vertical-align: middle; }
|
|
13663
13762
|
.fire {
|
|
13664
13763
|
display: inline-block; width: .65rem; height: .65rem; border-radius: 50%;
|
|
@@ -13777,7 +13876,7 @@ button { min-height: 44px; }
|
|
|
13777
13876
|
padding: 0 .875rem; border: 1px solid var(--border); border-radius: 999px;
|
|
13778
13877
|
text-decoration: none; color: var(--foreground); background: var(--card);
|
|
13779
13878
|
}
|
|
13780
|
-
.next-pager .skip:hover { border-color: color-mix(in srgb, var(--border) 60%, var(--muted-foreground)); }
|
|
13879
|
+
@media (hover: hover) and (pointer: fine) { .next-pager .skip:hover { border-color: color-mix(in srgb, var(--border) 60%, var(--muted-foreground)); } }
|
|
13781
13880
|
|
|
13782
13881
|
/* Motion: only where a human caused the change — navigation, presses,
|
|
13783
13882
|
overlays. Liveness swaps stay instant; the pulse dot is the one "alive"
|
|
@@ -13806,7 +13905,10 @@ button { min-height: 44px; }
|
|
|
13806
13905
|
@media (prefers-reduced-motion: no-preference) {
|
|
13807
13906
|
.tabbar a, .side nav a { transition: color .15s, background .15s; }
|
|
13808
13907
|
button:active { transform: scale(.985); }
|
|
13809
|
-
|
|
13908
|
+
/* Anchor buttons press like buttons; nav links fill instead. The palette
|
|
13909
|
+
and shortcuts overlay are keyboard-summoned, so they appear at once. */
|
|
13910
|
+
.button-link, .side .new-task, .content .new-task, .result-feedback-link, .next-pager .skip { transition: transform 160ms var(--so-ease-out); }
|
|
13911
|
+
:is(.button-link, .side .new-task, .content .new-task, .result-feedback-link, .next-pager .skip):active { transform: scale(.985); }
|
|
13810
13912
|
.chat-workspace.projects-open .chat-projects { animation: rise 200ms ease-out; }
|
|
13811
13913
|
.chat-workspace.projects-open::before { animation: fade 180ms ease-out; }
|
|
13812
13914
|
.switcher[open] .switcher-menu { animation: rise 160ms ease-out; }
|
|
@@ -13823,7 +13925,8 @@ button { min-height: 44px; }
|
|
|
13823
13925
|
@media (prefers-reduced-motion: reduce) {
|
|
13824
13926
|
*, *::before, *::after { animation: none !important; transition: none !important; }
|
|
13825
13927
|
::view-transition-group(*), ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
|
|
13826
|
-
button:active, button
|
|
13928
|
+
button:active, :is(.button-link, .side .new-task, .content .new-task, .result-feedback-link, .next-pager .skip):active { transform: none; }
|
|
13929
|
+
@media (hover: hover) and (pointer: fine) { button:hover, .side nav a:hover, .chat-project-card:hover, .lane-card:hover, .decide-card:hover, .menu-row:hover { transform: none; } }
|
|
13827
13930
|
}
|
|
13828
13931
|
|
|
13829
13932
|
/* The shortcuts overlay: display-only, toggled by the chrome layer, absent
|
|
@@ -13878,7 +13981,9 @@ button.pick-file { min-height: 1.75rem; padding: 0 .55rem; font-size: .75rem; }
|
|
|
13878
13981
|
}
|
|
13879
13982
|
`;
|
|
13880
13983
|
/** Appearance: a three-way segmented switch, one tap per choice. */
|
|
13881
|
-
const THEME_CONTROLS_CSS = `.task-repo select{width:100%;min-height:2.75rem;font-size:1rem}.task-repo-add{margin:.35rem .1rem .5rem}.task-repo-add a{display:inline-flex;align-items:center;min-height:2.25rem}details.result-request-open.result-request-form>summary{border:0;background:transparent;padding:.5rem 0;min-height:2.75rem;font-weight:600;display:list-item;list-style:revert}details.result-request-open.result-request-form>summary::-webkit-details-marker{display:revert}form.js-autosave button[type=submit]{display:none}.provider-row{border-bottom:1px solid var(--so-line);padding:.35rem 0}.provider-row:first-of-type{border-top:1px solid var(--so-line)}.provider-head{display:flex;align-items:center;gap:.75rem;margin:.4rem 0 0}.provider-status{display:inline-flex;align-items:center;gap:.4rem;color:var(--so-muted);font-size:.875rem}.provider-status i{width:.5rem;height:.5rem;border-radius:50%;background:var(--so-muted)}.provider-status--ok i{background:var(--so-success)}.provider-status--warn i{background:var(--so-attention)}.provider-status--off i{background:transparent;border:1.5px solid var(--so-muted)}details.provider-manage>summary{cursor:pointer;color:var(--so-accent-text);font-size:.875rem;min-height:2.5rem;display:list-item;padding-block:.5rem}.card.props .row{display:grid;gap:.1rem;margin:0 0 .75rem}.card.props .row>.meta{display:block;font-size:.75rem}.card.props .row>.meta::first-letter{text-transform:uppercase}.card.props .row>.mono{font-family:var(--font-sans);font-size:.875rem}.card.props .row>.mono .seal{font-family:var(--font-mono);font-size:.8125rem}details.evidence-files{margin:1rem 0}details.evidence-files>summary{cursor:pointer;min-height:2.75rem;display:list-item;padding-block:.7rem;font-weight:600}details.evidence-files ul{list-style:none;margin:0;padding:0}details.evidence-files li{display:flex;justify-content:space-between;gap:1rem;padding:.5rem 0;border-bottom:1px solid var(--so-line)}.result-action .result-feedback-link{display:inline-flex;align-items:center;min-height:2.5rem;padding:.5rem 1rem;border:1px solid var(--so-input-line);border-radius:.5rem;background:var(--so-paper);color:var(--so-ink);font-weight:600;text-decoration:none}.result-action .result-feedback-link:hover{background:var(--so-raised)}.so-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.verdict{margin:.5rem 0 .75rem}.verdict-chips{display:flex;flex-wrap:wrap;gap:.4rem;list-style:none;padding:0;margin:0}.verdict-chip{display:inline-flex;align-items:center;gap:.3rem;min-height:1.75rem;padding:.2rem .65rem;border-radius:999px;font-size:.8125rem;font-weight:600;background:var(--so-neutral-soft);color:var(--so-neutral-ink)}.verdict-chip svg{width:.9rem;height:.9rem}.verdict-chip--success{background:var(--so-success-soft);color:var(--so-success)}.verdict-chip--danger{background:var(--so-danger-soft);color:var(--so-danger)}.verdict-chip--warning{background:var(--so-warning-soft);color:var(--so-warning)}.verdict-chip--info{background:var(--so-info-soft);color:var(--so-info)}.verdict-by{margin:.4rem 0 0}details.result-request-open{margin:.5rem 0}details.result-request-open>summary{display:inline-flex;align-items:center;min-height:2.5rem;padding:.5rem 1rem;border:1px solid var(--so-input-line);border-radius:.5rem;background:var(--so-paper);color:var(--so-ink);font-weight:600;cursor:pointer;list-style:none}details.result-request-open>summary::-webkit-details-marker{display:none}details.result-request-open[open]>summary{margin-bottom:.75rem}.settings-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(8.5rem,1fr));gap:.5rem;margin:0 0 2rem}.settings-tiles a{display:flex;align-items:center;gap:.6rem;min-height:3rem;padding:.65rem .8rem;border:1px solid var(--so-line);border-radius:.625rem;background:var(--so-paper);color:var(--so-ink);text-decoration:none;font-weight:550;font-size:.875rem}.settings-tiles a:hover{border-color:var(--so-input-line);background:var(--so-raised)}.settings-tiles svg{width:1.1rem;height:1.1rem;flex-shrink:0;color:var(--so-accent-text)}details.settings-more{margin:.25rem 0 1.25rem}details.settings-more>summary{cursor:pointer;min-height:2.75rem;display:list-item;padding-block:.7rem;font-weight:550}details.settings-more>summary .meta{font-weight:400;margin-left:.35rem}.settings-changed{margin-top:-.25rem}.appearance{margin:0 0 28px}.appearance h2{margin:0 0 10px}.theme-switch{display:inline-flex;flex-wrap:nowrap;max-width:100%;gap:4px;padding:4px;margin:0;border:1px solid var(--so-line);border-radius:10px;background:var(--so-raised)}.theme-switch .theme-choice,.so-native-region .theme-switch .theme-choice{flex:1 1 0;width:auto;white-space:nowrap;min-height:40px;padding:8px 16px;border:0;border-radius:7px;background:transparent;color:var(--so-muted);font:inherit;font-weight:550;box-shadow:none;cursor:pointer}.theme-switch .theme-choice:hover{color:var(--so-ink)}.theme-switch .theme-choice[aria-pressed="true"]{background:var(--so-paper);color:var(--so-ink);box-shadow:0 1px 2px rgb(0 0 0 / .1)}.appearance .meta{margin:8px 0 0}@media(max-width:600px){.theme-switch .theme-choice{min-height:44px}}`;
|
|
13984
|
+
const THEME_CONTROLS_CSS = `.task-repo select{width:100%;min-height:2.75rem;font-size:1rem}.task-repo-add{margin:.35rem .1rem .5rem}.task-repo-add a{display:inline-flex;align-items:center;min-height:2.25rem}details.result-request-open.result-request-form>summary{border:0;background:transparent;padding:.5rem 0;min-height:2.75rem;font-weight:600;display:list-item;list-style:revert}details.result-request-open.result-request-form>summary::-webkit-details-marker{display:revert}form.js-autosave button[type=submit]{display:none}.provider-row{border-bottom:1px solid var(--so-line);padding:.35rem 0}.provider-row:first-of-type{border-top:1px solid var(--so-line)}.provider-head{display:flex;align-items:center;gap:.75rem;margin:.4rem 0 0}.provider-status{display:inline-flex;align-items:center;gap:.4rem;color:var(--so-muted);font-size:.875rem}.provider-status i{width:.5rem;height:.5rem;border-radius:50%;background:var(--so-muted)}.provider-status--ok i{background:var(--so-success)}.provider-status--warn i{background:var(--so-attention)}.provider-status--off i{background:transparent;border:1.5px solid var(--so-muted)}details.provider-manage>summary{cursor:pointer;color:var(--so-accent-text);font-size:.875rem;min-height:2.5rem;display:list-item;padding-block:.5rem}.card.props .row{display:grid;gap:.1rem;margin:0 0 .75rem}.card.props .row>.meta{display:block;font-size:.75rem}.card.props .row>.meta::first-letter{text-transform:uppercase}.card.props .row>.mono{font-family:var(--font-sans);font-size:.875rem}.card.props .row>.mono .seal{font-family:var(--font-mono);font-size:.8125rem}details.evidence-files{margin:1rem 0}details.evidence-files>summary{cursor:pointer;min-height:2.75rem;display:list-item;padding-block:.7rem;font-weight:600}details.evidence-files ul{list-style:none;margin:0;padding:0}details.evidence-files li{display:flex;justify-content:space-between;gap:1rem;padding:.5rem 0;border-bottom:1px solid var(--so-line)}.result-action .result-feedback-link{display:inline-flex;align-items:center;min-height:2.5rem;padding:.5rem 1rem;border:1px solid var(--so-input-line);border-radius:.5rem;background:var(--so-paper);color:var(--so-ink);font-weight:600;text-decoration:none}@media(hover:hover) and (pointer:fine){.result-action .result-feedback-link:hover{background:var(--so-raised)}}.so-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.verdict{margin:.5rem 0 .75rem}.verdict-chips{display:flex;flex-wrap:wrap;gap:.4rem;list-style:none;padding:0;margin:0}.verdict-chip{display:inline-flex;align-items:center;gap:.3rem;min-height:1.75rem;padding:.2rem .65rem;border-radius:999px;font-size:.8125rem;font-weight:600;background:var(--so-neutral-soft);color:var(--so-neutral-ink)}.verdict-chip svg{width:.9rem;height:.9rem}.verdict-chip--success{background:var(--so-success-soft);color:var(--so-success)}.verdict-chip--danger{background:var(--so-danger-soft);color:var(--so-danger)}.verdict-chip--warning{background:var(--so-warning-soft);color:var(--so-warning)}.verdict-chip--info{background:var(--so-info-soft);color:var(--so-info)}.verdict-by{margin:.4rem 0 0}details.result-request-open{margin:.5rem 0}details.result-request-open>summary{display:inline-flex;align-items:center;min-height:2.5rem;padding:.5rem 1rem;border:1px solid var(--so-input-line);border-radius:.5rem;background:var(--so-paper);color:var(--so-ink);font-weight:600;cursor:pointer;list-style:none}details.result-request-open>summary::-webkit-details-marker{display:none}details.result-request-open[open]>summary{margin-bottom:.75rem}.settings-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(8.5rem,1fr));gap:.5rem;margin:0 0 2rem}.settings-tiles a{display:flex;align-items:center;gap:.6rem;min-height:3rem;padding:.65rem .8rem;border:1px solid var(--so-line);border-radius:.625rem;background:var(--so-paper);color:var(--so-ink);text-decoration:none;font-weight:550;font-size:.875rem}@media(hover:hover) and (pointer:fine){.settings-tiles a:hover{border-color:var(--so-input-line);background:var(--so-raised)}}.settings-tiles svg{width:1.1rem;height:1.1rem;flex-shrink:0;color:var(--so-accent-text)}details.settings-more{margin:.25rem 0 1.25rem}details.settings-more>summary{cursor:pointer;min-height:2.75rem;display:list-item;padding-block:.7rem;font-weight:550}details.settings-more>summary .meta{font-weight:400;margin-left:.35rem}.settings-changed{margin-top:-.25rem}.appearance{margin:0 0 28px}.appearance h2{margin:0 0 10px}.theme-switch{display:inline-flex;flex-wrap:nowrap;max-width:100%;gap:4px;padding:4px;margin:0;border:1px solid var(--so-line);border-radius:10px;background:var(--so-raised)}.theme-switch .theme-choice,.so-native-region .theme-switch .theme-choice{flex:1 1 0;width:auto;white-space:nowrap;min-height:40px;padding:8px 16px;border:0;border-radius:7px;background:transparent;color:var(--so-muted);font:inherit;font-weight:550;box-shadow:none;cursor:pointer}@media(hover:hover) and (pointer:fine){.theme-switch .theme-choice:hover{color:var(--so-ink)}}.theme-switch .theme-choice[aria-pressed="true"]{background:var(--so-paper);color:var(--so-ink);box-shadow:0 1px 2px rgb(0 0 0 / .1)}.appearance .meta{margin:8px 0 0}@media(max-width:600px){.theme-switch .theme-choice{min-height:44px}}.update-notes{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit;max-height:18rem;overflow:auto}`;
|
|
13985
|
+
/** The page CSS this module writes itself (not the imported modules'), for the stylesheet contract tests. */
|
|
13986
|
+
export const PAGE_CSS = STYLE + THEME_CONTROLS_CSS;
|
|
13882
13987
|
const WORKSPACE_STYLE = styleAsset(STYLE + APPROVAL_RULES_CSS + SPEND_CSS + RETENTION_CSS + LIMITS_CSS + MONITORING_CSS + BACKUP_CSS + EXPORT_CSS + PROJECT_DELETE_CSS + POLICY_CSS + EVIDENCE_PACK_CSS + THEME_CONTROLS_CSS + CODING_CSS + CODING_SHIPPING_CSS + RECIPE_CSS + SKILLS_CSS + TOOLS_CSS + FLOWS_CSS + TEAMMATE_CSS + KITS_CSS + SSO_CSS + CREDENTIALS_CSS + KNOWLEDGE_CSS + MODELS_CSS + CHAT_POLISH_CSS + TRANSITIONS_CSS + WORKSPACE_MOTION_CSS + ASSIGNMENT_CSS + LEAD_CONTEXT_CSS + '.learning{min-width:0;overflow-wrap:anywhere}.learning .card{min-width:0}.learning code,.learning blockquote,.learning pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.learning button,.learning summary,.learning .button-link{min-height:44px}.learning button{white-space:nowrap}.learning summary{padding:12px 0;cursor:pointer}.learning form{margin:12px 0}.learning select{max-width:100%}.learning blockquote{margin:8px 0}.learning ul{padding-left:20px}');
|
|
13883
13988
|
/**
|
|
13884
13989
|
* The board's liveness: fetch this page's own fragment on a timer and swap
|
|
@@ -14126,8 +14231,8 @@ export const SENSITIVE_INPUT = /<input\b[^>]*[\s"']type\s*=\s*["']?password/i;
|
|
|
14126
14231
|
/** The design contract every page carries (impeccable direction, 2026-09-27). */
|
|
14127
14232
|
const DESIGN_CONTRACT = `<!-- THESIS: a control plane that stays quiet until a person is needed; it refuses the dashboard default of coloured status everywhere and an accent on every button.
|
|
14128
14233
|
OWN-WORLD: a neutral grey frame with paper sheets inset into it (Arc, Linear), Geist for words and Geist Mono for machine facts, ink for every act a person can take, one chart magenta only for what waits on a person plus focus and selection; hairlines and one soft sheet shadow, no glass, no gradients.
|
|
14129
|
-
STORY: glance, see the
|
|
14130
|
-
FIRST VIEWPORT: sidebar on the frame (
|
|
14234
|
+
STORY: glance, see the accent count, open the one thing that needs you, act with the one accent verb, leave.
|
|
14235
|
+
FIRST VIEWPORT: sidebar on the frame (accent mark, ink New task, the current page as a raised pill, the accent needs-you count); the main sheet with a 52px header over compact 13px rows; the Crew sheet beside it.
|
|
14131
14236
|
FORM: the Raycast and Arc canon, user-pinned; seed 9c849086.
|
|
14132
14237
|
FINISH: unreviewed and undocumented is unfinished; this build ends with the finish review, the verdict, and DESIGN.md -->`;
|
|
14133
14238
|
/** The shortcuts overlay: display-only, toggled by the chrome layer,
|
|
@@ -14284,7 +14389,13 @@ function shell(title, body, options = {}) {
|
|
|
14284
14389
|
: "") +
|
|
14285
14390
|
(chrome.modeBanner === undefined
|
|
14286
14391
|
? ""
|
|
14287
|
-
: `<div class="banner"><span class="badge badge-running">mode</span>${escape(chrome.modeBanner.words)} \u00b7 <a href="/mode">the terms \u00b7 end it</a></div>`)
|
|
14392
|
+
: `<div class="banner"><span class="badge badge-running">mode</span>${escape(chrome.modeBanner.words)} \u00b7 <a href="/mode">the terms \u00b7 end it</a></div>`) +
|
|
14393
|
+
(chrome.signIn ?? []).map(one => `<div class="banner sign-in-banner" data-sign-in="${escape(one.provider)}"><strong>${escape(one.title)}</strong> \u00b7 run <code>${escape(one.command)}</code> on this computer, then resume.${one.detail === "" ? "" : ` ${escape(one.detail)}`}` +
|
|
14394
|
+
`<form method="post" action="${escape(one.resumeHref)}" class="inline"><input type="hidden" name="csrf" value="${escape(chrome.csrf ?? "")}"><button type="submit">${escape(one.resumeLabel)}</button></form></div>`).join("") +
|
|
14395
|
+
(chrome.update === undefined
|
|
14396
|
+
? ""
|
|
14397
|
+
: `<div class="banner update-banner" data-update="${escape(chrome.update.version)}">${escape(updateNoticeWords(chrome.update))} \u00b7 <a href="${escape(chrome.update.href)}">What's new</a>` +
|
|
14398
|
+
`<form method="post" action="${escape(chrome.update.dismissHref)}" class="inline"><input type="hidden" name="csrf" value="${escape(chrome.csrf ?? "")}"><input type="hidden" name="version" value="${escape(chrome.update.version)}"><button type="submit">Dismiss</button></form></div>`);
|
|
14288
14399
|
// The scope bar sits between the banners and the main/split body, so it
|
|
14289
14400
|
// can never disappear with a responsive pane (portfolio arc §1).
|
|
14290
14401
|
const content = chrome.listPane === undefined
|
|
@@ -19744,6 +19855,7 @@ const REASON_WORDS = {
|
|
|
19744
19855
|
agent: "the agent failed",
|
|
19745
19856
|
"agent-reported": "the agent reported it could not finish",
|
|
19746
19857
|
"no-op": "nothing changed when something should have",
|
|
19858
|
+
"no-handoff": "the agent stopped before handing off; its work was kept and it is being resumed",
|
|
19747
19859
|
"moved-head": "the branch moved underneath the build",
|
|
19748
19860
|
"moved-branch": "the branch moved underneath the build",
|
|
19749
19861
|
timeout: "ran out of time",
|
|
@@ -21301,7 +21413,7 @@ function telegramDeliveryWords(store, bot) {
|
|
|
21301
21413
|
return `Telegram pushes new messages to ${state.url}${state.problem === null ? "." : `, but ${state.problem.charAt(0).toLowerCase()}${state.problem.slice(1)}.`}`;
|
|
21302
21414
|
return `Toolroll asks Telegram for new messages every few seconds${state?.problem ? ` (${state.problem})` : ""}.`;
|
|
21303
21415
|
}
|
|
21304
|
-
function settingsPage(chrome, existing, hasEnv, csrf, problem, messaging = null, push = null, providerKeys = null, digest = null, permissionDefault = null, qualityDefault = null, email = null, telegramDelivery = null, workers = null) {
|
|
21416
|
+
function settingsPage(chrome, existing, hasEnv, csrf, problem, messaging = null, push = null, providerKeys = null, digest = null, permissionDefault = null, qualityDefault = null, email = null, telegramDelivery = null, workers = null, updates = null) {
|
|
21305
21417
|
const permissionCard = permissionDefault === null
|
|
21306
21418
|
? ""
|
|
21307
21419
|
: [
|
|
@@ -21497,6 +21609,7 @@ function settingsPage(chrome, existing, hasEnv, csrf, problem, messaging = null,
|
|
|
21497
21609
|
telegram: { state: hasEnv ? "from the environment" : existing === null ? "not set" : "saved", current, delivery: telegramDelivery },
|
|
21498
21610
|
email,
|
|
21499
21611
|
workers,
|
|
21612
|
+
updates,
|
|
21500
21613
|
};
|
|
21501
21614
|
return screen("Settings", [
|
|
21502
21615
|
"<h1>Settings</h1>",
|
|
@@ -21506,6 +21619,7 @@ function settingsPage(chrome, existing, hasEnv, csrf, problem, messaging = null,
|
|
|
21506
21619
|
qualityCard,
|
|
21507
21620
|
keysCard,
|
|
21508
21621
|
workersCard(workers),
|
|
21622
|
+
updatesCard(updates, csrf),
|
|
21509
21623
|
pushCard === "" && messagingCard === "" && digestCard === "" ? "" : `<h2>Notifications</h2>`,
|
|
21510
21624
|
messagingCard,
|
|
21511
21625
|
pushCard,
|
|
@@ -21560,6 +21674,23 @@ function workersCard(workers) {
|
|
|
21560
21674
|
`<p class="meta">To change how many a worker runs at once: <code>toolroll runner capacity <name> <n></code></p>`) +
|
|
21561
21675
|
`</section>`;
|
|
21562
21676
|
}
|
|
21677
|
+
/** Settings → Updates, for browsers without the app script. */
|
|
21678
|
+
function updatesCard(updates, csrf) {
|
|
21679
|
+
if (updates === null)
|
|
21680
|
+
return "";
|
|
21681
|
+
const latest = updates.latest;
|
|
21682
|
+
return `<section id="updates" aria-labelledby="updates-title"><h2 id="updates-title">Updates</h2>` +
|
|
21683
|
+
`<p class="row">This version <span class="mono">${escape(updates.current)}</span> \u00b7 ${!updates.check.on ? "Update checks are off" : latest === null ? "Not checked yet" : latest.newer ? `Latest <span class="mono">${escape(latest.version)}</span>` : "Up to date"}</p>` +
|
|
21684
|
+
(latest !== null && latest.newer
|
|
21685
|
+
? `<p>Update with <code>${escape(updates.updateCommand)}</code> \u00b7 <a href="${escape(latest.url)}">Release notes</a></p>` +
|
|
21686
|
+
(latest.notes === "" ? "" : `<details class="settings-more"><summary>What's new in ${escape(latest.version)}</summary><pre class="update-notes">${escape(latest.notes)}</pre></details>`)
|
|
21687
|
+
: "") +
|
|
21688
|
+
(updates.check.canManage && !updates.check.byEnv
|
|
21689
|
+
? `<form method="post" action="/settings/updates" class="inline"><input type="hidden" name="csrf" value="${escape(csrf)}"><input type="hidden" name="check" value="${updates.check.on ? "off" : "on"}"><button type="submit">${updates.check.on ? "Turn off daily check" : "Turn on daily check"}</button></form>`
|
|
21690
|
+
: updates.check.byEnv ? `<p class="meta">Off by TOOLROLL_NO_UPDATE_CHECK.</p>` : "") +
|
|
21691
|
+
(updates.workers.length === 0 ? "" : `<ul>${updates.workers.map(one => `<li data-worker-version="${escape(one.name)}">${escape(one.name)} <span class="mono">${escape(one.version ?? "unknown")}</span>${one.older ? " \u00b7 older" : ""}</li>`).join("")}</ul>`) +
|
|
21692
|
+
`</section>`;
|
|
21693
|
+
}
|
|
21563
21694
|
/** Choices save the moment they change; without the script the Save
|
|
21564
21695
|
* button stays and the form works the same. */
|
|
21565
21696
|
const SETTINGS_AUTOSAVE_SCRIPT = `(function(){document.querySelectorAll('form[data-autosave]').forEach(function(form){form.classList.add('js-autosave');form.addEventListener('change',function(ev){var t=ev.target;if(t&&(t.type==='radio'||t.tagName==='SELECT')){if(form.requestSubmit)form.requestSubmit();else form.submit();}});});})();`;
|