@kevin5251984/guild 0.2.19 → 0.2.20
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/package.json +2 -2
- package/src/cli.ts +2 -5
- package/src/db.ts +39 -6
- package/src/generate.ts +3 -3
- package/src/handlers.ts +29 -45
- package/src/harness.ts +20 -4
- package/src/host-browse.ts +149 -4
- package/src/llm.ts +37 -15
- package/src/mcp.ts +32 -5
- package/src/mention.ts +80 -12
- package/src/oauth.ts +2 -2
- package/src/opencode-free.ts +3 -2
- package/src/public/chat.html +142 -26
- package/src/public/i18n.js +3 -0
- package/src/public/mobile.css +120 -14
- package/src/public/mobile.html +186 -16
- package/src/public/settings.html +51 -8
- package/src/reasoning-catalog.ts +346 -0
- package/src/router.ts +108 -8
- package/src/store.ts +15 -6
- package/src/subagent.ts +10 -5
- package/src/tools.ts +8 -3
- package/src/version.ts +25 -0
- package/vendor/protocol/src/index.ts +13 -1
package/src/public/chat.html
CHANGED
|
@@ -167,12 +167,7 @@
|
|
|
167
167
|
<a class="model-choice" href="/settings" data-i18n="model.settings">狀態欄</a>
|
|
168
168
|
</div>
|
|
169
169
|
</div>
|
|
170
|
-
<select id="model-reasoning" hidden>
|
|
171
|
-
<option value="minimal" data-i18n="reason.min">最低</option>
|
|
172
|
-
<option value="low" data-i18n="reason.low">低</option>
|
|
173
|
-
<option value="medium" data-i18n="reason.mid">中</option>
|
|
174
|
-
<option value="high" data-i18n="reason.high">高</option>
|
|
175
|
-
</select>
|
|
170
|
+
<select id="model-reasoning" hidden></select>
|
|
176
171
|
<input type="checkbox" id="model-fast" hidden />
|
|
177
172
|
</div>
|
|
178
173
|
</div>
|
|
@@ -266,6 +261,13 @@
|
|
|
266
261
|
</div>
|
|
267
262
|
<script>
|
|
268
263
|
const COLORS = ["#7c6af7", "#5b8def", "#2ea887", "#e07a3d", "#d4537e"];
|
|
264
|
+
const SEAT_HUE = {
|
|
265
|
+
infra: "#5b8def",
|
|
266
|
+
pm: "#2ea887",
|
|
267
|
+
rd: "#e07a3d",
|
|
268
|
+
design: "#7c6af7",
|
|
269
|
+
marketing: "#d4537e",
|
|
270
|
+
};
|
|
269
271
|
const CHANNEL_ROSTER_CAP = 6;
|
|
270
272
|
function isGeneralChannel(ch) {
|
|
271
273
|
return Boolean(ch && (ch.id === "channel-general" || ch.name === "general"));
|
|
@@ -321,6 +323,8 @@
|
|
|
321
323
|
};
|
|
322
324
|
|
|
323
325
|
function hue(key) {
|
|
326
|
+
const seat = SEAT_HUE[String(key).toLowerCase()];
|
|
327
|
+
if (seat) return seat;
|
|
324
328
|
let n = 0;
|
|
325
329
|
for (const ch of String(key)) n = (n + ch.charCodeAt(0) * 17) % COLORS.length;
|
|
326
330
|
return COLORS[n];
|
|
@@ -1036,14 +1040,48 @@
|
|
|
1036
1040
|
}
|
|
1037
1041
|
return out;
|
|
1038
1042
|
}
|
|
1043
|
+
function deferredHandles(text) {
|
|
1044
|
+
const raw = String(text || "");
|
|
1045
|
+
const out = [];
|
|
1046
|
+
const add = (name) => {
|
|
1047
|
+
const key = String(name || "").toLowerCase();
|
|
1048
|
+
if (key && out.indexOf(key) < 0) out.push(key);
|
|
1049
|
+
};
|
|
1050
|
+
const scan = raw.replace(/```[\s\S]*?```/g, " ");
|
|
1051
|
+
const cue =
|
|
1052
|
+
/(?:完成後|通過後|最後(?:才|由)?|才需要|才叫|才交)[^@\n]{0,24}@([A-Za-z0-9_-]+)/gi;
|
|
1053
|
+
const bare =
|
|
1054
|
+
/(?:完成後|才需要|才叫)\s*(?:call|ask|由)\s*`?@?([A-Za-z0-9_-]+)`?/gi;
|
|
1055
|
+
let row;
|
|
1056
|
+
while ((row = cue.exec(scan))) add(row[1]);
|
|
1057
|
+
while ((row = bare.exec(scan))) add(row[1]);
|
|
1058
|
+
const later =
|
|
1059
|
+
/^[ \t]*(?:\d+[.)、]\s*|[-*•]\s+)(?:通過後|最後|然後|之後|完成後)\s*`?@([A-Za-z0-9_-]+)`?/i;
|
|
1060
|
+
const lines = raw.split(/\r?\n/);
|
|
1061
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1062
|
+
const hit = lines[i].match(later);
|
|
1063
|
+
if (hit) add(hit[1]);
|
|
1064
|
+
}
|
|
1065
|
+
return out;
|
|
1066
|
+
}
|
|
1039
1067
|
function summonedBotIds(text) {
|
|
1040
1068
|
const raw = String(text || "");
|
|
1069
|
+
const deferred = new Set(deferredHandles(raw));
|
|
1070
|
+
const members = new Set(
|
|
1071
|
+
state.kind === "channel"
|
|
1072
|
+
? ((currentChannel() && currentChannel().memberIds) || [])
|
|
1073
|
+
: state.kind === "dm"
|
|
1074
|
+
? [state.id]
|
|
1075
|
+
: [],
|
|
1076
|
+
);
|
|
1041
1077
|
const known = new Map(
|
|
1042
|
-
(state.bots || [])
|
|
1078
|
+
(state.bots || [])
|
|
1079
|
+
.filter((bot) => !members.size || members.has(bot.id))
|
|
1080
|
+
.map((bot) => [String(bot.handle).toLowerCase(), bot.id]),
|
|
1043
1081
|
);
|
|
1044
1082
|
const out = [];
|
|
1045
|
-
const push = (id) => {
|
|
1046
|
-
if (id && !out.includes(id)) out.push(id);
|
|
1083
|
+
const push = (id, handle) => {
|
|
1084
|
+
if (id && !deferred.has(handle) && !out.includes(id)) out.push(id);
|
|
1047
1085
|
};
|
|
1048
1086
|
let fence = false;
|
|
1049
1087
|
const lines = raw.split(/\r?\n/);
|
|
@@ -1060,11 +1098,21 @@
|
|
|
1060
1098
|
if (!m) continue;
|
|
1061
1099
|
const names = m[1].match(/@([A-Za-z0-9_-]+)/g) || [];
|
|
1062
1100
|
for (let n = 0; n < names.length; n++) {
|
|
1063
|
-
|
|
1101
|
+
const handle = names[n].slice(1).toLowerCase();
|
|
1102
|
+
push(known.get(handle), handle);
|
|
1064
1103
|
}
|
|
1065
1104
|
}
|
|
1066
1105
|
if (out.length) return out;
|
|
1067
|
-
return mentionedBotIds(raw)
|
|
1106
|
+
return mentionedBotIds(raw)
|
|
1107
|
+
.filter((id) => {
|
|
1108
|
+
const bot = (state.bots || []).find((row) => row.id === id);
|
|
1109
|
+
return (
|
|
1110
|
+
bot &&
|
|
1111
|
+
(!members.size || members.has(id)) &&
|
|
1112
|
+
!deferred.has(String(bot.handle).toLowerCase())
|
|
1113
|
+
);
|
|
1114
|
+
})
|
|
1115
|
+
.slice(0, 1);
|
|
1068
1116
|
}
|
|
1069
1117
|
function lastBotAuthor() {
|
|
1070
1118
|
const msgs = state.messages || [];
|
|
@@ -1244,7 +1292,20 @@
|
|
|
1244
1292
|
}
|
|
1245
1293
|
function botModelMeta(bot) {
|
|
1246
1294
|
const model = botModelLabel(bot) || "—";
|
|
1247
|
-
const
|
|
1295
|
+
const spec = (() => {
|
|
1296
|
+
const ref = botModelRef(bot);
|
|
1297
|
+
if (!ref) return null;
|
|
1298
|
+
const hit = allModels().find(
|
|
1299
|
+
(row) => row.provider === ref.provider && row.model === ref.model,
|
|
1300
|
+
);
|
|
1301
|
+
return (hit && hit.reasoning) || null;
|
|
1302
|
+
})();
|
|
1303
|
+
const reasonVal = clampEffort(
|
|
1304
|
+
modelCfg && modelCfg.reasoning,
|
|
1305
|
+
spec,
|
|
1306
|
+
Boolean(modelCfg && modelCfg.fast),
|
|
1307
|
+
);
|
|
1308
|
+
const reason = reasonVal ? reasonLabel(reasonVal) : "—";
|
|
1248
1309
|
const provider = botProviderLabel(bot) || "—";
|
|
1249
1310
|
return model + " · " + reason + " · " + provider;
|
|
1250
1311
|
}
|
|
@@ -5332,6 +5393,7 @@
|
|
|
5332
5393
|
group: p.name || p.id,
|
|
5333
5394
|
ready: Boolean(p.ready),
|
|
5334
5395
|
kind: p.kind || "key",
|
|
5396
|
+
reasoning: m.reasoning || null,
|
|
5335
5397
|
});
|
|
5336
5398
|
}
|
|
5337
5399
|
}
|
|
@@ -5345,10 +5407,45 @@
|
|
|
5345
5407
|
return hit ? hit.name : ref.model;
|
|
5346
5408
|
}
|
|
5347
5409
|
function reasonLabel(value) {
|
|
5410
|
+
if (value === "none") return t("reason.none");
|
|
5348
5411
|
if (value === "minimal") return t("reason.min");
|
|
5349
5412
|
if (value === "low") return t("reason.low");
|
|
5413
|
+
if (value === "medium") return t("reason.mid");
|
|
5350
5414
|
if (value === "high") return t("reason.high");
|
|
5351
|
-
return t("reason.
|
|
5415
|
+
if (value === "xhigh") return t("reason.xhigh");
|
|
5416
|
+
if (value === "max") return t("reason.max");
|
|
5417
|
+
return value || t("reason.mid");
|
|
5418
|
+
}
|
|
5419
|
+
function activeReasoningSpec() {
|
|
5420
|
+
const ref = activeModelRef();
|
|
5421
|
+
if (!ref) return null;
|
|
5422
|
+
const hit = allModels().find(
|
|
5423
|
+
(row) => row.provider === ref.provider && row.model === ref.model,
|
|
5424
|
+
);
|
|
5425
|
+
return (hit && hit.reasoning) || null;
|
|
5426
|
+
}
|
|
5427
|
+
function effortChoices(spec) {
|
|
5428
|
+
const raw = spec && spec.supportedEfforts;
|
|
5429
|
+
if (!raw || !raw.length) return [];
|
|
5430
|
+
return spec.mandatory
|
|
5431
|
+
? raw.filter((key) => key !== "none")
|
|
5432
|
+
: raw.slice();
|
|
5433
|
+
}
|
|
5434
|
+
function clampEffort(want, spec, fast) {
|
|
5435
|
+
const choices = effortChoices(spec);
|
|
5436
|
+
if (!choices.length) return "";
|
|
5437
|
+
if (fast) {
|
|
5438
|
+
if (choices.indexOf("low") >= 0) return "low";
|
|
5439
|
+
if (choices.indexOf("minimal") >= 0) return "minimal";
|
|
5440
|
+
return choices[choices.length - 1];
|
|
5441
|
+
}
|
|
5442
|
+
if (want && choices.indexOf(want) >= 0) return want;
|
|
5443
|
+
if (spec.defaultEffort && choices.indexOf(spec.defaultEffort) >= 0) {
|
|
5444
|
+
return spec.defaultEffort;
|
|
5445
|
+
}
|
|
5446
|
+
if (choices.indexOf("high") >= 0) return "high";
|
|
5447
|
+
if (choices.indexOf("medium") >= 0) return "medium";
|
|
5448
|
+
return choices[0];
|
|
5352
5449
|
}
|
|
5353
5450
|
function speedLabel() {
|
|
5354
5451
|
return modelCfg.fast ? t("speed.fast") : t("speed.normal");
|
|
@@ -5450,25 +5547,44 @@
|
|
|
5450
5547
|
}
|
|
5451
5548
|
document.getElementById("model-results").innerHTML =
|
|
5452
5549
|
html || '<p class="model-empty">' + t("noModels") + "</p>";
|
|
5453
|
-
document.getElementById("model-reasoning").value = modelCfg.reasoning || "medium";
|
|
5454
5550
|
document.getElementById("model-fast").checked = Boolean(modelCfg.fast);
|
|
5455
|
-
const
|
|
5551
|
+
const spec = activeReasoningSpec();
|
|
5552
|
+
const choices = effortChoices(spec);
|
|
5553
|
+
const reasonVal = clampEffort(
|
|
5554
|
+
modelCfg.reasoning,
|
|
5555
|
+
spec,
|
|
5556
|
+
Boolean(modelCfg.fast),
|
|
5557
|
+
);
|
|
5558
|
+
const reasonSel = document.getElementById("model-reasoning");
|
|
5559
|
+
reasonSel.innerHTML = choices
|
|
5560
|
+
.map(
|
|
5561
|
+
(value) =>
|
|
5562
|
+
'<option value="' +
|
|
5563
|
+
value +
|
|
5564
|
+
'">' +
|
|
5565
|
+
reasonLabel(value) +
|
|
5566
|
+
"</option>",
|
|
5567
|
+
)
|
|
5568
|
+
.join("");
|
|
5569
|
+
if (reasonVal) reasonSel.value = reasonVal;
|
|
5570
|
+
const reasonRow = document.querySelector('.model-nav [data-sheet="reasoning"]');
|
|
5571
|
+
if (reasonRow) reasonRow.hidden = !choices.length;
|
|
5572
|
+
if (!choices.length) {
|
|
5573
|
+
const pane = document.getElementById("model-sheet-reasoning");
|
|
5574
|
+
if (pane && !pane.hidden) showModelSheet("models");
|
|
5575
|
+
}
|
|
5576
|
+
const reason = choices.length ? reasonLabel(reasonVal) : "";
|
|
5456
5577
|
const speed = speedLabel();
|
|
5457
5578
|
const name = displayName(current);
|
|
5458
5579
|
document.getElementById("model-btn-label").textContent = current
|
|
5459
|
-
? name + " " + reason
|
|
5580
|
+
? name + (reason ? " " + reason : "")
|
|
5460
5581
|
: name;
|
|
5461
5582
|
document.getElementById("model-row-model").textContent = name;
|
|
5462
|
-
document.getElementById("model-row-reason").textContent = reason;
|
|
5583
|
+
document.getElementById("model-row-reason").textContent = reason || "—";
|
|
5463
5584
|
document.getElementById("model-row-speed").textContent = speed;
|
|
5464
|
-
document.getElementById("model-reason-list").innerHTML =
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
["medium", t("reason.mid")],
|
|
5468
|
-
["high", t("reason.high")],
|
|
5469
|
-
]
|
|
5470
|
-
.map(([value, label]) => {
|
|
5471
|
-
const on = (modelCfg.reasoning || "medium") === value ? " on" : "";
|
|
5585
|
+
document.getElementById("model-reason-list").innerHTML = choices
|
|
5586
|
+
.map((value) => {
|
|
5587
|
+
const on = reasonVal === value ? " on" : "";
|
|
5472
5588
|
const tick = on ? '<span class="tick">✓</span>' : "";
|
|
5473
5589
|
return (
|
|
5474
5590
|
'<button type="button" class="model-choice' +
|
|
@@ -5476,7 +5592,7 @@
|
|
|
5476
5592
|
'" data-reason="' +
|
|
5477
5593
|
value +
|
|
5478
5594
|
'">' +
|
|
5479
|
-
|
|
5595
|
+
reasonLabel(value) +
|
|
5480
5596
|
tick +
|
|
5481
5597
|
"</button>"
|
|
5482
5598
|
);
|
package/src/public/i18n.js
CHANGED
|
@@ -119,10 +119,13 @@ var I18N_ROWS = [
|
|
|
119
119
|
["local", "本地", "Local"],
|
|
120
120
|
["you", "你", "You"],
|
|
121
121
|
["emptyParen", "(空)", "(empty)"],
|
|
122
|
+
["reason.none", "關閉", "Off"],
|
|
122
123
|
["reason.min", "最低", "Min"],
|
|
123
124
|
["reason.low", "低", "Low"],
|
|
124
125
|
["reason.mid", "中", "Mid"],
|
|
125
126
|
["reason.high", "高", "High"],
|
|
127
|
+
["reason.xhigh", "極高", "Extra high"],
|
|
128
|
+
["reason.max", "最大", "Max"],
|
|
126
129
|
["speed.normal", "標準", "Standard"],
|
|
127
130
|
["speed.fast", "快速", "Fast"],
|
|
128
131
|
["model", "模型", "Model"],
|
package/src/public/mobile.css
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
--panel: #10141A;
|
|
9
9
|
--lift: #161C24;
|
|
10
10
|
--card: #161C24;
|
|
11
|
+
--bubble: #1A222C;
|
|
11
12
|
--you: #242C36;
|
|
12
13
|
--text: #E8E4DC;
|
|
13
14
|
--muted: #8B8F96;
|
|
@@ -15,6 +16,10 @@
|
|
|
15
16
|
--fill: #141A22;
|
|
16
17
|
--ok: #9fdfb2;
|
|
17
18
|
--danger: #f0a8a8;
|
|
19
|
+
--press: 0.97;
|
|
20
|
+
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
|
|
21
|
+
--press-ms: 140ms;
|
|
22
|
+
--hover-ms: 160ms;
|
|
18
23
|
--tap: 44px;
|
|
19
24
|
--focus: 0 0 0 2px var(--bg), 0 0 0 3px var(--steel);
|
|
20
25
|
--vvh: 100dvh;
|
|
@@ -48,6 +53,58 @@ textarea:focus-visible {
|
|
|
48
53
|
outline: none;
|
|
49
54
|
box-shadow: var(--focus);
|
|
50
55
|
}
|
|
56
|
+
/* Enamel press language, shared with the hall: paper / steel / ghost. */
|
|
57
|
+
.navlist a,
|
|
58
|
+
.back,
|
|
59
|
+
.turn-stop,
|
|
60
|
+
.turn-steer,
|
|
61
|
+
.steer,
|
|
62
|
+
.send,
|
|
63
|
+
.stop-all,
|
|
64
|
+
.mention-item {
|
|
65
|
+
transition:
|
|
66
|
+
background-color var(--hover-ms) ease,
|
|
67
|
+
border-color var(--hover-ms) ease,
|
|
68
|
+
color var(--hover-ms) ease,
|
|
69
|
+
transform var(--press-ms) var(--ease-out),
|
|
70
|
+
opacity var(--hover-ms) ease;
|
|
71
|
+
}
|
|
72
|
+
.navlist a:active,
|
|
73
|
+
.back:active,
|
|
74
|
+
.turn-stop:active:not(:disabled),
|
|
75
|
+
.turn-steer:active:not(:disabled),
|
|
76
|
+
.steer:active:not(:disabled),
|
|
77
|
+
.send:active:not(:disabled),
|
|
78
|
+
.stop-all:active:not(:disabled),
|
|
79
|
+
.mention-item:active {
|
|
80
|
+
transform: scale(var(--press));
|
|
81
|
+
}
|
|
82
|
+
@media (prefers-reduced-motion: reduce) {
|
|
83
|
+
.navlist a,
|
|
84
|
+
.back,
|
|
85
|
+
.turn-stop,
|
|
86
|
+
.turn-steer,
|
|
87
|
+
.steer,
|
|
88
|
+
.send,
|
|
89
|
+
.stop-all,
|
|
90
|
+
.mention-item {
|
|
91
|
+
transition:
|
|
92
|
+
background-color var(--hover-ms) ease,
|
|
93
|
+
border-color var(--hover-ms) ease,
|
|
94
|
+
color var(--hover-ms) ease,
|
|
95
|
+
opacity var(--hover-ms) ease;
|
|
96
|
+
}
|
|
97
|
+
.navlist a:active,
|
|
98
|
+
.back:active,
|
|
99
|
+
.turn-stop:active,
|
|
100
|
+
.turn-steer:active,
|
|
101
|
+
.steer:active,
|
|
102
|
+
.send:active,
|
|
103
|
+
.stop-all:active,
|
|
104
|
+
.mention-item:active {
|
|
105
|
+
transform: none;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
51
108
|
.screen {
|
|
52
109
|
display: flex;
|
|
53
110
|
flex-direction: column;
|
|
@@ -214,6 +271,7 @@ textarea:focus-visible {
|
|
|
214
271
|
font-weight: 650;
|
|
215
272
|
color: #fff;
|
|
216
273
|
background: var(--steel);
|
|
274
|
+
box-shadow: 0 0 0 1px color-mix(in srgb, var(--steel) 38%, transparent);
|
|
217
275
|
}
|
|
218
276
|
.avatar img {
|
|
219
277
|
width: 100%;
|
|
@@ -242,6 +300,7 @@ textarea:focus-visible {
|
|
|
242
300
|
white-space: pre-wrap;
|
|
243
301
|
}
|
|
244
302
|
.msg.you .bubble { background: var(--you); }
|
|
303
|
+
.msg.bot .bubble { background: var(--bubble); }
|
|
245
304
|
.assistant-text {
|
|
246
305
|
margin: 0;
|
|
247
306
|
color: var(--text);
|
|
@@ -264,7 +323,7 @@ textarea:focus-visible {
|
|
|
264
323
|
.assistant-text .md-fence {
|
|
265
324
|
margin: 0.35rem 0 0.7rem;
|
|
266
325
|
border-radius: 12px;
|
|
267
|
-
background:
|
|
326
|
+
background: var(--fill);
|
|
268
327
|
overflow: auto;
|
|
269
328
|
}
|
|
270
329
|
.assistant-text .md-fence-bar {
|
|
@@ -301,11 +360,11 @@ textarea:focus-visible {
|
|
|
301
360
|
white-space: nowrap;
|
|
302
361
|
background: linear-gradient(
|
|
303
362
|
90deg,
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
363
|
+
var(--text) 0%,
|
|
364
|
+
var(--text) 40%,
|
|
365
|
+
var(--muted) 50%,
|
|
366
|
+
var(--text) 60%,
|
|
367
|
+
var(--text) 100%
|
|
309
368
|
);
|
|
310
369
|
background-position: 100% 0;
|
|
311
370
|
background-size: 250% 100%;
|
|
@@ -319,8 +378,8 @@ textarea:focus-visible {
|
|
|
319
378
|
font-size: 13px;
|
|
320
379
|
font-weight: 400;
|
|
321
380
|
font-variant-numeric: tabular-nums;
|
|
322
|
-
color:
|
|
323
|
-
-webkit-text-fill-color:
|
|
381
|
+
color: var(--muted);
|
|
382
|
+
-webkit-text-fill-color: var(--muted);
|
|
324
383
|
}
|
|
325
384
|
@keyframes m-shimmer { to { background-position: 0 0; } }
|
|
326
385
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -343,15 +402,22 @@ textarea:focus-visible {
|
|
|
343
402
|
line-height: 20px;
|
|
344
403
|
color: var(--muted);
|
|
345
404
|
}
|
|
346
|
-
.live-name { color:
|
|
405
|
+
.live-name { color: var(--text); font-weight: 500; }
|
|
347
406
|
.live-detail {
|
|
348
407
|
overflow: hidden;
|
|
349
408
|
text-overflow: ellipsis;
|
|
350
409
|
white-space: nowrap;
|
|
351
410
|
}
|
|
352
411
|
.live-step.running .live-name { color: var(--text); }
|
|
353
|
-
.turn-actions {
|
|
412
|
+
.turn-actions {
|
|
413
|
+
display: flex;
|
|
414
|
+
flex-wrap: wrap;
|
|
415
|
+
gap: 8px;
|
|
416
|
+
margin-top: 4px;
|
|
417
|
+
}
|
|
354
418
|
.turn-stop,
|
|
419
|
+
.turn-steer,
|
|
420
|
+
.steer,
|
|
355
421
|
.send,
|
|
356
422
|
.stop-all {
|
|
357
423
|
display: inline-flex;
|
|
@@ -369,19 +435,44 @@ textarea:focus-visible {
|
|
|
369
435
|
font-weight: 620;
|
|
370
436
|
cursor: pointer;
|
|
371
437
|
}
|
|
438
|
+
/* Stop is ghost steel: --fill at rest, --lift on hover, danger ink only. */
|
|
372
439
|
.turn-stop,
|
|
373
440
|
.stop-all {
|
|
374
441
|
color: var(--danger);
|
|
442
|
+
background: var(--fill);
|
|
443
|
+
border-color: color-mix(in srgb, var(--danger) 32%, var(--line));
|
|
444
|
+
}
|
|
445
|
+
.turn-stop:hover,
|
|
446
|
+
.stop-all:hover {
|
|
375
447
|
background: var(--lift);
|
|
376
|
-
border-color: color-mix(in srgb, var(--danger)
|
|
448
|
+
border-color: color-mix(in srgb, var(--danger) 45%, var(--line));
|
|
377
449
|
}
|
|
450
|
+
/* Steer rides the steel rail, never a filled danger slab. */
|
|
451
|
+
.turn-steer,
|
|
452
|
+
.steer {
|
|
453
|
+
color: var(--steel);
|
|
454
|
+
background: var(--fill);
|
|
455
|
+
border-color: color-mix(in srgb, var(--steel) 38%, var(--line));
|
|
456
|
+
}
|
|
457
|
+
.turn-steer:hover,
|
|
458
|
+
.steer:hover {
|
|
459
|
+
background: var(--lift);
|
|
460
|
+
color: var(--text);
|
|
461
|
+
border-color: color-mix(in srgb, var(--steel) 55%, var(--line));
|
|
462
|
+
}
|
|
463
|
+
/* The one paper CTA on the page. */
|
|
378
464
|
.send {
|
|
379
|
-
background: var(--
|
|
465
|
+
background: var(--paper);
|
|
380
466
|
color: var(--ink);
|
|
381
467
|
border-color: transparent;
|
|
382
468
|
}
|
|
469
|
+
.send:hover:not(:disabled) {
|
|
470
|
+
background: color-mix(in srgb, var(--paper) 88%, white);
|
|
471
|
+
}
|
|
383
472
|
.send:disabled,
|
|
384
|
-
.stop-all:disabled
|
|
473
|
+
.stop-all:disabled,
|
|
474
|
+
.steer:disabled,
|
|
475
|
+
.turn-stop:disabled {
|
|
385
476
|
opacity: 0.45;
|
|
386
477
|
cursor: default;
|
|
387
478
|
}
|
|
@@ -407,9 +498,17 @@ textarea:focus-visible {
|
|
|
407
498
|
gap: 0.5rem;
|
|
408
499
|
}
|
|
409
500
|
.composer-actions .send,
|
|
410
|
-
.composer-actions .stop-all
|
|
501
|
+
.composer-actions .stop-all,
|
|
502
|
+
.composer-actions .steer {
|
|
411
503
|
flex: 1;
|
|
412
504
|
}
|
|
505
|
+
.composer-steer {
|
|
506
|
+
color: var(--muted);
|
|
507
|
+
font-size: 0.75rem;
|
|
508
|
+
line-height: 1.4;
|
|
509
|
+
padding: 0 0.15rem;
|
|
510
|
+
}
|
|
511
|
+
.composer-steer[hidden] { display: none; }
|
|
413
512
|
#draft {
|
|
414
513
|
display: block;
|
|
415
514
|
width: 100%;
|
|
@@ -472,6 +571,7 @@ textarea:focus-visible {
|
|
|
472
571
|
color: #fff;
|
|
473
572
|
background: var(--steel);
|
|
474
573
|
flex: none;
|
|
574
|
+
box-shadow: 0 0 0 1px color-mix(in srgb, var(--steel) 38%, transparent);
|
|
475
575
|
}
|
|
476
576
|
.flash {
|
|
477
577
|
position: fixed;
|
|
@@ -489,3 +589,9 @@ textarea:focus-visible {
|
|
|
489
589
|
z-index: 8;
|
|
490
590
|
}
|
|
491
591
|
.flash.on { opacity: 1; }
|
|
592
|
+
/* Errors read as danger ink on enamel lift, never a filled red slab. */
|
|
593
|
+
.flash.err {
|
|
594
|
+
background: var(--lift);
|
|
595
|
+
color: var(--danger);
|
|
596
|
+
border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--line));
|
|
597
|
+
}
|