@spzxg/cli-ui 0.6.2 → 0.6.5
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/CHANGELOG.md +26 -0
- package/README.md +137 -11
- package/dist/cli-ui-embed-BmLN2Jjg.js +43290 -0
- package/dist/{cli-ui-index-BY5i3wId.js → cli-ui-index-D4qfPHMd.js} +2 -2
- package/dist/{cli-ui-jszip.min-BZsUwI-s.js → cli-ui-jszip.min-DC9898pC.js} +1 -1
- package/dist/{cli-ui-jszip.min-m01fexHW.js → cli-ui-jszip.min-DpEl0G1z.js} +2 -2
- package/dist/{cli-ui-xlsx-CpWicJLT.js → cli-ui-xlsx-zs7oEtPj.js} +1 -1
- package/dist/cli-ui.css +32 -1
- package/dist/cli-ui.js +1 -1
- package/package.json +14 -8
- package/src/board/BrowserPane.tsx +160 -74
- package/src/board/ChangesPane.tsx +2 -2
- package/src/board/CliBoard.tsx +79 -50
- package/src/board/FileViewer.tsx +32 -22
- package/src/board/FilesPane.tsx +32 -15
- package/src/board/PanelControls.tsx +28 -0
- package/src/board/blocks.tsx +5 -5
- package/src/board/context.ts +65 -0
- package/src/board/fileOps.ts +26 -3
- package/src/board/fileWatch.ts +88 -0
- package/src/board/folderDownload.ts +30 -14
- package/src/board/panels.ts +33 -0
- package/src/board/textLoad.ts +2 -2
- package/src/board/workspace.ts +38 -9
- package/src/chat/CliChat.tsx +73 -30
- package/src/chat/Composer.tsx +41 -41
- package/src/chat/RunControls.tsx +99 -0
- package/src/chat/configuration.ts +15 -0
- package/src/chat/draft.ts +41 -0
- package/src/chat/export.ts +2 -2
- package/src/components.tsx +13 -0
- package/src/core/blocks.ts +2 -2
- package/src/core/callbacks.ts +8 -0
- package/src/core/client.ts +8 -6
- package/src/core/conversation.ts +3 -3
- package/src/core/id.ts +15 -0
- package/src/core/identity.ts +33 -0
- package/src/core/provider.tsx +24 -14
- package/src/core/store.ts +83 -26
- package/src/core/types.ts +34 -0
- package/src/elements.tsx +135 -29
- package/src/embed.ts +6 -8
- package/src/index.ts +24 -17
- package/src/layout/CliSplit.tsx +1 -1
- package/src/msglist/CliMsgList.tsx +10 -10
- package/src/operator/branchFlow.ts +35 -0
- package/src/operator/index.ts +9 -0
- package/src/operator/liveStream.ts +1 -0
- package/src/operator/node/AgentConnect.tsx +624 -0
- package/src/operator/node/AppearancePanel.tsx +35 -0
- package/src/operator/node/BranchHistory.tsx +67 -0
- package/src/operator/node/BranchNavigator.tsx +36 -0
- package/src/operator/node/BrowserPane.tsx +128 -0
- package/src/operator/node/BrowserPopup.tsx +22 -0
- package/src/operator/node/Chat.tsx +517 -0
- package/src/operator/node/CommandPalette.tsx +213 -0
- package/src/operator/node/Composer.tsx +287 -0
- package/src/operator/node/InsightsView.tsx +831 -0
- package/src/operator/node/Layout.tsx +190 -0
- package/src/operator/node/Markdown.tsx +61 -0
- package/src/operator/node/Modal.tsx +179 -0
- package/src/operator/node/NeedsYouMenu.tsx +54 -0
- package/src/operator/node/OnboardingWizard.tsx +250 -0
- package/src/operator/node/Orchestrator.tsx +634 -0
- package/src/operator/node/ProjectLanding.tsx +86 -0
- package/src/operator/node/ProjectsColumn.tsx +137 -0
- package/src/operator/node/ProviderAuth.tsx +561 -0
- package/src/operator/node/RunControls.tsx +2 -0
- package/src/operator/node/RunSwitch.tsx +113 -0
- package/src/operator/node/Services.tsx +245 -0
- package/src/operator/node/SessionRail.tsx +260 -0
- package/src/operator/node/SessionView.tsx +288 -0
- package/src/operator/node/SettingsView.tsx +690 -0
- package/src/operator/node/TaskBoard.tsx +352 -0
- package/src/operator/node/TaskChanges.tsx +530 -0
- package/src/operator/node/TaskHero.tsx +50 -0
- package/src/operator/node/TasksColumn.tsx +200 -0
- package/src/operator/node/Terminal.tsx +143 -0
- package/src/operator/node/Transcript.tsx +463 -0
- package/src/operator/node/ViewFile.tsx +79 -0
- package/src/operator/node/Welcome.tsx +101 -0
- package/src/operator/node/WorktreePrune.tsx +333 -0
- package/src/operator/node/agents.ts +58 -0
- package/src/operator/node/api.ts +40 -0
- package/src/operator/node/boardBackend.ts +80 -0
- package/src/operator/node/boardEvents.ts +22 -0
- package/src/operator/node/boardInteraction.ts +15 -0
- package/src/operator/node/boardState.ts +57 -0
- package/src/operator/node/boardWorkspace.ts +12 -0
- package/src/operator/node/branchFlow.ts +1 -0
- package/src/operator/node/composerDraft.ts +52 -0
- package/src/operator/node/fileLinks.ts +81 -0
- package/src/operator/node/format.ts +290 -0
- package/src/operator/node/github.tsx +234 -0
- package/src/operator/node/icons.tsx +88 -0
- package/src/operator/node/liveStream.ts +270 -0
- package/src/operator/node/modals.tsx +720 -0
- package/src/operator/node/navHistory.ts +70 -0
- package/src/operator/node/persist.ts +26 -0
- package/src/operator/node/protocol/agents/optIn.ts +9 -0
- package/src/operator/node/protocol/agents/types.ts +394 -0
- package/src/operator/node/protocol/approvalFailure.ts +77 -0
- package/src/operator/node/protocol/authFailure.ts +66 -0
- package/src/operator/node/protocol/customModels.ts +23 -0
- package/src/operator/node/protocol/events.ts +13 -0
- package/src/operator/node/protocol/features.ts +70 -0
- package/src/operator/node/protocol/internalUsage.ts +1 -0
- package/src/operator/node/protocol/promptLimits.ts +52 -0
- package/src/operator/node/protocol/storageRetention.ts +13 -0
- package/src/operator/node/protocol/types.ts +332 -0
- package/src/operator/node/protocol/usageLimit.ts +72 -0
- package/src/operator/node/shared.tsx +89 -0
- package/src/operator/node/transcriptModel.ts +129 -0
- package/src/operator/node/types.ts +296 -0
- package/src/operator/node/useGlobalEvents.ts +97 -0
- package/src/operator/node/useOrchestrator.ts +696 -0
- package/src/operator/node/usePrefs.ts +114 -0
- package/src/operator/node/useRecaps.ts +91 -0
- package/src/operator/node/useTaskStream.ts +279 -0
- package/src/operator.base.css +1798 -0
- package/src/operator.css +186 -16
- package/src/styles.css +20 -0
- package/src/ui/Markdown.tsx +7 -2
- package/src/ui/Popover.tsx +77 -0
- package/src/ui/TextBlock.tsx +3 -3
- package/src/ui/Transcript.tsx +10 -8
- package/src/ui/fileLinks.ts +6 -0
- package/src/ui/format.ts +1 -1
- package/src/ui/shared.tsx +1 -1
- package/src/ui/transcriptModel.ts +2 -2
- package/dist/cli-ui-embed-6IJ_jRVh.js +0 -41223
package/src/operator.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* 由 scripts/scope-css.mjs 从
|
|
1
|
+
/* 由 scripts/scope-css.mjs 从 packages/cli-ui/src/operator.base.css 生成,勿手改(改了会被下次同步覆盖)。
|
|
2
2
|
组件自己的补充样式在 src/styles.css。 */
|
|
3
3
|
/* ==========================================================================
|
|
4
4
|
Orchestrator — hi-fi app styling
|
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
|
|
46
46
|
--grid:rgba(150,170,200,.05);
|
|
47
47
|
--row-sel:rgba(150,170,200,.07);
|
|
48
|
+
--selection-bg:#15243a;
|
|
49
|
+
--selection-line:rgba(77,140,255,.28);
|
|
48
50
|
--on-accent:#06070a; /* ink on a filled accent/blue/green button */
|
|
49
51
|
--topbar:rgba(8,10,14,.82);
|
|
50
52
|
|
|
@@ -112,6 +114,8 @@
|
|
|
112
114
|
|
|
113
115
|
--grid:rgba(20,30,50,.045);
|
|
114
116
|
--row-sel:rgba(20,23,28,.045);
|
|
117
|
+
--selection-bg:#eaf1fd;
|
|
118
|
+
--selection-line:rgba(31,95,214,.22);
|
|
115
119
|
--on-accent:#ffffff;
|
|
116
120
|
--topbar:rgba(244,241,234,.85);
|
|
117
121
|
|
|
@@ -128,6 +132,9 @@
|
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
.cli-ui, .cli-ui *{box-sizing:border-box;}
|
|
135
|
+
/* Theme changes are atomic; hover/focus transitions resume after the new palette
|
|
136
|
+
has painted. Otherwise dark surfaces briefly swallow the new dark text. */
|
|
137
|
+
.cli-ui html[data-theme-changing] *,.cli-ui html[data-theme-changing] *::before,.cli-ui html[data-theme-changing] *::after{transition:none!important;}
|
|
131
138
|
.cli-ui{
|
|
132
139
|
background:var(--bg);
|
|
133
140
|
color:var(--ink);
|
|
@@ -280,7 +287,7 @@
|
|
|
280
287
|
.cli-ui .proj:hover{background:var(--panel);}
|
|
281
288
|
.cli-ui .proj.dragging{opacity:.4;}
|
|
282
289
|
.cli-ui .proj.drag-over{box-shadow:inset 0 2px 0 var(--accent);}
|
|
283
|
-
.cli-ui .proj.sel{background:var(--
|
|
290
|
+
.cli-ui .proj.sel{background:var(--selection-bg);box-shadow:inset 0 0 0 1px var(--selection-line);}
|
|
284
291
|
.cli-ui .proj.sel::before{content:"";position:absolute;left:-8px;top:50%;transform:translateY(-50%);width:3px;height:20px;border-radius:3px;background:var(--accent);}
|
|
285
292
|
.cli-ui .proj .pic{
|
|
286
293
|
width:30px;height:30px;flex:0 0 auto;border-radius:9px;display:grid;place-items:center;position:relative;
|
|
@@ -321,7 +328,7 @@
|
|
|
321
328
|
.cli-ui .settings-nav-foot{padding:10px 9px 4px;border-top:1px solid var(--line);font-family:var(--mono);font-size:10px;color:var(--ink-3);}
|
|
322
329
|
.cli-ui .settings-head{flex:0 0 auto;display:flex;align-items:center;gap:8px;padding:14px 20px 12px;border-bottom:1px solid var(--line);background:linear-gradient(180deg,var(--surface) 0%, var(--panel) 100%);}
|
|
323
330
|
.cli-ui .settings-title{font-size:16px;font-weight:700;color:var(--ink);}
|
|
324
|
-
.cli-ui .settings-body{padding:20px;max-width:680px;}
|
|
331
|
+
.cli-ui .settings-body{padding:20px;max-width:680px;border:0;margin:0;min-width:0;width:100%;}
|
|
325
332
|
.cli-ui .settings-body code{font-family:var(--mono);font-size:12px;background:var(--surface-2);padding:1px 5px;border-radius:5px;}
|
|
326
333
|
.cli-ui .user-chip .av{width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,#C2603C,#D89A3A);display:grid;place-items:center;color:#fff;font-weight:700;font-size:12px;}
|
|
327
334
|
.cli-ui .user-chip .un{font-weight:600;font-size:13px;}
|
|
@@ -421,7 +428,7 @@
|
|
|
421
428
|
.cli-ui .view-toggle{display:flex;align-items:center;border:1px solid var(--line-2);border-radius:var(--r);overflow:hidden;flex:0 0 auto;}
|
|
422
429
|
.cli-ui .view-toggle button{display:flex;align-items:center;justify-content:center;width:27px;height:24px;background:none;border:0;color:var(--ink-3);cursor:pointer;font-size:13px;transition:color .12s,background .12s;}
|
|
423
430
|
.cli-ui .view-toggle button:hover{color:var(--ink);}
|
|
424
|
-
.cli-ui .view-toggle button.on{background:var(--
|
|
431
|
+
.cli-ui .view-toggle button.on{background:var(--selection-bg);color:var(--accent-ink);}
|
|
425
432
|
|
|
426
433
|
/* full-workspace shell: board mode owns everything right of the sidebar */
|
|
427
434
|
.cli-ui .board-ws{flex:1;min-width:0;background:var(--surface-2);position:relative;overflow:hidden;}
|
|
@@ -999,20 +1006,31 @@
|
|
|
999
1006
|
.cli-ui .err-note .btn{flex:0 0 auto;}
|
|
1000
1007
|
|
|
1001
1008
|
/* composer */
|
|
1002
|
-
.cli-ui .composer{flex:0 0 auto;padding:
|
|
1003
|
-
.cli-ui .clear-confirm{flex:0 0 auto;display:flex;align-items:center;gap:9px;padding:9px 18px;border-bottom:1px solid var(--line);background:var(--surface);font-size:12px;color:var(--ink-2);}
|
|
1009
|
+
.cli-ui .composer{flex:0 0 auto;padding:12px 20px 10px;background:var(--surface);container-type:inline-size;}
|
|
1010
|
+
.cli-ui .clear-confirm{flex:0 0 auto;display:flex;align-items:center;flex-wrap:wrap;gap:9px;padding:9px 18px;border-bottom:1px solid var(--line);background:var(--surface);font-size:12px;color:var(--ink-2);}
|
|
1004
1011
|
.cli-ui .clear-confirm .spacer{flex:1;}
|
|
1005
|
-
|
|
1006
|
-
.cli-ui .
|
|
1007
|
-
.cli-ui .comp-box
|
|
1008
|
-
.cli-ui .comp-
|
|
1009
|
-
.cli-ui .comp-area{display:
|
|
1010
|
-
.cli-ui .comp-area textarea{flex:1;border:none;background:none;resize:none;outline:none;font-family:var(--sans);font-size:14px;line-height:1.5;color:var(--ink);max-height:160px;min-height:22px;}
|
|
1012
|
+
.cli-ui .composer-inner{max-width:800px;margin:0 auto;position:relative;}
|
|
1013
|
+
.cli-ui .comp-box{border:1px solid var(--line-strong);border-radius:24px;background:var(--raise);box-shadow:0 4px 20px rgba(0,0,0,.04);transition:border-color .15s,box-shadow .15s;overflow:hidden;}
|
|
1014
|
+
.cli-ui .comp-box:focus-within{border-color:var(--ink-4);box-shadow:0 4px 24px rgba(0,0,0,.07);}
|
|
1015
|
+
.cli-ui .comp-area{padding:18px 20px 8px;}
|
|
1016
|
+
.cli-ui .comp-area textarea{display:block;width:100%;min-width:0;border:none;background:none;resize:none;outline:none;font-family:var(--sans);font-size:15px;line-height:1.6;color:var(--ink);max-height:200px;min-height:62px;}
|
|
1011
1017
|
.cli-ui .comp-area textarea::placeholder{color:var(--ink-4);}
|
|
1012
|
-
.cli-ui .
|
|
1018
|
+
.cli-ui .comp-toolbar{display:flex;align-items:center;gap:7px;padding:4px 12px 12px;min-width:0;}
|
|
1019
|
+
.cli-ui .comp-add{width:36px;height:36px;display:grid;place-items:center;border-radius:50%;flex:none;color:var(--ink-2);background:transparent;}
|
|
1020
|
+
.cli-ui .comp-add svg{width:22px;height:22px;}
|
|
1021
|
+
.cli-ui .comp-add:hover,.cli-ui .comp-setting:hover{background:var(--surface-2);}
|
|
1022
|
+
.cli-ui .comp-control{position:relative;min-width:0;}
|
|
1023
|
+
.cli-ui .comp-model{margin-left:auto;}
|
|
1024
|
+
.cli-ui .comp-setting{display:flex;align-items:center;gap:6px;min-height:36px;max-width:100%;padding:6px 8px;border-radius:10px;color:var(--ink-2);background:transparent;font-size:12px;white-space:nowrap;}
|
|
1025
|
+
.cli-ui .comp-setting svg{width:14px;height:14px;}
|
|
1026
|
+
.cli-ui .comp-setting.elevated{color:var(--amber);}
|
|
1027
|
+
.cli-ui .comp-model-name{min-width:0;max-width:220px;overflow:hidden;text-overflow:ellipsis;}
|
|
1028
|
+
.cli-ui .comp-reasoning{color:var(--ink-3);font-size:11px;}
|
|
1029
|
+
.cli-ui .comp-setting:disabled,.cli-ui .comp-add:disabled{opacity:.5;cursor:default;}
|
|
1030
|
+
.cli-ui .send{width:36px;height:36px;flex:none;border-radius:50%;background:var(--accent);color:var(--on-accent);display:grid;place-items:center;transition:filter .12s,opacity .12s;}
|
|
1013
1031
|
.cli-ui .send:hover{filter:brightness(1.07);}
|
|
1014
|
-
.cli-ui .send:disabled{opacity:.
|
|
1015
|
-
.cli-ui .send.stop{background:var(--
|
|
1032
|
+
.cli-ui .send:disabled{opacity:.35;cursor:default;filter:none;}
|
|
1033
|
+
.cli-ui .send.stop{background:var(--blue);color:#fff;}
|
|
1016
1034
|
.cli-ui .send.stop svg{fill:currentColor;width:15px;height:15px;}
|
|
1017
1035
|
/* mid-turn: queue button + stop button sit side by side */
|
|
1018
1036
|
.cli-ui .send-group{display:flex;align-items:flex-end;gap:8px;flex:0 0 auto;}
|
|
@@ -1032,6 +1050,49 @@
|
|
|
1032
1050
|
@container (max-width:560px){.cli-ui .comp-foot .comp-hint-extra{display:none;}}
|
|
1033
1051
|
@container (max-width:420px){.cli-ui .comp-foot .hint .kbd{display:none;}.cli-ui .run-sw{max-width:130px;}}
|
|
1034
1052
|
|
|
1053
|
+
.cli-ui .send-group{display:flex;align-items:center;gap:6px;flex:none;}
|
|
1054
|
+
.cli-ui .send svg{width:19px;height:19px;}
|
|
1055
|
+
.cli-ui .comp-caption{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:4px;padding:8px 6px 0;font-size:10px;color:var(--ink-4);}
|
|
1056
|
+
.cli-ui .comp-caption button{display:flex;align-items:center;gap:4px;background:transparent;color:var(--ink-3);font-size:10px;}
|
|
1057
|
+
.cli-ui .comp-caption button:disabled{opacity:.45;cursor:default;}
|
|
1058
|
+
.cli-ui .comp-error,.cli-ui .control-error{padding:8px 10px;font-size:12px;line-height:1.5;color:var(--red);overflow-wrap:anywhere;}
|
|
1059
|
+
.cli-ui .comp-upload-status{padding:6px 10px;font-size:11px;color:var(--ink-3);}
|
|
1060
|
+
.cli-ui .comp-quote{display:flex;align-items:flex-start;gap:10px;margin:12px 14px 0;padding:10px 12px;border-left:3px solid var(--accent);border-radius:5px 12px 12px 5px;background:var(--surface-2);font-size:12px;}
|
|
1061
|
+
.cli-ui .comp-quote>div{flex:1;min-width:0;}
|
|
1062
|
+
.cli-ui .comp-quote strong{font-weight:500;color:var(--ink-2);}
|
|
1063
|
+
.cli-ui .comp-quote p{margin:4px 0 0;color:var(--ink-3);overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow-wrap:anywhere;white-space:pre-wrap;}
|
|
1064
|
+
.cli-ui .comp-quote .icon-btn{width:24px;height:24px;flex:none;}
|
|
1065
|
+
.cli-ui .hero-controls{display:flex;align-items:center;gap:12px;padding:12px 20px;border-bottom:1px solid var(--line);}
|
|
1066
|
+
.cli-ui .hero-controls .comp-model{margin-left:0;}
|
|
1067
|
+
.cli-ui .compact-chat{flex:1;}
|
|
1068
|
+
.cli-ui .compact-chat[hidden]{display:none;}
|
|
1069
|
+
.cli-ui .compact-session>.sess-rail{flex:1;}
|
|
1070
|
+
.cli-ui .compact-session .sess-head{flex-wrap:wrap;}
|
|
1071
|
+
.cli-ui .compact-session .sh-tools{flex-wrap:wrap;}
|
|
1072
|
+
.cli-ui .msg-actions{display:flex;margin-top:6px;}
|
|
1073
|
+
.cli-ui .msg-actions button{display:flex;align-items:center;gap:4px;padding:4px 6px;border-radius:6px;background:transparent;font-size:11px;color:var(--ink-4);}
|
|
1074
|
+
.cli-ui .msg-actions button:hover,.cli-ui .msg-actions button:focus-visible{background:var(--surface-2);color:var(--ink);}
|
|
1075
|
+
.cli-ui .msg-body{overflow-wrap:anywhere;min-width:0;}
|
|
1076
|
+
.cli-ui button.pop-item{width:100%;text-align:left;background:transparent;color:var(--ink);font:inherit;}
|
|
1077
|
+
.cli-ui button.pop-item:hover{background:var(--surface-2);}
|
|
1078
|
+
.cli-ui .pop-item .pi-sub{display:block;}
|
|
1079
|
+
.cli-ui .pop-item:disabled{opacity:.5;cursor:default;}
|
|
1080
|
+
.cli-ui .popover{max-width:calc(100vw - 16px);overscroll-behavior:contain;}
|
|
1081
|
+
.cli-ui .composer button:focus-visible,.cli-ui .msg-actions button:focus-visible,.cli-ui .popover button:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
|
|
1082
|
+
@container (max-width:420px){
|
|
1083
|
+
.cli-ui .comp-toolbar{flex-wrap:wrap;gap:4px;padding:4px 10px 10px;}
|
|
1084
|
+
.cli-ui .comp-setting{font-size:11px;padding:5px;gap:4px;}
|
|
1085
|
+
.cli-ui .comp-model-name{max-width:145px;}
|
|
1086
|
+
.cli-ui .comp-area{padding:14px 16px 6px;}
|
|
1087
|
+
.cli-ui .comp-caption{font-size:9px;}
|
|
1088
|
+
}
|
|
1089
|
+
@container (max-width:310px){
|
|
1090
|
+
.cli-ui .comp-control:has(.permission){flex:1;}
|
|
1091
|
+
.cli-ui .comp-model{margin-left:0;flex:1;}
|
|
1092
|
+
.cli-ui .comp-model-name{max-width:120px;}
|
|
1093
|
+
.cli-ui .comp-toolbar>.send-group{margin-left:auto;}
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1035
1096
|
/* image attachments — composer chips + drop state */
|
|
1036
1097
|
.cli-ui .comp-box.dropping{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft);}
|
|
1037
1098
|
.cli-ui .attach-row{display:flex;flex-wrap:wrap;gap:8px;padding:10px 12px 0;}
|
|
@@ -1392,7 +1453,7 @@
|
|
|
1392
1453
|
the home indicator. The keyboard hints are mouse-era noise on a phone. */
|
|
1393
1454
|
.cli-ui .app.mobile .composer{padding:10px 12px calc(12px + env(safe-area-inset-bottom));}
|
|
1394
1455
|
.cli-ui .app.mobile .comp-area textarea{font-size:16px;}
|
|
1395
|
-
.cli-ui .app.mobile .comp-
|
|
1456
|
+
.cli-ui .app.mobile .comp-caption>span{display:none;}
|
|
1396
1457
|
.cli-ui .app.mobile .send{width:40px;height:40px;}
|
|
1397
1458
|
|
|
1398
1459
|
/* Ask card options are tap targets — give them room. */
|
|
@@ -1626,3 +1687,112 @@
|
|
|
1626
1687
|
.cli-ui .bp-logins-name{flex:1;min-width:0;font-size:12.5px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
1627
1688
|
.cli-ui .bp-ok{border:1px solid var(--green);background:var(--green-soft);color:var(--ink);border-radius:var(--r-sm);padding:8px 10px;font-size:12px;line-height:1.6;}
|
|
1628
1689
|
.cli-ui .bp-kept{border:1px solid var(--line-strong);background:var(--surface-2);border-radius:var(--r-sm);padding:10px 12px;font-size:12.5px;line-height:1.6;color:var(--ink-2);}
|
|
1690
|
+
|
|
1691
|
+
/* Keep usable chat width even when persisted sidebar widths exceed the window. */
|
|
1692
|
+
.cli-ui .body:not(.mobile)>.col-projects{max-width:24vw;}
|
|
1693
|
+
.cli-ui .body:not(.mobile)>.col-tasks{max-width:30vw;}
|
|
1694
|
+
.cli-ui .compact-session .sh-main{overflow:hidden;}
|
|
1695
|
+
.cli-ui .compact-session .crumb{flex-wrap:wrap;}
|
|
1696
|
+
.cli-ui .compact-session .sh-tools{min-width:0;max-width:100%;}
|
|
1697
|
+
.cli-ui .compact-session .viewseg{flex-shrink:0;}
|
|
1698
|
+
|
|
1699
|
+
.cli-ui .popover .pop-custom-row input{min-width:0;}
|
|
1700
|
+
.cli-ui .popover .pop-item>span:first-child{min-width:0;overflow-wrap:anywhere;}
|
|
1701
|
+
|
|
1702
|
+
@media(max-width:760px){
|
|
1703
|
+
.cli-ui .app.mobile .sh-main{flex:1 1 0;order:0;}
|
|
1704
|
+
.cli-ui .app.mobile .sess-head{padding:10px 12px;gap:8px;}
|
|
1705
|
+
.cli-ui .app.mobile .tb-right{gap:6px;}
|
|
1706
|
+
.cli-ui .app.mobile .needs-you-pill{white-space:nowrap;flex-shrink:0;font-size:11px;}
|
|
1707
|
+
.cli-ui .app.mobile .tb-avatar{display:none;}
|
|
1708
|
+
}
|
|
1709
|
+
@media(max-width:420px){.cli-ui .app.mobile .tb-word{display:none;}}
|
|
1710
|
+
|
|
1711
|
+
/* Chat navigation: a quiet title row, a separate view switch and contextual actions. */
|
|
1712
|
+
.cli-ui .sess-head{padding:14px 22px;gap:12px;flex-wrap:nowrap;}
|
|
1713
|
+
.cli-ui .sess-head .sh-main{min-width:0;}
|
|
1714
|
+
.cli-ui .sess-head .sh-title{font-size:18px;font-weight:600;}
|
|
1715
|
+
.cli-ui .session-project-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
1716
|
+
.cli-ui .sess-head .sh-tools{gap:8px;flex-wrap:nowrap;flex-shrink:0;}
|
|
1717
|
+
.cli-ui .session-more{width:32px;height:32px;}
|
|
1718
|
+
.cli-ui .session-viewbar{display:flex;align-items:center;gap:16px;min-height:42px;padding:0 22px;border-bottom:1px solid var(--line-2);}
|
|
1719
|
+
.cli-ui .session-agent-label{color:var(--ink-3);font-size:12px;}
|
|
1720
|
+
.cli-ui .session-branch{margin-left:auto;min-width:0;display:flex;align-items:center;gap:6px;color:var(--ink-4);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
1721
|
+
.cli-ui .session-viewbar .viewseg{flex-shrink:0;}
|
|
1722
|
+
.cli-ui .compact-session .sh-tools{flex-wrap:nowrap;}
|
|
1723
|
+
.cli-ui .composer{padding:16px 24px 12px;}
|
|
1724
|
+
.cli-ui .comp-box{border-radius:22px;}
|
|
1725
|
+
.cli-ui .comp-area{padding:18px 20px 10px;}
|
|
1726
|
+
.cli-ui .comp-toolbar{padding:4px 14px 12px;}
|
|
1727
|
+
.cli-ui .comp-setting{font-size:13px;}
|
|
1728
|
+
.cli-ui .comp-caption{font-size:11px;padding-top:9px;}
|
|
1729
|
+
.cli-ui .comp-caption button{font-size:11px;}
|
|
1730
|
+
.cli-ui .transcript .tw{max-width:800px;}
|
|
1731
|
+
.cli-ui .task-origin{display:flex;align-items:center;gap:5px;font-size:10px;color:var(--ink-3);padding-top:7px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
1732
|
+
.cli-ui .fork-sources{display:flex;flex-wrap:wrap;gap:6px;padding:12px 14px 0;}
|
|
1733
|
+
.cli-ui .fork-sources>span{max-width:100%;display:flex;align-items:center;gap:6px;padding:6px 8px;border:1px solid var(--line);background:var(--surface-2);border-radius:8px;font-size:11px;color:var(--ink-2);overflow-wrap:anywhere;}
|
|
1734
|
+
.cli-ui .fork-sources button{display:grid;place-items:center;color:var(--ink-3);background:transparent;flex-shrink:0;}
|
|
1735
|
+
.cli-ui .mention-menu{position:absolute;bottom:calc(100% + 8px);left:0;right:0;z-index:30;max-height:min(320px,45vh);overflow:auto;border:1px solid var(--line-strong);border-radius:14px;background:var(--raise);box-shadow:var(--sh-3);}
|
|
1736
|
+
.cli-ui .mention-heading{position:sticky;top:0;display:flex;justify-content:space-between;padding:12px 14px;font-size:11px;color:var(--ink-3);background:var(--raise);border-bottom:1px solid var(--line);}
|
|
1737
|
+
.cli-ui .mention-menu>p{padding:14px;font-size:12px;color:var(--ink-3);line-height:1.6;}
|
|
1738
|
+
.cli-ui .mention-menu>button{display:flex;align-items:center;gap:10px;width:100%;padding:11px 14px;text-align:left;color:var(--ink);background:transparent;}
|
|
1739
|
+
.cli-ui .mention-menu>button:hover,.cli-ui .mention-menu>button[aria-selected=true]{background:var(--accent-soft);}
|
|
1740
|
+
.cli-ui .mention-menu>button>span{flex:1;min-width:0;}
|
|
1741
|
+
.cli-ui .mention-menu strong,.cli-ui .mention-menu small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
1742
|
+
.cli-ui .mention-menu strong{font-size:13px;font-weight:500;}
|
|
1743
|
+
.cli-ui .mention-menu small{font-size:10px;color:var(--ink-3);margin-top:4px;}
|
|
1744
|
+
.cli-ui .branch-nav{margin:28px 0 24px;min-width:0;}
|
|
1745
|
+
.cli-ui .branch-ending{display:flex;align-items:center;gap:12px;margin-bottom:16px;color:var(--ink-3);font-size:11px;}
|
|
1746
|
+
.cli-ui .branch-ending::before,.cli-ui .branch-ending::after{content:"";height:1px;flex:1;background:var(--line-strong);}
|
|
1747
|
+
.cli-ui .branch-nav-head{display:flex;align-items:center;gap:6px;margin-bottom:10px;font-size:12px;color:var(--ink-2);}
|
|
1748
|
+
.cli-ui .branch-nav-head>span:first-child{display:flex;gap:6px;align-items:center;}
|
|
1749
|
+
.cli-ui .branch-nav-hint{margin-left:auto;font-size:10px;color:var(--ink-4);}
|
|
1750
|
+
.cli-ui .branch-nav-head .icon-btn{width:28px;height:28px;}
|
|
1751
|
+
.cli-ui .branch-cards{display:flex;gap:10px;overflow-x:auto;padding:1px 1px 5px;scroll-snap-type:x proximity;scrollbar-width:thin;overscroll-behavior-x:contain;}
|
|
1752
|
+
.cli-ui .branch-card{display:flex;flex-direction:column;gap:7px;flex:0 0 calc((100% - 10px)/2);min-width:148px;text-align:left;padding:12px;border-radius:12px;border:1px solid var(--line);background:var(--panel);color:var(--ink-2);scroll-snap-align:start;}
|
|
1753
|
+
.cli-ui .branch-card:only-child{flex-basis:100%;}
|
|
1754
|
+
.cli-ui .branch-card:hover{border-color:var(--line-strong);background:var(--row-sel);}
|
|
1755
|
+
.cli-ui .branch-card.selected{border-color:var(--accent);background:var(--accent-soft);}
|
|
1756
|
+
.cli-ui .branch-card-meta{display:flex;justify-content:space-between;gap:8px;font-size:10px;color:var(--ink-3);}
|
|
1757
|
+
.cli-ui .branch-card.selected .branch-card-meta{color:var(--accent);}
|
|
1758
|
+
.cli-ui .branch-card strong{font-size:13px;font-weight:500;line-height:1.5;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;}
|
|
1759
|
+
.cli-ui .branch-card small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:10px;color:var(--ink-3);}
|
|
1760
|
+
.cli-ui .flow-task-heading{display:flex;flex-direction:column;gap:6px;margin:4px 0 20px;}
|
|
1761
|
+
.cli-ui .flow-task-heading>span{font-size:10px;color:var(--ink-3);}
|
|
1762
|
+
.cli-ui .flow-task-heading strong,.cli-ui .flow-task-heading button{font-size:14px;font-weight:600;color:var(--ink);text-align:left;overflow-wrap:anywhere;}
|
|
1763
|
+
.cli-ui .flow-task-heading button{background:transparent;align-self:flex-start;}
|
|
1764
|
+
.cli-ui .flow-task-heading button:hover{color:var(--accent);}
|
|
1765
|
+
.cli-ui .flow-task-heading.current{border-left:2px solid var(--accent);padding-left:12px;}
|
|
1766
|
+
.cli-ui .flow-history-note{padding:12px;background:var(--surface-2);border-radius:8px;font-size:12px;color:var(--ink-3);line-height:1.6;}
|
|
1767
|
+
.cli-ui .flow-history-note pre{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit;max-height:240px;overflow:auto;}
|
|
1768
|
+
.cli-ui .flow-history-note summary{cursor:pointer;margin-top:8px;}
|
|
1769
|
+
.cli-ui .fork-merge-note{padding:12px 16px;color:var(--ink-3);font-size:11px;line-height:1.6;border-bottom:1px solid var(--line-2);overflow-wrap:anywhere;}
|
|
1770
|
+
.cli-ui .sess-responsive{flex:1;min-height:0;display:flex;flex-direction:column;}
|
|
1771
|
+
.cli-ui .sess-responsive>.sess-rail{flex:1;}
|
|
1772
|
+
@media(max-width:760px){
|
|
1773
|
+
|
|
1774
|
+
.cli-ui .app.mobile .tb-word{display:inline;font-size:12px;}
|
|
1775
|
+
.cli-ui .app.mobile .titlebar{height:calc(44px + env(safe-area-inset-top));}
|
|
1776
|
+
.cli-ui .app.mobile .workspace-menu{width:36px;height:36px;}
|
|
1777
|
+
.cli-ui .app.mobile .sess-head{padding:10px 12px;gap:8px;flex-wrap:nowrap;}
|
|
1778
|
+
.cli-ui .app.mobile .sh-main{flex:1;order:0;min-width:0;}
|
|
1779
|
+
.cli-ui .app.mobile .sh-tools{order:0;flex:0 0 auto;overflow:visible;padding:0;}
|
|
1780
|
+
.cli-ui .app.mobile .sh-title{font-size:16px;}
|
|
1781
|
+
.cli-ui .app.mobile .sh-edit{display:none;}
|
|
1782
|
+
.cli-ui .app.mobile .crumb{font-size:10px;flex-wrap:nowrap;}
|
|
1783
|
+
.cli-ui .app.mobile .mobile-back{width:28px;height:32px;}
|
|
1784
|
+
.cli-ui .app.mobile .status-ctl{padding:0 7px;height:30px;font-size:10px;gap:4px;}
|
|
1785
|
+
.cli-ui .app.mobile .session-more{width:28px;height:32px;}
|
|
1786
|
+
.cli-ui .app.mobile .session-viewbar{padding:0 14px;gap:8px;min-height:40px;}
|
|
1787
|
+
.cli-ui .app.mobile .session-branch{font-size:9px;}
|
|
1788
|
+
.cli-ui .app.mobile .branch-nav{margin:24px 0 20px;}
|
|
1789
|
+
.cli-ui .app.mobile .branch-card{padding:10px;}
|
|
1790
|
+
.cli-ui .app.mobile .branch-nav-hint{display:none;}
|
|
1791
|
+
.cli-ui .app.mobile .comp-setting{font-size:11px;}
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
/* The responsive shell always owns all remaining vertical space. */
|
|
1795
|
+
.cli-ui .sess-responsive>.sess-main{flex:1;min-height:0;display:flex;flex-direction:column;}
|
|
1796
|
+
.cli-ui .sess-responsive>.sess-main[hidden]{display:none;}
|
|
1797
|
+
|
|
1798
|
+
@media(max-width:760px){.cli-ui .app.mobile .tb-icon:not(.workspace-menu){display:none;}}
|
package/src/styles.css
CHANGED
|
@@ -304,3 +304,23 @@ cli-split{display:block;}
|
|
|
304
304
|
.cli-ui .cli-fv-md pre,.cli-ui .cli-fv-md table{overflow-wrap:normal;}
|
|
305
305
|
.cli-ui .cli-refs.cli-ctxs+.cli-refs{padding-top:4px;}
|
|
306
306
|
.cli-ui .cli-ref.ctx{padding-right:9px;background:var(--surface-2);border-color:var(--line-2);}
|
|
307
|
+
|
|
308
|
+
/* Shared panel controls never shrink the active content or overflow narrow rails. */
|
|
309
|
+
.cli-ui .cli-panel-controls{position:relative;flex:none;}
|
|
310
|
+
.cli-ui .rail-tabs-scroll{min-width:0;}
|
|
311
|
+
.cli-ui .cli-board-empty{padding:24px 16px;color:var(--ink-3);font-size:13px;line-height:1.6;}
|
|
312
|
+
.cli-ui .operator-file-tabs{display:flex;flex:1;min-width:0;overflow-x:auto;align-items:center;scrollbar-width:thin;}
|
|
313
|
+
|
|
314
|
+
/* Keep browser controls readable in a narrow desktop rail and on phones. */
|
|
315
|
+
.cli-ui .prev-actions.bp-actions{display:flex;flex-wrap:wrap;gap:8px;}
|
|
316
|
+
.cli-ui .prev-actions.bp-actions>.btn{flex:0 0 auto;white-space:nowrap;min-width:0;}
|
|
317
|
+
.cli-ui .prev-actions.bp-actions>span{min-width:0;}
|
|
318
|
+
|
|
319
|
+
.cli-ui .cli-board-background{display:flex;align-items:flex-start;gap:10px;margin:0 14px 8px;padding:8px 10px;border:1px solid var(--line);border-radius:10px;color:var(--muted);font-size:12px;min-width:0;}
|
|
320
|
+
.cli-ui .cli-board-background details{flex:1;min-width:0;overflow-wrap:anywhere;}
|
|
321
|
+
.cli-ui .cli-board-background summary{cursor:pointer;}
|
|
322
|
+
.cli-ui .cli-board-background label{display:flex;align-items:center;gap:4px;white-space:nowrap;cursor:pointer;}
|
|
323
|
+
.cli-ui .cli-board-background ul{padding-left:18px;max-height:150px;overflow:auto;}
|
|
324
|
+
|
|
325
|
+
.cli-ui .cli-auto-background{font-size:12px;color:var(--muted);margin-top:8px;}
|
|
326
|
+
.cli-ui .cli-auto-background pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:220px;overflow:auto;}
|
package/src/ui/Markdown.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { memo, useContext, type ComponentProps, type MouseEvent } from "react";
|
|
|
2
2
|
import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
|
|
3
3
|
import remarkGfm from "remark-gfm";
|
|
4
4
|
import rehypeHighlight from "rehype-highlight";
|
|
5
|
-
import { FileLinkContext, FileOpenContext, localFilePath } from "./fileLinks
|
|
5
|
+
import { FileLinkContext, FileOpenContext, inlineFilePath, localFilePath } from "./fileLinks";
|
|
6
6
|
|
|
7
7
|
// 渲染 Agent 的 markdown 输出:标题、列表、表格、带语言的代码块(高亮)、行内代码、链接。
|
|
8
8
|
// 与 Operator 的 Markdown.tsx 一致:只高亮写明语言的代码块(自动识别在流式输出时很耗),
|
|
@@ -24,6 +24,11 @@ function MdLink({ href, children, ...props }: ComponentProps<"a">) {
|
|
|
24
24
|
return <a {...props} href={href} target="_blank" rel="noreferrer">{children}</a>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function MdCode({children, ...props}: ComponentProps<"code">) {
|
|
28
|
+
const path=inlineFilePath(children);
|
|
29
|
+
return path ? <MdLink href={path}><code {...props}>{children}</code></MdLink> : <code {...props}>{children}</code>;
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
function MdImg({ src, alt, ...props }: ComponentProps<"img">) {
|
|
28
33
|
const resolve = useContext(FileLinkContext);
|
|
29
34
|
const local = typeof src === "string" ? localFilePath(src) : null;
|
|
@@ -45,7 +50,7 @@ export const Markdown = memo(function Markdown({ children }: { children: string
|
|
|
45
50
|
<ReactMarkdown
|
|
46
51
|
remarkPlugins={[remarkGfm]}
|
|
47
52
|
rehypePlugins={[[rehypeHighlight, { detect: false, ignoreMissing: true }]]}
|
|
48
|
-
components={{ a: MdLink, img: MdImg }}
|
|
53
|
+
components={{ a: MdLink, img: MdImg, code: MdCode }}
|
|
49
54
|
urlTransform={urlTransform}
|
|
50
55
|
>
|
|
51
56
|
{children}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
3
|
+
export function Popover({ children, onClose }: { children: React.ReactNode; onClose: () => void }) {
|
|
4
|
+
const markerRef = useRef<HTMLSpanElement>(null);
|
|
5
|
+
const menuRef = useRef<HTMLDivElement>(null);
|
|
6
|
+
const [pos, setPos] = useState<{ top: number; left: number } | null>(null);
|
|
7
|
+
|
|
8
|
+
useLayoutEffect(() => {
|
|
9
|
+
const anchor = markerRef.current?.parentElement;
|
|
10
|
+
const menu = menuRef.current;
|
|
11
|
+
if (!anchor || !menu) return;
|
|
12
|
+
const place = () => {
|
|
13
|
+
const r = anchor.getBoundingClientRect();
|
|
14
|
+
const vv = window.visualViewport;
|
|
15
|
+
const topEdge = vv?.offsetTop ?? 0;
|
|
16
|
+
const vh = vv?.height ?? window.innerHeight;
|
|
17
|
+
const vw = window.innerWidth;
|
|
18
|
+
menu.style.maxHeight = `${Math.max(100, vh - 24)}px`;
|
|
19
|
+
const mw = menu.offsetWidth, mh = menu.offsetHeight;
|
|
20
|
+
const left = Math.max(8, Math.min(r.right - mw, vw - mw - 8));
|
|
21
|
+
const below = r.bottom + 6;
|
|
22
|
+
const top = below + mh <= topEdge + vh - 8 ? below : Math.max(topEdge + 8, r.top - mh - 6);
|
|
23
|
+
setPos({ top, left });
|
|
24
|
+
};
|
|
25
|
+
place();
|
|
26
|
+
const observer = new ResizeObserver(place);
|
|
27
|
+
observer.observe(menu);
|
|
28
|
+
window.addEventListener("resize", place);
|
|
29
|
+
window.visualViewport?.addEventListener("resize", place);
|
|
30
|
+
return () => { observer.disconnect(); window.removeEventListener("resize", place); window.visualViewport?.removeEventListener("resize", place); };
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
const placed = pos !== null;
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (placed) menuRef.current?.querySelector<HTMLElement>("button:not(:disabled),input:not(:disabled),[tabindex='0']")?.focus({ preventScroll: true });
|
|
36
|
+
}, [placed]);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const close = (e: PointerEvent) => {
|
|
40
|
+
if (!menuRef.current?.contains(e.target as Node) && !markerRef.current?.parentElement?.contains(e.target as Node)) onClose();
|
|
41
|
+
};
|
|
42
|
+
const onScroll = (e: Event) => { if (!menuRef.current?.contains(e.target as Node)) onClose(); };
|
|
43
|
+
const onKey = (e: KeyboardEvent) => {
|
|
44
|
+
if (e.key === "Escape") {
|
|
45
|
+
e.preventDefault(); e.stopPropagation(); onClose();
|
|
46
|
+
markerRef.current?.parentElement?.querySelector<HTMLButtonElement>("button")?.focus();
|
|
47
|
+
}
|
|
48
|
+
if (e.key === "Tab") {
|
|
49
|
+
const items = Array.from(menuRef.current?.querySelectorAll<HTMLElement>("button:not(:disabled),input:not(:disabled),a[href],[tabindex='0']") ?? []);
|
|
50
|
+
if (!items.length) return;
|
|
51
|
+
if (e.shiftKey && document.activeElement === items[0]) { e.preventDefault(); items.at(-1)?.focus(); }
|
|
52
|
+
else if (!e.shiftKey && document.activeElement === items.at(-1)) { e.preventDefault(); items[0].focus(); }
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
window.addEventListener("pointerdown", close, true);
|
|
56
|
+
window.addEventListener("scroll", onScroll, true);
|
|
57
|
+
window.addEventListener("keydown", onKey, true);
|
|
58
|
+
return () => { window.removeEventListener("pointerdown", close, true); window.removeEventListener("scroll", onScroll, true); window.removeEventListener("keydown", onKey, true); };
|
|
59
|
+
}, [onClose]);
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
<span ref={markerRef} style={{ display: "none" }} />
|
|
64
|
+
{createPortal(
|
|
65
|
+
<div className="cli-ui" data-theme={markerRef.current?.closest("[data-theme]")?.getAttribute("data-theme") ?? "dark"}><div
|
|
66
|
+
ref={menuRef}
|
|
67
|
+
className="popover" role="dialog" aria-label="选项"
|
|
68
|
+
style={{ position: "fixed", top: pos?.top ?? -9999, left: pos?.left ?? -9999, right: "auto", visibility: pos ? "visible" : "hidden" }}
|
|
69
|
+
onClick={(e) => e.stopPropagation()}
|
|
70
|
+
>
|
|
71
|
+
{children}
|
|
72
|
+
</div></div>,
|
|
73
|
+
document.body,
|
|
74
|
+
)}
|
|
75
|
+
</>
|
|
76
|
+
);
|
|
77
|
+
}
|
package/src/ui/TextBlock.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { memo, useState } from "react";
|
|
2
|
-
import { charCount, type TextBlock } from "../core/blocks
|
|
3
|
-
import { fmtCount } from "./format
|
|
4
|
-
import { Icon } from "./icons
|
|
2
|
+
import { charCount, type TextBlock } from "../core/blocks";
|
|
3
|
+
import { fmtCount } from "./format";
|
|
4
|
+
import { Icon } from "./icons";
|
|
5
5
|
|
|
6
6
|
/** 展开后一次最多渲染这么多字;更长的再点「显示全部」,避免一次塞几十万字进 DOM。 */
|
|
7
7
|
const EXPANDED_STEP = 20_000;
|
package/src/ui/Transcript.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// 转录里的一行。移植自 operator-oss/app/orchestrator/Transcript.tsx,类名、结构、文案保持一致;
|
|
2
2
|
// 去掉了对话任务用不到的部分(/clear、重新登录、浏览器交还……这些动作 center 不提供)。
|
|
3
3
|
import { memo, useState } from "react";
|
|
4
|
-
import type { AskAnswers, AskQuestion, InputRequest, Msg, ToolData, ToolPeek } from "../core/types
|
|
5
|
-
import { splitMessage } from "../core/blocks
|
|
6
|
-
import { Icon } from "./icons
|
|
7
|
-
import { Markdown } from "./Markdown
|
|
8
|
-
import { clockTime, diffCls, splitAttachments, type MsgAttachment } from "./format
|
|
9
|
-
import { Avatar } from "./shared
|
|
10
|
-
import { toolDataOf } from "./transcriptModel
|
|
11
|
-
import { TextBlockCard, isLongMessage } from "./TextBlock
|
|
4
|
+
import type { AskAnswers, AskQuestion, InputRequest, Msg, ToolData, ToolPeek } from "../core/types";
|
|
5
|
+
import { splitMessage } from "../core/blocks";
|
|
6
|
+
import { Icon } from "./icons";
|
|
7
|
+
import { Markdown } from "./Markdown";
|
|
8
|
+
import { clockTime, diffCls, splitAttachments, type MsgAttachment } from "./format";
|
|
9
|
+
import { Avatar } from "./shared";
|
|
10
|
+
import { toolDataOf } from "./transcriptModel";
|
|
11
|
+
import { TextBlockCard, isLongMessage } from "./TextBlock";
|
|
12
12
|
|
|
13
13
|
// Operator 写进转录的几类系统提示(取开头一段做匹配):按「上下文超限」那种醒目样式显示。
|
|
14
14
|
const LOUD_NOTICES = [
|
|
@@ -319,6 +319,7 @@ export interface MessageViewOptions {
|
|
|
319
319
|
const DEFAULT_VIEW: MessageViewOptions = { timestamps: true, initialBadge: true, userLabel: "你", agentName: "智能体" };
|
|
320
320
|
|
|
321
321
|
export interface MessageHandlers {
|
|
322
|
+
onQuote?: (msg:Msg,selected?:string)=>void;
|
|
322
323
|
/** browser_ask_user 卡片提交(返回错误文案或 null)。后端不支持时不给,卡片只读。 */
|
|
323
324
|
onInput?: (inputId: string, values: string[]) => Promise<string | null>;
|
|
324
325
|
/** 「查看浏览器」:宿主有浏览器面板时给。 */
|
|
@@ -395,6 +396,7 @@ export const MessageView = memo(function MessageView({ m, initial: isInitial, hi
|
|
|
395
396
|
{initial && <div className="initial-tag">{Icon.spark()} 已随项目上下文发送</div>}
|
|
396
397
|
{isUser ? <UserBody content={m.content} attachmentUrl={handlers.attachmentUrl} viewing={view.viewingBlock} /> : m.content && <Markdown>{m.content}</Markdown>}
|
|
397
398
|
</div>
|
|
399
|
+
{handlers.onQuote && m.content.trim() && <div className="msg-actions"><button aria-label="引用消息" title="引用消息或选中文字" onMouseDown={e=>e.preventDefault()} onClick={e=>{const selection=window.getSelection();const body=e.currentTarget.closest('.msg')?.querySelector('.msg-body');const selected=selection&&body?.contains(selection.anchorNode)&&body.contains(selection.focusNode)?selection.toString().trim():'';handlers.onQuote?.(m,selected||undefined);}}>{Icon.doc()} 引用</button></div>}
|
|
398
400
|
</div>
|
|
399
401
|
);
|
|
400
402
|
});
|
package/src/ui/fileLinks.ts
CHANGED
|
@@ -44,3 +44,9 @@ export function localFilePath(href: string | undefined | null): string | null {
|
|
|
44
44
|
if (h.startsWith("/") || WIN_ABS.test(h)) return WIN_ABS.test(h) ? h : normalize(h);
|
|
45
45
|
return normalize(h);
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
/** File-shaped inline code is clickable; commands, models and code blocks remain code. */
|
|
49
|
+
export function inlineFilePath(value: unknown): string | null {
|
|
50
|
+
if (typeof value !== 'string' || /\s/.test(value) || !/\.(?:md|markdown|txt|log|csv|tsv|json|ya?ml|toml|ini|xml|[cm]?[jt]sx?|css|scss|html?|py|sh|go|rs|java|c|cpp|h|pdf|png|jpe?g|gif|webp|svg|docx?|xlsx?|pptx?|zip)(?::\d+(?::\d+)?)?$/i.test(value)) return null;
|
|
51
|
+
return localFilePath(value);
|
|
52
|
+
}
|
package/src/ui/format.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// 格式化 / 推导小工具。规则与 Operator(operator-oss/app/orchestrator/format.ts)一致,
|
|
2
2
|
// 只保留对话任务用得到的部分。
|
|
3
|
-
import type { AskAnswers, AskQuestion, Msg } from "../core/types
|
|
3
|
+
import type { AskAnswers, AskQuestion, Msg } from "../core/types";
|
|
4
4
|
|
|
5
5
|
// 紧凑 token 数:1234 → "1.2k",1_200_000 → "1.2M"。
|
|
6
6
|
export function fmtTokens(n: number): string {
|
package/src/ui/shared.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// 小部件:状态点、头像、智能体标签、骨架条。与 Operator 的 shared.tsx 同样的类名和规则。
|
|
2
2
|
import type { CSSProperties } from "react";
|
|
3
|
-
import { AgentMark, Icon } from "./icons
|
|
3
|
+
import { AgentMark, Icon } from "./icons";
|
|
4
4
|
|
|
5
5
|
/** Operator 的状态色:r 未开始 · a 进行中 · h 搁置 · g 完成 · x 取消 · b 运行中(蓝,闪)· c 等你处理(珊瑚,闪)。 */
|
|
6
6
|
export type DotCls = "r" | "a" | "h" | "g" | "x";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 实时转录背后的纯函数(无 React,可单测)。规则与 Operator 的 transcriptModel.ts 一致:
|
|
2
2
|
// 为长对话设计——成千上万条消息、几 MB 的工具输出。
|
|
3
|
-
import type { Msg, ToolData } from "../core/types
|
|
4
|
-
import { buildSessions } from "./format
|
|
3
|
+
import type { Msg, ToolData } from "../core/types";
|
|
4
|
+
import { buildSessions } from "./format";
|
|
5
5
|
|
|
6
6
|
/** 跟随最新输出时 DOM 里保留的行数。 */
|
|
7
7
|
export const RENDER_WINDOW = 120;
|