@qilin-agent/client-ui-workspace 3.1.5 → 3.1.7
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.i18n.yaml +5 -2
- package/README.md +10 -2
- package/README.zh.md +10 -2
- package/lib/client.js +435 -123
- package/lib/types/client/contract/slots.d.ts +26 -0
- package/lib/types/client/contract/toast.d.ts +55 -0
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/locales.d.ts +12 -0
- package/lib/types/client/navigation.d.ts +22 -7
- package/lib/types/client/rows/Rows.d.ts +7 -3
- package/lib/types/client/rows/WorkspaceBrowser.d.ts +1 -1
- package/lib/types/client/toast/RowActionToast.d.ts +9 -0
- package/lib/types/client/toast/Toast.d.ts +15 -0
- package/lib/types/client/toast/register.d.ts +8 -0
- package/package.json +23 -23
package/lib/client.js
CHANGED
|
@@ -4,11 +4,11 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let _qilin_agent_client_ui_primitives = require("@qilin-agent/client-ui-primitives");
|
|
7
8
|
let _qilin_agent_client_store = require("@qilin-agent/client-store");
|
|
8
9
|
let _qilin_agent_kylin = require("@qilin-agent/kylin");
|
|
9
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
11
|
let react = require("react");
|
|
11
|
-
let _qilin_agent_client_ui_primitives = require("@qilin-agent/client-ui-primitives");
|
|
12
12
|
//#region lib/types/client/shortcuts.js
|
|
13
13
|
/**
|
|
14
14
|
* Create the private browser request source shared by commands and controls.
|
|
@@ -688,6 +688,7 @@ window.__ModuleLoader__.load({
|
|
|
688
688
|
workspaces;
|
|
689
689
|
sessions;
|
|
690
690
|
view;
|
|
691
|
+
notify;
|
|
691
692
|
connecting = /* @__PURE__ */ new Map();
|
|
692
693
|
lifetime = new AbortController();
|
|
693
694
|
/**
|
|
@@ -703,13 +704,15 @@ window.__ModuleLoader__.load({
|
|
|
703
704
|
* @param workspaces - pure Workspace Controller.
|
|
704
705
|
* @param sessions - pure Session Controller.
|
|
705
706
|
* @param view - the browser's viewing-store write set, which owns saved Session order.
|
|
707
|
+
* @param notify - show one notice through the Workspace notice channel.
|
|
706
708
|
*/
|
|
707
|
-
constructor(ctx, directoryPicker, workspaces, sessions, view) {
|
|
709
|
+
constructor(ctx, directoryPicker, workspaces, sessions, view, notify) {
|
|
708
710
|
super(ctx, "uiWorkspace");
|
|
709
711
|
this.directoryPicker = directoryPicker;
|
|
710
712
|
this.workspaces = workspaces;
|
|
711
713
|
this.sessions = sessions;
|
|
712
714
|
this.view = view;
|
|
715
|
+
this.notify = notify;
|
|
713
716
|
ctx.effect(() => {
|
|
714
717
|
const stop = this.watchNavigation();
|
|
715
718
|
return () => {
|
|
@@ -721,29 +724,49 @@ window.__ModuleLoader__.load({
|
|
|
721
724
|
};
|
|
722
725
|
}, "ui-workspace: Workspace navigation policy");
|
|
723
726
|
}
|
|
724
|
-
async connectWorkspace(workspaceId) {
|
|
727
|
+
async connectWorkspace(workspaceId, options) {
|
|
725
728
|
const workspace = this.workspaces.list.getSnapshot().items.find((item) => item.workspaceId === workspaceId);
|
|
726
729
|
if (workspace === void 0) throw new Error(`uiWorkspace.connectWorkspace: unknown workspace ${workspaceId}`);
|
|
727
730
|
const inflight = this.connecting.get(workspaceId);
|
|
728
|
-
if (inflight !== void 0) return inflight;
|
|
731
|
+
if (inflight !== void 0 && inflight.agentPreset === options?.agentPreset) return inflight.attempt;
|
|
729
732
|
const archived = this.workspaces.list.getSnapshot().archivedSessionIds;
|
|
730
733
|
const sessions = this.sessions.list.getSnapshot();
|
|
731
734
|
for (const id of sessions.ids) {
|
|
732
735
|
const summary = sessions.byId[id];
|
|
733
|
-
|
|
736
|
+
const listed = summary?.projectionValues?.agentPreset;
|
|
737
|
+
if (summary !== void 0 && summary.blank && summary.cwd === workspace.path && workspace.sessionIds.includes(summary.id) && !archived.includes(summary.id) && (options?.agentPreset === void 0 || typeof listed !== "string" || listed === options.agentPreset)) {
|
|
738
|
+
const reuse = this.sessions.create({
|
|
739
|
+
workspaceId,
|
|
740
|
+
sessionId: summary.id,
|
|
741
|
+
...options?.agentPreset === void 0 ? {} : { agentPreset: options.agentPreset }
|
|
742
|
+
}).finally(() => {
|
|
743
|
+
this.connecting.delete(workspaceId);
|
|
744
|
+
});
|
|
745
|
+
this.connecting.set(workspaceId, {
|
|
746
|
+
agentPreset: options?.agentPreset,
|
|
747
|
+
attempt: reuse
|
|
748
|
+
});
|
|
749
|
+
return reuse;
|
|
750
|
+
}
|
|
734
751
|
}
|
|
735
|
-
const attempt = this.sessions.create({
|
|
752
|
+
const attempt = this.sessions.create({
|
|
753
|
+
workspaceId,
|
|
754
|
+
...options?.agentPreset === void 0 ? {} : { agentPreset: options.agentPreset }
|
|
755
|
+
}).finally(() => {
|
|
736
756
|
this.connecting.delete(workspaceId);
|
|
737
757
|
});
|
|
738
|
-
this.connecting.set(workspaceId,
|
|
758
|
+
this.connecting.set(workspaceId, {
|
|
759
|
+
agentPreset: options?.agentPreset,
|
|
760
|
+
attempt
|
|
761
|
+
});
|
|
739
762
|
return attempt;
|
|
740
763
|
}
|
|
741
764
|
openSession(target) {
|
|
742
765
|
this.replaceMain(target, this.lifetime.signal);
|
|
743
766
|
}
|
|
744
|
-
async openWorkspace(workspaceId, beforeOpen) {
|
|
767
|
+
async openWorkspace(workspaceId, beforeOpen, options) {
|
|
745
768
|
const navigation = AbortSignal.any([this.ctx.layout.beginNavigation(), this.lifetime.signal]);
|
|
746
|
-
const sessionId = await this.connectWorkspace(workspaceId);
|
|
769
|
+
const sessionId = await this.connectWorkspace(workspaceId, options);
|
|
747
770
|
if (navigation.aborted) return;
|
|
748
771
|
this.replaceMain(sessionId, navigation, beforeOpen);
|
|
749
772
|
}
|
|
@@ -755,7 +778,10 @@ window.__ModuleLoader__.load({
|
|
|
755
778
|
});
|
|
756
779
|
if (!navigation.aborted) this.replaceMain(childId, navigation);
|
|
757
780
|
}
|
|
758
|
-
startSession(workspaceId) {
|
|
781
|
+
startSession(workspaceId, options) {
|
|
782
|
+
const { agentPreset, ...draft } = options ?? {};
|
|
783
|
+
const draftOptions = options === void 0 ? void 0 : structuredClone(draft);
|
|
784
|
+
const initializeDraft = draftOptions !== void 0 && (draftOptions.prompt !== void 0 || (draftOptions.whenDraftExists ?? (draftOptions.clearPreviousDraft === true ? "replace" : "keep")) === "replace");
|
|
759
785
|
const workspace = this.workspaces.list.getSnapshot();
|
|
760
786
|
const sessions = this.sessions.list.getSnapshot();
|
|
761
787
|
const current = this.mainReference?.sessionId;
|
|
@@ -763,13 +789,37 @@ window.__ModuleLoader__.load({
|
|
|
763
789
|
const recent = workspace.phase === "ready" && sessions.phase === "ready" ? recentWorkspace(workspace.items, sessions.byId) : void 0;
|
|
764
790
|
const target = workspaceId ?? currentWorkspaceId ?? recent;
|
|
765
791
|
if (target === void 0) {
|
|
792
|
+
if (initializeDraft) {
|
|
793
|
+
this.notify({
|
|
794
|
+
kind: "createFailed",
|
|
795
|
+
message: this.ctx.locale.bind("workspace")("draft.workspaceRequired")
|
|
796
|
+
});
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
766
799
|
this.clearMain();
|
|
767
800
|
return;
|
|
768
801
|
}
|
|
769
|
-
this.openWorkspace(target
|
|
802
|
+
this.openWorkspace(target, initializeDraft ? (id) => {
|
|
803
|
+
const binding = this.sessions.binding(id);
|
|
804
|
+
if (binding === void 0) this.draftPreparationFailed();
|
|
805
|
+
this.prepareDraft(binding, draftOptions);
|
|
806
|
+
} : void 0, agentPreset === void 0 ? void 0 : { agentPreset }).catch((reason) => {
|
|
770
807
|
console.warn("new session failed:", reason);
|
|
771
808
|
});
|
|
772
809
|
}
|
|
810
|
+
prepareDraft(binding, options) {
|
|
811
|
+
const conversation = this.ctx.get("conversation");
|
|
812
|
+
if (conversation === void 0) this.draftPreparationFailed();
|
|
813
|
+
if (conversation.input.requestDraftInitialization(binding, options) === "blocked") this.draftPreparationFailed();
|
|
814
|
+
}
|
|
815
|
+
draftPreparationFailed() {
|
|
816
|
+
const message = this.ctx.locale.bind("workspace")("draft.initializationFailed");
|
|
817
|
+
this.notify({
|
|
818
|
+
kind: "createFailed",
|
|
819
|
+
message
|
|
820
|
+
});
|
|
821
|
+
throw new Error(message);
|
|
822
|
+
}
|
|
773
823
|
async archiveSession(sessionId) {
|
|
774
824
|
await this.workspaces.archiveSession(sessionId);
|
|
775
825
|
if (this.mainReference?.sessionId === sessionId) this.clearMain();
|
|
@@ -927,7 +977,7 @@ window.__ModuleLoader__.load({
|
|
|
927
977
|
}
|
|
928
978
|
//#endregion
|
|
929
979
|
//#region \0qilin-css:/home/runner/work/QiLin/QiLin/packages/client/ui-workspace/src/client/rows/Rows.module.css.mjs
|
|
930
|
-
const css$2 = ".RrwzeW_projectRow,.RrwzeW_sessionRow{padding:0 8px;cursor:pointer;user-select:none;color:var(--qilin-alias-label-primary);border-radius:8px;align-items:center;gap:6px;padding-inline-start:calc(8px + var(--dsh-workspace-indent,0px));display:flex}.RrwzeW_projectRow:hover,.RrwzeW_sessionRow:hover,.RrwzeW_sessionRow.RrwzeW_selected{background:var(--qilin-alias-interactive-bg-hover)}.RrwzeW_searchResultRow{box-sizing:border-box;cursor:pointer;text-align:left;width:100%;min-height:48px;color:var(--qilin-alias-label-primary);background:0 0;border:none;border-radius:8px;flex-direction:column;align-items:stretch;padding:4px 8px;display:flex}.RrwzeW_searchResultRow:hover,.RrwzeW_searchResultRow.RrwzeW_selected{background:var(--qilin-alias-interactive-bg-hover)}.RrwzeW_searchResultHeading{align-items:center;min-width:0;display:flex}.RrwzeW_searchResultTitle{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;margin-left:4px;font-size:14px;line-height:20px;overflow:hidden}.RrwzeW_searchResultMeta{align-items:center;gap:6px;min-width:0;margin-left:20px;display:flex}.RrwzeW_searchResultWorkspace,.RrwzeW_searchResultSnippet{text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:17px;overflow:hidden}.RrwzeW_searchResultWorkspace{max-width:40%;color:var(--qilin-alias-label-tertiary);flex:none}.RrwzeW_searchResultSnippet{min-width:0;color:var(--qilin-alias-label-secondary);flex:1}.RrwzeW_projectRow{box-sizing:border-box;align-items:center;height:34px}.RrwzeW_projectRow .RrwzeW_rowActions{height:20px}.RrwzeW_sessionRow{height:32px;animation:RrwzeW_row-in .15s var(--ds-ease-in-out);gap:0}.RrwzeW_sessionRow .RrwzeW_title{margin:0 6px 0 4px}@keyframes RrwzeW_row-in{0%{opacity:0}}.RrwzeW_slot{width:16px;height:20px;color:var(--qilin-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}.RrwzeW_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.RrwzeW_folderActive{color:var(--qilin-alias-state-business-primary)}.RrwzeW_projectRow .RrwzeW_chevron{display:none}.RrwzeW_projectRow:hover .RrwzeW_chevron{display:inline-flex}.RrwzeW_projectRow:hover .RrwzeW_folder{display:none}.RrwzeW_arrow{transition:transform .15s var(--ds-ease-in-out)}.RrwzeW_arrowOpen{transform:rotate(90deg)}.RrwzeW_projectText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.RrwzeW_title{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;line-height:20px;overflow:hidden}.RrwzeW_renameInput{border:.5px solid var(--qilin-alias-border-l4);background:var(--qilin-alias-button-elevated-fill);min-width:0;color:inherit;border-radius:4px;outline:none;padding:0 2px;font-size:14px;line-height:20px}.RrwzeW_sessionRow .RrwzeW_title{
|
|
980
|
+
const css$2 = ".RrwzeW_projectRow,.RrwzeW_sessionRow{padding:0 8px;cursor:pointer;user-select:none;color:var(--qilin-alias-label-primary);border-radius:8px;align-items:center;gap:6px;padding-inline-start:calc(8px + var(--dsh-workspace-indent,0px));display:flex}.RrwzeW_projectRow:hover,.RrwzeW_sessionRow:hover,.RrwzeW_sessionRow.RrwzeW_selected{background:var(--qilin-alias-interactive-bg-hover)}.RrwzeW_searchResultRow{box-sizing:border-box;cursor:pointer;text-align:left;width:100%;min-height:48px;color:var(--qilin-alias-label-primary);background:0 0;border:none;border-radius:8px;flex-direction:column;align-items:stretch;padding:4px 8px;display:flex}.RrwzeW_searchResultRow:hover,.RrwzeW_searchResultRow.RrwzeW_selected{background:var(--qilin-alias-interactive-bg-hover)}.RrwzeW_searchResultHeading{align-items:center;min-width:0;display:flex}.RrwzeW_searchResultTitle{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;margin-left:4px;font-size:14px;line-height:20px;overflow:hidden}.RrwzeW_searchResultMeta{align-items:center;gap:6px;min-width:0;margin-left:20px;display:flex}.RrwzeW_searchResultWorkspace,.RrwzeW_searchResultSnippet{text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:17px;overflow:hidden}.RrwzeW_searchResultWorkspace{max-width:40%;color:var(--qilin-alias-label-tertiary);flex:none}.RrwzeW_searchResultSnippet{min-width:0;color:var(--qilin-alias-label-secondary);flex:1}.RrwzeW_projectRow{box-sizing:border-box;align-items:center;height:34px}.RrwzeW_projectRow .RrwzeW_rowActions{height:20px}.RrwzeW_sessionRow{height:32px;animation:RrwzeW_row-in .15s var(--ds-ease-in-out);gap:0}.RrwzeW_sessionRow .RrwzeW_title{margin:0 6px 0 4px}@keyframes RrwzeW_row-in{0%{opacity:0}}.RrwzeW_slot{width:16px;height:20px;color:var(--qilin-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}.RrwzeW_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.RrwzeW_folderActive{color:var(--qilin-alias-state-business-primary)}.RrwzeW_projectRow .RrwzeW_chevron{display:none}.RrwzeW_projectRow:hover .RrwzeW_chevron{display:inline-flex}.RrwzeW_projectRow:hover .RrwzeW_folder{display:none}.RrwzeW_arrow{transition:transform .15s var(--ds-ease-in-out)}.RrwzeW_arrowOpen{transform:rotate(90deg)}.RrwzeW_projectText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.RrwzeW_title{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;line-height:20px;overflow:hidden}.RrwzeW_renameInput{border:.5px solid var(--qilin-alias-border-l4);background:var(--qilin-alias-button-elevated-fill);min-width:0;color:inherit;border-radius:4px;outline:none;padding:0 2px;font-size:14px;line-height:20px}.RrwzeW_sessionRow .RrwzeW_title{flex:1}.RrwzeW_sessionRow .RrwzeW_title[data-scrolled]{mask-image:linear-gradient(90deg,#0000,#000 12px)}.RrwzeW_sessionRow .RrwzeW_title[data-clipped]{mask-image:linear-gradient(270deg,#0000,#000 12px)}.RrwzeW_sessionRow .RrwzeW_title[data-scrolled][data-clipped]{mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000)}@media (hover:hover){.RrwzeW_sessionRow:hover .RrwzeW_title,.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_title{text-overflow:clip}}.RrwzeW_meta{text-overflow:ellipsis;white-space:nowrap;color:var(--qilin-alias-label-tertiary);font-size:12px;line-height:20px;overflow:hidden}.RrwzeW_time{color:var(--qilin-alias-label-tertiary);flex:none;font-size:12px;line-height:20px}.RrwzeW_dot{flex:none}.RrwzeW_pinIndicator{width:16px;height:20px;color:var(--qilin-alias-label-caption);flex:none;justify-content:center;align-items:center;margin-left:6px;display:inline-flex}.RrwzeW_sessionRow:hover .RrwzeW_pinIndicator,.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_pinIndicator{display:none}.RrwzeW_rowActions{flex:none;align-items:center;gap:15px;display:none}.RrwzeW_projectRow:hover .RrwzeW_rowActions,.RrwzeW_sessionRow:hover .RrwzeW_rowActions,.RrwzeW_projectRow.RrwzeW_menuOpen .RrwzeW_rowActions,.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_rowActions{display:inline-flex}.RrwzeW_sessionRow:hover .RrwzeW_time,.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_time{display:none}.RrwzeW_projectRow.RrwzeW_menuOpen,.RrwzeW_sessionRow.RrwzeW_menuOpen{background:var(--qilin-alias-interactive-bg-hover)}.RrwzeW_sessionRow.RrwzeW_dropBefore,.RrwzeW_sessionRow.RrwzeW_dropAfter{position:relative}.RrwzeW_sessionRow.RrwzeW_dropBefore:before,.RrwzeW_sessionRow.RrwzeW_dropAfter:after{content:\"\";z-index:1;background:linear-gradient(55deg, transparent calc(50% - 1px), var(--qilin-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--qilin-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--qilin-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute;left:0;right:4px}.RrwzeW_sessionRow.RrwzeW_dropBefore:before{top:-7px}.RrwzeW_sessionRow.RrwzeW_dropAfter:after{bottom:-7px}.RrwzeW_hoverContent{flex-direction:column;gap:8px;display:flex}.RrwzeW_hoverTitle{color:#fff;overflow-wrap:break-word;font-size:14px;line-height:20px}.RrwzeW_hoverPath{color:#cfd3d6;word-break:break-all;font-size:12px;line-height:16px}.RrwzeW_hoverTime{color:#cfd3d6;font-size:12px;line-height:16px}.RrwzeW_hoverStatus{color:#adb2b8;align-items:center;gap:8px;font-size:12px;line-height:20px;display:flex}.RrwzeW_iconButton{cursor:pointer;width:16px;height:16px;color:var(--qilin-alias-label-tertiary);background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.RrwzeW_iconButton:hover{color:var(--qilin-alias-label-primary)}.RrwzeW_chevron{color:var(--qilin-alias-label-caption)}@media (prefers-reduced-motion:reduce){.RrwzeW_sessionRow,.RrwzeW_arrow{transition:none;animation:none}}";
|
|
931
981
|
const tagId$2 = "@qilin-agent/client-ui-workspace/Rows.module.css";
|
|
932
982
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
933
983
|
const tag = document.createElement("style");
|
|
@@ -978,7 +1028,8 @@ window.__ModuleLoader__.load({
|
|
|
978
1028
|
* Workspace browser tree row components (figma Cell set 14:3080): pure presentational —
|
|
979
1029
|
* all data and callbacks arrive via props. Hover swaps (folder->chevron,
|
|
980
1030
|
* time->ellipsis, action buttons) are CSS-only, and a session row's clipped
|
|
981
|
-
* title
|
|
1031
|
+
* title marquees programmatically while the row is hovered, fading both
|
|
1032
|
+
* clipping edges from the depth of its travel. Row ... menus are
|
|
982
1033
|
* visual-only except workspace Rename/Delete and session Rename/Fork/Archive; the
|
|
983
1034
|
* session and workspace hover cards are suppressed while a menu is open.
|
|
984
1035
|
*/
|
|
@@ -986,29 +1037,91 @@ window.__ModuleLoader__.load({
|
|
|
986
1037
|
function displayTitle(node, t) {
|
|
987
1038
|
return node.blank ? t("session.new") : node.title || t("session.untitled");
|
|
988
1039
|
}
|
|
1040
|
+
const MIN_TITLE_REVEAL_PX = 8;
|
|
1041
|
+
const TITLE_MARQUEE_PX_PER_MS = .03;
|
|
1042
|
+
/** Whether the platform asked for reduced motion; jsdom implements no matchMedia. */
|
|
1043
|
+
function prefersReducedMotion() {
|
|
1044
|
+
return typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1045
|
+
}
|
|
989
1046
|
/**
|
|
990
|
-
*
|
|
991
|
-
*
|
|
992
|
-
*
|
|
993
|
-
* to the start in one step, because the resting ellipsis and the narrowed cell
|
|
994
|
-
* would otherwise meet the text while it travelled back. A title that fits has
|
|
995
|
-
* no scroll range to move, and the stylesheet decides whether either move
|
|
996
|
-
* glides or jumps.
|
|
1047
|
+
* Place the title's scroll position and publish the stylesheet's fade-mask
|
|
1048
|
+
* hooks: `data-scrolled` while the title has left its start (left fade) and
|
|
1049
|
+
* `data-clipped` while text remains beyond the right edge (right fade).
|
|
997
1050
|
* @param title - the row's clipping title element.
|
|
998
|
-
* @param
|
|
1051
|
+
* @param left - scroll offset in CSS pixels.
|
|
1052
|
+
* @param range - the title's maximum scroll offset in CSS pixels.
|
|
999
1053
|
*/
|
|
1000
|
-
function
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1054
|
+
function placeTitle(title, left, range) {
|
|
1055
|
+
if (typeof title.scrollTo === "function") title.scrollTo({
|
|
1056
|
+
left,
|
|
1057
|
+
behavior: "instant"
|
|
1058
|
+
});
|
|
1059
|
+
else title.scrollLeft = left;
|
|
1060
|
+
if (left > 0) title.dataset.scrolled = "";
|
|
1061
|
+
else delete title.dataset.scrolled;
|
|
1062
|
+
if (left < range) title.dataset.clipped = "";
|
|
1063
|
+
else delete title.dataset.clipped;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Return the title to its resting state: scrolled to the start with both fade
|
|
1067
|
+
* masks off, so the resting ellipsis renders at full strength.
|
|
1068
|
+
* @param title - the row's clipping title element.
|
|
1069
|
+
*/
|
|
1070
|
+
function restTitle(title) {
|
|
1007
1071
|
if (typeof title.scrollTo === "function") title.scrollTo({
|
|
1008
1072
|
left: 0,
|
|
1009
1073
|
behavior: "instant"
|
|
1010
1074
|
});
|
|
1011
1075
|
else title.scrollLeft = 0;
|
|
1076
|
+
delete title.dataset.scrolled;
|
|
1077
|
+
delete title.dataset.clipped;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Marquee a title wider than its one-line cell while its row is hovered: the
|
|
1081
|
+
* title clips its own text, so entering crawls it at a constant speed until the
|
|
1082
|
+
* far edge (a fork's incremented title, for example) is in view, then rests
|
|
1083
|
+
* there under the pointer. Overflow of at most {@link MIN_TITLE_REVEAL_PX}
|
|
1084
|
+
* stays put — a barely-clipped title moving a few pixels reads as jitter, not a
|
|
1085
|
+
* reveal. Leaving returns the title to the start in one step, because the
|
|
1086
|
+
* resting ellipsis and the narrowed cell would otherwise meet the text while it
|
|
1087
|
+
* travelled back. Reduced motion jumps to the far edge instead of crawling.
|
|
1088
|
+
* @param title - ref to the row's clipping title element.
|
|
1089
|
+
* @returns stable pointer enter/leave handlers for the row.
|
|
1090
|
+
*/
|
|
1091
|
+
function useTitleMarquee(title) {
|
|
1092
|
+
const frame = (0, react.useRef)(0);
|
|
1093
|
+
(0, react.useEffect)(() => () => {
|
|
1094
|
+
cancelAnimationFrame(frame.current);
|
|
1095
|
+
}, []);
|
|
1096
|
+
return (0, react.useMemo)(() => ({
|
|
1097
|
+
enter: () => {
|
|
1098
|
+
/* v8 ignore next -- defensive: the title span renders unconditionally. */
|
|
1099
|
+
if (title.current === null) return;
|
|
1100
|
+
const element = title.current;
|
|
1101
|
+
const range = element.scrollWidth - element.clientWidth;
|
|
1102
|
+
if (range <= MIN_TITLE_REVEAL_PX) return;
|
|
1103
|
+
if (prefersReducedMotion()) {
|
|
1104
|
+
placeTitle(element, range, range);
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
cancelAnimationFrame(frame.current);
|
|
1108
|
+
let previous;
|
|
1109
|
+
let position = 0;
|
|
1110
|
+
const step = (now) => {
|
|
1111
|
+
position += previous === void 0 ? 0 : (now - previous) * TITLE_MARQUEE_PX_PER_MS;
|
|
1112
|
+
previous = now;
|
|
1113
|
+
placeTitle(element, Math.min(position, range), range);
|
|
1114
|
+
if (position < range) frame.current = requestAnimationFrame(step);
|
|
1115
|
+
};
|
|
1116
|
+
frame.current = requestAnimationFrame(step);
|
|
1117
|
+
},
|
|
1118
|
+
leave: () => {
|
|
1119
|
+
cancelAnimationFrame(frame.current);
|
|
1120
|
+
/* v8 ignore next -- defensive: the title span renders unconditionally. */
|
|
1121
|
+
if (title.current === null) return;
|
|
1122
|
+
restTitle(title.current);
|
|
1123
|
+
}
|
|
1124
|
+
}), [title]);
|
|
1012
1125
|
}
|
|
1013
1126
|
/** Localized compact relative time ("刚刚"/"5分钟" in zh, "now"/"5min" in en). */
|
|
1014
1127
|
function timeLabel(updatedAt, now, t) {
|
|
@@ -1081,11 +1194,11 @@ window.__ModuleLoader__.load({
|
|
|
1081
1194
|
const workspaceMenuItems = [{
|
|
1082
1195
|
id: "rename",
|
|
1083
1196
|
label: t("rename"),
|
|
1084
|
-
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1197
|
+
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEditOutlineRegular, {})
|
|
1085
1198
|
}, {
|
|
1086
1199
|
id: "delete",
|
|
1087
1200
|
label: t("delete.workspace"),
|
|
1088
|
-
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1201
|
+
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconTrashOutlineRegular, {}),
|
|
1089
1202
|
danger: true
|
|
1090
1203
|
}];
|
|
1091
1204
|
const ownRow = (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1103,11 +1216,11 @@ window.__ModuleLoader__.load({
|
|
|
1103
1216
|
children: [
|
|
1104
1217
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1105
1218
|
className: clsx(Rows_module_css_default.slot, Rows_module_css_default.folder, active && Rows_module_css_default.folderActive),
|
|
1106
|
-
children: row.expanded ? (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1219
|
+
children: row.expanded ? (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconFolderOpenRegular, {}) : (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconFolderCloseRegular, {})
|
|
1107
1220
|
}),
|
|
1108
1221
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1109
1222
|
className: clsx(Rows_module_css_default.slot, Rows_module_css_default.chevron),
|
|
1110
|
-
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1223
|
+
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconTriangleRightFillRegular, { className: clsx(Rows_module_css_default.arrow, row.expanded && Rows_module_css_default.arrowOpen) })
|
|
1111
1224
|
}),
|
|
1112
1225
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1113
1226
|
className: Rows_module_css_default.projectText,
|
|
@@ -1141,7 +1254,7 @@ window.__ModuleLoader__.load({
|
|
|
1141
1254
|
e.stopPropagation();
|
|
1142
1255
|
setMenuOpen((v) => !v);
|
|
1143
1256
|
},
|
|
1144
|
-
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1257
|
+
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEllipsisOutlineRegular, {})
|
|
1145
1258
|
})
|
|
1146
1259
|
}), (0, react_jsx_runtime.jsx)("button", {
|
|
1147
1260
|
type: "button",
|
|
@@ -1151,7 +1264,7 @@ window.__ModuleLoader__.load({
|
|
|
1151
1264
|
e.stopPropagation();
|
|
1152
1265
|
onCreate();
|
|
1153
1266
|
},
|
|
1154
|
-
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1267
|
+
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPlusOutlineRegular, {})
|
|
1155
1268
|
})]
|
|
1156
1269
|
})
|
|
1157
1270
|
]
|
|
@@ -1314,8 +1427,9 @@ window.__ModuleLoader__.load({
|
|
|
1314
1427
|
});
|
|
1315
1428
|
}
|
|
1316
1429
|
/**
|
|
1317
|
-
* One top-level
|
|
1318
|
-
* own or descendant activity), title
|
|
1430
|
+
* One top-level 32px session row: status dot (pending user interaction outranks
|
|
1431
|
+
* own or descendant activity), a title that marquees while the row is hovered,
|
|
1432
|
+
* relative time, and the hover action strip (menu, archive, pin).
|
|
1319
1433
|
* @param props.node - derived session node.
|
|
1320
1434
|
* @param props.currentId - selected session id (row highlight).
|
|
1321
1435
|
* @param props.now - epoch ms for relative-time formatting.
|
|
@@ -1323,6 +1437,7 @@ window.__ModuleLoader__.load({
|
|
|
1323
1437
|
* @param props.onRename - open the session rename dialog (id + current title).
|
|
1324
1438
|
* @param props.onFork - fork a session at its last completed turn.
|
|
1325
1439
|
* @param props.onArchive - archive a session by id.
|
|
1440
|
+
* @param props.onCopyId - copy a session's complete id without opening it.
|
|
1326
1441
|
* @param props.onPin - pin a session by id (leading its group and the flat list).
|
|
1327
1442
|
* @param props.onUnpin - drop a session's pin.
|
|
1328
1443
|
* @param props.onReveal - scroll this row into view after search navigation, then acknowledge it.
|
|
@@ -1330,7 +1445,7 @@ window.__ModuleLoader__.load({
|
|
|
1330
1445
|
* @param props.t - the browser root's locale seat.
|
|
1331
1446
|
* @returns the session row.
|
|
1332
1447
|
*/
|
|
1333
|
-
function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onPin, onUnpin, onReveal, drag, renderSlot, t }) {
|
|
1448
|
+
function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onCopyId, onPin, onUnpin, onReveal, drag, renderSlot, t }) {
|
|
1334
1449
|
const row = node;
|
|
1335
1450
|
const title = displayTitle(node, t);
|
|
1336
1451
|
const selected = node.id === currentId;
|
|
@@ -1340,6 +1455,7 @@ window.__ModuleLoader__.load({
|
|
|
1340
1455
|
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
1341
1456
|
const rowRef = (0, react.useRef)(null);
|
|
1342
1457
|
const titleRef = (0, react.useRef)(null);
|
|
1458
|
+
const marquee = useTitleMarquee(titleRef);
|
|
1343
1459
|
(0, react.useEffect)(() => {
|
|
1344
1460
|
if (onReveal === void 0) return;
|
|
1345
1461
|
rowRef.current?.scrollIntoView({ block: "nearest" });
|
|
@@ -1354,17 +1470,22 @@ window.__ModuleLoader__.load({
|
|
|
1354
1470
|
{
|
|
1355
1471
|
id: "rename",
|
|
1356
1472
|
label: t("rename"),
|
|
1357
|
-
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1473
|
+
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEditOutlineRegular, {})
|
|
1358
1474
|
},
|
|
1359
1475
|
{
|
|
1360
1476
|
id: "fork",
|
|
1361
1477
|
label: t("menu.fork"),
|
|
1362
|
-
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1478
|
+
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconBranchOutlineRegular, {})
|
|
1363
1479
|
},
|
|
1364
1480
|
{
|
|
1365
1481
|
id: "archive",
|
|
1366
1482
|
label: t("menu.archiveSession"),
|
|
1367
|
-
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1483
|
+
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconArchiveOutlineRegular, { size: 16 })
|
|
1484
|
+
},
|
|
1485
|
+
{
|
|
1486
|
+
id: "copyId",
|
|
1487
|
+
label: t("menu.sessionId"),
|
|
1488
|
+
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconCopyOutlineRegular, {})
|
|
1368
1489
|
}
|
|
1369
1490
|
];
|
|
1370
1491
|
return (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.HoverCard, {
|
|
@@ -1376,12 +1497,8 @@ window.__ModuleLoader__.load({
|
|
|
1376
1497
|
onClick: () => {
|
|
1377
1498
|
onOpen(node.id);
|
|
1378
1499
|
},
|
|
1379
|
-
onPointerEnter:
|
|
1380
|
-
|
|
1381
|
-
},
|
|
1382
|
-
onPointerLeave: () => {
|
|
1383
|
-
revealClippedTitle(titleRef.current, false);
|
|
1384
|
-
},
|
|
1500
|
+
onPointerEnter: marquee.enter,
|
|
1501
|
+
onPointerLeave: marquee.leave,
|
|
1385
1502
|
draggable,
|
|
1386
1503
|
onDragStart: drag === void 0 || row.blank ? void 0 : (e) => {
|
|
1387
1504
|
e.dataTransfer.effectAllowed = "move";
|
|
@@ -1417,41 +1534,67 @@ window.__ModuleLoader__.load({
|
|
|
1417
1534
|
row.pinned && !row.blank && (0, react_jsx_runtime.jsx)(PinnedIndicator, { t }),
|
|
1418
1535
|
!row.blank && (0, react_jsx_runtime.jsxs)("span", {
|
|
1419
1536
|
className: Rows_module_css_default.rowActions,
|
|
1420
|
-
children: [
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
items: sessionMenuItems,
|
|
1435
|
-
onSelect: (id) => {
|
|
1436
|
-
setMenuOpen(false);
|
|
1437
|
-
if (id === "pin") (row.pinned ? onUnpin : onPin)(node.id);
|
|
1438
|
-
if (id === "rename") onRename(node.id, row.title);
|
|
1439
|
-
if (id === "fork") onFork(node.id);
|
|
1440
|
-
if (id === "archive") onArchive(node.id);
|
|
1441
|
-
},
|
|
1442
|
-
portal: true,
|
|
1443
|
-
closeOnPointerLeave: true,
|
|
1444
|
-
anchor: (0, react_jsx_runtime.jsx)("button", {
|
|
1445
|
-
type: "button",
|
|
1446
|
-
className: Rows_module_css_default.iconButton,
|
|
1447
|
-
"aria-label": t("actions.session.aria", { name: title }),
|
|
1448
|
-
onClick: (e) => {
|
|
1449
|
-
e.stopPropagation();
|
|
1450
|
-
setMenuOpen((v) => !v);
|
|
1537
|
+
children: [
|
|
1538
|
+
(0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.Menu, {
|
|
1539
|
+
open: menuOpen,
|
|
1540
|
+
onClose: () => {
|
|
1541
|
+
setMenuOpen(false);
|
|
1542
|
+
},
|
|
1543
|
+
items: sessionMenuItems,
|
|
1544
|
+
onSelect: (id) => {
|
|
1545
|
+
setMenuOpen(false);
|
|
1546
|
+
if (id === "pin") (row.pinned ? onUnpin : onPin)(node.id);
|
|
1547
|
+
if (id === "rename") onRename(node.id, row.title);
|
|
1548
|
+
if (id === "fork") onFork(node.id);
|
|
1549
|
+
if (id === "archive") onArchive(node.id);
|
|
1550
|
+
if (id === "copyId") onCopyId(node.id);
|
|
1451
1551
|
},
|
|
1452
|
-
|
|
1552
|
+
portal: true,
|
|
1553
|
+
closeOnPointerLeave: true,
|
|
1554
|
+
anchor: (0, react_jsx_runtime.jsx)("button", {
|
|
1555
|
+
type: "button",
|
|
1556
|
+
className: Rows_module_css_default.iconButton,
|
|
1557
|
+
"aria-label": t("actions.session.aria", { name: title }),
|
|
1558
|
+
onClick: (e) => {
|
|
1559
|
+
e.stopPropagation();
|
|
1560
|
+
setMenuOpen((v) => !v);
|
|
1561
|
+
},
|
|
1562
|
+
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEllipsisOutlineRegular, {})
|
|
1563
|
+
})
|
|
1564
|
+
}),
|
|
1565
|
+
(0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.Tooltip, {
|
|
1566
|
+
label: t("actions.archive"),
|
|
1567
|
+
side: "bottom",
|
|
1568
|
+
delayMs: 500,
|
|
1569
|
+
portal: true,
|
|
1570
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
1571
|
+
type: "button",
|
|
1572
|
+
className: Rows_module_css_default.iconButton,
|
|
1573
|
+
"aria-label": t("menu.archiveSession"),
|
|
1574
|
+
onClick: (e) => {
|
|
1575
|
+
e.stopPropagation();
|
|
1576
|
+
onArchive(node.id);
|
|
1577
|
+
},
|
|
1578
|
+
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconArchiveOutlineRegular, { size: 16 })
|
|
1579
|
+
})
|
|
1580
|
+
}),
|
|
1581
|
+
(0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.Tooltip, {
|
|
1582
|
+
label: t(row.pinned ? "actions.unpin" : "actions.pin"),
|
|
1583
|
+
side: "bottom",
|
|
1584
|
+
delayMs: 500,
|
|
1585
|
+
portal: true,
|
|
1586
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
1587
|
+
type: "button",
|
|
1588
|
+
className: Rows_module_css_default.iconButton,
|
|
1589
|
+
"aria-label": t(row.pinned ? "actions.unpin" : "actions.pin"),
|
|
1590
|
+
onClick: (e) => {
|
|
1591
|
+
e.stopPropagation();
|
|
1592
|
+
(row.pinned ? onUnpin : onPin)(node.id);
|
|
1593
|
+
},
|
|
1594
|
+
children: row.pinned ? (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPinFillRegular, { size: 14 }) : (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPinOutlineRegular, { size: 14 })
|
|
1595
|
+
})
|
|
1453
1596
|
})
|
|
1454
|
-
|
|
1597
|
+
]
|
|
1455
1598
|
})
|
|
1456
1599
|
]
|
|
1457
1600
|
}),
|
|
@@ -1507,14 +1650,14 @@ window.__ModuleLoader__.load({
|
|
|
1507
1650
|
const addEntries = flowAvailable ? [{
|
|
1508
1651
|
id: ADD_WORKSPACE,
|
|
1509
1652
|
label: t("menu.addWorkspace"),
|
|
1510
|
-
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1653
|
+
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPlusOutlineRegular, { size: 16 }),
|
|
1511
1654
|
disabled: flowBusy
|
|
1512
1655
|
}] : [];
|
|
1513
1656
|
const pinAdd = !addOnly && workspaces.length > 0;
|
|
1514
1657
|
const items = pinAdd ? workspaces.map((workspace) => ({
|
|
1515
1658
|
id: workspace.workspaceId,
|
|
1516
1659
|
label: workspace.title,
|
|
1517
|
-
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1660
|
+
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconFolderCloseRegular, { size: 16 }),
|
|
1518
1661
|
disabled: flowBusy
|
|
1519
1662
|
})) : addEntries;
|
|
1520
1663
|
const menuIsEmpty = items.length === 0;
|
|
@@ -1710,22 +1853,46 @@ window.__ModuleLoader__.load({
|
|
|
1710
1853
|
const SEARCH_DEBOUNCE_MS = 250;
|
|
1711
1854
|
/** `session.search` wire bound, measured in JavaScript UTF-16 code units. */
|
|
1712
1855
|
const SEARCH_QUERY_MAX_CODE_UNITS = 500;
|
|
1713
|
-
/** Session rows visible per Workspace
|
|
1714
|
-
const
|
|
1715
|
-
/**
|
|
1716
|
-
|
|
1717
|
-
|
|
1856
|
+
/** Non-blank Session rows visible per Workspace while no Session raises its watermark. */
|
|
1857
|
+
const SESSION_WATERMARK_BASE = 5;
|
|
1858
|
+
/** Highest watermark that attention-bearing Sessions raise automatically; Show more may exceed it. */
|
|
1859
|
+
const SESSION_WATERMARK_AUTO_MAX = 50;
|
|
1860
|
+
/** Show the blank row plus the first `watermark` non-blank rows, in list order. */
|
|
1861
|
+
function watermarkRows(sessions, watermark) {
|
|
1862
|
+
let count = 0;
|
|
1718
1863
|
const rows = sessions.filter((session) => {
|
|
1719
1864
|
if (session.blank) return true;
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
return true;
|
|
1865
|
+
count += 1;
|
|
1866
|
+
return count <= watermark;
|
|
1723
1867
|
});
|
|
1724
1868
|
return {
|
|
1725
1869
|
rows,
|
|
1726
1870
|
hiddenCount: sessions.length - rows.length
|
|
1727
1871
|
};
|
|
1728
1872
|
}
|
|
1873
|
+
/**
|
|
1874
|
+
* Smallest watermark that covers every non-blank row within the automatic maximum that is running, has a running
|
|
1875
|
+
* child, awaits the user, carries an unread completion, or is current, and the reveal target at any position.
|
|
1876
|
+
*/
|
|
1877
|
+
function requiredWatermark(sessions, current, revealSessionId) {
|
|
1878
|
+
let index = 0;
|
|
1879
|
+
let required = 0;
|
|
1880
|
+
for (const session of sessions) {
|
|
1881
|
+
if (session.blank) continue;
|
|
1882
|
+
index += 1;
|
|
1883
|
+
if (index <= SESSION_WATERMARK_AUTO_MAX && (session.running || session.runningSubagentCount > 0 || session.pendingInteraction !== void 0 || session.completed || session.id === current) || session.id === revealSessionId) required = index;
|
|
1884
|
+
}
|
|
1885
|
+
return required;
|
|
1886
|
+
}
|
|
1887
|
+
/** Raise each listed group's watermark to its requirement; returns `marks` itself when nothing rises. */
|
|
1888
|
+
function raiseWatermarks(marks, required) {
|
|
1889
|
+
let raised = marks;
|
|
1890
|
+
for (const [key, watermark] of required) if (watermark > (raised[key] ?? SESSION_WATERMARK_BASE)) raised = {
|
|
1891
|
+
...raised,
|
|
1892
|
+
[key]: watermark
|
|
1893
|
+
};
|
|
1894
|
+
return raised;
|
|
1895
|
+
}
|
|
1729
1896
|
/** Keep controlled input and RPC payload inside the session.search wire contract. */
|
|
1730
1897
|
function sanitizeSearchQuery(value) {
|
|
1731
1898
|
const withoutNul = value.replaceAll("\0", "");
|
|
@@ -1736,10 +1903,6 @@ window.__ModuleLoader__.load({
|
|
|
1736
1903
|
if (last >= 55296 && last <= 56319 && next >= 56320 && next <= 57343) end--;
|
|
1737
1904
|
return withoutNul.slice(0, end);
|
|
1738
1905
|
}
|
|
1739
|
-
/** Immutable membership toggle for the local expand-all array. */
|
|
1740
|
-
function toggled(list, key) {
|
|
1741
|
-
return list.includes(key) ? list.filter((k) => k !== key) : [...list, key];
|
|
1742
|
-
}
|
|
1743
1906
|
/**
|
|
1744
1907
|
* Accept the native drag at document level while a row drag is active: row
|
|
1745
1908
|
* hover still owns the insertion marker, and releasing outside the list must
|
|
@@ -1827,7 +1990,7 @@ window.__ModuleLoader__.load({
|
|
|
1827
1990
|
onClick: () => {
|
|
1828
1991
|
setOpen((v) => !v);
|
|
1829
1992
|
},
|
|
1830
|
-
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
1993
|
+
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPersonalizationOutlineRegular, {})
|
|
1831
1994
|
})
|
|
1832
1995
|
})
|
|
1833
1996
|
});
|
|
@@ -1838,12 +2001,12 @@ window.__ModuleLoader__.load({
|
|
|
1838
2001
|
return e.clientY < rect.top + rect.height / 2 ? "before" : "after";
|
|
1839
2002
|
}
|
|
1840
2003
|
/** The scrolling session tree; unmounting drops the sessions subscription and expand-all state. */
|
|
1841
|
-
function SessionTree({ list, useSessionStatus, startSession, open, forkSession, workspaces, ungroupedSessionIds, archivedSessionIds, pinnedSessionIds, onSessionPin, onSessionUnpin, workspaceReady, usePanelInfo, onRenameRequest, onDeleteRequest, onSessionRename, onSessionArchive, insertWorkspaceBefore, nestWorkspaces, groupExpansion, setGroupExpanded, setSessionOrder, home, t, renderSlot, revealSessionId, onSessionRevealed }) {
|
|
2004
|
+
function SessionTree({ list, useSessionStatus, startSession, open, forkSession, copySessionId, workspaces, ungroupedSessionIds, archivedSessionIds, pinnedSessionIds, onSessionPin, onSessionUnpin, workspaceReady, usePanelInfo, onRenameRequest, onDeleteRequest, onSessionRename, onSessionArchive, insertWorkspaceBefore, nestWorkspaces, groupExpansion, setGroupExpanded, setSessionOrder, home, t, renderSlot, revealSessionId, onSessionRevealed }) {
|
|
1842
2005
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
1843
2006
|
const statuses = useSessionStatus((s) => s);
|
|
1844
2007
|
const current = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
1845
2008
|
const revealGroup = revealSessionId === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, revealSessionId);
|
|
1846
|
-
const [
|
|
2009
|
+
const [storedWatermarks, setStoredWatermarks] = (0, react.useState)({});
|
|
1847
2010
|
const [drag, setDrag] = (0, react.useState)(null);
|
|
1848
2011
|
const sessionDropCommitted = (0, react.useRef)(false);
|
|
1849
2012
|
const [workspaceDrag, setWorkspaceDrag] = (0, react.useState)(null);
|
|
@@ -1904,17 +2067,23 @@ window.__ModuleLoader__.load({
|
|
|
1904
2067
|
revealGroup,
|
|
1905
2068
|
setGroupExpanded
|
|
1906
2069
|
]);
|
|
1907
|
-
(
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
2070
|
+
const baselines = new Map(groups.map((group) => [group.key, Math.max(SESSION_WATERMARK_BASE, requiredWatermark(group.sessions, current, group.key === revealGroup ? revealSessionId : void 0))]));
|
|
2071
|
+
const foldedKeys = new Set(groups.filter((group) => !group.expanded).map((group) => group.key));
|
|
2072
|
+
const underFoldedAncestor = (key) => {
|
|
2073
|
+
for (let owner = parents.get(key); owner !== void 0; owner = parents.get(owner)) if (foldedKeys.has(owner)) return true;
|
|
2074
|
+
return false;
|
|
2075
|
+
};
|
|
2076
|
+
const requiredWatermarks = new Map([...baselines].filter(([key]) => !underFoldedAncestor(key)));
|
|
2077
|
+
const sessionWatermarks = raiseWatermarks(storedWatermarks, requiredWatermarks);
|
|
2078
|
+
if (sessionWatermarks !== storedWatermarks) setStoredWatermarks((marks) => raiseWatermarks(marks, requiredWatermarks));
|
|
2079
|
+
const dropWatermarks = (drop) => {
|
|
2080
|
+
if (!Object.keys(storedWatermarks).some(drop)) return;
|
|
2081
|
+
setStoredWatermarks((marks) => Object.fromEntries(Object.entries(marks).filter(([key]) => !drop(key))));
|
|
2082
|
+
};
|
|
2083
|
+
const withinGroup = (key, root) => {
|
|
2084
|
+
for (let owner = key; owner !== void 0; owner = parents.get(owner)) if (owner === root) return true;
|
|
2085
|
+
return false;
|
|
2086
|
+
};
|
|
1918
2087
|
const now = Date.now();
|
|
1919
2088
|
const commitSessionDrag = (activeDrag, over) => {
|
|
1920
2089
|
if (sessionDropCommitted.current) return;
|
|
@@ -1922,8 +2091,10 @@ window.__ModuleLoader__.load({
|
|
|
1922
2091
|
setDrag(null);
|
|
1923
2092
|
const group = groups.find((candidate) => candidate.key === activeDrag.accountKey);
|
|
1924
2093
|
if (group === void 0) return;
|
|
1925
|
-
const
|
|
1926
|
-
const
|
|
2094
|
+
const watermark = sessionWatermarks[group.key] ?? SESSION_WATERMARK_BASE;
|
|
2095
|
+
const visible = watermarkRows(group.sessions, watermark);
|
|
2096
|
+
const sessionsExpanded = visible.hiddenCount === 0;
|
|
2097
|
+
const renderedSessions = visible.rows;
|
|
1927
2098
|
const targetIndex = renderedSessions.findIndex((session) => session.id === over.id);
|
|
1928
2099
|
if (targetIndex === -1) return;
|
|
1929
2100
|
const sourceIndex = renderedSessions.findIndex((session) => session.id === activeDrag.sessionId);
|
|
@@ -1951,10 +2122,10 @@ window.__ModuleLoader__.load({
|
|
|
1951
2122
|
nextOrder.splice(insertAt === -1 ? nextOrder.length : insertAt, 0, activeDrag.sessionId);
|
|
1952
2123
|
if (!sessionsExpanded && sourceIndex !== -1) {
|
|
1953
2124
|
const nodes = new Map(group.sessions.map((node) => [node.id, node]));
|
|
1954
|
-
if (!
|
|
2125
|
+
if (!watermarkRows(nextOrder.flatMap((id) => {
|
|
1955
2126
|
const node = nodes.get(id);
|
|
1956
2127
|
return node === void 0 ? [] : [node];
|
|
1957
|
-
})).rows.some((node) => node.id === activeDrag.sessionId)) return;
|
|
2128
|
+
}), watermark).rows.some((node) => node.id === activeDrag.sessionId)) return;
|
|
1958
2129
|
}
|
|
1959
2130
|
const currentBlank = group.sessions.find((node) => node.blank)?.id;
|
|
1960
2131
|
setSessionOrder(activeDrag.accountKey, pinCurrentBlank(nextOrder, currentBlank));
|
|
@@ -1992,8 +2163,10 @@ window.__ModuleLoader__.load({
|
|
|
1992
2163
|
const workspaceId = group.workspaceId;
|
|
1993
2164
|
const children = childrenByParent.get(group.key) ?? [];
|
|
1994
2165
|
const compatibleDrag = workspaceDrag !== null && parents.get(workspaceDrag.workspaceId) === parents.get(group.key);
|
|
1995
|
-
const
|
|
1996
|
-
const
|
|
2166
|
+
const watermark = sessionWatermarks[group.key] ?? SESSION_WATERMARK_BASE;
|
|
2167
|
+
const collapsed = watermarkRows(group.sessions, baselines.get(group.key) ?? SESSION_WATERMARK_BASE);
|
|
2168
|
+
const visible = watermarkRows(group.sessions, watermark);
|
|
2169
|
+
const sessionsExpanded = visible.hiddenCount === 0;
|
|
1997
2170
|
const workspaceMarker = workspaceId !== void 0 && workspaceDrag?.over?.id === workspaceId ? workspaceDrag.over.half : null;
|
|
1998
2171
|
const workspaceDragProps = workspaceId === void 0 ? void 0 : {
|
|
1999
2172
|
start: () => {
|
|
@@ -2058,7 +2231,7 @@ window.__ModuleLoader__.load({
|
|
|
2058
2231
|
home,
|
|
2059
2232
|
t,
|
|
2060
2233
|
onToggle: () => {
|
|
2061
|
-
if (group.expanded)
|
|
2234
|
+
if (group.expanded) dropWatermarks((key) => withinGroup(key, group.key));
|
|
2062
2235
|
setGroupExpanded(group.key, !group.expanded);
|
|
2063
2236
|
},
|
|
2064
2237
|
onCreate: () => {
|
|
@@ -2083,7 +2256,7 @@ window.__ModuleLoader__.load({
|
|
|
2083
2256
|
role: "group",
|
|
2084
2257
|
children: children.map((child) => renderGroup(child, depth + 1))
|
|
2085
2258
|
}),
|
|
2086
|
-
|
|
2259
|
+
visible.rows.map((node) => {
|
|
2087
2260
|
const sameGroupDrag = drag !== null && drag.accountKey === group.key;
|
|
2088
2261
|
const normalizeHalf = (half) => node.blank ? "after" : half;
|
|
2089
2262
|
const dragProps = {
|
|
@@ -2129,6 +2302,9 @@ window.__ModuleLoader__.load({
|
|
|
2129
2302
|
onRename: onSessionRename,
|
|
2130
2303
|
onFork: forkSession,
|
|
2131
2304
|
onArchive: onSessionArchive,
|
|
2305
|
+
onCopyId: (sessionId) => {
|
|
2306
|
+
copySessionId(sessionId);
|
|
2307
|
+
},
|
|
2132
2308
|
onPin: onSessionPin,
|
|
2133
2309
|
onUnpin: onSessionUnpin,
|
|
2134
2310
|
onReveal: node.id === revealSessionId && group.key === revealGroup ? () => {
|
|
@@ -2144,9 +2320,16 @@ window.__ModuleLoader__.load({
|
|
|
2144
2320
|
className: WorkspaceBrowser_module_css_default.sessionOverflowButton,
|
|
2145
2321
|
"aria-expanded": sessionsExpanded,
|
|
2146
2322
|
onClick: () => {
|
|
2147
|
-
|
|
2323
|
+
if (sessionsExpanded) {
|
|
2324
|
+
dropWatermarks((key) => key === group.key);
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2327
|
+
setStoredWatermarks((marks) => ({
|
|
2328
|
+
...marks,
|
|
2329
|
+
[group.key]: visible.hiddenCount <= SESSION_WATERMARK_BASE ? Infinity : watermark + SESSION_WATERMARK_BASE
|
|
2330
|
+
}));
|
|
2148
2331
|
},
|
|
2149
|
-
children: sessionsExpanded ? t("sessions.collapse") : t("sessions.expand", { n:
|
|
2332
|
+
children: sessionsExpanded ? t("sessions.collapse") : t("sessions.expand", { n: visible.hiddenCount })
|
|
2150
2333
|
})
|
|
2151
2334
|
]
|
|
2152
2335
|
}, group.key);
|
|
@@ -2172,7 +2355,7 @@ window.__ModuleLoader__.load({
|
|
|
2172
2355
|
});
|
|
2173
2356
|
}
|
|
2174
2357
|
/** The flat "In one list" body: every session is one draggable top-level row. */
|
|
2175
|
-
function FlatList({ list, sessionIds, pinnedSessionIds, useSessionStatus, open, forkSession, onSessionRename, onSessionArchive, onSessionPin, onSessionUnpin, usePanelInfo, setSessionOrder, revealSessionId, onSessionRevealed, t, renderSlot }) {
|
|
2358
|
+
function FlatList({ list, sessionIds, pinnedSessionIds, useSessionStatus, open, forkSession, copySessionId, onSessionRename, onSessionArchive, onSessionPin, onSessionUnpin, usePanelInfo, setSessionOrder, revealSessionId, onSessionRevealed, t, renderSlot }) {
|
|
2176
2359
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
2177
2360
|
const statuses = useSessionStatus((s) => s);
|
|
2178
2361
|
const rows = (0, react.useMemo)(() => deriveFlat(list, sessionIds, pinnedSessionIds, statuses), [
|
|
@@ -2223,6 +2406,9 @@ window.__ModuleLoader__.load({
|
|
|
2223
2406
|
onRename: onSessionRename,
|
|
2224
2407
|
onFork: forkSession,
|
|
2225
2408
|
onArchive: onSessionArchive,
|
|
2409
|
+
onCopyId: (sessionId) => {
|
|
2410
|
+
copySessionId(sessionId);
|
|
2411
|
+
},
|
|
2226
2412
|
onPin: onSessionPin,
|
|
2227
2413
|
onUnpin: onSessionUnpin,
|
|
2228
2414
|
onReveal: node.id === revealSessionId ? () => {
|
|
@@ -2333,7 +2519,7 @@ window.__ModuleLoader__.load({
|
|
|
2333
2519
|
* @param props - composed slot props (shell owner share + store + injected actions).
|
|
2334
2520
|
* @returns the region element tree.
|
|
2335
2521
|
*/
|
|
2336
|
-
function WorkspaceBrowser({ wide, usePanelInfo, expandSidebar, useSessions, useSessionStatus, useWorkspaces, useStore, actions, startSession, open, renameSession, forkSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, archiveSession, pinSession, unpinSession, createWorkspace, searchSessions, searchResultLimit, requestSearch, requestAddWorkspace, closeAddWorkspace, closeRenameRequest, setDirectoryBusy, useDirectoryFlow, useHostInfo, useWorkspaceShortcuts, useShortcuts, renderSlot, t }) {
|
|
2522
|
+
function WorkspaceBrowser({ wide, usePanelInfo, expandSidebar, useSessions, useSessionStatus, useWorkspaces, useStore, actions, startSession, open, renameSession, forkSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, archiveSession, copySessionId, pinSession, unpinSession, createWorkspace, searchSessions, searchResultLimit, requestSearch, requestAddWorkspace, closeAddWorkspace, closeRenameRequest, setDirectoryBusy, useDirectoryFlow, useHostInfo, useWorkspaceShortcuts, useShortcuts, renderSlot, t }) {
|
|
2337
2523
|
const home = useHostInfo((info) => info.home);
|
|
2338
2524
|
const list = useSessions((state) => state);
|
|
2339
2525
|
const workspaces = useWorkspaces((state) => state.items);
|
|
@@ -2735,7 +2921,7 @@ window.__ModuleLoader__.load({
|
|
|
2735
2921
|
setSearchExpanded(true);
|
|
2736
2922
|
requestSearch();
|
|
2737
2923
|
},
|
|
2738
|
-
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
2924
|
+
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconSearchOutlineRegular, { size: searchExpanded ? 11 : 14 })
|
|
2739
2925
|
})
|
|
2740
2926
|
}),
|
|
2741
2927
|
(0, react_jsx_runtime.jsx)("input", {
|
|
@@ -2764,7 +2950,7 @@ window.__ModuleLoader__.load({
|
|
|
2764
2950
|
setQuery("");
|
|
2765
2951
|
setSearchExpanded(false);
|
|
2766
2952
|
},
|
|
2767
|
-
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
2953
|
+
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconCloseFillRegular, {})
|
|
2768
2954
|
})
|
|
2769
2955
|
]
|
|
2770
2956
|
})
|
|
@@ -2795,7 +2981,7 @@ window.__ModuleLoader__.load({
|
|
|
2795
2981
|
onClick: () => {
|
|
2796
2982
|
requestAddWorkspace();
|
|
2797
2983
|
},
|
|
2798
|
-
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.
|
|
2984
|
+
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconProjectAddOutlineRegular, { size: 16 })
|
|
2799
2985
|
})
|
|
2800
2986
|
})]
|
|
2801
2987
|
}),
|
|
@@ -2839,6 +3025,7 @@ window.__ModuleLoader__.load({
|
|
|
2839
3025
|
useSessionStatus,
|
|
2840
3026
|
open,
|
|
2841
3027
|
forkSession,
|
|
3028
|
+
copySessionId,
|
|
2842
3029
|
onSessionRename,
|
|
2843
3030
|
onSessionArchive,
|
|
2844
3031
|
onSessionPin,
|
|
@@ -2853,6 +3040,7 @@ window.__ModuleLoader__.load({
|
|
|
2853
3040
|
usePanelInfo,
|
|
2854
3041
|
list,
|
|
2855
3042
|
useSessionStatus,
|
|
3043
|
+
copySessionId,
|
|
2856
3044
|
renderSlot,
|
|
2857
3045
|
onSessionRename,
|
|
2858
3046
|
onSessionArchive,
|
|
@@ -3026,6 +3214,91 @@ window.__ModuleLoader__.load({
|
|
|
3026
3214
|
});
|
|
3027
3215
|
}
|
|
3028
3216
|
//#endregion
|
|
3217
|
+
//#region lib/types/client/toast/RowActionToast.js
|
|
3218
|
+
/**
|
|
3219
|
+
* Dispatch the current notice without interpreting its business payload.
|
|
3220
|
+
* @param props - Current notice, dismissal, and the declared keyed renderer.
|
|
3221
|
+
* @returns The selected notice presentation, or nothing.
|
|
3222
|
+
*/
|
|
3223
|
+
function RowActionToast({ useToast, dismissToast, renderSlot }) {
|
|
3224
|
+
const notice = useToast((current) => current);
|
|
3225
|
+
if (notice === null) return null;
|
|
3226
|
+
return renderSlot("workspace.toast", {
|
|
3227
|
+
...notice,
|
|
3228
|
+
dismissToast
|
|
3229
|
+
}, { entryKey: notice.kind });
|
|
3230
|
+
}
|
|
3231
|
+
//#endregion
|
|
3232
|
+
//#region lib/types/client/toast/Toast.js
|
|
3233
|
+
/** One presentation Factory and the adapters for keyed Workspace notices. */
|
|
3234
|
+
/**
|
|
3235
|
+
* Render one notice using the shared Factory.
|
|
3236
|
+
* @param props - Keyed payload, injected display mapping, locale, and Factory renderer.
|
|
3237
|
+
* @returns The shared Toast occurrence.
|
|
3238
|
+
*/
|
|
3239
|
+
function ToastEntry(props) {
|
|
3240
|
+
const { dismissToast, display, t, renderFactorySlot, seq } = props;
|
|
3241
|
+
return renderFactorySlot("workspace.toast.content", {
|
|
3242
|
+
...display(props, t, dismissToast),
|
|
3243
|
+
seq,
|
|
3244
|
+
onDone: dismissToast
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
/**
|
|
3248
|
+
* Present plain display data with the shared primitive's timing and portal.
|
|
3249
|
+
* @param props - Localized display fields, display identity, and dismissal.
|
|
3250
|
+
* @returns One Toast, remounted only for a new display identity.
|
|
3251
|
+
*/
|
|
3252
|
+
function WorkspaceToast({ seq, text, tone, onDone }) {
|
|
3253
|
+
return (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.Toast, {
|
|
3254
|
+
text,
|
|
3255
|
+
onDone,
|
|
3256
|
+
...tone === "success" ? { tone: "success" } : {},
|
|
3257
|
+
...tone === "warning" ? { icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconWarningOutlineRegular, {}) } : {}
|
|
3258
|
+
}, seq);
|
|
3259
|
+
}
|
|
3260
|
+
//#endregion
|
|
3261
|
+
//#region lib/types/client/toast/register.js
|
|
3262
|
+
/**
|
|
3263
|
+
* Register notice presentations with their business-owned display fields.
|
|
3264
|
+
* @param ctx - Workspace plugin context.
|
|
3265
|
+
*/
|
|
3266
|
+
function registerToasts(ctx) {
|
|
3267
|
+
ctx.slots.registerFactory({
|
|
3268
|
+
name: "workspace.toast.content",
|
|
3269
|
+
scope: "root"
|
|
3270
|
+
}, WorkspaceToast);
|
|
3271
|
+
ctx.slots.inject("workspace.toast", function* () {
|
|
3272
|
+
yield ctx.slots.register({
|
|
3273
|
+
name: "workspace.toast",
|
|
3274
|
+
key: "sessionIdCopied",
|
|
3275
|
+
locale: "workspace",
|
|
3276
|
+
inject: () => ({ display: (_notice, t) => ({
|
|
3277
|
+
text: t("toast.sessionIdCopied"),
|
|
3278
|
+
tone: "success"
|
|
3279
|
+
}) })
|
|
3280
|
+
}, ToastEntry);
|
|
3281
|
+
yield ctx.slots.register({
|
|
3282
|
+
name: "workspace.toast",
|
|
3283
|
+
key: "createFailed",
|
|
3284
|
+
locale: "workspace",
|
|
3285
|
+
inject: () => ({ display: (notice) => ({
|
|
3286
|
+
text: notice.message,
|
|
3287
|
+
tone: "warning"
|
|
3288
|
+
}) })
|
|
3289
|
+
}, ToastEntry);
|
|
3290
|
+
yield ctx.slots.register({
|
|
3291
|
+
name: "workspace.toast",
|
|
3292
|
+
key: "sessionIdCopyFailed",
|
|
3293
|
+
locale: "workspace",
|
|
3294
|
+
inject: () => ({ display: (_notice, t) => ({
|
|
3295
|
+
text: t("toast.sessionIdCopyFailed"),
|
|
3296
|
+
tone: "warning"
|
|
3297
|
+
}) })
|
|
3298
|
+
}, ToastEntry);
|
|
3299
|
+
});
|
|
3300
|
+
}
|
|
3301
|
+
//#endregion
|
|
3029
3302
|
//#region lib/types/client/locales.js
|
|
3030
3303
|
/**
|
|
3031
3304
|
* `workspace` namespace dictionaries: the browsing region (section header,
|
|
@@ -3034,6 +3307,8 @@ window.__ModuleLoader__.load({
|
|
|
3034
3307
|
*/
|
|
3035
3308
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
3036
3309
|
const zh = {
|
|
3310
|
+
"draft.workspaceRequired": "请先选择工作区",
|
|
3311
|
+
"draft.initializationFailed": "暂时无法填入草稿,请稍后重试",
|
|
3037
3312
|
"group.ungrouped": "未分组",
|
|
3038
3313
|
"session.new": "新会话",
|
|
3039
3314
|
"session.untitled": "未命名",
|
|
@@ -3081,12 +3356,16 @@ window.__ModuleLoader__.load({
|
|
|
3081
3356
|
"menu.unpinSession": "取消置顶",
|
|
3082
3357
|
"menu.fork": "分叉会话",
|
|
3083
3358
|
"menu.archiveSession": "归档会话",
|
|
3359
|
+
"menu.sessionId": "复制 Session ID",
|
|
3360
|
+
"toast.sessionIdCopied": "Session ID 已复制",
|
|
3361
|
+
"toast.sessionIdCopyFailed": "复制失败,请重试",
|
|
3084
3362
|
"row.pinned": "已置顶",
|
|
3085
3363
|
"sessions.count.one": "{n} 个会话",
|
|
3086
3364
|
"sessions.count.other": "{n} 个会话",
|
|
3087
3365
|
"actions.workspace.aria": "工作区“{name}”的操作",
|
|
3088
3366
|
"actions.session.aria": "会话“{name}”的操作",
|
|
3089
3367
|
"actions.newSession.aria": "在“{name}”中新建会话",
|
|
3368
|
+
"actions.archive": "归档会话",
|
|
3090
3369
|
"actions.pin": "置顶会话",
|
|
3091
3370
|
"actions.unpin": "取消置顶",
|
|
3092
3371
|
"status.running": "进行中",
|
|
@@ -3110,6 +3389,8 @@ window.__ModuleLoader__.load({
|
|
|
3110
3389
|
};
|
|
3111
3390
|
/** English dictionary, checked complete against the zh key set. */
|
|
3112
3391
|
const en = {
|
|
3392
|
+
"draft.workspaceRequired": "Choose a workspace first.",
|
|
3393
|
+
"draft.initializationFailed": "Could not fill the draft. Please try again shortly.",
|
|
3113
3394
|
"group.ungrouped": "Ungrouped",
|
|
3114
3395
|
"session.new": "New Session",
|
|
3115
3396
|
"session.untitled": "Untitled",
|
|
@@ -3157,12 +3438,16 @@ window.__ModuleLoader__.load({
|
|
|
3157
3438
|
"menu.unpinSession": "Unpin session",
|
|
3158
3439
|
"menu.fork": "Fork session",
|
|
3159
3440
|
"menu.archiveSession": "Archive session",
|
|
3441
|
+
"menu.sessionId": "Copy Session ID",
|
|
3442
|
+
"toast.sessionIdCopied": "Session ID copied",
|
|
3443
|
+
"toast.sessionIdCopyFailed": "Copy failed. Try again.",
|
|
3160
3444
|
"row.pinned": "Pinned",
|
|
3161
3445
|
"sessions.count.one": "{n} session",
|
|
3162
3446
|
"sessions.count.other": "{n} sessions",
|
|
3163
3447
|
"actions.workspace.aria": "Workspace actions for {name}",
|
|
3164
3448
|
"actions.session.aria": "Session actions for {name}",
|
|
3165
3449
|
"actions.newSession.aria": "New session in {name}",
|
|
3450
|
+
"actions.archive": "Archive",
|
|
3166
3451
|
"actions.pin": "Pin",
|
|
3167
3452
|
"actions.unpin": "Unpin",
|
|
3168
3453
|
"status.running": "Running",
|
|
@@ -3221,7 +3506,15 @@ window.__ModuleLoader__.load({
|
|
|
3221
3506
|
...viewHandle,
|
|
3222
3507
|
create: () => viewInstance
|
|
3223
3508
|
};
|
|
3224
|
-
const
|
|
3509
|
+
const rowToast = (0, _qilin_agent_client_store.createSnapshotStore)(null);
|
|
3510
|
+
let toastSeq = 0;
|
|
3511
|
+
const notify = (toast) => {
|
|
3512
|
+
rowToast.set({
|
|
3513
|
+
...toast,
|
|
3514
|
+
seq: ++toastSeq
|
|
3515
|
+
});
|
|
3516
|
+
};
|
|
3517
|
+
const uiWorkspace = new UiWorkspaceService(ctx, ctx.remote.directoryPicker, workspaces, sessions, viewInstance.actions, notify);
|
|
3225
3518
|
ctx.slots.provideRoot({ hooks: { workspaces: workspaces.list } });
|
|
3226
3519
|
ctx.effect(() => ctx.locale.register(NS, {
|
|
3227
3520
|
zh,
|
|
@@ -3280,6 +3573,9 @@ window.__ModuleLoader__.load({
|
|
|
3280
3573
|
closeAddWorkspace: shortcutControls.closeAdd,
|
|
3281
3574
|
closeRenameRequest: shortcutControls.closeRename,
|
|
3282
3575
|
setDirectoryBusy: shortcutControls.directoryBusy,
|
|
3576
|
+
copySessionId: async (sessionId) => {
|
|
3577
|
+
notify({ kind: await (0, _qilin_agent_client_ui_primitives.writeClipboard)(sessionId) ? "sessionIdCopied" : "sessionIdCopyFailed" });
|
|
3578
|
+
},
|
|
3283
3579
|
hooks: {
|
|
3284
3580
|
directoryFlow: browserFlowSource,
|
|
3285
3581
|
hostInfo,
|
|
@@ -3290,6 +3586,13 @@ window.__ModuleLoader__.load({
|
|
|
3290
3586
|
installWorkspaceShortcuts(ctx, uiWorkspace, shortcutControls, (sessionId) => {
|
|
3291
3587
|
uiWorkspace.archiveSession(sessionId);
|
|
3292
3588
|
});
|
|
3589
|
+
registerToasts(ctx);
|
|
3590
|
+
const rowToastInjected = () => ({
|
|
3591
|
+
hooks: { toast: rowToast },
|
|
3592
|
+
dismissToast: () => {
|
|
3593
|
+
rowToast.set(null);
|
|
3594
|
+
}
|
|
3595
|
+
});
|
|
3293
3596
|
const pickerInjected = () => ({
|
|
3294
3597
|
createWorkspace: (input) => workspaces.create(input),
|
|
3295
3598
|
hooks: { directoryFlow: pickerFlowSource }
|
|
@@ -3314,6 +3617,15 @@ window.__ModuleLoader__.load({
|
|
|
3314
3617
|
inject: browserInjected,
|
|
3315
3618
|
locale: NS
|
|
3316
3619
|
}, WorkspaceBrowser));
|
|
3620
|
+
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
3621
|
+
name: "shell.overlay",
|
|
3622
|
+
id: "workspace.row-toast",
|
|
3623
|
+
inject: rowToastInjected,
|
|
3624
|
+
children: { "workspace.toast": {
|
|
3625
|
+
kind: "keyed",
|
|
3626
|
+
scope: "root"
|
|
3627
|
+
} }
|
|
3628
|
+
}, RowActionToast));
|
|
3317
3629
|
ctx.slots.inject("conversation.hero.workspace", () => ctx.slots.register({
|
|
3318
3630
|
name: "conversation.hero.workspace",
|
|
3319
3631
|
children: { "conversation.hero.workspace.directoryFlow": {
|