@celljs/ta-web 4.7.0 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/esm/browser/chat/chat-protocol.d.ts +4 -0
- package/esm/browser/chat/chat-view.js +4 -1
- package/esm/browser/chat/chat-view.js.map +1 -1
- package/esm/browser/chat/chat.d.ts +8 -1
- package/esm/browser/chat/chat.js +34 -15
- package/esm/browser/chat/chat.js.map +1 -1
- package/esm/browser/chat/draft-model.d.ts +15 -0
- package/esm/browser/chat/draft-model.js +53 -0
- package/esm/browser/chat/draft-model.js.map +1 -0
- package/esm/browser/components/app-view.js +10 -3
- package/esm/browser/components/app-view.js.map +1 -1
- package/esm/browser/components/composer-box.d.ts +6 -0
- package/esm/browser/components/composer-box.js +50 -13
- package/esm/browser/components/composer-box.js.map +1 -1
- package/esm/browser/components/composer.js +1 -1
- package/esm/browser/components/composer.js.map +1 -1
- package/esm/browser/components/dialog-view.d.ts +4 -1
- package/esm/browser/components/dialog-view.js +11 -7
- package/esm/browser/components/dialog-view.js.map +1 -1
- package/esm/browser/components/notice-center.js +3 -1
- package/esm/browser/components/notice-center.js.map +1 -1
- package/esm/browser/components/session-view.js +48 -4
- package/esm/browser/components/session-view.js.map +1 -1
- package/esm/browser/components/terminal-art.d.ts +7 -0
- package/esm/browser/components/terminal-art.js +12 -0
- package/esm/browser/components/terminal-art.js.map +1 -0
- package/esm/browser/components/tool-view.js +1 -1
- package/esm/browser/components/tool-view.js.map +1 -1
- package/esm/browser/components/web-composer.js +1 -1
- package/esm/browser/components/web-composer.js.map +1 -1
- package/esm/browser/extension/icons.js +4 -1
- package/esm/browser/extension/icons.js.map +1 -1
- package/esm/browser/extension/web-extension-api.d.ts +117 -1
- package/esm/browser/extension/web-extensions.d.ts +3 -0
- package/esm/browser/extension/web-extensions.js +22 -0
- package/esm/browser/extension/web-extensions.js.map +1 -1
- package/esm/browser/i18n/locales.d.ts +13 -1
- package/esm/browser/i18n/messages.d.ts +12 -0
- package/esm/browser/i18n/messages.js +24 -0
- package/esm/browser/i18n/messages.js.map +1 -1
- package/esm/browser/notices/notices-protocol.d.ts +3 -0
- package/esm/browser/notices/notices.d.ts +3 -0
- package/esm/browser/notices/notices.js +15 -0
- package/esm/browser/notices/notices.js.map +1 -1
- package/esm/browser/style/conversation.d.ts +1 -1
- package/esm/browser/style/conversation.js +16 -1
- package/esm/browser/style/conversation.js.map +1 -1
- package/esm/browser/style/layout.d.ts +1 -1
- package/esm/browser/style/layout.js +5 -0
- package/esm/browser/style/layout.js.map +1 -1
- package/esm/browser/style/shell.d.ts +1 -1
- package/esm/browser/style/shell.js +1 -0
- package/esm/browser/style/shell.js.map +1 -1
- package/esm/browser/terminal/canvas-font.d.ts +10 -0
- package/esm/browser/terminal/canvas-font.js +34 -0
- package/esm/browser/terminal/canvas-font.js.map +1 -0
- package/esm/browser/terminal/terminal-links.d.ts +18 -0
- package/esm/browser/terminal/terminal-links.js +72 -0
- package/esm/browser/terminal/terminal-links.js.map +1 -0
- package/esm/browser/terminal/terminal-theme.d.ts +8 -0
- package/esm/browser/terminal/terminal-theme.js +65 -0
- package/esm/browser/terminal/terminal-theme.js.map +1 -0
- package/esm/browser/terminal/web-terminal.d.ts +74 -0
- package/esm/browser/terminal/web-terminal.js +300 -0
- package/esm/browser/terminal/web-terminal.js.map +1 -0
- package/esm/browser/terminal/xterm.d.ts +5 -0
- package/esm/browser/terminal/xterm.js +9 -0
- package/esm/browser/terminal/xterm.js.map +1 -0
- package/esm/browser/util/desktop.d.ts +43 -0
- package/esm/browser/util/desktop.js +55 -0
- package/esm/browser/util/desktop.js.map +1 -0
- package/esm/browser/util/file-drag.d.ts +26 -0
- package/esm/browser/util/file-drag.js +73 -0
- package/esm/browser/util/file-drag.js.map +1 -0
- package/esm/browser/util/terminal-text.d.ts +33 -0
- package/esm/browser/util/terminal-text.js +209 -0
- package/esm/browser/util/terminal-text.js.map +1 -0
- package/esm/browser/util/tool.js +16 -0
- package/esm/browser/util/tool.js.map +1 -1
- package/lib/browser/chat/chat-protocol.d.ts +4 -0
- package/lib/browser/chat/chat-protocol.d.ts.map +1 -1
- package/lib/browser/chat/chat-view.d.ts.map +1 -1
- package/lib/browser/chat/chat-view.js +4 -1
- package/lib/browser/chat/chat-view.js.map +1 -1
- package/lib/browser/chat/chat.d.ts +8 -1
- package/lib/browser/chat/chat.d.ts.map +1 -1
- package/lib/browser/chat/chat.js +34 -15
- package/lib/browser/chat/chat.js.map +1 -1
- package/lib/browser/chat/chat.spec.js +30 -0
- package/lib/browser/chat/chat.spec.js.map +1 -1
- package/lib/browser/chat/draft-model.d.ts +16 -0
- package/lib/browser/chat/draft-model.d.ts.map +1 -0
- package/lib/browser/chat/draft-model.js +90 -0
- package/lib/browser/chat/draft-model.js.map +1 -0
- package/lib/browser/chat/draft-model.spec.d.ts +2 -0
- package/lib/browser/chat/draft-model.spec.d.ts.map +1 -0
- package/lib/browser/chat/draft-model.spec.js +31 -0
- package/lib/browser/chat/draft-model.spec.js.map +1 -0
- package/lib/browser/components/app-view.d.ts.map +1 -1
- package/lib/browser/components/app-view.js +10 -3
- package/lib/browser/components/app-view.js.map +1 -1
- package/lib/browser/components/composer-box.d.ts +6 -0
- package/lib/browser/components/composer-box.d.ts.map +1 -1
- package/lib/browser/components/composer-box.js +51 -14
- package/lib/browser/components/composer-box.js.map +1 -1
- package/lib/browser/components/composer.d.ts.map +1 -1
- package/lib/browser/components/composer.js +1 -1
- package/lib/browser/components/composer.js.map +1 -1
- package/lib/browser/components/dialog-view.d.ts +4 -1
- package/lib/browser/components/dialog-view.d.ts.map +1 -1
- package/lib/browser/components/dialog-view.js +11 -7
- package/lib/browser/components/dialog-view.js.map +1 -1
- package/lib/browser/components/notice-center.d.ts.map +1 -1
- package/lib/browser/components/notice-center.js +3 -1
- package/lib/browser/components/notice-center.js.map +1 -1
- package/lib/browser/components/plus-menu.spec.d.ts +2 -0
- package/lib/browser/components/plus-menu.spec.d.ts.map +1 -0
- package/lib/browser/components/plus-menu.spec.js +18 -0
- package/lib/browser/components/plus-menu.spec.js.map +1 -0
- package/lib/browser/components/session-view.d.ts.map +1 -1
- package/lib/browser/components/session-view.js +48 -4
- package/lib/browser/components/session-view.js.map +1 -1
- package/lib/browser/components/terminal-art.d.ts +8 -0
- package/lib/browser/components/terminal-art.d.ts.map +1 -0
- package/lib/browser/components/terminal-art.js +48 -0
- package/lib/browser/components/terminal-art.js.map +1 -0
- package/lib/browser/components/tool-view.js +1 -1
- package/lib/browser/components/tool-view.js.map +1 -1
- package/lib/browser/components/web-composer.d.ts.map +1 -1
- package/lib/browser/components/web-composer.js +1 -1
- package/lib/browser/components/web-composer.js.map +1 -1
- package/lib/browser/extension/icons.d.ts.map +1 -1
- package/lib/browser/extension/icons.js +3 -0
- package/lib/browser/extension/icons.js.map +1 -1
- package/lib/browser/extension/web-extension-api.d.ts +117 -1
- package/lib/browser/extension/web-extension-api.d.ts.map +1 -1
- package/lib/browser/extension/web-extensions.d.ts +3 -0
- package/lib/browser/extension/web-extensions.d.ts.map +1 -1
- package/lib/browser/extension/web-extensions.js +22 -0
- package/lib/browser/extension/web-extensions.js.map +1 -1
- package/lib/browser/i18n/locales.d.ts +13 -1
- package/lib/browser/i18n/locales.d.ts.map +1 -1
- package/lib/browser/i18n/messages.d.ts +12 -0
- package/lib/browser/i18n/messages.d.ts.map +1 -1
- package/lib/browser/i18n/messages.js +24 -0
- package/lib/browser/i18n/messages.js.map +1 -1
- package/lib/browser/notices/notices-protocol.d.ts +3 -0
- package/lib/browser/notices/notices-protocol.d.ts.map +1 -1
- package/lib/browser/notices/notices.d.ts +3 -0
- package/lib/browser/notices/notices.d.ts.map +1 -1
- package/lib/browser/notices/notices.js +15 -0
- package/lib/browser/notices/notices.js.map +1 -1
- package/lib/browser/notices/notices.spec.js +7 -2
- package/lib/browser/notices/notices.spec.js.map +1 -1
- package/lib/browser/style/conversation.d.ts +1 -1
- package/lib/browser/style/conversation.d.ts.map +1 -1
- package/lib/browser/style/conversation.js +16 -1
- package/lib/browser/style/conversation.js.map +1 -1
- package/lib/browser/style/layout.d.ts +1 -1
- package/lib/browser/style/layout.d.ts.map +1 -1
- package/lib/browser/style/layout.js +5 -0
- package/lib/browser/style/layout.js.map +1 -1
- package/lib/browser/style/shell.d.ts +1 -1
- package/lib/browser/style/shell.d.ts.map +1 -1
- package/lib/browser/style/shell.js +1 -0
- package/lib/browser/style/shell.js.map +1 -1
- package/lib/browser/terminal/canvas-font.d.ts +11 -0
- package/lib/browser/terminal/canvas-font.d.ts.map +1 -0
- package/lib/browser/terminal/canvas-font.js +37 -0
- package/lib/browser/terminal/canvas-font.js.map +1 -0
- package/lib/browser/terminal/canvas-font.spec.d.ts +2 -0
- package/lib/browser/terminal/canvas-font.spec.d.ts.map +1 -0
- package/lib/browser/terminal/canvas-font.spec.js +32 -0
- package/lib/browser/terminal/canvas-font.spec.js.map +1 -0
- package/lib/browser/terminal/terminal-links.d.ts +19 -0
- package/lib/browser/terminal/terminal-links.d.ts.map +1 -0
- package/lib/browser/terminal/terminal-links.js +75 -0
- package/lib/browser/terminal/terminal-links.js.map +1 -0
- package/lib/browser/terminal/terminal-links.spec.d.ts +2 -0
- package/lib/browser/terminal/terminal-links.spec.d.ts.map +1 -0
- package/lib/browser/terminal/terminal-links.spec.js +30 -0
- package/lib/browser/terminal/terminal-links.spec.js.map +1 -0
- package/lib/browser/terminal/terminal-theme.d.ts +9 -0
- package/lib/browser/terminal/terminal-theme.d.ts.map +1 -0
- package/lib/browser/terminal/terminal-theme.js +68 -0
- package/lib/browser/terminal/terminal-theme.js.map +1 -0
- package/lib/browser/terminal/terminal-theme.spec.d.ts +2 -0
- package/lib/browser/terminal/terminal-theme.spec.d.ts.map +1 -0
- package/lib/browser/terminal/terminal-theme.spec.js +24 -0
- package/lib/browser/terminal/terminal-theme.spec.js.map +1 -0
- package/lib/browser/terminal/web-terminal.d.ts +75 -0
- package/lib/browser/terminal/web-terminal.d.ts.map +1 -0
- package/lib/browser/terminal/web-terminal.js +304 -0
- package/lib/browser/terminal/web-terminal.js.map +1 -0
- package/lib/browser/terminal/xterm.d.ts +6 -0
- package/lib/browser/terminal/xterm.d.ts.map +1 -0
- package/lib/browser/terminal/xterm.js +16 -0
- package/lib/browser/terminal/xterm.js.map +1 -0
- package/lib/browser/util/desktop.d.ts +44 -0
- package/lib/browser/util/desktop.d.ts.map +1 -0
- package/lib/browser/util/desktop.js +58 -0
- package/lib/browser/util/desktop.js.map +1 -0
- package/lib/browser/util/desktop.spec.d.ts +2 -0
- package/lib/browser/util/desktop.spec.d.ts.map +1 -0
- package/lib/browser/util/desktop.spec.js +67 -0
- package/lib/browser/util/desktop.spec.js.map +1 -0
- package/lib/browser/util/file-drag.d.ts +27 -0
- package/lib/browser/util/file-drag.d.ts.map +1 -0
- package/lib/browser/util/file-drag.js +76 -0
- package/lib/browser/util/file-drag.js.map +1 -0
- package/lib/browser/util/file-drag.spec.d.ts +2 -0
- package/lib/browser/util/file-drag.spec.d.ts.map +1 -0
- package/lib/browser/util/file-drag.spec.js +46 -0
- package/lib/browser/util/file-drag.spec.js.map +1 -0
- package/lib/browser/util/terminal-text.d.ts +34 -0
- package/lib/browser/util/terminal-text.d.ts.map +1 -0
- package/lib/browser/util/terminal-text.js +212 -0
- package/lib/browser/util/terminal-text.js.map +1 -0
- package/lib/browser/util/terminal-text.spec.d.ts +2 -0
- package/lib/browser/util/terminal-text.spec.d.ts.map +1 -0
- package/lib/browser/util/terminal-text.spec.js +54 -0
- package/lib/browser/util/terminal-text.spec.js.map +1 -0
- package/lib/browser/util/tool.d.ts.map +1 -1
- package/lib/browser/util/tool.js +16 -0
- package/lib/browser/util/tool.js.map +1 -1
- package/lib/browser/util/tool.spec.js +12 -0
- package/lib/browser/util/tool.spec.js.map +1 -1
- package/package.json +11 -7
- package/src/browser/chat/chat-protocol.ts +4 -0
- package/src/browser/chat/chat-view.tsx +4 -0
- package/src/browser/chat/chat.spec.ts +31 -0
- package/src/browser/chat/chat.ts +36 -14
- package/src/browser/chat/draft-model.spec.tsx +31 -0
- package/src/browser/chat/draft-model.tsx +63 -0
- package/src/browser/components/app-view.tsx +10 -3
- package/src/browser/components/composer-box.tsx +52 -6
- package/src/browser/components/composer.tsx +1 -0
- package/src/browser/components/dialog-view.tsx +16 -7
- package/src/browser/components/notice-center.tsx +7 -1
- package/src/browser/components/plus-menu.spec.ts +16 -0
- package/src/browser/components/session-view.tsx +65 -4
- package/src/browser/components/terminal-art.tsx +21 -0
- package/src/browser/components/tool-view.tsx +1 -1
- package/src/browser/components/web-composer.tsx +1 -0
- package/src/browser/extension/icons.ts +6 -0
- package/src/browser/extension/web-extension-api.ts +131 -1
- package/src/browser/extension/web-extensions.ts +36 -1
- package/src/browser/i18n/messages.ts +24 -0
- package/src/browser/notices/notices-protocol.ts +3 -0
- package/src/browser/notices/notices.spec.ts +8 -2
- package/src/browser/notices/notices.ts +17 -1
- package/src/browser/style/conversation.ts +16 -1
- package/src/browser/style/layout.ts +5 -0
- package/src/browser/style/shell.ts +1 -0
- package/src/browser/terminal/canvas-font.spec.ts +29 -0
- package/src/browser/terminal/canvas-font.ts +32 -0
- package/src/browser/terminal/terminal-links.spec.ts +32 -0
- package/src/browser/terminal/terminal-links.ts +75 -0
- package/src/browser/terminal/terminal-theme.spec.ts +23 -0
- package/src/browser/terminal/terminal-theme.ts +66 -0
- package/src/browser/terminal/web-terminal.ts +330 -0
- package/src/browser/terminal/xterm.ts +10 -0
- package/src/browser/util/desktop.spec.ts +68 -0
- package/src/browser/util/desktop.ts +96 -0
- package/src/browser/util/file-drag.spec.ts +48 -0
- package/src/browser/util/file-drag.ts +78 -0
- package/src/browser/util/terminal-text.spec.ts +60 -0
- package/src/browser/util/terminal-text.ts +223 -0
- package/src/browser/util/tool.spec.ts +12 -0
- package/src/browser/util/tool.ts +17 -0
|
@@ -4,6 +4,7 @@ import * as React from 'react';
|
|
|
4
4
|
import { useNotices, useOpenLink, useSessionState } from '../hooks';
|
|
5
5
|
import { useT } from '../i18n/use-t';
|
|
6
6
|
import { MessageUtil } from '../util/message';
|
|
7
|
+
import { TerminalText } from '../util/terminal-text';
|
|
7
8
|
import { Composer } from './composer';
|
|
8
9
|
import type { ComposerBoxHandle } from './composer-box';
|
|
9
10
|
import { useConversationScroll } from './conversation-scroll';
|
|
@@ -14,11 +15,17 @@ import { useMessageActions } from './message-actions';
|
|
|
14
15
|
import { MessageActions } from './message-footer';
|
|
15
16
|
import { ConversationSession, MessageList } from './message-list';
|
|
16
17
|
import { useSessionSettings } from './session-settings';
|
|
18
|
+
import { TerminalArt } from './terminal-art';
|
|
17
19
|
|
|
18
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* What the extensions of the session say with `notify` goes to the notices of the page; what was said before it
|
|
22
|
+
* opened stays quiet. A picture in it (a QR code to sign in) is drawn, and stays until the next notice of the
|
|
23
|
+
* session, which says how it went or brings a new code.
|
|
24
|
+
*/
|
|
19
25
|
function useSessionNotices(notices: Notice[]) {
|
|
20
26
|
const [, center] = useNotices();
|
|
21
27
|
const seen = React.useRef<Set<string> | undefined>(undefined);
|
|
28
|
+
const picture = React.useRef<number>(undefined);
|
|
22
29
|
React.useEffect(() => {
|
|
23
30
|
if (!seen.current) {
|
|
24
31
|
seen.current = new Set(notices.map(notice => notice.id));
|
|
@@ -27,12 +34,46 @@ function useSessionNotices(notices: Notice[]) {
|
|
|
27
34
|
for (const notice of notices) {
|
|
28
35
|
if (!seen.current.has(notice.id)) {
|
|
29
36
|
seen.current.add(notice.id);
|
|
30
|
-
|
|
37
|
+
if (picture.current !== undefined) {
|
|
38
|
+
center.dismiss(picture.current);
|
|
39
|
+
picture.current = undefined;
|
|
40
|
+
}
|
|
41
|
+
const { text, art } = TerminalText.art(notice.message);
|
|
42
|
+
const id = center.notify(text, { level: notice.type, art, sticky: art !== undefined });
|
|
43
|
+
picture.current = art ? id : undefined;
|
|
31
44
|
}
|
|
32
45
|
}
|
|
33
46
|
}, [notices, center]);
|
|
34
47
|
}
|
|
35
48
|
|
|
49
|
+
/** What an extension shows above or below the message box (`setWidget`): its lines as a terminal draws them, a picture in them drawn. */
|
|
50
|
+
function WidgetView({ lines }: { lines: string[] }) {
|
|
51
|
+
const t = useT();
|
|
52
|
+
const { text, art } = React.useMemo(() => TerminalText.art(lines.join('\n')), [lines]);
|
|
53
|
+
return (
|
|
54
|
+
<div className="ta-widget">
|
|
55
|
+
{text && <pre className="ta-widget-text">{text}</pre>}
|
|
56
|
+
{art && <TerminalArt art={art} className="ta-widget-art" label={t('ta.notice.picture')} />}
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The status of an extension, as a chip: its label, then each part, with a dot for the sign of its state. */
|
|
62
|
+
function StatusChip({ text }: { text: string }) {
|
|
63
|
+
const { label, segments } = React.useMemo(() => TerminalText.status(text), [text]);
|
|
64
|
+
return (
|
|
65
|
+
<span className="ta-statusline-item" title={text}>
|
|
66
|
+
{label && <span className="ta-statusline-label">{label}</span>}
|
|
67
|
+
{segments.map((segment, index) => (
|
|
68
|
+
<span key={index} className={`ta-statusline-part${segment.tone ? ` ta-tone-${segment.tone}` : ''}`}>
|
|
69
|
+
{segment.tone && <span className={`ta-status ta-status-${segment.tone}`} />}
|
|
70
|
+
{segment.text}
|
|
71
|
+
</span>
|
|
72
|
+
))}
|
|
73
|
+
</span>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
36
77
|
export interface SessionViewProps {
|
|
37
78
|
store: SessionStore;
|
|
38
79
|
server: TaServer;
|
|
@@ -80,6 +121,15 @@ export function SessionView({
|
|
|
80
121
|
const composer = React.useRef<ComposerBoxHandle>(null);
|
|
81
122
|
const cwd = info?.cwd;
|
|
82
123
|
const messageActions = useMessageActions(store, server, state.working, cwd, composer, onActivity);
|
|
124
|
+
// An extension puts text in the message box, as in pi's terminal.
|
|
125
|
+
const editorText = state.editorText;
|
|
126
|
+
const takenText = React.useRef(editorText?.id);
|
|
127
|
+
React.useEffect(() => {
|
|
128
|
+
if (editorText && editorText.id !== takenText.current) {
|
|
129
|
+
takenText.current = editorText.id;
|
|
130
|
+
composer.current?.setText(TerminalText.plain(editorText.text));
|
|
131
|
+
}
|
|
132
|
+
}, [editorText]);
|
|
83
133
|
React.useEffect(() => {
|
|
84
134
|
if (draftText) {
|
|
85
135
|
composer.current?.setText(draftText);
|
|
@@ -97,7 +147,10 @@ export function SessionView({
|
|
|
97
147
|
}
|
|
98
148
|
}, [state.closed, onClosed]);
|
|
99
149
|
|
|
100
|
-
const statuses = Object.
|
|
150
|
+
const statuses = Object.entries(state.status).filter(([, text]) => TerminalText.plain(text).trim());
|
|
151
|
+
const widgets = Object.entries(state.widgets);
|
|
152
|
+
const widgetsAt = (placement: 'aboveEditor' | 'belowEditor') =>
|
|
153
|
+
widgets.filter(([, widget]) => widget.placement === placement).map(([key, widget]) => <WidgetView key={key} lines={widget.lines} />);
|
|
101
154
|
const dialog = state.pendingUi[0];
|
|
102
155
|
const title = info?.sessionName || (info ? MessageUtil.basename(info.cwd) : t('ta.session.title'));
|
|
103
156
|
|
|
@@ -134,6 +187,7 @@ export function SessionView({
|
|
|
134
187
|
<div className="ta-bottom">
|
|
135
188
|
{dialog && <DialogView key={dialog.id} request={dialog} respond={response => store.respondUi(response).catch(fail)} />}
|
|
136
189
|
{aboveComposer}
|
|
190
|
+
{widgetsAt('aboveEditor')}
|
|
137
191
|
<Composer
|
|
138
192
|
ref={composer}
|
|
139
193
|
store={store}
|
|
@@ -146,7 +200,14 @@ export function SessionView({
|
|
|
146
200
|
onError={fail}
|
|
147
201
|
/>
|
|
148
202
|
{belowComposer}
|
|
149
|
-
{
|
|
203
|
+
{widgetsAt('belowEditor')}
|
|
204
|
+
{statuses.length > 0 && (
|
|
205
|
+
<div className="ta-statusline">
|
|
206
|
+
{statuses.map(([key, text]) => (
|
|
207
|
+
<StatusChip key={key} text={text} />
|
|
208
|
+
))}
|
|
209
|
+
</div>
|
|
210
|
+
)}
|
|
150
211
|
</div>
|
|
151
212
|
</section>
|
|
152
213
|
);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { TerminalText } from '../util/terminal-text';
|
|
3
|
+
|
|
4
|
+
/** A picture of cells, as a QR code to scan: black on white whatever the theme, with a margin of white. */
|
|
5
|
+
export function TerminalArt({ art, className, label }: { art: boolean[][]; className: string; label: string }) {
|
|
6
|
+
const path = React.useMemo(() => TerminalText.path(art), [art]);
|
|
7
|
+
const width = Math.max(...art.map(row => row.length));
|
|
8
|
+
const margin = 2;
|
|
9
|
+
return (
|
|
10
|
+
<svg
|
|
11
|
+
className={className}
|
|
12
|
+
viewBox={`${-margin} ${-margin} ${width + margin * 2} ${art.length + margin * 2}`}
|
|
13
|
+
role="img"
|
|
14
|
+
aria-label={label}
|
|
15
|
+
shapeRendering="crispEdges"
|
|
16
|
+
>
|
|
17
|
+
<rect x={-margin} y={-margin} width={width + margin * 2} height={art.length + margin * 2} fill="#fff" />
|
|
18
|
+
<path d={path} fill="#111" />
|
|
19
|
+
</svg>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -63,7 +63,7 @@ function ToolRow({ name, args, status, trailing, details }: ToolRowProps) {
|
|
|
63
63
|
const line = (
|
|
64
64
|
<>
|
|
65
65
|
<StatusDot status={status} />
|
|
66
|
-
<span className="ta-tool-verb">{summary.verb ? t(summary.verb) : name}</span>
|
|
66
|
+
<span className="ta-tool-verb">{summary.verb ? t(summary.verb) : summary.name}</span>
|
|
67
67
|
{summary.target && <span className={`ta-tool-target ${summary.code ? 'ta-mono' : ''}`}>{summary.target}</span>}
|
|
68
68
|
{details ? <Icon icon={ChevronRight} size={13} className="ta-tool-chevron" /> : <span className="ta-tool-chevron" />}
|
|
69
69
|
</>
|
|
@@ -55,6 +55,7 @@ export function WebComposer({
|
|
|
55
55
|
mentionLabel={mentions ? mentionLabel : undefined}
|
|
56
56
|
actions={actions}
|
|
57
57
|
beside={onPickCwd && listDirectory && <DirectoryButton cwd={cwd || serverCwd || ''} list={listDirectory} remote={remote} onPick={onPickCwd} />}
|
|
58
|
+
cwd={cwd || serverCwd}
|
|
58
59
|
onError={cause => notices.error(cause)}
|
|
59
60
|
/>
|
|
60
61
|
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AlarmClock,
|
|
2
3
|
ArrowDown,
|
|
3
4
|
ArrowLeft,
|
|
4
5
|
ArrowRight,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
ChevronRight,
|
|
13
14
|
ChevronsDownUp,
|
|
14
15
|
ChevronsUpDown,
|
|
16
|
+
CircleAlert,
|
|
15
17
|
Clipboard,
|
|
16
18
|
Code,
|
|
17
19
|
Columns2,
|
|
@@ -51,6 +53,7 @@ import {
|
|
|
51
53
|
Pencil,
|
|
52
54
|
Pin,
|
|
53
55
|
PinOff,
|
|
56
|
+
Play,
|
|
54
57
|
Plus,
|
|
55
58
|
Puzzle,
|
|
56
59
|
RefreshCw,
|
|
@@ -83,6 +86,7 @@ import type { WebIcon } from './web-extension-api';
|
|
|
83
86
|
|
|
84
87
|
/** The icons a web extension gets as `pi.ui.icons`; names are lucide's. Only add: removing one breaks extensions. */
|
|
85
88
|
export const WEB_ICONS: Readonly<Record<string, WebIcon>> = Object.freeze({
|
|
89
|
+
AlarmClock,
|
|
86
90
|
ArrowDown,
|
|
87
91
|
ArrowLeft,
|
|
88
92
|
ArrowRight,
|
|
@@ -96,6 +100,7 @@ export const WEB_ICONS: Readonly<Record<string, WebIcon>> = Object.freeze({
|
|
|
96
100
|
ChevronRight,
|
|
97
101
|
ChevronsDownUp,
|
|
98
102
|
ChevronsUpDown,
|
|
103
|
+
CircleAlert,
|
|
99
104
|
Clipboard,
|
|
100
105
|
Code,
|
|
101
106
|
Columns2,
|
|
@@ -134,6 +139,7 @@ export const WEB_ICONS: Readonly<Record<string, WebIcon>> = Object.freeze({
|
|
|
134
139
|
PackageOpen,
|
|
135
140
|
Pencil,
|
|
136
141
|
Pin,
|
|
142
|
+
Play,
|
|
137
143
|
PinOff,
|
|
138
144
|
Plus,
|
|
139
145
|
Puzzle,
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
TaEvent,
|
|
3
|
+
TaPackageServer,
|
|
4
|
+
TaRemoteServer,
|
|
5
|
+
TaServer,
|
|
6
|
+
TaSessionInfo,
|
|
7
|
+
TaSettingsServer,
|
|
8
|
+
TaTerminalServer,
|
|
9
|
+
TaWorkspaceServer,
|
|
10
|
+
} from '@celljs/ta-protocol';
|
|
2
11
|
import type * as React from 'react';
|
|
3
12
|
import type { Mood } from '../avatar/dino-avatar';
|
|
4
13
|
import type { Language, Theme } from '../settings/settings-protocol';
|
|
@@ -21,6 +30,8 @@ export interface WebExtensionAPI {
|
|
|
21
30
|
readonly packages: TaPackageServer;
|
|
22
31
|
/** The other machines of `ssh://` directories (TaLocation): their ports, for a page served there. */
|
|
23
32
|
readonly remote: TaRemoteServer;
|
|
33
|
+
/** pi's settings of ta (`settings.json`): `getSettings(cwd)` also lists the models with credentials, for a model picker. */
|
|
34
|
+
readonly settings: TaSettingsServer;
|
|
24
35
|
readonly ui: WebExtensionUI;
|
|
25
36
|
/** A view of its own: a page or a sidebar view, in the navigation on the left, or a tool of the workbench on the right. */
|
|
26
37
|
registerView(name: string, view: WebView): void;
|
|
@@ -129,6 +140,31 @@ export interface WebExtensionUI {
|
|
|
129
140
|
editor(title: string, prefill?: string): Promise<string | undefined>;
|
|
130
141
|
readonly theme: Theme;
|
|
131
142
|
readonly language: Language;
|
|
143
|
+
/**
|
|
144
|
+
* Whether a view can show a site in a `<webview>` element, as in Ta Desktop: a page of its own rather
|
|
145
|
+
* than a frame, which sites cannot refuse, where signing in works and stays, with the history, title and
|
|
146
|
+
* icon of the page as events and methods of the element (Electron's). Elsewhere, an iframe.
|
|
147
|
+
*/
|
|
148
|
+
readonly supportsWebview: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Ta Desktop's way to the file manager of the system (Finder, File Explorer, that of Linux), for paths of
|
|
151
|
+
* this machine; undefined in a browser and the mobile app. A directory of `ssh://` is not of this machine.
|
|
152
|
+
*/
|
|
153
|
+
readonly desktop?: WebDesktop;
|
|
154
|
+
/**
|
|
155
|
+
* Files dragged between views, as the explorer drags them: `set` them in `dragstart`. Dropped on the
|
|
156
|
+
* message box of a conversation they become `@` references (relative to its directory when they are in
|
|
157
|
+
* it); a terminal takes their `paths`; anywhere else they are their paths as text.
|
|
158
|
+
*/
|
|
159
|
+
readonly fileDrag: WebFileDrag;
|
|
160
|
+
/**
|
|
161
|
+
* Ta Desktop: sends a command of the Chrome DevTools Protocol to the page of a `<webview>` of this page
|
|
162
|
+
* (its `getWebContentsId()`), as that page's debugger, and resolves with its result; `undefined`
|
|
163
|
+
* elsewhere. Only commands on the page itself: Accessibility, CSS, DOM, DOMSnapshot, Emulation, Input,
|
|
164
|
+
* Log, Overlay, Page and Runtime, without those that touch files, downloads or navigate (use the
|
|
165
|
+
* element's `loadURL`); the app refuses the rest.
|
|
166
|
+
*/
|
|
167
|
+
readonly debugWebview?: (webContentsId: number, method: string, params?: Record<string, unknown>) => Promise<unknown>;
|
|
132
168
|
/**
|
|
133
169
|
* A tab in the editor area beside the conversation (a file, a diff, a browser); an open id is shown
|
|
134
170
|
* again. With `preserveFocus`, it opens behind the tab shown, and the workbench is not revealed: for
|
|
@@ -166,6 +202,11 @@ export interface WebExtensionUI {
|
|
|
166
202
|
readonly CodeEditor: React.ComponentType<CodeEditorProps>;
|
|
167
203
|
/** Monaco's diff editor on two texts of a file, side by side or inline; the right side can be edited. */
|
|
168
204
|
readonly DiffEditor: React.ComponentType<DiffEditorProps>;
|
|
205
|
+
/**
|
|
206
|
+
* A terminal as VS Code's: xterm.js drawn on the GPU, in the theme of the page. It loads the first
|
|
207
|
+
* time one is made; what it shows stays while no view shows it (`attach` it to the next one).
|
|
208
|
+
*/
|
|
209
|
+
createTerminal(options?: WebTerminalOptions): WebTerminal;
|
|
169
210
|
/** The message box of the conversations of the page, with its + menu: one look for every conversation. */
|
|
170
211
|
readonly Composer: React.ComponentType<WebComposerProps>;
|
|
171
212
|
/** Markdown as the conversations show it. */
|
|
@@ -176,6 +217,95 @@ export interface WebExtensionUI {
|
|
|
176
217
|
readonly FolderPicker: React.ComponentType<WebFolderPickerProps>;
|
|
177
218
|
}
|
|
178
219
|
|
|
220
|
+
export interface WebDesktop {
|
|
221
|
+
/** Node's `process.platform` of the machine (`darwin`, `win32`, `linux`): what its file manager is called. */
|
|
222
|
+
readonly platform: string;
|
|
223
|
+
/** Opens the folder of an absolute path of this machine in the file manager, the item selected. */
|
|
224
|
+
showItemInFolder(path: string): Promise<void>;
|
|
225
|
+
/** The sign-ins of the user's browsers taken into the browser of Ta; macOS and Windows only. */
|
|
226
|
+
readonly browserData?: WebBrowserData;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** A profile of the user's Chrome, Edge, Brave, Arc, Vivaldi or Chromium. */
|
|
230
|
+
export interface WebBrowserProfile {
|
|
231
|
+
/** What `import` takes. */
|
|
232
|
+
id: string;
|
|
233
|
+
/** The name of the browser, `Google Chrome`. */
|
|
234
|
+
browser: string;
|
|
235
|
+
/** The name of the profile, as the browser shows it. */
|
|
236
|
+
name: string;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** The cookies an import took into the browser of Ta. */
|
|
240
|
+
export interface WebBrowserImport {
|
|
241
|
+
imported: number;
|
|
242
|
+
/** Those that could not be read or set. */
|
|
243
|
+
skipped: number;
|
|
244
|
+
/** Windows: those under Chrome's app-bound encryption, which only Chrome can read: sign in again. */
|
|
245
|
+
appBound: number;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Ta Desktop's browser of the workbench keeps its own sign-ins. These take those of the user's browser:
|
|
250
|
+
* its cookies, never its passwords or history, once the user said yes in a dialog of the app (on macOS,
|
|
251
|
+
* the Keychain asks too).
|
|
252
|
+
*/
|
|
253
|
+
export interface WebBrowserData {
|
|
254
|
+
/** The profiles with cookies of the browsers installed. */
|
|
255
|
+
profiles(): Promise<WebBrowserProfile[]>;
|
|
256
|
+
/** Takes the cookies of a profile; `undefined` when the user said no. */
|
|
257
|
+
import(profileId: string): Promise<WebBrowserImport | undefined>;
|
|
258
|
+
/** Forgets the cookies, storage and cache of the browser of Ta. */
|
|
259
|
+
clear(): Promise<void>;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Files of a directory on a drag. */
|
|
263
|
+
export interface WebDraggedFiles {
|
|
264
|
+
/** The directory they are of (`ui.useCwd()` or another): a path of the machine of ta serve, or `ssh://`. */
|
|
265
|
+
cwd: string;
|
|
266
|
+
/** Relative to `cwd`, with `/`. */
|
|
267
|
+
paths: string[];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export interface WebFileDrag {
|
|
271
|
+
set(dataTransfer: DataTransfer, files: WebDraggedFiles): void;
|
|
272
|
+
/** Whether a drag holds files: what `dragover` can tell, before the drop. */
|
|
273
|
+
has(dataTransfer: DataTransfer): boolean;
|
|
274
|
+
/** The files of a drop; undefined when it holds none. */
|
|
275
|
+
get(dataTransfer: DataTransfer): WebDraggedFiles | undefined;
|
|
276
|
+
/** Their absolute paths inside their machine. */
|
|
277
|
+
paths(files: WebDraggedFiles): string[];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export interface WebTerminalOptions {
|
|
281
|
+
/** Its size until a view fits it: the size the program started with. Default: 80 × 24. */
|
|
282
|
+
cols?: number;
|
|
283
|
+
rows?: number;
|
|
284
|
+
/**
|
|
285
|
+
* The directory of the program: paths of its output (`src/a.ts:10`) are read from there, and from the
|
|
286
|
+
* one a shell reports (OSC 7) once it does. URLs and paths are links, opened with Cmd or Ctrl + click
|
|
287
|
+
* as the links of a reply are (with Shift, a page in the browser of the system).
|
|
288
|
+
*/
|
|
289
|
+
cwd?: string;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** A terminal screen; the program behind it is the extension's (`pi.terminals` for a shell of the server). */
|
|
293
|
+
export interface WebTerminal {
|
|
294
|
+
/** Its size in characters. */
|
|
295
|
+
readonly cols: number;
|
|
296
|
+
readonly rows: number;
|
|
297
|
+
/** Output of the program, with its control sequences. */
|
|
298
|
+
write(data: string): void;
|
|
299
|
+
/** What the user types or pastes, and the mouse reports the program asked for, as a terminal sends them. */
|
|
300
|
+
onData(listener: (data: string) => void): () => void;
|
|
301
|
+
/** It fitted a view of another size: the program should know it (`resizeTerminal`). */
|
|
302
|
+
onResize(listener: (size: { cols: number; rows: number }) => void): () => void;
|
|
303
|
+
/** Shows it in `element`, which it fills and follows in size; the function returned takes it out again. */
|
|
304
|
+
attach(element: HTMLElement): () => void;
|
|
305
|
+
focus(): void;
|
|
306
|
+
dispose(): void;
|
|
307
|
+
}
|
|
308
|
+
|
|
179
309
|
export interface WebFolderPickerProps {
|
|
180
310
|
/** Where it opens; default: the directory of ta serve. */
|
|
181
311
|
initial?: string;
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { Autowired, Component, Optional, PostConstruct } from '@celljs/core';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
TaEvent,
|
|
4
|
+
TaPackageServer,
|
|
5
|
+
TaRemoteServer,
|
|
6
|
+
TaServer,
|
|
7
|
+
TaSettingsServer,
|
|
8
|
+
TaTerminalServer,
|
|
9
|
+
TaWebExtensionInfo,
|
|
10
|
+
TaWorkspaceServer,
|
|
11
|
+
} from '@celljs/ta-protocol';
|
|
3
12
|
import * as React from 'react';
|
|
4
13
|
import { CodeEditor } from '../components/code-editor';
|
|
5
14
|
import { DiffEditor } from '../components/diff-editor';
|
|
@@ -9,9 +18,13 @@ import { TaAvatar } from '../components/ta-avatar';
|
|
|
9
18
|
import { WebComposer } from '../components/web-composer';
|
|
10
19
|
import { WebDialog } from '../components/web-dialog';
|
|
11
20
|
import { message as messageOf } from '../i18n/messages';
|
|
21
|
+
import { TaLinks } from '../links/links-protocol';
|
|
12
22
|
import { TaNotices } from '../notices/notices-protocol';
|
|
13
23
|
import { TaSettings } from '../settings/settings-protocol';
|
|
14
24
|
import { type StateSpec, StateUtil, TaState } from '../state/state-protocol';
|
|
25
|
+
import { DefaultWebTerminal } from '../terminal/web-terminal';
|
|
26
|
+
import { DesktopUtil } from '../util/desktop';
|
|
27
|
+
import { FileDragUtil } from '../util/file-drag';
|
|
15
28
|
import { TaWorkbench } from '../workbench-protocol';
|
|
16
29
|
import { WEB_ICONS } from './icons';
|
|
17
30
|
import type {
|
|
@@ -21,9 +34,12 @@ import type {
|
|
|
21
34
|
WebEditor,
|
|
22
35
|
WebExtensionAPI,
|
|
23
36
|
WebExtensionFactory,
|
|
37
|
+
WebFileDrag,
|
|
24
38
|
WebFolderPickerProps,
|
|
25
39
|
WebOpener,
|
|
26
40
|
WebStorage,
|
|
41
|
+
WebTerminal,
|
|
42
|
+
WebTerminalOptions,
|
|
27
43
|
WebTool,
|
|
28
44
|
WebView,
|
|
29
45
|
WidgetOptions,
|
|
@@ -41,6 +57,8 @@ import {
|
|
|
41
57
|
|
|
42
58
|
const DEFAULT_ORDER = 100;
|
|
43
59
|
|
|
60
|
+
const FILE_DRAG: WebFileDrag = Object.freeze({ set: FileDragUtil.set, has: FileDragUtil.has, get: FileDragUtil.get, paths: FileDragUtil.paths });
|
|
61
|
+
|
|
44
62
|
/** One web extension that runs, and what it registered. */
|
|
45
63
|
interface Running {
|
|
46
64
|
loaded: LoadedWebExtension;
|
|
@@ -125,6 +143,10 @@ export class TaWebExtensionsImpl implements TaWebExtensions {
|
|
|
125
143
|
@Autowired(TaSettings)
|
|
126
144
|
protected readonly settings: TaSettings;
|
|
127
145
|
|
|
146
|
+
/** Detached: TaLinks asks the openers of the extensions. */
|
|
147
|
+
@Autowired({ id: TaLinks, detached: true })
|
|
148
|
+
protected readonly links: TaLinks;
|
|
149
|
+
|
|
128
150
|
@Autowired(TaNotices)
|
|
129
151
|
protected readonly notices: TaNotices;
|
|
130
152
|
@Autowired(TaState)
|
|
@@ -518,6 +540,9 @@ export class TaWebExtensionsImpl implements TaWebExtensions {
|
|
|
518
540
|
get remote(): TaRemoteServer {
|
|
519
541
|
return workbench.remote;
|
|
520
542
|
},
|
|
543
|
+
get settings(): TaSettingsServer {
|
|
544
|
+
return workbench.settings;
|
|
545
|
+
},
|
|
521
546
|
ui: {
|
|
522
547
|
setWidget: (key, component, options = {}) =>
|
|
523
548
|
register(() => (component ? run.widgets.set(key, { component, options }) : run.widgets.delete(key))),
|
|
@@ -536,6 +561,10 @@ export class TaWebExtensionsImpl implements TaWebExtensions {
|
|
|
536
561
|
get language() {
|
|
537
562
|
return settings.state.language;
|
|
538
563
|
},
|
|
564
|
+
supportsWebview: DesktopUtil.has('webview'),
|
|
565
|
+
desktop: DesktopUtil.fileManager(),
|
|
566
|
+
fileDrag: FILE_DRAG,
|
|
567
|
+
debugWebview: DesktopUtil.debugWebview(),
|
|
539
568
|
openEditor: (editor, options) => this.openEditor(run, editor, options?.preserveFocus === true, options?.preview === true),
|
|
540
569
|
closeEditor: id => this.removeEditors(editor => editor.id === id && editor.extension === run.loaded.id),
|
|
541
570
|
pinEditor: id => {
|
|
@@ -574,6 +603,12 @@ export class TaWebExtensionsImpl implements TaWebExtensions {
|
|
|
574
603
|
icons: WEB_ICONS,
|
|
575
604
|
CodeEditor: this.codeEditor,
|
|
576
605
|
DiffEditor: this.diffEditor,
|
|
606
|
+
// Ends with the extension.
|
|
607
|
+
createTerminal: (options?: WebTerminalOptions): WebTerminal => {
|
|
608
|
+
const terminal = new DefaultWebTerminal(settings, (target, open) => void this.links.open(target, open), options);
|
|
609
|
+
run.unsubscribes.push(() => terminal.dispose());
|
|
610
|
+
return terminal;
|
|
611
|
+
},
|
|
577
612
|
Composer: this.composer,
|
|
578
613
|
Markdown,
|
|
579
614
|
Dialog: WebDialog,
|
|
@@ -28,6 +28,10 @@ export const EN_MESSAGES = {
|
|
|
28
28
|
'ta.link.trustHint': 'Pages of this host open without asking from now on; the list is in Settings.',
|
|
29
29
|
'ta.link.copied': 'Link copied',
|
|
30
30
|
'ta.link.notFound': '{path} is not there.',
|
|
31
|
+
'ta.terminal.openLink': 'Ctrl + click to open',
|
|
32
|
+
'ta.terminal.openLinkMac': 'Cmd + click to open',
|
|
33
|
+
'ta.terminal.openUrl': 'Ctrl + click to open, with Shift in your browser',
|
|
34
|
+
'ta.terminal.openUrlMac': 'Cmd + click to open, with Shift in your browser',
|
|
31
35
|
'ta.link.noOpener': 'Nothing opens files: turn on the package @celljs/ta-workspace in Settings.',
|
|
32
36
|
'ta.settings.links': 'Links',
|
|
33
37
|
'ta.settings.trustedDomains': 'Trusted domains',
|
|
@@ -53,8 +57,14 @@ export const EN_MESSAGES = {
|
|
|
53
57
|
'ta.connection.retryNow': 'Retry now',
|
|
54
58
|
'ta.connection.back': 'Connected again',
|
|
55
59
|
'ta.connection.notDone': 'Not done: the Ta service is not connected',
|
|
60
|
+
'ta.error.sessionNotFound': 'This conversation is no longer open; open it again from the list',
|
|
61
|
+
'ta.error.sessionLocked': 'This conversation is open in another Ta; close it there first',
|
|
62
|
+
'ta.error.tooManySessions': 'Too many conversations are open; close one and try again',
|
|
63
|
+
'ta.error.messageConflict': 'The message was not sent: it clashed with another; send it again',
|
|
64
|
+
'ta.error.modelNotFound': 'The model is not available; pick another or sign in to its service',
|
|
56
65
|
'ta.connection.notDoneHint': 'Try again once the connection is back.',
|
|
57
66
|
'ta.notices': 'Notifications',
|
|
67
|
+
'ta.notice.picture': 'Picture of the extension, such as a QR code to scan',
|
|
58
68
|
'ta.packages.removeDetail': 'Its pages, cards and commands go with it.',
|
|
59
69
|
'ta.packages.removed': 'Removed {name}',
|
|
60
70
|
'ta.packages.reloaded': 'Extensions loaded again',
|
|
@@ -310,6 +320,8 @@ export const EN_MESSAGES = {
|
|
|
310
320
|
'ta.composer.image.size': '{name} is larger than 5 MB.',
|
|
311
321
|
'ta.composer.image.count': 'At most 8 images per message.',
|
|
312
322
|
'ta.composer.filesUnavailable': 'File search is not available on this server yet',
|
|
323
|
+
'ta.composer.filter': 'Search',
|
|
324
|
+
'ta.composer.noMatch': 'Nothing matches',
|
|
313
325
|
'ta.composer.noFiles': 'No matching file',
|
|
314
326
|
'ta.composer.queue.steering': 'Steer',
|
|
315
327
|
'ta.composer.queue.followUp': 'Queued',
|
|
@@ -490,6 +502,10 @@ export const ZH_MESSAGES: Record<MessageId, string> = {
|
|
|
490
502
|
'ta.link.trustHint': '以后这个域名的网页不再询问,直接打开;可在设置中管理。',
|
|
491
503
|
'ta.link.copied': '链接已复制',
|
|
492
504
|
'ta.link.notFound': '{path} 不存在。',
|
|
505
|
+
'ta.terminal.openLink': 'Ctrl + 点击打开',
|
|
506
|
+
'ta.terminal.openLinkMac': 'Cmd + 点击打开',
|
|
507
|
+
'ta.terminal.openUrl': 'Ctrl + 点击打开,加按 Shift 在系统浏览器中打开',
|
|
508
|
+
'ta.terminal.openUrlMac': 'Cmd + 点击打开,加按 Shift 在系统浏览器中打开',
|
|
493
509
|
'ta.link.noOpener': '没有能打开文件的包:请在设置中开启 @celljs/ta-workspace。',
|
|
494
510
|
'ta.settings.links': '链接',
|
|
495
511
|
'ta.settings.trustedDomains': '受信任的域名',
|
|
@@ -514,8 +530,14 @@ export const ZH_MESSAGES: Record<MessageId, string> = {
|
|
|
514
530
|
'ta.connection.retryNow': '立即重试',
|
|
515
531
|
'ta.connection.back': '已重新连接',
|
|
516
532
|
'ta.connection.notDone': '没有完成:未连接到 Ta 服务',
|
|
533
|
+
'ta.error.sessionNotFound': '这个对话已关闭,请从列表里重新打开',
|
|
534
|
+
'ta.error.sessionLocked': '这个对话在另一个 Ta 里打开着,请先在那里关闭',
|
|
535
|
+
'ta.error.tooManySessions': '打开的对话太多了,请关掉一个再试',
|
|
536
|
+
'ta.error.messageConflict': '消息没有发出:与另一条消息冲突,请重新发送',
|
|
537
|
+
'ta.error.modelNotFound': '这个模型不可用,请换一个模型,或先登录它的服务',
|
|
517
538
|
'ta.connection.notDoneHint': '连接恢复后再试一次。',
|
|
518
539
|
'ta.notices': '通知',
|
|
540
|
+
'ta.notice.picture': '扩展给出的图像,如要扫描的二维码',
|
|
519
541
|
'ta.packages.removeDetail': '它的页面、卡片和命令也会一起移除。',
|
|
520
542
|
'ta.packages.removed': '已移除 {name}',
|
|
521
543
|
'ta.packages.reloaded': '已重新加载扩展',
|
|
@@ -770,6 +792,8 @@ export const ZH_MESSAGES: Record<MessageId, string> = {
|
|
|
770
792
|
'ta.composer.image.size': '{name} 超过 5 MB。',
|
|
771
793
|
'ta.composer.image.count': '每条消息最多 8 张图片。',
|
|
772
794
|
'ta.composer.filesUnavailable': '此服务暂不支持搜索文件',
|
|
795
|
+
'ta.composer.filter': '搜索',
|
|
796
|
+
'ta.composer.noMatch': '没有匹配的项',
|
|
773
797
|
'ta.composer.noFiles': '没有匹配的文件',
|
|
774
798
|
'ta.composer.queue.steering': '引导',
|
|
775
799
|
'ta.composer.queue.followUp': '排队',
|
|
@@ -20,6 +20,8 @@ export interface NoticeOptions {
|
|
|
20
20
|
key?: string;
|
|
21
21
|
/** Stays until the user closes it. */
|
|
22
22
|
sticky?: boolean;
|
|
23
|
+
/** A picture of cells under the message, dark ones true: a QR code an extension drew with characters. */
|
|
24
|
+
art?: boolean[][];
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export interface Notice {
|
|
@@ -30,6 +32,7 @@ export interface Notice {
|
|
|
30
32
|
action?: NoticeAction;
|
|
31
33
|
key?: string;
|
|
32
34
|
sticky?: boolean;
|
|
35
|
+
art?: boolean[][];
|
|
33
36
|
/** How many times it came. */
|
|
34
37
|
count: number;
|
|
35
38
|
}
|
|
@@ -48,13 +48,19 @@ describe('TaNoticesImpl', () => {
|
|
|
48
48
|
expect(notices.state.notices).toHaveLength(1);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
it('says an error
|
|
51
|
+
it('says an error ta serve does not name with its message and hint', () => {
|
|
52
|
+
const notices = setup('zh-CN');
|
|
53
|
+
notices.error(new TaRemoteError('No such file.', TaErrorCode.COMMAND_FAILED, 'Fix it.'));
|
|
54
|
+
expect(notices.state.notices.map(({ message, detail }) => ({ message, detail }))).toEqual([{ message: 'No such file.', detail: 'Fix it.' }]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('says a failure of ta serve in the language of the page, and a lost connection once, whatever failed', () => {
|
|
52
58
|
const notices = setup('zh-CN');
|
|
53
59
|
notices.error(new TaRemoteError('Locked.', TaErrorCode.SESSION_LOCKED, 'Close it there first.'));
|
|
54
60
|
notices.error(new TaRemoteError('Not connected to ta serve.', TaErrorCode.CONNECTION_LOST));
|
|
55
61
|
notices.error(new TaRemoteError('The connection to ta serve was lost.', TaErrorCode.CONNECTION_LOST));
|
|
56
62
|
expect(notices.state.notices.map(({ message, detail, level, count }) => ({ message, detail, level, count }))).toEqual([
|
|
57
|
-
{ message: '
|
|
63
|
+
{ message: '这个对话在另一个 Ta 里打开着,请先在那里关闭', detail: 'Locked.', level: 'error', count: 1 },
|
|
58
64
|
{ message: '没有完成:未连接到 Ta 服务', detail: '连接恢复后再试一次。', level: 'error', count: 2 },
|
|
59
65
|
]);
|
|
60
66
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Autowired, Component } from '@celljs/core';
|
|
2
2
|
import { TaErrorCode } from '@celljs/ta-protocol';
|
|
3
|
-
import { message } from '../i18n/messages';
|
|
3
|
+
import { type MessageId, message } from '../i18n/messages';
|
|
4
4
|
import { TaSettings } from '../settings/settings-protocol';
|
|
5
5
|
import {
|
|
6
6
|
type ConfirmOptions,
|
|
@@ -50,6 +50,15 @@ export namespace NoticesUtil {
|
|
|
50
50
|
return kept;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/** The failures of ta serve the user can read about in the language of the page: what they mean. */
|
|
54
|
+
export const LOCALIZED: Partial<Record<string, MessageId>> = {
|
|
55
|
+
[TaErrorCode.SESSION_NOT_FOUND]: 'ta.error.sessionNotFound',
|
|
56
|
+
[TaErrorCode.SESSION_LOCKED]: 'ta.error.sessionLocked',
|
|
57
|
+
[TaErrorCode.TOO_MANY_SESSIONS]: 'ta.error.tooManySessions',
|
|
58
|
+
[TaErrorCode.MESSAGE_CONFLICT]: 'ta.error.messageConflict',
|
|
59
|
+
[TaErrorCode.MODEL_NOT_FOUND]: 'ta.error.modelNotFound',
|
|
60
|
+
};
|
|
61
|
+
|
|
53
62
|
/** What an error says: its message, and for a failure ta serve names (TaRemoteError) its hint. */
|
|
54
63
|
export function describe(cause: unknown): { message: string; detail?: string; code?: string } {
|
|
55
64
|
if (cause instanceof Error) {
|
|
@@ -91,6 +100,7 @@ export class TaNoticesImpl implements TaNotices {
|
|
|
91
100
|
action: options.action,
|
|
92
101
|
key,
|
|
93
102
|
sticky: options.sticky,
|
|
103
|
+
art: options.art,
|
|
94
104
|
count: same && same.message === text ? same.count + 1 : 1,
|
|
95
105
|
};
|
|
96
106
|
this.update({ notices: NoticesUtil.keep([...this.current.notices.filter(other => other !== same), notice]) });
|
|
@@ -114,6 +124,12 @@ export class TaNoticesImpl implements TaNotices {
|
|
|
114
124
|
detail: message(language, 'ta.connection.notDoneHint'),
|
|
115
125
|
});
|
|
116
126
|
}
|
|
127
|
+
const known = code ? NoticesUtil.LOCALIZED[code] : undefined;
|
|
128
|
+
if (known) {
|
|
129
|
+
// ta serve speaks English to its clients; the user reads what the error means, its words beneath.
|
|
130
|
+
const language = this.settings.state.language;
|
|
131
|
+
return this.notify(message(language, known), { ...options, level: 'error', detail: text });
|
|
132
|
+
}
|
|
117
133
|
return this.notify(text, { detail, ...options, level: 'error' });
|
|
118
134
|
}
|
|
119
135
|
|
|
@@ -23,7 +23,16 @@ export const CONVERSATION_STYLE = `
|
|
|
23
23
|
.ta-footer-usage { display: inline-flex; align-items: center; gap: 6px; min-height: 26px; padding: 0 6px; white-space: nowrap; }
|
|
24
24
|
.ta-footer-usage > span { display: inline-flex; align-items: center; gap: 1px; }
|
|
25
25
|
.ta-working { display: flex; align-items: center; gap: 8px; color: var(--ta-text); font-size: 13px; font-weight: 500; }
|
|
26
|
-
.ta-statusline { font-size: 11.5px; color: var(--ta-
|
|
26
|
+
.ta-statusline { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 4px; font-size: 11.5px; color: var(--ta-secondary); }
|
|
27
|
+
.ta-statusline-item { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; height: 22px; padding: 0 9px; border-radius: 999px; background: var(--ta-chip); white-space: nowrap; }
|
|
28
|
+
.ta-statusline-label { color: var(--ta-text); font-weight: 500; }
|
|
29
|
+
.ta-statusline-part { display: inline-flex; align-items: center; gap: 4px; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
30
|
+
.ta-statusline-part + .ta-statusline-part::before { content: ''; width: 1px; height: 10px; margin-right: 2px; background: var(--ta-border-field); }
|
|
31
|
+
.ta-statusline-part.ta-tone-error { color: var(--ta-red); }
|
|
32
|
+
.ta-statusline-part > .ta-status { width: 8px; }
|
|
33
|
+
.ta-widget { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; padding: 8px 12px; border-radius: 12px; border: 1px solid var(--ta-border); background: var(--ta-side); }
|
|
34
|
+
.ta-widget-text { margin: 0; max-width: 100%; overflow-x: auto; font-family: var(--ta-mono); font-size: 12px; line-height: 1.5; color: var(--ta-secondary); white-space: pre; }
|
|
35
|
+
.ta-widget-art { width: 180px; height: auto; border-radius: 8px; }
|
|
27
36
|
.ta-console-composer { position: relative; }
|
|
28
37
|
.ta-console-box { display: flex; align-items: flex-end; gap: 8px; padding: 8px 8px 8px 14px; border-radius: 16px; border: 1px solid var(--ta-border-field); background: var(--ta-input); box-shadow: var(--ta-shadow-composer); transition: border-color 150ms; }
|
|
29
38
|
.ta-console-box:focus-within { border-color: var(--ta-border-focus); }
|
|
@@ -68,6 +77,12 @@ export const CONVERSATION_STYLE = `
|
|
|
68
77
|
.ta-code-editor { position: relative; flex: 1; width: 100%; height: 100%; min-height: 0; background: var(--ta-code); }
|
|
69
78
|
.ta-code-editor-host { position: absolute; inset: 0; }
|
|
70
79
|
.ta-code-editor-state { position: absolute; inset: 0; padding: 16px; color: var(--ta-muted); font-size: 13px; }
|
|
80
|
+
/* A terminal (WebTerminal) fills the element it is attached to, which must have a size and position: relative. */
|
|
81
|
+
.ta-terminal { position: absolute; inset: 0; background: var(--ta-code); }
|
|
82
|
+
/* The padding is the terminal's own: xterm.js fits its rows and columns inside it. */
|
|
83
|
+
.ta-terminal .xterm { height: 100%; padding: 6px 0 4px 12px; }
|
|
84
|
+
.ta-terminal .xterm-viewport { background: transparent !important; }
|
|
85
|
+
.ta-terminal .xterm-helper-textarea { caret-color: transparent; }
|
|
71
86
|
.ta-code pre { margin: 0; padding: 10px 12px; overflow-x: auto; color: var(--ta-body); }
|
|
72
87
|
.ta-code-actions { display: flex; align-items: center; gap: 2px; }
|
|
73
88
|
.ta-code-actions .ta-icon-button { width: 24px; height: 24px; border-radius: 6px; color: var(--ta-muted); }
|