@oharness/server 0.7.1 → 0.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/dist/browser-pane.d.ts +48 -0
- package/dist/browser-pane.js +185 -0
- package/dist/browser-pane.js.map +1 -0
- package/dist/i18n.js +132 -0
- package/dist/i18n.js.map +1 -1
- package/dist/main.js +20 -0
- package/dist/main.js.map +1 -1
- package/dist/page/client/account.d.ts +8 -0
- package/dist/page/client/account.js +1895 -0
- package/dist/page/client/account.js.map +1 -0
- package/dist/page/client/artifacts.d.ts +7 -0
- package/dist/page/client/artifacts.js +767 -0
- package/dist/page/client/artifacts.js.map +1 -0
- package/dist/page/client/base.d.ts +8 -0
- package/dist/page/client/base.js +400 -0
- package/dist/page/client/base.js.map +1 -0
- package/dist/page/client/browser.d.ts +21 -0
- package/dist/page/client/browser.js +904 -0
- package/dist/page/client/browser.js.map +1 -0
- package/dist/page/client/composer.d.ts +8 -0
- package/dist/page/client/composer.js +1694 -0
- package/dist/page/client/composer.js.map +1 -0
- package/dist/page/client/dialogs.d.ts +7 -0
- package/dist/page/client/dialogs.js +724 -0
- package/dist/page/client/dialogs.js.map +1 -0
- package/dist/page/client/events.d.ts +7 -0
- package/dist/page/client/events.js +288 -0
- package/dist/page/client/events.js.map +1 -0
- package/dist/page/client/index.d.ts +16 -0
- package/dist/page/client/index.js +46 -0
- package/dist/page/client/index.js.map +1 -0
- package/dist/page/client/panels.d.ts +7 -0
- package/dist/page/client/panels.js +659 -0
- package/dist/page/client/panels.js.map +1 -0
- package/dist/page/client/pickers.d.ts +7 -0
- package/dist/page/client/pickers.js +624 -0
- package/dist/page/client/pickers.js.map +1 -0
- package/dist/page/client/routines.d.ts +6 -0
- package/dist/page/client/routines.js +910 -0
- package/dist/page/client/routines.js.map +1 -0
- package/dist/page/client/shell.d.ts +6 -0
- package/dist/page/client/shell.js +690 -0
- package/dist/page/client/shell.js.map +1 -0
- package/dist/page/client/state.d.ts +7 -0
- package/dist/page/client/state.js +1275 -0
- package/dist/page/client/state.js.map +1 -0
- package/dist/page/client/transcript.d.ts +8 -0
- package/dist/page/client/transcript.js +699 -0
- package/dist/page/client/transcript.js.map +1 -0
- package/dist/page/client/usage.d.ts +7 -0
- package/dist/page/client/usage.js +954 -0
- package/dist/page/client/usage.js.map +1 -0
- package/dist/page/style.d.ts +1 -0
- package/dist/page/style.js +2793 -0
- package/dist/page/style.js.map +1 -0
- package/dist/page.js +62 -13871
- package/dist/page.js.map +1 -1
- package/dist/server.d.ts +24 -0
- package/dist/server.js +349 -22
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../../src/page/client/artifacts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAuvB/B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preamble: the token, the desktop flag, the phone query, the string
|
|
3
|
+
* table and the locale, the icon set, and the handles to the elements the
|
|
4
|
+
* rest of the script paints into.
|
|
5
|
+
*
|
|
6
|
+
* One part of the client script; see ./index.ts for how the parts join.
|
|
7
|
+
*/
|
|
8
|
+
export declare const BASE: string;
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preamble: the token, the desktop flag, the phone query, the string
|
|
3
|
+
* table and the locale, the icon set, and the handles to the elements the
|
|
4
|
+
* rest of the script paints into.
|
|
5
|
+
*
|
|
6
|
+
* One part of the client script; see ./index.ts for how the parts join.
|
|
7
|
+
*/
|
|
8
|
+
export const BASE = String.raw `
|
|
9
|
+
const STRINGS = window.__STRINGS;
|
|
10
|
+
/**
|
|
11
|
+
* The token, from the desktop bridge when there is one.
|
|
12
|
+
*
|
|
13
|
+
* The browser gets it in the query string because that is the only channel a
|
|
14
|
+
* bare URL has. The desktop shell hands it over through a preload instead: a
|
|
15
|
+
* token in the address bar lands in history, in the window title, and in every
|
|
16
|
+
* screenshot of a bug report.
|
|
17
|
+
*/
|
|
18
|
+
const token =
|
|
19
|
+
(window.oharness && window.oharness.token) ||
|
|
20
|
+
new URLSearchParams(location.search).get("token") ||
|
|
21
|
+
"";
|
|
22
|
+
|
|
23
|
+
// Only the shell knows whether the window has a title bar of its own; in a
|
|
24
|
+
// browser tab the reserved strip would be a gap with nothing in it.
|
|
25
|
+
if (window.oharness && window.oharness.insetTitlebar) {
|
|
26
|
+
document.body.classList.add("inset-titlebar");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Whether this page is the desktop window rather than a browser tab. */
|
|
30
|
+
const IS_DESKTOP = Boolean(window.oharness && window.oharness.desktop);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether this page is inside the browser extension's side panel.
|
|
34
|
+
*
|
|
35
|
+
* The panel is about as wide as a phone, so the phone layout serves it; what
|
|
36
|
+
* it drops is the built-in browser, which beside the person's own browser
|
|
37
|
+
* would be a browser inside the browser. The server lets this page be framed
|
|
38
|
+
* by the extension and by nothing else.
|
|
39
|
+
*/
|
|
40
|
+
const IS_EMBED = new URLSearchParams(location.search).get("embed") === "sidepanel";
|
|
41
|
+
if (IS_EMBED) document.documentElement.classList.add("embed");
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Tell the side panel around this page something. The frame's parent is the
|
|
45
|
+
* extension, the only origin allowed to embed this page; outside a panel this
|
|
46
|
+
* says nothing to anyone.
|
|
47
|
+
*/
|
|
48
|
+
function embedTell(message) {
|
|
49
|
+
if (!IS_EMBED || window.parent === window) return;
|
|
50
|
+
const parentOrigin = (location.ancestorOrigins && location.ancestorOrigins[0]) || "*";
|
|
51
|
+
window.parent.postMessage(message, parentOrigin);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Whether the page is laid out for a phone right now; the stylesheet's breakpoint. */
|
|
55
|
+
const PHONE_QUERY = window.matchMedia("(max-width:760px)");
|
|
56
|
+
const isPhone = () => PHONE_QUERY.matches;
|
|
57
|
+
|
|
58
|
+
// --- language --------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* An inline icon, built as SVG nodes.
|
|
62
|
+
*
|
|
63
|
+
* Unicode glyphs come from whichever font happens to carry them, so a row of
|
|
64
|
+
* them arrives at inconsistent weights and optical sizes. These share one
|
|
65
|
+
* viewBox and one stroke width with the icons in the markup.
|
|
66
|
+
*/
|
|
67
|
+
const ICON_PATHS = {
|
|
68
|
+
// A padlock: the shackle as a path, the body as a rect.
|
|
69
|
+
lock: ["M5.33 7.33V5.33a2.67 2.67 0 0 1 5.34 0v2"],
|
|
70
|
+
lockRects: [[3.33, 7.33, 9.34, 6.67, 1.6]],
|
|
71
|
+
// Same drawing as the Settings item in the nav, so the model picker's
|
|
72
|
+
// "manage" link and the place it leads to are recognisably one thing.
|
|
73
|
+
settings: ["M12.67 4.67h-6", "M9.33 11.33H3.33"],
|
|
74
|
+
settingsCircles: [[4.67, 4.67, 2], [11.33, 11.33, 2]],
|
|
75
|
+
// Shut and open, drawn on the same body so the swap reads as one folder
|
|
76
|
+
// moving rather than as two icons trading places: the back panel, the left
|
|
77
|
+
// edge and the bottom hold still, and only the front flap tips forward.
|
|
78
|
+
//
|
|
79
|
+
// Two things were wrong with the pair this replaces. It sat on a 1.33–14.67
|
|
80
|
+
// box while every icon beside it sits on 2–14, so the folder was the widest
|
|
81
|
+
// mark in the menu and read as the heaviest. And its tab was 1.8 units deep
|
|
82
|
+
// out of ten: under two pixels at the size this is actually drawn, which
|
|
83
|
+
// left a rounded rectangle with a nick in it rather than a folder.
|
|
84
|
+
folder: [
|
|
85
|
+
"M3.33 3h2.87a1.33 1.33 0 0 1 1.09.57l.85 1.22a1.33 1.33 0 0 0 1.09.57H12.67" +
|
|
86
|
+
"A1.33 1.33 0 0 1 14 6.7v4.97A1.33 1.33 0 0 1 12.67 13H3.33" +
|
|
87
|
+
"A1.33 1.33 0 0 1 2 11.67V4.33A1.33 1.33 0 0 1 3.33 3Z",
|
|
88
|
+
],
|
|
89
|
+
folderOpen: [
|
|
90
|
+
"M4.4 10.3l.87-1.71a1.33 1.33 0 0 1 1.19-.72H12.8a1.33 1.33 0 0 1 1.2 1.6" +
|
|
91
|
+
"l-.8 2.7a1.33 1.33 0 0 1-1.42.83H3.33A1.33 1.33 0 0 1 2 11.67V4.33" +
|
|
92
|
+
"A1.33 1.33 0 0 1 3.33 3h2.87a1.33 1.33 0 0 1 1.09.57l.85 1.22" +
|
|
93
|
+
"a1.33 1.33 0 0 0 1.09.57H11.6a1.33 1.33 0 0 1 1.33 1.33v1.18",
|
|
94
|
+
],
|
|
95
|
+
moreDots: [[3.2, 8], [8, 8], [12.8, 8]],
|
|
96
|
+
// The same chevron the composer's pickers draw, and pointing the same way:
|
|
97
|
+
// both menus open upward, and both mark themselves with a caret that reads as
|
|
98
|
+
// "there is a list here" rather than one that promises a direction.
|
|
99
|
+
caret: ["M4.5 6.5 8 10l3.5-3.5"],
|
|
100
|
+
// The account control can both open and close its menu, so it uses the
|
|
101
|
+
// familiar paired chevrons instead of borrowing a one-way dropdown caret.
|
|
102
|
+
chevronsVertical: ["M5.5 6.5 8 4l2.5 2.5", "M5.5 9.5 8 12l2.5-2.5"],
|
|
103
|
+
// The three themes, drawn the same way the site draws them so the two
|
|
104
|
+
// controls are recognisably the same control.
|
|
105
|
+
themeSystem: ["M6 13.4h4"],
|
|
106
|
+
themeLight: ["M8 1.4v1.4M8 13.2v1.4M1.4 8h1.4M13.2 8h1.4M3.4 3.4l1 1M11.6 11.6l1 1M12.6 3.4l-1 1M4.4 11.6l-1 1"],
|
|
107
|
+
themeDark: ["M13.2 9.6A5.6 5.6 0 0 1 6.4 2.8a5.6 5.6 0 1 0 6.8 6.8Z"],
|
|
108
|
+
themeSystemRects: [[1.8, 2.6, 12.4, 8.6, 1.6]],
|
|
109
|
+
themeLightCircles: [[8, 8, 3.1]],
|
|
110
|
+
// A page with its corner turned back, which is the one mark that reads as
|
|
111
|
+
// "file" at thirteen pixels without any lines of text inside it.
|
|
112
|
+
file: [
|
|
113
|
+
"M9.33 1.33H4a1.33 1.33 0 0 0-1.33 1.34v10.66A1.33 1.33 0 0 0 4 14.67h8" +
|
|
114
|
+
"a1.33 1.33 0 0 0 1.33-1.34V5.33Z",
|
|
115
|
+
"M9.33 1.33v4h4",
|
|
116
|
+
],
|
|
117
|
+
// Two pins and a body: a connector is the thing something else plugs into.
|
|
118
|
+
plug: ["M6 1.33v3.34", "M10 1.33v3.34", "M4 4.67h8v3.33a4 4 0 0 1-8 0Z", "M8 12v2.67"],
|
|
119
|
+
// Stacked plates, because a plugin arrives as a bundle of contributions
|
|
120
|
+
// rather than as one thing.
|
|
121
|
+
plugin: ["M8 1.33 1.33 4.67 8 8l6.67-3.33Z", "M1.33 11.33 8 14.67l6.67-3.34", "M1.33 8 8 11.33 14.67 8"],
|
|
122
|
+
/* A bookmark: one path, and the only shape in this menu that is neither a
|
|
123
|
+
sheet nor a folder. The open book it replaced was two mirrored paths
|
|
124
|
+
meeting at a spine, which at the 13px these rows use closed into a blob —
|
|
125
|
+
two strokes a pixel apart read as one thick one. */
|
|
126
|
+
skill: ["M4.5 2.5h7v11l-3.5-2.6-3.5 2.6Z"],
|
|
127
|
+
/* A prompt caret: what a command line looks like anywhere else. */
|
|
128
|
+
command: ["M4 5.5 6.5 8 4 10.5", "M8.5 10.5H12"],
|
|
129
|
+
/* A bare chevron. The other back arrow has a shaft, which is right on a
|
|
130
|
+
page header and heavy on a menu row sitting one line above the thing it
|
|
131
|
+
returns to. */
|
|
132
|
+
chevronLeft: ["M9.5 4.5 6 8l3.5 3.5"],
|
|
133
|
+
/* Two strokes, for the corner of a dialog. */
|
|
134
|
+
close: ["M4.5 4.5l7 7", "M11.5 4.5l-7 7"],
|
|
135
|
+
/* Its counterpart, for adding one. */
|
|
136
|
+
plus: ["M8 3.5v9", "M3.5 8h9"],
|
|
137
|
+
search: ["M11.5 11.5 14 14"],
|
|
138
|
+
searchCircles: [[7.3, 7.3, 4.6]],
|
|
139
|
+
// A shopfront: an awning over a counter, for the list of what is on offer.
|
|
140
|
+
market: ["M2 6.67h12", "M3.33 6.67 4.67 2.67h6.66l1.34 4", "M2.67 6.67v6.66h10.67V6.67"],
|
|
141
|
+
// An arrow pointing back the way the menu came in.
|
|
142
|
+
back: ["M12.67 8H3.33", "M7.33 12 3.33 8l4-4"],
|
|
143
|
+
// An arrow leaving a tray, for the upload that still goes to this machine.
|
|
144
|
+
upload: ["M8 10.67V2", "M4.67 5.33 8 2l3.33 3.33", "M2 10.67v2A1.33 1.33 0 0 0 3.33 14h9.34A1.33 1.33 0 0 0 14 12.67v-2"],
|
|
145
|
+
// A trunk with a branch leaving it, which is what forking a turn does to the
|
|
146
|
+
// transcript: the arc lands on the new node rather than crossing it.
|
|
147
|
+
fork: ["M5 2.6v7.7", "M11 6a5.7 5.7 0 0 1-4.3 6"],
|
|
148
|
+
forkCircles: [[11, 4.3], [5, 12]],
|
|
149
|
+
copy: [
|
|
150
|
+
"M6.67 5.33h6.66a1.33 1.33 0 0 1 1.34 1.34v6.66a1.33 1.33 0 0 1-1.34 1.34" +
|
|
151
|
+
"H6.67a1.33 1.33 0 0 1-1.34-1.34V6.67a1.33 1.33 0 0 1 1.34-1.34Z",
|
|
152
|
+
"M2.67 10.67a1.33 1.33 0 0 1-1.34-1.34V2.67a1.33 1.33 0 0 1 1.34-1.34h6.66" +
|
|
153
|
+
"a1.33 1.33 0 0 1 1.34 1.34",
|
|
154
|
+
],
|
|
155
|
+
// A pin seen from the side: head, shaft, and the point going into the page.
|
|
156
|
+
pin: [
|
|
157
|
+
"M9.5 2.5 13.5 6.5",
|
|
158
|
+
"M10.2 3.2 7.6 5a2.7 2.7 0 0 1-1.7.5l-1.6-.1a.6.6 0 0 0-.45 1.03l5.72 5.72a.6.6 0 0 0 1.03-.45l-.1-1.6A2.7 2.7 0 0 1 11 8.4l1.8-2.6",
|
|
159
|
+
"M6.2 9.8 2.8 13.2",
|
|
160
|
+
],
|
|
161
|
+
// The product's own mark: two open arcs around a centre, the same glyph the
|
|
162
|
+
// application icon draws, at half its scale. Drawn rather than fetched,
|
|
163
|
+
// because the official plan's rows have to have a mark on a machine that
|
|
164
|
+
// has never reached the network.
|
|
165
|
+
oharness: ["M10 3H6a3 3 0 0 0-3 3v4", "M6 13h4a3 3 0 0 0 3-3V6"],
|
|
166
|
+
oharnessDots: [[8, 8]],
|
|
167
|
+
// A speech bubble with a tail: what a session is, before it has a title.
|
|
168
|
+
chat: [
|
|
169
|
+
"M14 10a1.33 1.33 0 0 1-1.33 1.33H4.67L2 14V3.33A1.33 1.33 0 0 1 3.33 2h9.34" +
|
|
170
|
+
"A1.33 1.33 0 0 1 14 3.33Z",
|
|
171
|
+
],
|
|
172
|
+
// The five permission modes. Five short words in a column look alike until
|
|
173
|
+
// they are read; a glyph in front of each lets the one in use be found by
|
|
174
|
+
// shape from the bar, the way the vendor mark does for the model.
|
|
175
|
+
//
|
|
176
|
+
// Manual is by hand, and a raised palm is also the sign for "wait": three
|
|
177
|
+
// fingers, a thumb, and the heel of the hand in one sweep. Four fingers
|
|
178
|
+
// would be the honest count, but at fifteen pixels the fourth turns the
|
|
179
|
+
// top edge into a comb.
|
|
180
|
+
modeAsk: [
|
|
181
|
+
"M12 7.4V4a1.33 1.33 0 0 0-2.67 0",
|
|
182
|
+
"M9.33 6.7V2.7a1.33 1.33 0 0 0-2.66 0v1.3",
|
|
183
|
+
"M6.67 7V4A1.33 1.33 0 0 0 4 4v5.33",
|
|
184
|
+
"M12 5.33a1.33 1.33 0 1 1 2.67 0v4a5.33 5.33 0 0 1-5.34 5.34H8" +
|
|
185
|
+
"c-1.87 0-3-.58-4-1.56l-2.4-2.4a1.33 1.33 0 0 1 1.89-1.89L4.67 10",
|
|
186
|
+
],
|
|
187
|
+
// Accept edits lets the pencil through.
|
|
188
|
+
modeEdits: ["M11.3 2.7 13.3 4.7 5.3 12.7H3.3v-2Z", "M9.8 4.2l2 2"],
|
|
189
|
+
// Plan is read-only: an eye, which looks and does not touch.
|
|
190
|
+
modePlan: ["M1.6 8s2.4-4.4 6.4-4.4S14.4 8 14.4 8s-2.4 4.4-6.4 4.4S1.6 8 1.6 8Z"],
|
|
191
|
+
modePlanCircles: [[8, 8, 2.1]],
|
|
192
|
+
// Auto waves the routine through: a bolt.
|
|
193
|
+
modeAuto: ["M8.8 1.6 3.6 9h4l-.7 5.4L12.4 7h-4Z"],
|
|
194
|
+
// Bypass leaves the gate open: a padlock with its shackle swung clear.
|
|
195
|
+
modeBypass: ["M5.4 7.4V5.2a2.6 2.6 0 0 1 5-1"],
|
|
196
|
+
modeBypassRects: [[3.2, 7.4, 9.6, 6.2, 1.6]],
|
|
197
|
+
// The date picker's trigger and its month arrows. The same marks the site's
|
|
198
|
+
// picker draws, because it is the same control.
|
|
199
|
+
calendar: ["M2 6.5h12M5.5 1.5v3M10.5 1.5v3"],
|
|
200
|
+
calendarRects: [[2, 3, 12, 11, 2.5]],
|
|
201
|
+
chevronLeft: ["M10 3.5 5.5 8l4.5 4.5"],
|
|
202
|
+
chevronRight: ["M6 3.5 10.5 8 6 12.5"],
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
function svgIcon(name, extra) {
|
|
206
|
+
const NS = "http://www.w3.org/2000/svg";
|
|
207
|
+
const svg = document.createElementNS(NS, "svg");
|
|
208
|
+
svg.setAttribute("viewBox", "0 0 16 16");
|
|
209
|
+
svg.setAttribute("fill", "none");
|
|
210
|
+
svg.setAttribute("stroke", "currentColor");
|
|
211
|
+
svg.setAttribute("stroke-width", "1.5");
|
|
212
|
+
svg.setAttribute("stroke-linecap", "round");
|
|
213
|
+
svg.setAttribute("stroke-linejoin", "round");
|
|
214
|
+
svg.setAttribute("class", "ico-sm" + (extra ? " " + extra : ""));
|
|
215
|
+
for (const d of ICON_PATHS[name] || []) {
|
|
216
|
+
const path = document.createElementNS(NS, "path");
|
|
217
|
+
path.setAttribute("d", d);
|
|
218
|
+
svg.appendChild(path);
|
|
219
|
+
}
|
|
220
|
+
// The radius is per-circle because the icons it serves disagree: a fork's
|
|
221
|
+
// node is a dot, a settings knob is a ring you look through.
|
|
222
|
+
for (const [x, y, w, h, rx] of ICON_PATHS[name + "Rects"] || []) {
|
|
223
|
+
const rect = document.createElementNS(NS, "rect");
|
|
224
|
+
rect.setAttribute("x", String(x));
|
|
225
|
+
rect.setAttribute("y", String(y));
|
|
226
|
+
rect.setAttribute("width", String(w));
|
|
227
|
+
rect.setAttribute("height", String(h));
|
|
228
|
+
rect.setAttribute("rx", String(rx ?? 1.6));
|
|
229
|
+
svg.appendChild(rect);
|
|
230
|
+
}
|
|
231
|
+
for (const [cx, cy, r] of ICON_PATHS[name + "Circles"] || []) {
|
|
232
|
+
const circle = document.createElementNS(NS, "circle");
|
|
233
|
+
circle.setAttribute("cx", String(cx));
|
|
234
|
+
circle.setAttribute("cy", String(cy));
|
|
235
|
+
circle.setAttribute("r", String(r ?? 1.7));
|
|
236
|
+
svg.appendChild(circle);
|
|
237
|
+
}
|
|
238
|
+
// Solid, not stroked: a ring that small reads as a smudge, and three of them
|
|
239
|
+
// in a row read as a chain rather than an ellipsis.
|
|
240
|
+
for (const [cx, cy] of ICON_PATHS[name + "Dots"] || []) {
|
|
241
|
+
const dot = document.createElementNS(NS, "circle");
|
|
242
|
+
dot.setAttribute("cx", String(cx));
|
|
243
|
+
dot.setAttribute("cy", String(cy));
|
|
244
|
+
dot.setAttribute("r", "1.35");
|
|
245
|
+
dot.setAttribute("fill", "currentColor");
|
|
246
|
+
dot.setAttribute("stroke", "none");
|
|
247
|
+
svg.appendChild(dot);
|
|
248
|
+
}
|
|
249
|
+
return svg;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const LOCALE = (() => {
|
|
253
|
+
const asked = new URLSearchParams(location.search).get("lang");
|
|
254
|
+
if (asked && STRINGS[asked]) return asked;
|
|
255
|
+
// The browser's list, most preferred first; the base language is enough to
|
|
256
|
+
// match, so zh-Hans-CN finds zh.
|
|
257
|
+
for (const tag of navigator.languages || [navigator.language || "en"]) {
|
|
258
|
+
const base = String(tag).toLowerCase().split("-")[0];
|
|
259
|
+
if (STRINGS[base]) return base;
|
|
260
|
+
}
|
|
261
|
+
return "en";
|
|
262
|
+
})();
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* A translated string.
|
|
266
|
+
*
|
|
267
|
+
* Falls back to English rather than to the key: a missing translation should
|
|
268
|
+
* still read as a sentence. Placeholders are named so a translation can move
|
|
269
|
+
* them, which positional ones cannot survive.
|
|
270
|
+
*/
|
|
271
|
+
function t(key, vars) {
|
|
272
|
+
const text = (STRINGS[LOCALE] && STRINGS[LOCALE][key]) || STRINGS.en[key] || key;
|
|
273
|
+
if (!vars) return text;
|
|
274
|
+
return text.replace(/\{([a-z]+)\}/g, (whole, name) =>
|
|
275
|
+
vars[name] === undefined ? whole : String(vars[name]),
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* The same string, in the shell that is reading it.
|
|
280
|
+
*
|
|
281
|
+
* This page is both a desktop window and a tab in someone's browser, and a
|
|
282
|
+
* few lines can only be true in one of them — "no browser on this machine"
|
|
283
|
+
* is nonsense read inside a browser, and the sign-in column called itself an
|
|
284
|
+
* app to a reader looking at a web page. The browser wording lives under the
|
|
285
|
+
* same key with .web on the end; a key with no such sibling is shell-neutral
|
|
286
|
+
* and answers the same either way, which is nearly all of them.
|
|
287
|
+
*/
|
|
288
|
+
function ts(key, vars) {
|
|
289
|
+
const web = key + ".web";
|
|
290
|
+
const known = (STRINGS[LOCALE] && STRINGS[LOCALE][web]) || STRINGS.en[web];
|
|
291
|
+
return t(!IS_DESKTOP && known ? web : key, vars);
|
|
292
|
+
}
|
|
293
|
+
/*
|
|
294
|
+
* A path that already carries a query needs an ampersand, not a second "?".
|
|
295
|
+
* With one, /history?session=<id>?token=<t> sent the token as part of the
|
|
296
|
+
* session id and the server answered 401 — so resuming a session showed the
|
|
297
|
+
* empty invitation instead of its transcript, on every request that passes a
|
|
298
|
+
* parameter of its own.
|
|
299
|
+
*/
|
|
300
|
+
const q = (path) =>
|
|
301
|
+
path + (path.includes("?") ? "&" : "?") + "token=" + encodeURIComponent(token);
|
|
302
|
+
/**
|
|
303
|
+
* A GET that reports why it failed.
|
|
304
|
+
*
|
|
305
|
+
* The panels used to swallow errors into an empty object, which rendered as a
|
|
306
|
+
* page of em-dashes with no hint that anything had gone wrong — a 401 from a
|
|
307
|
+
* stale token looked exactly like a working directory that is somehow blank.
|
|
308
|
+
*/
|
|
309
|
+
async function getJson(path) {
|
|
310
|
+
try {
|
|
311
|
+
const response = await fetch(q(path), { signal: AbortSignal.timeout(20000) });
|
|
312
|
+
const body = await response.json().catch(() => ({}));
|
|
313
|
+
if (!response.ok) {
|
|
314
|
+
return { error: body.error || "HTTP " + response.status + " from " + path };
|
|
315
|
+
}
|
|
316
|
+
return body;
|
|
317
|
+
} catch (err) {
|
|
318
|
+
return { error: err.name === "TimeoutError" ? path + " timed out" : String(err.message || err) };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** A banner for a panel that could not load, in place of blank fields. */
|
|
323
|
+
/**
|
|
324
|
+
* A memory error in this page's language. The engine sends a code and its
|
|
325
|
+
* values with the English message; the page says it in its own words when it
|
|
326
|
+
* has them, and passes the message through when it does not.
|
|
327
|
+
*/
|
|
328
|
+
function memoryText(failure) {
|
|
329
|
+
const key = failure && failure.code ? "memory.err." + failure.code : "";
|
|
330
|
+
if (key && (STRINGS.en[key] || (STRINGS[LOCALE] && STRINGS[LOCALE][key]))) return t(key, failure.params || {});
|
|
331
|
+
return (failure && failure.message) || t("memory.failed");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** Stands in while a panel fetches, so a click never lands on a blank page. */
|
|
335
|
+
function panelLoading() {
|
|
336
|
+
const panel = el("div", "panel panel-loading");
|
|
337
|
+
panel.appendChild(el("span", "spinner"));
|
|
338
|
+
panel.appendChild(el("span", "working-label", t("market.loading")));
|
|
339
|
+
return panel;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function panelError(message) {
|
|
343
|
+
const panel = el("div", "panel");
|
|
344
|
+
const box = el("div", "row-card");
|
|
345
|
+
box.appendChild(el("div", "row-title", t("error.title")));
|
|
346
|
+
box.appendChild(el("div", "muted small", message));
|
|
347
|
+
box.appendChild(el("div", "muted small", t("error.stale")));
|
|
348
|
+
panel.appendChild(box);
|
|
349
|
+
return panel;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const api = (path, body, timeoutMs) =>
|
|
353
|
+
fetch(q(path), {
|
|
354
|
+
method: "POST",
|
|
355
|
+
headers: { "content-type": "application/json" },
|
|
356
|
+
body: JSON.stringify(body || {}),
|
|
357
|
+
// Every button that flips to a busy label needs the promise to settle, or
|
|
358
|
+
// it stays busy forever when the server does not answer.
|
|
359
|
+
signal: AbortSignal.timeout(timeoutMs || 30000),
|
|
360
|
+
})
|
|
361
|
+
.then((r) => r.json().catch(() => ({})))
|
|
362
|
+
.catch((err) => ({ error: err.name === "TimeoutError" ? "timed out" : String(err.message || err) }));
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Record that a feature was used. Does nothing unless analytics are on.
|
|
366
|
+
*
|
|
367
|
+
* window.__TRACK exists only when the server was started with a PostHog key —
|
|
368
|
+
* see analytics.ts — so the calls below read the same either way and nobody
|
|
369
|
+
* has to remember which branch they are writing in.
|
|
370
|
+
*
|
|
371
|
+
* There is deliberately no way to send text through here: every caller passes
|
|
372
|
+
* an event name and a handful of enumerable properties — which panel, whether
|
|
373
|
+
* a permission was allowed, which model — and never anything the user typed.
|
|
374
|
+
*/
|
|
375
|
+
function track(event, props) {
|
|
376
|
+
if (window.__TRACK) window.__TRACK(event, props);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const log = document.getElementById("log");
|
|
380
|
+
const panelView = document.getElementById("panel-view");
|
|
381
|
+
const panelBody = document.getElementById("panel-body");
|
|
382
|
+
const panelNotes = document.getElementById("panel-notes");
|
|
383
|
+
const input = document.getElementById("input");
|
|
384
|
+
const sendBtn = document.getElementById("send");
|
|
385
|
+
const stopBtn = document.getElementById("stop");
|
|
386
|
+
const jumpBtn = document.getElementById("jump");
|
|
387
|
+
const metaEl = document.getElementById("cost");
|
|
388
|
+
const tokensEl = document.getElementById("tokens");
|
|
389
|
+
const cacheEl = document.getElementById("cache");
|
|
390
|
+
const ctxBar = document.querySelector("#ctx i");
|
|
391
|
+
const ctxText = document.getElementById("ctxtext");
|
|
392
|
+
const sessionList = document.getElementById("sessions");
|
|
393
|
+
const sidebarEl = document.querySelector("body > aside");
|
|
394
|
+
const ask = document.getElementById("ask");
|
|
395
|
+
|
|
396
|
+
let pendingId = null;
|
|
397
|
+
let atBottom = true;
|
|
398
|
+
|
|
399
|
+
`;
|
|
400
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/page/client/base.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuY7B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser pane: what the agent's browser is looking at, live, and a way to
|
|
3
|
+
* take the mouse off it.
|
|
4
|
+
*
|
|
5
|
+
* A column beside the transcript, not a panel over it. Watching what the model
|
|
6
|
+
* does is only useful next to what it is saying, and the layout already had the
|
|
7
|
+
* shape for that — the diff pane is a sibling of the main column and slides in
|
|
8
|
+
* the same way, so this is built the same way rather than inventing a second
|
|
9
|
+
* kind of side surface.
|
|
10
|
+
*
|
|
11
|
+
* The picture is an img pointed at an MJPEG stream, so the browser decodes the
|
|
12
|
+
* frames and this file never touches one. The element is created once and kept:
|
|
13
|
+
* the pane closes by collapsing to zero width, which leaves the stream running
|
|
14
|
+
* rather than tearing it down and reconnecting on the next toggle.
|
|
15
|
+
*
|
|
16
|
+
* Taking over sends the same input events the tools send, through the same
|
|
17
|
+
* call, so a person and the model reach the page identically.
|
|
18
|
+
*
|
|
19
|
+
* One part of the client script; see ./index.ts for how the parts join.
|
|
20
|
+
*/
|
|
21
|
+
export declare const BROWSER: string;
|