@songsid/agend 2.3.0-beta.26 → 2.3.0-beta.27
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 +7 -5
- package/README.zh-TW.md +7 -5
- package/dist/agent-whats-new.js +12 -2
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +2 -0
- package/dist/channel/adapters/discord.js +11 -1
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +29 -0
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +4 -2
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.js +3 -1
- package/dist/daemon.js.map +1 -1
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +10 -0
- package/dist/doctor.js.map +1 -1
- package/dist/fleet-manager.d.ts +8 -1
- package/dist/fleet-manager.js +83 -13
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +10 -10
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +10 -10
- package/dist/idle-gate-log.d.ts +19 -0
- package/dist/idle-gate-log.js +15 -0
- package/dist/idle-gate-log.js.map +1 -0
- package/dist/locale.js +18 -6
- package/dist/locale.js.map +1 -1
- package/dist/public-web-gateway.js +3 -0
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/start-attempt.d.ts +1 -0
- package/dist/start-attempt.js +18 -0
- package/dist/start-attempt.js.map +1 -0
- package/dist/start-failure-notice.d.ts +4 -0
- package/dist/start-failure-notice.js +20 -0
- package/dist/start-failure-notice.js.map +1 -0
- package/dist/start-failure.d.ts +147 -0
- package/dist/start-failure.js +281 -0
- package/dist/start-failure.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/ui/chat-render.js +6 -2
- package/dist/ui/chat-strings.js +2 -2
- package/dist/ui/panel-chat.js +21 -9
- package/dist/ui/shared/app-shell.js +50 -9
- package/dist/ui/shared/app.css +34 -14
- package/dist/ui/shared/tokens.css +4 -0
- package/dist/update-progress.d.ts +3 -1
- package/dist/update-progress.js +3 -1
- package/dist/update-progress.js.map +1 -1
- package/dist/web-api.d.ts +4 -0
- package/dist/web-api.js +6 -3
- package/dist/web-api.js.map +1 -1
- package/package.json +1 -1
package/dist/ui/panel-chat.js
CHANGED
|
@@ -189,6 +189,10 @@ function useChatTick(name, kinds) {
|
|
|
189
189
|
|
|
190
190
|
/** #1722: the chat's title — the name that leads, the other on hover. */
|
|
191
191
|
const headerName = ({ primary, secondary }) => (secondary ? html`<span title=${secondary}>${primary}</span>` : primary);
|
|
192
|
+
/** #2478: who the composer writes to — the display name, else the instance's name (a long id made the box two lines). */
|
|
193
|
+
const composerLabel = (inst, name) => (inst && typeof inst.display_name === "string" && inst.display_name.trim()) || name;
|
|
194
|
+
/** #2478: a touch screen (a coarse pointer) — Return is a new line there and only the Send button sends. Asked each time. */
|
|
195
|
+
const touchScreen = () => typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
|
|
192
196
|
|
|
193
197
|
export function ChatPanel({ route, navKey }) {
|
|
194
198
|
const lease = useLease(navKey);
|
|
@@ -553,9 +557,9 @@ function Dock({ name, inst, lease, exec, awaiting, workingLabel }) {
|
|
|
553
557
|
<${CommandCard} name=${name} lease=${lease} />
|
|
554
558
|
<${QuickActions} name=${name} inst=${inst} lease=${lease} />
|
|
555
559
|
${s.waking[name] ? html`<p class="send-waking" role="status">${t("chat.waking", name)}</p>` : null}
|
|
556
|
-
<${Composer} name=${name} lease=${lease} busy=${R().isBusy(exec)} />
|
|
560
|
+
<${Composer} name=${name} label=${composerLabel(inst, name)} lease=${lease} busy=${R().isBusy(exec)} />
|
|
557
561
|
<div class="composer-row">${guestPage() ? html`<span class="guest-note" title=${t("chat.guestNote")}>${t("chat.guestNote")}</span>` : html`<${ComposerAuth} />`}<span
|
|
558
|
-
class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t("chat.composerHint")}</span></div>
|
|
562
|
+
class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t(touchScreen() ? "chat.composerHintTouch" : "chat.composerHint")}</span></div>
|
|
559
563
|
</div></div>`;
|
|
560
564
|
}
|
|
561
565
|
|
|
@@ -638,7 +642,7 @@ function replySnippet(r) {
|
|
|
638
642
|
return text || (r.media === "photo" ? t("chat.replyPhoto") : r.media === "document" ? t("chat.replyFile") : "");
|
|
639
643
|
}
|
|
640
644
|
|
|
641
|
-
function Composer({ name, lease, busy }) {
|
|
645
|
+
function Composer({ name, label = name, lease, busy }) {
|
|
642
646
|
const s = store.state;
|
|
643
647
|
const input = useRef(null), fileIn = useRef(null);
|
|
644
648
|
const [, tick] = useState(0);
|
|
@@ -653,8 +657,13 @@ function Composer({ name, lease, busy }) {
|
|
|
653
657
|
const guest = guestPage();
|
|
654
658
|
const palette = guest || files.length || closedFor === value ? null : paletteFor(value);
|
|
655
659
|
const pick = palette && palette.length ? palette[Math.min(sel, palette.length - 1)] : null;
|
|
656
|
-
// Grow with the text, up to a limit: set through the CSSOM, never a style attribute (#1300).
|
|
657
|
-
|
|
660
|
+
// Grow with the text, up to a limit: set through the CSSOM, never a style attribute (#1300). Empty, it is one line
|
|
661
|
+
// (#2478 — a long name's placeholder made the box two lines tall).
|
|
662
|
+
useLayoutEffect(() => {
|
|
663
|
+
const el = input.current; if (!el) return;
|
|
664
|
+
el.style.height = "auto";
|
|
665
|
+
if (value) el.style.height = `${Math.min(el.scrollHeight, 240)}px`;
|
|
666
|
+
}, [value]);
|
|
658
667
|
useEffect(() => { if (input.current) input.current.focus(); }, [name]);
|
|
659
668
|
useEffect(() => { refreshTourSpot(); });
|
|
660
669
|
const setValue = (v) => { store.setDraft(name, v); setSel(0); tick(n => n + 1); };
|
|
@@ -689,7 +698,7 @@ function Composer({ name, lease, busy }) {
|
|
|
689
698
|
if (pick && e.key === "Tab" && !e.shiftKey) { e.preventDefault(); complete(pick); return; }
|
|
690
699
|
if (palette && e.key === "Escape" && !e.isComposing) { e.preventDefault(); setClosedFor(value); return; }
|
|
691
700
|
if (reply && e.key === "Escape" && !e.isComposing) { e.preventDefault(); store.clearReply(name); return; } // #2272
|
|
692
|
-
if (R().composerKey(e) === "send") { e.preventDefault(); submit(); }
|
|
701
|
+
if (R().composerKey(e, { touch: touchScreen() }) === "send") { e.preventDefault(); submit(); }
|
|
693
702
|
};
|
|
694
703
|
const onInput = (e) => { setValue(e.target.value); };
|
|
695
704
|
// Files in a paste are attached; a very long text paste goes as a text file — only once it is really attached.
|
|
@@ -717,9 +726,12 @@ function Composer({ name, lease, busy }) {
|
|
|
717
726
|
<button id="attachBtn" type="button" class="icon-btn" title=${t("chat.attachTitle")} aria-label=${t("chat.attachTitle")} onClick=${() => fileIn.current && fileIn.current.click()}><${Icon} name="attach" /></button>
|
|
718
727
|
<input ref=${fileIn} type="file" multiple hidden
|
|
719
728
|
onChange=${(e) => { store.addFiles(name, e.target.files); e.target.value = ""; }} />
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
729
|
+
${/* #2478: the placeholder is drawn beside the box, one line ending in "…" — a textarea's own placeholder is clipped,
|
|
730
|
+
never ellipsized, and a long name wrapped it to two lines. The box keeps the words as its accessible name. */ ""}
|
|
731
|
+
<span class="composer-field"><textarea id="msgIn" ref=${input} rows="1" value=${value} aria-label=${t("chat.composerPlaceholder", label)}
|
|
732
|
+
enterkeyhint=${touchScreen() ? "enter" : undefined} autocomplete="off" aria-controls=${palette ? "cmdPalette" : undefined} aria-expanded=${palette ? "true" : "false"}
|
|
733
|
+
aria-activedescendant=${pick ? optId(pick) : undefined} onInput=${onInput} onKeyDown=${onKeyDown} onPaste=${onPaste}></textarea>${value ? null
|
|
734
|
+
: html`<span class="composer-ph" aria-hidden="true">${t("chat.composerPlaceholder", label)}</span>`}</span>
|
|
723
735
|
<button id="stopBtn" type="button" class="btn btn-stop" hidden=${!busy} title=${t("chat.stopReplyTitle")}
|
|
724
736
|
disabled=${!!s.cancelling[name]} onClick=${() => store.cancelReply(name)}><${Icon} name="stop" size=${14} /><span class="stop-label">${t("chat.stopReply")}</span></button>
|
|
725
737
|
<button id="sendBtn" type="button" class="btn btn-primary btn-send" hidden=${busy && !content} disabled=${!content}
|
|
@@ -70,12 +70,14 @@ export const isMacLike = () => typeof navigator !== "undefined" && /Mac|iPhone|i
|
|
|
70
70
|
/** What the row and tooltips show: ⌘K on a Mac, Ctrl K elsewhere. */
|
|
71
71
|
export const searchShortcut = () => (isMacLike() ? "⌘K" : "Ctrl K");
|
|
72
72
|
/**
|
|
73
|
-
* Whether this page offers search: an admin's page
|
|
74
|
-
* the
|
|
73
|
+
* Whether this page offers search: an admin's page. A guest's never. #2478 (the user's request): over the public link
|
|
74
|
+
* too, unless the fleet keeps it off there (web.search.public_link: false — the page says data-search-link="0", the
|
|
75
|
+
* server refuses it), so then no entry, no shortcut, and Ctrl+F stays the browser's.
|
|
75
76
|
*/
|
|
76
77
|
export function searchOffered(admin) {
|
|
77
|
-
const
|
|
78
|
-
|
|
78
|
+
const body = typeof document !== "undefined" ? document.body : null;
|
|
79
|
+
const offLink = !!body && body.dataset.publicLink === "1" && body.dataset.searchLink === "0";
|
|
80
|
+
return !!admin && !guestPage() && !offLink;
|
|
79
81
|
}
|
|
80
82
|
let searchModule = null;
|
|
81
83
|
/** Open search; `preset.instance`: narrowed to that agent (Ctrl+F in its chat). */
|
|
@@ -644,6 +646,20 @@ function usePageTheme(ref, route) {
|
|
|
644
646
|
}, [agent]);
|
|
645
647
|
}
|
|
646
648
|
|
|
649
|
+
/** #2478: how far short of its tallest the view must be, with a text field focused, to be a keyboard (not a browser bar). */
|
|
650
|
+
export const KEYBOARD_MIN_PX = 150;
|
|
651
|
+
/** #2478: the on-screen keyboard is up — something is being typed into, and the view is well short of its tallest. */
|
|
652
|
+
export function keyboardUp(height, tallest, typing) { return !!typing && tallest - height > KEYBOARD_MIN_PX; }
|
|
653
|
+
const NOT_TYPED = /^(button|checkbox|radio|range|color|file|submit|reset|image|hidden)$/i;
|
|
654
|
+
/** #2478: an element the keyboard types into — a textarea, a text-like input, or an editable region. */
|
|
655
|
+
export function isTextEntry(el) {
|
|
656
|
+
if (!el || !el.tagName) return false;
|
|
657
|
+
const tag = String(el.tagName).toUpperCase();
|
|
658
|
+
if (tag === "TEXTAREA") return !el.readOnly && !el.disabled;
|
|
659
|
+
if (tag === "INPUT") return !NOT_TYPED.test(el.type || "text") && !el.readOnly && !el.disabled;
|
|
660
|
+
return el.isContentEditable === true;
|
|
661
|
+
}
|
|
662
|
+
|
|
647
663
|
export function Shell({ panels, onNewInstance, viewOnly = false }) {
|
|
648
664
|
const nav = useStore(navStore);
|
|
649
665
|
const shell = useStore(shellStore);
|
|
@@ -653,13 +669,38 @@ export function Shell({ panels, onNewInstance, viewOnly = false }) {
|
|
|
653
669
|
// A language switch re-renders everything and gives the panel a new lease (its key includes the language).
|
|
654
670
|
useEffect(() => onLang(() => relang(n => n + 1)), []);
|
|
655
671
|
useEffect(() => { closeDrawer(); }, [nav.seq]);
|
|
656
|
-
// The bottom tabs give their room to the keyboard on a phone.
|
|
672
|
+
// The bottom tabs (and the line under the composer) give their room to the keyboard on a phone (#2478).
|
|
657
673
|
useEffect(() => {
|
|
658
674
|
const vv = typeof window !== "undefined" ? window.visualViewport : null;
|
|
659
|
-
if (!vv) return undefined;
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
675
|
+
if (!vv || typeof document === "undefined") return undefined;
|
|
676
|
+
// The tallest the view has been at this width: what it is with no keyboard. innerHeight is no measure on its own —
|
|
677
|
+
// it shrinks with the keyboard on iOS Safari and, under interactive-widget=resizes-content, on Android — so
|
|
678
|
+
// innerHeight − height stayed near 0 and the strips stayed between the composer and the keyboard.
|
|
679
|
+
// Only a touch screen (a coarse pointer) has an on-screen keyboard: a narrow desk window made shorter is not one.
|
|
680
|
+
// Turned with the keyboard up and the field still focused, the new width has no height without it yet: the keyboard
|
|
681
|
+
// stays up until the field is left, or the view grows from its lowest by more than a keyboard (put away).
|
|
682
|
+
let width = -1, tallest = 0, held = false, lowest = 0;
|
|
683
|
+
const coarse = () => typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
|
|
684
|
+
const check = () => {
|
|
685
|
+
const w = Math.round(vv.width), h = vv.height;
|
|
686
|
+
const typing = coarse() && isTextEntry(document.activeElement);
|
|
687
|
+
if (w !== width) { // turned, or a new width: measured again
|
|
688
|
+
held = width !== -1 && typing && shellStore.get().keyboard;
|
|
689
|
+
width = w; tallest = 0; lowest = h;
|
|
690
|
+
}
|
|
691
|
+
if (held) {
|
|
692
|
+
lowest = Math.min(lowest, h);
|
|
693
|
+
if (!typing || h - lowest > KEYBOARD_MIN_PX) held = false; // left the field, or the keyboard was put away
|
|
694
|
+
}
|
|
695
|
+
if (!held) tallest = Math.max(tallest, window.innerHeight || 0, typing ? 0 : h); // the view itself only when not typing
|
|
696
|
+
const up = held || keyboardUp(h, tallest, typing);
|
|
697
|
+
if (up !== shellStore.get().keyboard) shellStore.set({ keyboard: up });
|
|
698
|
+
};
|
|
699
|
+
check();
|
|
700
|
+
vv.addEventListener("resize", check);
|
|
701
|
+
document.addEventListener("focusin", check);
|
|
702
|
+
document.addEventListener("focusout", check);
|
|
703
|
+
return () => { vv.removeEventListener("resize", check); document.removeEventListener("focusin", check); document.removeEventListener("focusout", check); };
|
|
663
704
|
}, []);
|
|
664
705
|
const guest = guestPage(); // #1920: a guest's page — no operations, usage, pending changes or owner CSS
|
|
665
706
|
const cls = ["shell", shell.collapsed ? "sb-collapsed" : "", shell.drawer ? "sb-open" : "", shell.keyboard ? "kb-open" : "", guest ? "guest" : ""].filter(Boolean).join(" ");
|
package/dist/ui/shared/app.css
CHANGED
|
@@ -130,9 +130,15 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
|
|
|
130
130
|
A short box that clips to its corners (.msg-quote) moves the bar in (--lbar-left) and shortens it (--lbar-inset) so
|
|
131
131
|
the corner never cuts its ends round.
|
|
132
132
|
tests/web-left-bar-2330.test.ts keeps a coloured border-left off rounded boxes. */
|
|
133
|
-
.lbar, .reply-bar, .msg-quote, .p-log .log-item.k-error, .p-log .log-item.s-failed, .org-node, .us-basis, .us-partial, .cmd-card.bad, .tb-tile
|
|
133
|
+
.lbar, .reply-bar, .msg-quote, .p-log .log-item.k-error, .p-log .log-item.s-failed, .org-node, .us-basis, .us-partial, .cmd-card.bad, .tb-tile,
|
|
134
|
+
.search-row.on, .search-recent-item:hover, .search-recent-item:focus-visible, .search-ctx-line.here, .tw-line:hover, .tw-line:focus-visible,
|
|
135
|
+
.u-crow:focus-visible { position: relative; }
|
|
134
136
|
.lbar::before, .reply-bar::before, .msg-quote::before, .p-log .log-item.k-error::before, .p-log .log-item.s-failed::before, .org-node::before,
|
|
135
|
-
.us-basis::before, .us-partial::before, .cmd-card.bad::before, .tb-tile::before
|
|
137
|
+
.us-basis::before, .us-partial::before, .cmd-card.bad::before, .tb-tile::before,
|
|
138
|
+
/* #2478 item 7 (the user's fixed template): a selected, hovered or keyboard-focused row is the straight bar and a flat
|
|
139
|
+
background at most — never a rounded fill, a curved inset edge or a rounded ring. */
|
|
140
|
+
.search-row.on::before, .search-recent-item:hover::before, .search-recent-item:focus-visible::before, .search-ctx-line.here::before,
|
|
141
|
+
.tw-line:hover::before, .tw-line:focus-visible::before, .u-crow:focus-visible::before {
|
|
136
142
|
content: ""; position: absolute; left: var(--lbar-left, 0); top: var(--lbar-inset, var(--s-2)); bottom: var(--lbar-inset, var(--s-2)); width: 3px;
|
|
137
143
|
background: var(--lbar-color, var(--accent)); pointer-events: none; }
|
|
138
144
|
.idc-swatches, .idc-places { border: 0; padding: 0; margin: 0 0 var(--s-3); display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
@@ -645,8 +651,8 @@ html.lb-open .scroller { overflow: hidden; }
|
|
|
645
651
|
.p-chat > .panel-head { --bg: var(--chrome-bg); --surface: var(--chrome-surface); --text: var(--chrome-text); --text-2: var(--chrome-text-2); --accent: var(--chrome-accent); background: var(--bg); color: var(--text); }
|
|
646
652
|
.p-chat.th { background: var(--bg); color: var(--text); }
|
|
647
653
|
.p-chat.th .msg.user .body { border-radius: var(--bubble-radius, var(--r-xl)); }
|
|
648
|
-
.p-chat.th-font-system .thread, .p-chat.th-font-system .composer textarea { font-family: system-ui, -apple-system, "Segoe UI", sans-serif, var(--font-emoji); }
|
|
649
|
-
.p-chat.th-font-mono .thread, .p-chat.th-font-mono .composer textarea { font-family: var(--font-mono); }
|
|
654
|
+
.p-chat.th-font-system .thread, .p-chat.th-font-system .composer textarea, .p-chat.th-font-system .composer-ph { font-family: system-ui, -apple-system, "Segoe UI", sans-serif, var(--font-emoji); }
|
|
655
|
+
.p-chat.th-font-mono .thread, .p-chat.th-font-mono .composer textarea, .p-chat.th-font-mono .composer-ph { font-family: var(--font-mono); }
|
|
650
656
|
.p-chat.th-bubble-thin .msg.user .body { box-shadow: inset 0 0 0 1px var(--border); }
|
|
651
657
|
.p-chat.th-bubble-accent .msg.user .body { box-shadow: inset 0 0 0 1px var(--accent); }
|
|
652
658
|
.p-chat.th-agent-bubble .msg.agent .body { background: var(--agent-bubble); border-radius: var(--bubble-radius, var(--r-xl)); padding: 10px var(--s-4); }
|
|
@@ -780,6 +786,11 @@ img.amb-p { object-fit: contain; }
|
|
|
780
786
|
font-size: var(--fs-md); line-height: var(--lh-md); resize: none; min-height: 24px; max-height: 240px; overflow-y: auto;
|
|
781
787
|
}
|
|
782
788
|
.composer textarea::placeholder { color: var(--text-3); }
|
|
789
|
+
/* #2478: the placeholder, drawn over the empty box at its text's place: one line ending in "…" (a textarea's own is
|
|
790
|
+
clipped, never ellipsized). The same size and line as the box's text; it never takes a tap. */
|
|
791
|
+
.composer-field { position: relative; flex: 1; min-width: 0; display: flex; }
|
|
792
|
+
.composer-ph { position: absolute; left: var(--s-1); right: var(--s-1); top: 7px; pointer-events: none; color: var(--text-3);
|
|
793
|
+
font-size: var(--fs-md); line-height: var(--lh-md); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
783
794
|
.btn-send { width: 36px; padding: 0; }
|
|
784
795
|
.btn-send .send-label { display: none; }
|
|
785
796
|
/* A narrow phone: Stop reply beside Send leaves the message box no room — the label is for screen readers there. */
|
|
@@ -952,6 +963,8 @@ img.amb-p { object-fit: contain; }
|
|
|
952
963
|
padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
|
|
953
964
|
}
|
|
954
965
|
.kb-open .tabs { display: none; }
|
|
966
|
+
/* #2478: with the keyboard up the line under the composer goes too — the box sits on the keyboard; back on blur. */
|
|
967
|
+
.kb-open .composer-row { display: none; }
|
|
955
968
|
.tab { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px; min-height: 56px; justify-content: center; font-size: 11px; color: var(--text-2); position: relative; }
|
|
956
969
|
.tab.active { color: var(--text); font-weight: var(--fw-semibold); }
|
|
957
970
|
.tab-badge { position: absolute; top: 6px; left: calc(50% + 6px); min-width: 16px; height: 16px; padding: 0 4px; border-radius: var(--r-full); background: var(--danger-fill); color: var(--on-danger); font-size: 10px; line-height: 16px; text-align: center; }
|
|
@@ -1169,7 +1182,10 @@ body.side-resizing, body.side-resizing * { cursor: col-resize !important; user-s
|
|
|
1169
1182
|
background: transparent; color: inherit; font: inherit; line-height: 1.3; text-align: left; cursor: pointer; }
|
|
1170
1183
|
.u-cg-head + .u-crow { border-top: 0; }
|
|
1171
1184
|
.u-crow:hover { background: var(--surface-2); }
|
|
1172
|
-
.u-
|
|
1185
|
+
/* #2478 (Prism r1): the compact rows sit in .u-cgroup, rounded and clipping (overflow: hidden) — the bar keeps clear of
|
|
1186
|
+
its 12 px corners (4 px in, 6 px off the row's ends) and there is no full-width underline to be rounded at the ends:
|
|
1187
|
+
the bar is the focus mark (hover has none). */
|
|
1188
|
+
.u-crow:focus-visible { outline: none; background: var(--surface-2); --lbar-left: var(--s-1); --lbar-inset: 6px; }
|
|
1173
1189
|
/* The name never gives way to the plan (#2415); the reset keeps its room on the right. */
|
|
1174
1190
|
.u-crow-head { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: var(--fs-sm); }
|
|
1175
1191
|
.u-crow-name { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--fw-semibold); color: var(--text); }
|
|
@@ -1584,7 +1600,7 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
|
|
|
1584
1600
|
.oo-desks:focus-within { position: sticky; left: var(--s-2); bottom: var(--s-2); width: auto; height: auto; max-height: 40%; margin: 0; padding: var(--s-1); clip: auto; overflow-y: auto;
|
|
1585
1601
|
display: inline-flex; flex-wrap: wrap; gap: 4px; max-width: calc(100% - 2 * var(--s-2)); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); white-space: normal; }
|
|
1586
1602
|
.oo-desk { font-size: var(--fs-xs); padding: 2px 6px; border-radius: var(--r-sm); background: var(--bg); color: var(--text); border: 1px solid var(--border); }
|
|
1587
|
-
.oo-desk:focus-visible { outline:
|
|
1603
|
+
.oo-desk:focus-visible { outline: none; border-color: var(--focus); box-shadow: inset 0 -2px 0 var(--focus); }
|
|
1588
1604
|
@media (max-width: 640px) {
|
|
1589
1605
|
.oo-stage { height: calc(100dvh - 300px); min-height: 340px; }
|
|
1590
1606
|
.oo-pick .tb-select { max-width: 150px; }
|
|
@@ -1908,6 +1924,8 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
|
|
|
1908
1924
|
.chat-fx-notice > span { display: block; }
|
|
1909
1925
|
|
|
1910
1926
|
/* ── #2293: global search — the sidebar row's shortcut, the header's button (collapsed sidebar, phone), the dialog ── */
|
|
1927
|
+
/* #2478: a touch screen has no Ctrl/⌘ K — the shortcut's hint is not shown there (the row is). */
|
|
1928
|
+
@media (pointer: coarse) { .side-search .kbd-hint { display: none; } }
|
|
1911
1929
|
.kbd-hint { font: inherit; font-size: var(--fs-xs); color: var(--text-3); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 0 var(--s-1); line-height: 18px; white-space: nowrap; }
|
|
1912
1930
|
.panel-head .sb-search { display: none; }
|
|
1913
1931
|
.sb-collapsed .panel-head .sb-search { display: inline-flex; }
|
|
@@ -1933,15 +1951,17 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
|
|
|
1933
1951
|
.search-note { color: var(--warn); }
|
|
1934
1952
|
.search-recent-head { display: flex; align-items: center; justify-content: space-between; padding: var(--s-2) var(--s-3) 0; color: var(--text-3); font-size: var(--fs-xs); }
|
|
1935
1953
|
.search-recent ul { list-style: none; margin: 0; padding: 0; }
|
|
1936
|
-
.search-recent-item { display: flex; align-items: center; gap: var(--s-2); width: 100%; border: 0; border-radius:
|
|
1937
|
-
.search-recent-item:hover { background: var(--surface-2); }
|
|
1954
|
+
.search-recent-item { display: flex; align-items: center; gap: var(--s-2); width: 100%; border: 0; border-radius: 0; --lbar-inset: 4px; background: transparent; color: var(--text); padding: var(--s-2) var(--s-3); font-size: var(--fs-sm); text-align: left; cursor: pointer; }
|
|
1955
|
+
.search-recent-item:hover, .search-recent-item:focus-visible { background: var(--surface-2); }
|
|
1956
|
+
/* Keyboard focus: the bar and a straight underline (no rounded ring). */
|
|
1957
|
+
.search-recent-item:focus-visible { outline: none; box-shadow: inset 0 -2px 0 var(--focus); }
|
|
1938
1958
|
.search-recent-item svg { color: var(--text-3); flex: none; }
|
|
1939
1959
|
.search-results.stale { opacity: 0.6; }
|
|
1940
1960
|
.search-group + .search-group { margin-top: var(--s-2); }
|
|
1941
1961
|
.search-group-head { display: flex; justify-content: space-between; gap: var(--s-2); padding: var(--s-2) var(--s-3) var(--s-1); font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--text-2); }
|
|
1942
1962
|
.search-count { color: var(--text-3); font-weight: var(--fw-regular); }
|
|
1943
|
-
.search-row { padding: var(--s-2) var(--s-3); border-radius:
|
|
1944
|
-
.search-row.on { background: var(--surface-2);
|
|
1963
|
+
.search-row { padding: var(--s-2) var(--s-3); border-radius: 0; cursor: pointer; }
|
|
1964
|
+
.search-row.on { background: var(--surface-2); }
|
|
1945
1965
|
.search-row-top { display: flex; align-items: center; gap: var(--s-2); font-size: var(--fs-xs); color: var(--text-2); }
|
|
1946
1966
|
.search-who { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1947
1967
|
.search-kind { color: var(--text-3); }
|
|
@@ -1955,8 +1975,8 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
|
|
|
1955
1975
|
.search-detail-head { display: flex; align-items: center; gap: var(--s-2); padding: var(--s-1) var(--s-1) var(--s-2); font-size: var(--fs-sm); }
|
|
1956
1976
|
.search-detail-head .chip-btn { display: inline-flex; align-items: center; gap: 4px; }
|
|
1957
1977
|
.search-ctx { list-style: none; margin: 0; padding: 0 var(--s-1); display: flex; flex-direction: column; gap: var(--s-1); }
|
|
1958
|
-
.search-ctx-line { padding: var(--s-2) var(--s-3); border-radius:
|
|
1959
|
-
.search-ctx-line.here { background: var(--surface-2);
|
|
1978
|
+
.search-ctx-line { padding: var(--s-2) var(--s-3); border-radius: 0; }
|
|
1979
|
+
.search-ctx-line.here { background: var(--surface-2); }
|
|
1960
1980
|
.search-ctx-text { margin-top: 2px; font-size: var(--fs-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
1961
1981
|
.search-detail-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--s-2); padding: var(--s-3) var(--s-1) var(--s-1); }
|
|
1962
1982
|
@media (max-width: 899px) {
|
|
@@ -2132,9 +2152,9 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
|
|
|
2132
2152
|
.tw-desc { margin: 0; font-size: var(--fs-xs); overflow-wrap: anywhere; }
|
|
2133
2153
|
.tw-none { margin: 0; font-size: var(--fs-xs); }
|
|
2134
2154
|
.tw-lines { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
|
|
2135
|
-
.tw-line { display: flex; align-items: baseline; gap: var(--s-2); width: 100%; padding: 2px var(--s-1); background: none; border: 0; border-radius:
|
|
2155
|
+
.tw-line { display: flex; align-items: baseline; gap: var(--s-2); width: 100%; padding: 2px var(--s-1); background: none; border: 0; border-radius: 0; --lbar-inset: 2px; color: var(--text); text-align: left; cursor: pointer; font-size: var(--fs-sm); min-width: 0; }
|
|
2136
2156
|
.tw-line:hover { background: var(--surface-2); }
|
|
2137
|
-
.tw-line:focus-visible { outline:
|
|
2157
|
+
.tw-line:focus-visible { outline: none; background: var(--surface-2); box-shadow: inset 0 -2px 0 var(--focus); }
|
|
2138
2158
|
.tw-kind { flex-shrink: 0; min-width: 4.5em; font-size: 11px; font-weight: var(--fw-semibold); color: var(--text-2); }
|
|
2139
2159
|
.tw-kind.k-doing { color: var(--accent); }
|
|
2140
2160
|
.tw-kind.k-blocked, .tw-kind.k-back { color: var(--danger); }
|
|
@@ -166,3 +166,7 @@
|
|
|
166
166
|
@media (pointer: coarse), (max-width: 899px) {
|
|
167
167
|
input, select, textarea { --fs-xs: 16px; --fs-sm: 16px; --fs-md: 16px; }
|
|
168
168
|
}
|
|
169
|
+
/* #2478: the composer's drawn placeholder (a span over the box) is the box's text in size too. */
|
|
170
|
+
@media (pointer: coarse), (max-width: 899px) {
|
|
171
|
+
.composer-ph { --fs-md: 16px; }
|
|
172
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type UpdateMarker } from "./update-marker.js";
|
|
2
2
|
export declare function updateElapsedSeconds(startedAt: number, now?: number): number;
|
|
3
|
-
/** Render the persisted CLI-side stage while the old fleet is still alive.
|
|
3
|
+
/** Render the persisted CLI-side stage while the old fleet is still alive.
|
|
4
|
+
* Stage `starting` is owned by RestartProgress in the new process (#2476); the
|
|
5
|
+
* monitor must not post this text then. */
|
|
4
6
|
export declare function formatUpdateProgress(marker: UpdateMarker & {
|
|
5
7
|
progress: NonNullable<UpdateMarker["progress"]>;
|
|
6
8
|
}, now?: number): string;
|
package/dist/update-progress.js
CHANGED
|
@@ -6,7 +6,9 @@ export function updateElapsedSeconds(startedAt, now = Date.now()) {
|
|
|
6
6
|
function stageLabel(stage) {
|
|
7
7
|
return t(`update.stage.${stage ?? "preparing"}`);
|
|
8
8
|
}
|
|
9
|
-
/** Render the persisted CLI-side stage while the old fleet is still alive.
|
|
9
|
+
/** Render the persisted CLI-side stage while the old fleet is still alive.
|
|
10
|
+
* Stage `starting` is owned by RestartProgress in the new process (#2476); the
|
|
11
|
+
* monitor must not post this text then. */
|
|
10
12
|
export function formatUpdateProgress(marker, now = Date.now()) {
|
|
11
13
|
const elapsed = updateElapsedSeconds(marker.startedAt, now);
|
|
12
14
|
const progress = marker.progress;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-progress.js","sourceRoot":"","sources":["../src/update-progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,aAAa,CAAC;AAChC,OAAO,EAAE,uBAAuB,EAA+C,MAAM,oBAAoB,CAAC;AAE1G,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IACtE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,UAAU,CAAC,KAAsC;IACxD,OAAO,CAAC,CAAC,gBAAgB,KAAK,IAAI,WAAW,EAAE,CAAC,CAAC;AACnD,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"update-progress.js","sourceRoot":"","sources":["../src/update-progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,aAAa,CAAC;AAChC,OAAO,EAAE,uBAAuB,EAA+C,MAAM,oBAAoB,CAAC;AAE1G,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IACtE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,UAAU,CAAC,KAAsC;IACxD,OAAO,CAAC,CAAC,gBAAgB,KAAK,IAAI,WAAW,EAAE,CAAC,CAAC;AACnD,CAAC;AAED;;2CAE2C;AAC3C,MAAM,UAAU,oBAAoB,CAAC,MAA0E,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAC/H,MAAM,OAAO,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,IAAI,uBAAuB,CAAC,QAAQ,CAAC,KAAK,cAAc,EAAE,CAAC;QACzD,IAAI,QAAQ,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,CAAC,CAAC,yBAAyB,EAAE,QAAQ,CAAC,KAAK,IAAI,eAAe,EAAE,OAAO,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,QAAQ,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAClC,OAAO,CAAC,CAAC,2BAA2B,EAAE,QAAQ,CAAC,OAAO,IAAI,GAAG,EAAE,OAAO,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,QAAQ,CAAC,KAAK,KAAK,UAAU;YAClC,CAAC,CAAC,CAAC,CAAC,2BAA2B,EAAE,OAAO,CAAC;YACzC,CAAC,CAAC,CAAC,CAAC,2BAA2B,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IACD,QAAQ,QAAQ,CAAC,KAAK,EAAE,CAAC;QACvB,KAAK,WAAW;YACd,OAAO,CAAC,CAAC,2BAA2B,EAAE,OAAO,CAAC,CAAC;QACjD,KAAK,aAAa;YAChB,OAAO,CAAC,CAAC,6BAA6B,EAAE,OAAO,CAAC,CAAC;QACnD,KAAK,WAAW;YACd,OAAO,CAAC,CAAC,2BAA2B,EAAE,QAAQ,CAAC,OAAO,IAAI,GAAG,EAAE,OAAO,CAAC,CAAC;QAC1E,KAAK,UAAU;YACb,OAAO,CAAC,CAAC,0BAA0B,EAAE,OAAO,CAAC,CAAC;QAChD,KAAK,UAAU;YACb,OAAO,CAAC,CAAC,0BAA0B,EAAE,OAAO,CAAC,CAAC;QAChD,KAAK,UAAU;YACb,OAAO,CAAC,CAAC,iCAAiC,EAAE,QAAQ,CAAC,OAAO,IAAI,GAAG,EAAE,OAAO,CAAC,CAAC;QAChF,KAAK,QAAQ;YACX,OAAO,CAAC,CAAC,wBAAwB,EAAE,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,KAAK,IAAI,eAAe,EAAE,OAAO,CAAC,CAAC;IACrH,CAAC;AACH,CAAC"}
|
package/dist/web-api.d.ts
CHANGED
|
@@ -68,6 +68,7 @@ export interface WebApiContext {
|
|
|
68
68
|
upload_max_total_mb?: number;
|
|
69
69
|
search?: {
|
|
70
70
|
enabled?: boolean;
|
|
71
|
+
public_link?: boolean;
|
|
71
72
|
};
|
|
72
73
|
};
|
|
73
74
|
defaults?: {
|
|
@@ -438,6 +439,9 @@ export interface AppShellContext {
|
|
|
438
439
|
experimental?: {
|
|
439
440
|
canvas_worker?: boolean;
|
|
440
441
|
};
|
|
442
|
+
search?: {
|
|
443
|
+
public_link?: boolean;
|
|
444
|
+
};
|
|
441
445
|
};
|
|
442
446
|
} | null;
|
|
443
447
|
/** #1665: the host's theme and language go into the page itself, so a device paints them before any script reads. */
|
package/dist/web-api.js
CHANGED
|
@@ -3171,11 +3171,12 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
3171
3171
|
}
|
|
3172
3172
|
return true;
|
|
3173
3173
|
}
|
|
3174
|
-
// #2293: global search — an admin's only: the header token or an admin session. Never
|
|
3175
|
-
//
|
|
3174
|
+
// #2293: global search — an admin's only: the header token or an admin session. Never a guest (not in GUEST_ROUTES).
|
|
3175
|
+
// #2478 (the user's request): over the public link too, unless web.search.public_link is false. Asked on arrival and
|
|
3176
|
+
// again before the answer is sent.
|
|
3176
3177
|
if (method === "GET" && (path === "/ui/search" || path === "/ui/search/context")) {
|
|
3177
3178
|
const refusal = () => {
|
|
3178
|
-
if (gatewayRequestContext(req))
|
|
3179
|
+
if (gatewayRequestContext(req) && ctx.fleetConfig?.web?.search?.public_link === false)
|
|
3179
3180
|
return [404, "search_not_over_public_link"];
|
|
3180
3181
|
if (guestRequestOf(req))
|
|
3181
3182
|
return [403, "guest_forbidden"];
|
|
@@ -4065,6 +4066,8 @@ function shellBodyTag(req, ctx, mode, viewer) {
|
|
|
4065
4066
|
+ ` data-owner-css-state="${ownerCssState}"${ownerCss ? ` data-owner-css="${attr(ownerCss)}"` : ""}`
|
|
4066
4067
|
// #1589: the public link shows attachments as downloads only (no inline text or media): its scope is unchanged.
|
|
4067
4068
|
+ `${gatewayRequestContext(req) ? ' data-public-link="1"' : ""}`
|
|
4069
|
+
// #2478: search is offered over the public link unless web.search.public_link is false (the page then shows no entry).
|
|
4070
|
+
+ `${gatewayRequestContext(req) && ctx.fleetConfig?.web?.search?.public_link === false ? ' data-search-link="0"' : ""}`
|
|
4068
4071
|
// #1920 phase 3: a guest's page — the shell shows only what a guest may use, and who it is signed in as.
|
|
4069
4072
|
+ `${mode === "full" && viewer?.guest && viewer.owner ? guestBodyAttrs(ctx, viewer.owner, attr) : ""}>`;
|
|
4070
4073
|
}
|