dsh-web-annotator 0.0.0-stage → 0.2.0-alpha.12
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 +125 -0
- package/CONTRIBUTING.md +90 -0
- package/LICENSE +21 -0
- package/README.md +110 -2
- package/README_en.md +136 -0
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/cordis.patch.yml +15 -0
- package/docs/AGENTS.md +16 -0
- package/docs/README.md +25 -0
- package/docs/architecture.md +170 -0
- package/docs/browser-integration.md +94 -0
- package/docs/distribution.md +66 -0
- package/docs/harness-browser-annotation.patch +1909 -0
- package/docs/images/annotation-expanded-dark.png +0 -0
- package/docs/images/annotation-expanded.png +0 -0
- package/docs/images/annotation-image.png +0 -0
- package/docs/images/annotation-message.png +0 -0
- package/docs/images/annotation-region.png +0 -0
- package/docs/marketplace/cslkkl__dsh-web-annotator.yml +7 -0
- package/docs/publishing.md +169 -0
- package/docs/roadmap.md +31 -0
- package/docs/verification.md +179 -0
- package/dsh-plugin.naming.json +27 -0
- package/examples/responsive-demo/AGENTS.md +12 -0
- package/examples/responsive-demo/README.md +33 -0
- package/examples/responsive-demo/index.html +13 -0
- package/examples/responsive-demo/src/main.tsx +110 -0
- package/examples/responsive-demo/src/style.css +344 -0
- package/examples/responsive-demo/vite.config.ts +9 -0
- package/lib/THIRD_PARTY_LICENSES.md +345 -0
- package/lib/bridge.js +890 -0
- package/lib/bridge.js.map +7 -0
- package/lib/client.js +1083 -0
- package/lib/client.js.map +7 -0
- package/lib/host/THIRD_PARTY_NOTICES.md +15 -0
- package/lib/host/browser-provider/LICENSE +21 -0
- package/lib/host/browser-provider/README.md +130 -0
- package/lib/host/browser-provider/lib/client.css +217 -0
- package/lib/host/browser-provider/lib/client.css.map +7 -0
- package/lib/host/browser-provider/lib/client.js +2360 -0
- package/lib/host/browser-provider/lib/client.js.map +7 -0
- package/lib/host/browser-provider/lib/index.js +7 -0
- package/lib/host/browser-provider/lib/index.js.map +7 -0
- package/lib/host/browser-provider/package.json +23 -0
- package/lib/host/bundle.json +27 -0
- package/lib/host/chat-provider/LICENSE +21 -0
- package/lib/host/chat-provider/README.md +186 -0
- package/lib/host/chat-provider/lib/client.css +1604 -0
- package/lib/host/chat-provider/lib/client.css.map +7 -0
- package/lib/host/chat-provider/lib/client.js +11074 -0
- package/lib/host/chat-provider/lib/client.js.map +7 -0
- package/lib/host/chat-provider/lib/index.js +911 -0
- package/lib/host/chat-provider/lib/index.js.map +7 -0
- package/lib/host/chat-provider/package.json +31 -0
- package/lib/host/layout-provider/LICENSE +21 -0
- package/lib/host/layout-provider/README.md +109 -0
- package/lib/host/layout-provider/lib/client.css +177 -0
- package/lib/host/layout-provider/lib/client.css.map +7 -0
- package/lib/host/layout-provider/lib/client.js +565 -0
- package/lib/host/layout-provider/lib/client.js.map +7 -0
- package/lib/host/layout-provider/lib/index.js +7 -0
- package/lib/host/layout-provider/lib/index.js.map +7 -0
- package/lib/host/layout-provider/package.json +25 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +9 -0
- package/lib/index.js.map +7 -0
- package/lib/vite.d.ts +8 -0
- package/lib/vite.js +1254 -0
- package/lib/vite.js.map +7 -0
- package/licenses/deepseek-harness-MIT.txt +21 -0
- package/locale/AGENTS.md +11 -0
- package/locale/README.md +11 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +154 -4
- package/screenshots.json +7 -0
|
@@ -0,0 +1,2360 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({ id: '@deepseek-ai/dsh-client-ui-sidebar-browser', factory: function(require) { var module = { exports: {} }; var exports = module.exports;
|
|
2
|
+
const cssDispose = () => { const style = document.createElement('style'); style.textContent = "/* .host-source/packages/client/ui-sidebar-browser/src/client/view/Browser.module.css */\n.Browser_root {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n}\n.Browser_toolbar {\n display: flex;\n flex: 0 0 auto;\n gap: 4px;\n align-items: center;\n box-sizing: border-box;\n height: 38px;\n padding: 5px 6px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l3);\n}\n.Browser_tool {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n color: var(--dsw-alias-label-secondary);\n background: transparent;\n border: 0;\n border-radius: var(--dsw-radius-sm);\n cursor: pointer;\n}\n.Browser_tool:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n.Browser_tool:disabled {\n color: var(--dsw-alias-label-quaternary);\n cursor: default;\n}\n.Browser_sandboxOff {\n color: var(--dsw-alias-state-error-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n.Browser_addressBox {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n}\n.Browser_address {\n width: 100%;\n height: 28px;\n box-sizing: border-box;\n padding: 0 34px 0 9px;\n color: var(--dsw-alias-label-primary);\n font: var(--dsw-font-xxs-12);\n background: var(--dsw-alias-bg-layer-1);\n border: 0.5px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n}\n.Browser_addressGo {\n position: absolute;\n top: 0;\n right: 0;\n visibility: hidden;\n opacity: 0;\n}\n.Browser_addressBox:focus-within .Browser_addressGo {\n visibility: visible;\n opacity: 1;\n}\n.Browser_addressUnknown {\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-bg-layer-2);\n}\n.Browser_addressUnknown:focus {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-layer-1);\n}\n.Browser_addressChanged {\n position: absolute;\n top: 6px;\n right: 8px;\n padding-left: 8px;\n color: var(--dsw-alias-label-tertiary);\n font: var(--dsw-font-xxxs-11);\n line-height: 16px;\n pointer-events: none;\n background: var(--dsw-alias-bg-layer-2);\n}\n.Browser_addressBox:focus-within .Browser_addressChanged {\n visibility: hidden;\n opacity: 0;\n}\n.Browser_address:focus {\n outline: 1px solid var(--dsw-alias-state-business-primary);\n outline-offset: -1px;\n}\n.Browser_frame {\n display: flex;\n flex: 1 1 auto;\n width: 100%;\n min-height: 0;\n border: 0;\n background: var(--dsw-alias-bg-base);\n}\n.Browser_content {\n position: relative;\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n}\n.Browser_viewport {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n}\n.Browser_placeholder {\n position: absolute;\n inset: 0;\n display: flex;\n pointer-events: none;\n}\n.Browser_restore {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n padding: 24px;\n overflow: auto;\n text-align: center;\n}\n.Browser_restoreLabel {\n margin: auto 0 0;\n color: var(--dsw-alias-label-tertiary);\n font: var(--dsw-font-xxs-12);\n}\n.Browser_restoreTitle {\n max-width: 100%;\n margin: 0;\n overflow-wrap: anywhere;\n font: var(--dsw-font-xs-13);\n font-weight: 500;\n}\n.Browser_restoreUrl {\n max-width: 100%;\n margin: 0;\n overflow-wrap: anywhere;\n color: var(--dsw-alias-label-secondary);\n font: var(--dsw-font-xxs-12);\n}\n.Browser_restore > :last-child {\n flex-shrink: 0;\n margin-bottom: auto;\n}\n.Browser_webview {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n width: 100%;\n height: 100%;\n border: 0;\n -webkit-app-region: no-drag;\n}\nhtml:has([data-dockkit-pointer]) .Browser_webview {\n pointer-events: none;\n}\n.Browser_start {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n justify-content: center;\n min-height: 0;\n padding: 24px;\n color: var(--dsw-alias-label-tertiary);\n font: var(--dsw-font-xs-13);\n text-align: center;\n}\n.Browser_failure {\n flex: 0 0 auto;\n padding: 6px 12px;\n color: var(--dsw-alias-state-error-primary);\n font: var(--dsw-font-xxxs-11);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);\n}\n.Browser_sandboxWarning {\n flex: 0 0 auto;\n padding: 6px 12px;\n color: var(--dsw-alias-label-secondary);\n font: var(--dsw-font-xxxs-11);\n background: var(--dsw-alias-state-warn-tertiary);\n}\n.Browser_limit {\n flex: 0 0 auto;\n margin: 0;\n padding: 4px 10px;\n overflow: hidden;\n color: var(--dsw-alias-label-tertiary);\n font: var(--dsw-font-xxxs-11);\n text-overflow: ellipsis;\n white-space: nowrap;\n border-top: 0.5px solid var(--dsw-alias-border-l3);\n}\n.Browser_titleIcon {\n flex: 0 0 auto;\n margin-right: 4px;\n}\n/*# sourceMappingURL=client.css.map */\n"; document.head.append(style); return () => style.remove(); };
|
|
3
|
+
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/index.ts
|
|
23
|
+
var index_exports = {};
|
|
24
|
+
__export(index_exports, {
|
|
25
|
+
apply: () => apply,
|
|
26
|
+
inject: () => inject
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/view/BrowserBody.tsx
|
|
31
|
+
var import_react = require("react");
|
|
32
|
+
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
33
|
+
|
|
34
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/browser/BrowserFrame.ts
|
|
35
|
+
function emptyBrowserFrame() {
|
|
36
|
+
return {
|
|
37
|
+
target: void 0,
|
|
38
|
+
address: "empty",
|
|
39
|
+
loading: false,
|
|
40
|
+
canGoBack: false,
|
|
41
|
+
canGoForward: false,
|
|
42
|
+
error: void 0,
|
|
43
|
+
sandboxEnabled: void 0
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/browser/BrowserPersistence.ts
|
|
48
|
+
function currentBrowserTarget(state) {
|
|
49
|
+
return state === void 0 || state.index < 0 ? void 0 : state.entries[state.index];
|
|
50
|
+
}
|
|
51
|
+
function browserAddressCheckpoint(target, revision) {
|
|
52
|
+
return {
|
|
53
|
+
entries: [target],
|
|
54
|
+
index: 0,
|
|
55
|
+
request: { target, revision },
|
|
56
|
+
navigation: { status: "known", revision },
|
|
57
|
+
failure: void 0
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/view/Browser.module.css
|
|
62
|
+
var Browser_default = {
|
|
63
|
+
root: "Browser_root",
|
|
64
|
+
toolbar: "Browser_toolbar",
|
|
65
|
+
tool: "Browser_tool",
|
|
66
|
+
sandboxOff: "Browser_sandboxOff",
|
|
67
|
+
addressBox: "Browser_addressBox",
|
|
68
|
+
address: "Browser_address",
|
|
69
|
+
addressGo: "Browser_addressGo",
|
|
70
|
+
addressUnknown: "Browser_addressUnknown",
|
|
71
|
+
addressChanged: "Browser_addressChanged",
|
|
72
|
+
frame: "Browser_frame",
|
|
73
|
+
content: "Browser_content",
|
|
74
|
+
viewport: "Browser_viewport",
|
|
75
|
+
placeholder: "Browser_placeholder",
|
|
76
|
+
restore: "Browser_restore",
|
|
77
|
+
restoreLabel: "Browser_restoreLabel",
|
|
78
|
+
restoreTitle: "Browser_restoreTitle",
|
|
79
|
+
restoreUrl: "Browser_restoreUrl",
|
|
80
|
+
webview: "Browser_webview",
|
|
81
|
+
start: "Browser_start",
|
|
82
|
+
failure: "Browser_failure",
|
|
83
|
+
sandboxWarning: "Browser_sandboxWarning",
|
|
84
|
+
limit: "Browser_limit",
|
|
85
|
+
titleIcon: "Browser_titleIcon"
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/view/BrowserBody.tsx
|
|
89
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
90
|
+
var EMPTY_FRAME = emptyBrowserFrame();
|
|
91
|
+
function SandboxPolicyIcon({ sandboxed }) {
|
|
92
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: [
|
|
93
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: import_dsh_client_ui_primitives.SHIELD_OUTLINE_PATH, stroke: "currentColor", strokeWidth: import_dsh_client_ui_primitives.ICON_REGULAR_STROKE, strokeLinejoin: "round" }),
|
|
94
|
+
sandboxed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12.1654 5.7552L8.9447 9.41475C8.73044 9.65816 8.53628 9.8804 8.35774 10.0423C8.1713 10.2114 7.94235 10.3717 7.64016 10.4254C7.48207 10.4535 7.32 10.4552 7.16151 10.4294C6.85843 10.3801 6.62728 10.2223 6.43836 10.0559C6.25752 9.89653 6.06037 9.67732 5.84264 9.43705L4.72925 8.20897L5.63557 7.38707L6.74897 8.61594C6.98603 8.87755 7.12974 9.03533 7.24673 9.13839C7.31033 9.19443 7.34485 9.21476 7.35823 9.22122C7.38068 9.22484 7.40352 9.22515 7.42593 9.22122C7.40522 9.22502 7.42893 9.23294 7.53583 9.136C7.65132 9.03126 7.79316 8.87139 8.02643 8.60638L11.2479 4.94763L12.1654 5.7552Z", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10.6074 4.40278L8.00975 6.99973L10.6074 9.59739L9.59736 10.6074L6.9997 8.00978L4.40274 10.6074L3.3927 9.59739L5.98966 6.99973L3.3927 4.40278L4.40274 3.39273L6.9997 5.98969L9.59736 3.39273L10.6074 4.40278Z", fill: "currentColor", transform: "translate(1.2 0.8)" })
|
|
95
|
+
] });
|
|
96
|
+
}
|
|
97
|
+
function useBrowserDraft(url, revision) {
|
|
98
|
+
const [edit, setEdit] = (0, import_react.useState)();
|
|
99
|
+
return [
|
|
100
|
+
edit?.revision === revision ? edit.value : url ?? "",
|
|
101
|
+
(value) => {
|
|
102
|
+
setEdit({ revision, value });
|
|
103
|
+
}
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
function BrowserBody(props) {
|
|
107
|
+
const { mount, loadUrl, restore, goBack, goForward, reload, setSandbox, useBrowserState, useStore, useTabInfo, t } = props;
|
|
108
|
+
const { tab } = useTabInfo();
|
|
109
|
+
(0, import_react.useEffect)(() => tab.actions.bindCommands({ refresh: () => {
|
|
110
|
+
reload(tab.id);
|
|
111
|
+
} }), [tab.actions, tab.id, reload]);
|
|
112
|
+
const saved = useStore((state2) => state2.byTab[tab.id]);
|
|
113
|
+
const initial = (0, import_react.useRef)(saved);
|
|
114
|
+
const initialUrl = (0, import_react.useRef)(tab.navigation.params?.url);
|
|
115
|
+
const viewportId = (0, import_react.useId)();
|
|
116
|
+
const [mountEpoch, setMountEpoch] = (0, import_react.useState)(0);
|
|
117
|
+
const state = useBrowserState(tab.id);
|
|
118
|
+
const frame = state?.frame ?? EMPTY_FRAME;
|
|
119
|
+
const restoreTarget = state === void 0 ? currentBrowserTarget(initial.current) : state.restoreTarget;
|
|
120
|
+
const target = frame.target ?? restoreTarget;
|
|
121
|
+
const [draft, setDraft] = useBrowserDraft(target?.url ?? initialUrl.current, state?.addressRevision ?? 0);
|
|
122
|
+
(0, import_react.useLayoutEffect)(() => {
|
|
123
|
+
const hide = mount({
|
|
124
|
+
tabId: tab.id,
|
|
125
|
+
signal: tab.signal,
|
|
126
|
+
viewportId,
|
|
127
|
+
applicationOrigin: window.location.origin,
|
|
128
|
+
initial: initial.current,
|
|
129
|
+
initialUrl: initialUrl.current,
|
|
130
|
+
openTab: (url) => {
|
|
131
|
+
tab.actions.openTab("browser", { params: { url }, revealIfOpened: false });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
setMountEpoch((value) => value + 1);
|
|
135
|
+
return hide;
|
|
136
|
+
}, [mount, tab.id, tab.signal, tab.actions, viewportId, props.actions]);
|
|
137
|
+
const unknown = frame.address === "unknown";
|
|
138
|
+
const externalUrl = unknown ? void 0 : target?.url;
|
|
139
|
+
const sandboxed = frame.sandboxEnabled;
|
|
140
|
+
const failure = state?.addressFailure;
|
|
141
|
+
const error = frame.error;
|
|
142
|
+
const submit = (event) => {
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
loadUrl(tab.id, draft);
|
|
145
|
+
};
|
|
146
|
+
const annotationOwner = (0, import_react.useMemo)(() => ({
|
|
147
|
+
tabId: tab.id,
|
|
148
|
+
url: unknown ? void 0 : frame.target?.url,
|
|
149
|
+
loading: frame.loading || mountEpoch === 0,
|
|
150
|
+
annotate: (options) => props.annotate(tab.id, options),
|
|
151
|
+
cancelAnnotation: () => {
|
|
152
|
+
props.cancelAnnotation(tab.id);
|
|
153
|
+
}
|
|
154
|
+
}), [tab.id, unknown, frame.target?.url, frame.loading, mountEpoch, props.annotate, props.cancelAnnotation]);
|
|
155
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Browser_default.root, children: [
|
|
156
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: Browser_default.toolbar, onSubmit: submit, children: [
|
|
157
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: Browser_default.tool, "aria-label": t("back"), title: t("back"), disabled: !frame.canGoBack, onClick: () => {
|
|
158
|
+
goBack(tab.id);
|
|
159
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronLeftOutlineRegular, {}) }),
|
|
160
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: Browser_default.tool, "aria-label": t("forward"), title: t("forward"), disabled: !frame.canGoForward, onClick: () => {
|
|
161
|
+
goForward(tab.id);
|
|
162
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronRightOutlineRegular, {}) }),
|
|
163
|
+
props.renderSlot("sidebar.right.tab.browser.toolbar", annotationOwner),
|
|
164
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tooltip, { label: t("reload"), shortcutKeys: tab.refreshShortcut?.keys, side: "bottom", delayMs: 500, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: Browser_default.tool, "aria-label": t("reload"), "aria-keyshortcuts": tab.refreshShortcut?.aria, disabled: target === void 0 || mountEpoch === 0, onClick: () => {
|
|
165
|
+
reload(tab.id);
|
|
166
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconRefreshOutlineRegular, {}) }) }),
|
|
167
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Browser_default.addressBox, children: [
|
|
168
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
169
|
+
"input",
|
|
170
|
+
{
|
|
171
|
+
className: [Browser_default.address, unknown ? Browser_default.addressUnknown : ""].join(" "),
|
|
172
|
+
value: draft,
|
|
173
|
+
"aria-label": t("address.placeholder"),
|
|
174
|
+
placeholder: t("address.placeholder"),
|
|
175
|
+
spellCheck: false,
|
|
176
|
+
onChange: (event) => {
|
|
177
|
+
setDraft(event.currentTarget.value);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
),
|
|
181
|
+
unknown && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Browser_default.addressChanged, children: t("address.changed") }),
|
|
182
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", className: [Browser_default.tool, Browser_default.addressGo].join(" "), "aria-label": t("go"), title: t("go"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconLinkOutlineRegular, {}) })
|
|
183
|
+
] }),
|
|
184
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
185
|
+
"button",
|
|
186
|
+
{
|
|
187
|
+
type: "button",
|
|
188
|
+
className: Browser_default.tool,
|
|
189
|
+
"aria-label": t("external"),
|
|
190
|
+
title: t("external"),
|
|
191
|
+
disabled: externalUrl === void 0,
|
|
192
|
+
onClick: externalUrl === void 0 ? void 0 : () => {
|
|
193
|
+
window.open(externalUrl, "_blank", "noopener,noreferrer");
|
|
194
|
+
},
|
|
195
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconRightUpOutlineRegular, { size: 14 })
|
|
196
|
+
}
|
|
197
|
+
),
|
|
198
|
+
sandboxed !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
199
|
+
"button",
|
|
200
|
+
{
|
|
201
|
+
type: "button",
|
|
202
|
+
className: [Browser_default.tool, sandboxed ? "" : Browser_default.sandboxOff].join(" "),
|
|
203
|
+
"aria-label": t(sandboxed ? "sandbox.disable" : "sandbox.enable"),
|
|
204
|
+
title: t(sandboxed ? "sandbox.disable" : "sandbox.enable"),
|
|
205
|
+
"aria-pressed": !sandboxed,
|
|
206
|
+
onClick: () => {
|
|
207
|
+
setSandbox(tab.id, !sandboxed);
|
|
208
|
+
},
|
|
209
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SandboxPolicyIcon, { sandboxed })
|
|
210
|
+
}
|
|
211
|
+
)
|
|
212
|
+
] }),
|
|
213
|
+
sandboxed === false && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: Browser_default.sandboxWarning, role: "status", children: t("sandbox.warning") }),
|
|
214
|
+
error !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: Browser_default.failure, role: "status", children: error.code !== void 0 && error.description !== void 0 ? t("load.failed.detail", { code: String(error.code), description: error.description }) : t("load.failed") }),
|
|
215
|
+
failure !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: Browser_default.failure, role: "alert", children: t(`error.${failure}`) }),
|
|
216
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Browser_default.content, "aria-busy": frame.loading, children: [
|
|
217
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { id: viewportId, className: Browser_default.viewport, "aria-label": t("type.label") }),
|
|
218
|
+
restoreTarget !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: Browser_default.restore, "aria-label": t("restore.previous"), children: [
|
|
219
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Browser_default.restoreLabel, children: t("restore.previous") }),
|
|
220
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Browser_default.restoreTitle, children: restoreTarget.title }),
|
|
221
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Browser_default.restoreUrl, children: restoreTarget.url }),
|
|
222
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", size: "sm", disabled: mountEpoch === 0, onClick: () => {
|
|
223
|
+
restore(tab.id);
|
|
224
|
+
}, children: t("restore.action") })
|
|
225
|
+
] }),
|
|
226
|
+
restoreTarget === void 0 && (target === void 0 || frame.loading) && error === void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: Browser_default.placeholder, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: Browser_default.start, children: t(target === void 0 ? "start" : "loading") }) })
|
|
227
|
+
] }),
|
|
228
|
+
props.renderSlot("sidebar.right.tab.browser.annotations", annotationOwner),
|
|
229
|
+
unknown && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Browser_default.limit, children: t("address.unknown") })
|
|
230
|
+
] });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/view/BrowserTitle.tsx
|
|
234
|
+
var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
235
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
236
|
+
function BrowserTitle({ useTabInfo, useStore }) {
|
|
237
|
+
const { tab } = useTabInfo();
|
|
238
|
+
const entry = useStore((state) => currentBrowserTarget(state.byTab[tab.id]));
|
|
239
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
240
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.IconGlobeOutlineRegular, { className: Browser_default.titleIcon }),
|
|
241
|
+
entry?.title ?? tab.title
|
|
242
|
+
] });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/browser/BrowserController.ts
|
|
246
|
+
var import_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
247
|
+
|
|
248
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/browser/url.ts
|
|
249
|
+
var MAX_BROWSER_URL_LENGTH = 16 * 1024;
|
|
250
|
+
function parseBrowserAddress(input, applicationOrigin) {
|
|
251
|
+
const trimmed = input.trim();
|
|
252
|
+
if (trimmed === "") return { ok: false, reason: "empty" };
|
|
253
|
+
if (trimmed.length > MAX_BROWSER_URL_LENGTH) return { ok: false, reason: "invalid" };
|
|
254
|
+
const explicitScheme = /^[A-Za-z][A-Za-z\d+.-]*:(?!\d+(?:[/?#]|$))/u.test(trimmed);
|
|
255
|
+
const candidate = explicitScheme ? trimmed : `https://${trimmed}`;
|
|
256
|
+
let url;
|
|
257
|
+
try {
|
|
258
|
+
url = new URL(candidate);
|
|
259
|
+
} catch {
|
|
260
|
+
return { ok: false, reason: "invalid" };
|
|
261
|
+
}
|
|
262
|
+
if (url.username !== "" || url.password !== "") return { ok: false, reason: "credentials" };
|
|
263
|
+
if (url.protocol === "https:" || url.protocol === "http:") {
|
|
264
|
+
if (applicationOrigin !== void 0 && applicationOrigin !== "null") {
|
|
265
|
+
try {
|
|
266
|
+
if (url.origin === new URL(applicationOrigin).origin) return { ok: false, reason: "application-origin" };
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return { ok: true, target: { kind: url.protocol === "https:" ? "https" : "http", url: url.href, title: url.hostname } };
|
|
271
|
+
}
|
|
272
|
+
return { ok: false, reason: "protocol" };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/browser/BrowserController.ts
|
|
276
|
+
var BrowserController = class {
|
|
277
|
+
/** @param options - identity, persistence, page factory and source-tab navigation. */
|
|
278
|
+
constructor(options) {
|
|
279
|
+
this.options = options;
|
|
280
|
+
this.actions = options.actions;
|
|
281
|
+
this.checkpoint = options.initial;
|
|
282
|
+
this.page = options.createPage({
|
|
283
|
+
initial: options.initial,
|
|
284
|
+
persist: (state) => {
|
|
285
|
+
if (this.disposed) return;
|
|
286
|
+
this.checkpoint = state;
|
|
287
|
+
this.actions.replace(options.tabId, state);
|
|
288
|
+
},
|
|
289
|
+
openRequested: (value) => {
|
|
290
|
+
if (this.disposed) return;
|
|
291
|
+
const result = parseBrowserAddress(value, options.applicationOrigin);
|
|
292
|
+
if (!result.ok) {
|
|
293
|
+
this.addressFailed(result.reason);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
options.openTab(result.target.url);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
this.store = (0, import_dsh_client_store.createSnapshotStore)({
|
|
300
|
+
frame: this.page.frame.getSnapshot(),
|
|
301
|
+
restoreTarget: currentBrowserTarget(this.checkpoint),
|
|
302
|
+
addressFailure: void 0,
|
|
303
|
+
addressRevision: 0
|
|
304
|
+
});
|
|
305
|
+
this.unsubscribe = this.page.frame.subscribe(() => {
|
|
306
|
+
if (this.disposed) return;
|
|
307
|
+
const current = this.store.getSnapshot();
|
|
308
|
+
const frame = this.page.frame.getSnapshot();
|
|
309
|
+
const changed = frame.target?.url !== current.frame.target?.url;
|
|
310
|
+
this.store.set({
|
|
311
|
+
frame,
|
|
312
|
+
restoreTarget: frame.target === void 0 ? currentBrowserTarget(this.checkpoint) : void 0,
|
|
313
|
+
addressFailure: changed ? void 0 : current.addressFailure,
|
|
314
|
+
addressRevision: current.addressRevision + Number(changed)
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
options.signal.addEventListener("abort", this.abort, { once: true });
|
|
318
|
+
}
|
|
319
|
+
options;
|
|
320
|
+
page;
|
|
321
|
+
store;
|
|
322
|
+
unsubscribe;
|
|
323
|
+
actions;
|
|
324
|
+
checkpoint;
|
|
325
|
+
started = false;
|
|
326
|
+
disposed = false;
|
|
327
|
+
disposal;
|
|
328
|
+
annotation;
|
|
329
|
+
abort = () => {
|
|
330
|
+
void this.dispose();
|
|
331
|
+
};
|
|
332
|
+
/** @returns immutable state for the common toolbar. */
|
|
333
|
+
getSnapshot = () => this.store.getSnapshot();
|
|
334
|
+
/** @param listener - state invalidation. @returns unsubscribe callback. */
|
|
335
|
+
subscribe = (listener) => this.store.subscribe(listener);
|
|
336
|
+
/**
|
|
337
|
+
* Attach the page without transferring ownership of its tab occurrence.
|
|
338
|
+
* @param viewportId - mounted content container.
|
|
339
|
+
* @returns physical attachment cleanup only.
|
|
340
|
+
*/
|
|
341
|
+
mount(viewportId) {
|
|
342
|
+
this.publishSaved();
|
|
343
|
+
const unmount = this.page.presentation.mount(viewportId);
|
|
344
|
+
return () => {
|
|
345
|
+
this.cancelAnnotation();
|
|
346
|
+
unmount();
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
/** @param options - localized picker configuration. @returns saved evidence or cancellation. */
|
|
350
|
+
async annotate(options) {
|
|
351
|
+
this.cancelAnnotation();
|
|
352
|
+
const frame = this.page.frame.getSnapshot();
|
|
353
|
+
if (this.disposed || frame.loading || frame.address === "unknown" || frame.target === void 0 || this.page.presentation.annotate === void 0) throw new Error("annotation-unavailable");
|
|
354
|
+
const lifetime = new AbortController();
|
|
355
|
+
this.annotation = lifetime;
|
|
356
|
+
try {
|
|
357
|
+
const result = await this.page.presentation.annotate(frame.target.url, options, lifetime.signal);
|
|
358
|
+
return lifetime.signal.aborted ? { cancelled: true } : result;
|
|
359
|
+
} finally {
|
|
360
|
+
if (this.annotation === lifetime) this.annotation = void 0;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/** Cancel the current tab's picker. */
|
|
364
|
+
cancelAnnotation() {
|
|
365
|
+
this.annotation?.abort();
|
|
366
|
+
this.annotation = void 0;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Consume initial navigation once; a saved checkpoint alone never starts a page.
|
|
370
|
+
* @param initialUrl - explicit typed-open address, or absence.
|
|
371
|
+
*/
|
|
372
|
+
start(initialUrl) {
|
|
373
|
+
if (this.started || this.disposed) return;
|
|
374
|
+
this.started = true;
|
|
375
|
+
if (initialUrl !== void 0) this.loadUrl(initialUrl);
|
|
376
|
+
}
|
|
377
|
+
/** Load the saved address only after an explicit restore action. */
|
|
378
|
+
restore() {
|
|
379
|
+
const target = this.store.getSnapshot().restoreTarget;
|
|
380
|
+
if (target !== void 0) this.loadUrl(target.url);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Validate an address before navigation, publishing invalid input for correction.
|
|
384
|
+
* @param value - address-bar or typed-open input.
|
|
385
|
+
*/
|
|
386
|
+
loadUrl(value) {
|
|
387
|
+
if (this.disposed) return;
|
|
388
|
+
const parsed = parseBrowserAddress(value, this.options.applicationOrigin);
|
|
389
|
+
if (!parsed.ok) {
|
|
390
|
+
this.addressFailed(parsed.reason);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
this.command(() => {
|
|
394
|
+
this.page.frame.loadUrl(parsed.target);
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
/** Delegate Back to the page's navigation provider. */
|
|
398
|
+
goBack() {
|
|
399
|
+
this.command(() => {
|
|
400
|
+
this.page.frame.goBack();
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
/** Delegate Forward to the page's navigation provider. */
|
|
404
|
+
goForward() {
|
|
405
|
+
this.command(() => {
|
|
406
|
+
this.page.frame.goForward();
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
/** Restore a saved address, or reload the already requested page. */
|
|
410
|
+
reload() {
|
|
411
|
+
if (this.store.getSnapshot().restoreTarget !== void 0) this.restore();
|
|
412
|
+
else this.command(() => {
|
|
413
|
+
this.page.frame.reload();
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Apply the optional embedding-sandbox control; unsupported providers remain unchanged.
|
|
418
|
+
* @param enabled - whether to enforce the provider's embedding sandbox.
|
|
419
|
+
*/
|
|
420
|
+
setSandbox(enabled) {
|
|
421
|
+
const sandbox = this.page.frame.sandbox;
|
|
422
|
+
if (sandbox !== void 0) this.command(() => {
|
|
423
|
+
sandbox.setEnabled(enabled);
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Redirect future checkpoint writes to a replacement Session binding.
|
|
428
|
+
* @param actions - replacement persistence writer.
|
|
429
|
+
*/
|
|
430
|
+
rebind(actions) {
|
|
431
|
+
this.actions = actions;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Release the page and detach occurrence and state listeners.
|
|
435
|
+
* @returns after page teardown; repeated callers join the same disposal.
|
|
436
|
+
*/
|
|
437
|
+
dispose() {
|
|
438
|
+
if (this.disposal !== void 0) return this.disposal;
|
|
439
|
+
this.disposed = true;
|
|
440
|
+
this.cancelAnnotation();
|
|
441
|
+
this.options.signal.removeEventListener("abort", this.abort);
|
|
442
|
+
this.unsubscribe();
|
|
443
|
+
this.disposal = this.page.frame.dispose();
|
|
444
|
+
return this.disposal;
|
|
445
|
+
}
|
|
446
|
+
publishSaved() {
|
|
447
|
+
if (this.checkpoint !== void 0) this.actions.replace(this.options.tabId, this.checkpoint);
|
|
448
|
+
}
|
|
449
|
+
addressFailed(reason) {
|
|
450
|
+
this.store.set({ ...this.store.getSnapshot(), addressFailure: reason });
|
|
451
|
+
}
|
|
452
|
+
command(run) {
|
|
453
|
+
if (this.disposed) return;
|
|
454
|
+
this.cancelAnnotation();
|
|
455
|
+
const current = this.store.getSnapshot();
|
|
456
|
+
this.store.set({ ...current, addressFailure: void 0, addressRevision: current.addressRevision + 1 });
|
|
457
|
+
run();
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
function createBrowserControllers(actions, createPage, isTabOpen) {
|
|
461
|
+
let currentActions = actions;
|
|
462
|
+
const controllers = /* @__PURE__ */ new Map();
|
|
463
|
+
const controller = (id) => controllers.get(id)?.controller;
|
|
464
|
+
return {
|
|
465
|
+
annotate: (id, options) => controller(id)?.annotate(options) ?? Promise.reject(new Error("annotation-unavailable")),
|
|
466
|
+
cancelAnnotation: (id) => {
|
|
467
|
+
controller(id)?.cancelAnnotation();
|
|
468
|
+
},
|
|
469
|
+
keyedHooks: { browserState: (key) => controller(key) },
|
|
470
|
+
mount(request) {
|
|
471
|
+
const { tabId, signal } = request;
|
|
472
|
+
if (signal.aborted) return () => {
|
|
473
|
+
};
|
|
474
|
+
let held = controllers.get(tabId);
|
|
475
|
+
if (held?.signal !== signal) {
|
|
476
|
+
if (held !== void 0) {
|
|
477
|
+
held.signal.removeEventListener("abort", held.forget);
|
|
478
|
+
void held.controller.dispose();
|
|
479
|
+
}
|
|
480
|
+
const created = new BrowserController({ ...request, actions: currentActions, createPage });
|
|
481
|
+
const forget = () => {
|
|
482
|
+
controllers.delete(tabId);
|
|
483
|
+
if (!isTabOpen(tabId)) currentActions.forget(tabId);
|
|
484
|
+
};
|
|
485
|
+
held = { signal, controller: created, forget };
|
|
486
|
+
controllers.set(tabId, held);
|
|
487
|
+
signal.addEventListener("abort", forget, { once: true });
|
|
488
|
+
}
|
|
489
|
+
const hide = held.controller.mount(request.viewportId);
|
|
490
|
+
held.controller.start(request.initialUrl);
|
|
491
|
+
return hide;
|
|
492
|
+
},
|
|
493
|
+
dispose: async () => {
|
|
494
|
+
const pending = [...controllers.values()].map(({ signal, controller: controller2, forget }) => {
|
|
495
|
+
signal.removeEventListener("abort", forget);
|
|
496
|
+
return controller2.dispose();
|
|
497
|
+
});
|
|
498
|
+
controllers.clear();
|
|
499
|
+
await Promise.all(pending);
|
|
500
|
+
},
|
|
501
|
+
rebind: (actions2) => {
|
|
502
|
+
currentActions = actions2;
|
|
503
|
+
for (const { controller: controller2 } of controllers.values()) controller2.rebind(actions2);
|
|
504
|
+
},
|
|
505
|
+
loadUrl: (id, value) => {
|
|
506
|
+
controller(id)?.loadUrl(value);
|
|
507
|
+
},
|
|
508
|
+
restore: (id) => {
|
|
509
|
+
controller(id)?.restore();
|
|
510
|
+
},
|
|
511
|
+
goBack: (id) => {
|
|
512
|
+
controller(id)?.goBack();
|
|
513
|
+
},
|
|
514
|
+
goForward: (id) => {
|
|
515
|
+
controller(id)?.goForward();
|
|
516
|
+
},
|
|
517
|
+
reload: (id) => {
|
|
518
|
+
controller(id)?.reload();
|
|
519
|
+
},
|
|
520
|
+
setSandbox: (id, enabled) => {
|
|
521
|
+
controller(id)?.setSandbox(enabled);
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/browser/IframeImpl.ts
|
|
527
|
+
var import_dsh_client_store2 = require("@deepseek-ai/dsh-client-store");
|
|
528
|
+
|
|
529
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/browser/BrowserNavigation.ts
|
|
530
|
+
var MAX_BROWSER_HISTORY = 100;
|
|
531
|
+
var BrowserNavigation = class _BrowserNavigation {
|
|
532
|
+
value;
|
|
533
|
+
/**
|
|
534
|
+
* @param initial - persisted state restored for this tab, or a fresh empty state.
|
|
535
|
+
*/
|
|
536
|
+
constructor(initial = _BrowserNavigation.empty()) {
|
|
537
|
+
this.value = initial;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Create state before a tab has a controlled navigation target.
|
|
541
|
+
* @returns empty serializable state.
|
|
542
|
+
*/
|
|
543
|
+
static empty() {
|
|
544
|
+
return { entries: [], index: -1, request: void 0, navigation: { status: "empty" }, failure: void 0 };
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Read the selected application-history entry.
|
|
548
|
+
* @param state - serializable tab state.
|
|
549
|
+
* @returns the current target, if any.
|
|
550
|
+
*/
|
|
551
|
+
static current(state) {
|
|
552
|
+
return state === void 0 || state.index < 0 ? void 0 : state.entries[state.index];
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Test whether the Web carrier can use the preceding application-history entry.
|
|
556
|
+
* @param state - serializable tab state.
|
|
557
|
+
* @returns whether Back is available.
|
|
558
|
+
*/
|
|
559
|
+
static canGoBack(state) {
|
|
560
|
+
return state.navigation.status !== "unknown" && state.index > 0;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Test whether the Web carrier can use the following application-history entry.
|
|
564
|
+
* @param state - serializable tab state.
|
|
565
|
+
* @returns whether Forward is available.
|
|
566
|
+
*/
|
|
567
|
+
static canGoForward(state) {
|
|
568
|
+
return state.navigation.status !== "unknown" && state.index >= 0 && state.index < state.entries.length - 1;
|
|
569
|
+
}
|
|
570
|
+
/** Current immutable serializable state. */
|
|
571
|
+
get snapshot() {
|
|
572
|
+
return this.value;
|
|
573
|
+
}
|
|
574
|
+
/** Whether the Web iframe can safely use the application-owned Back entry. */
|
|
575
|
+
get canGoBack() {
|
|
576
|
+
return _BrowserNavigation.canGoBack(this.value);
|
|
577
|
+
}
|
|
578
|
+
/** Whether the Web iframe can safely use the application-owned Forward entry. */
|
|
579
|
+
get canGoForward() {
|
|
580
|
+
return _BrowserNavigation.canGoForward(this.value);
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Add a controlled target and discard its stale forward branch.
|
|
584
|
+
* @param target - validated canonical target.
|
|
585
|
+
* @returns the new load request.
|
|
586
|
+
*/
|
|
587
|
+
navigate(target) {
|
|
588
|
+
const entries = [...this.value.entries.slice(0, this.value.index + 1), target];
|
|
589
|
+
if (entries.length > MAX_BROWSER_HISTORY) entries.splice(0, entries.length - MAX_BROWSER_HISTORY);
|
|
590
|
+
return this.request(target, { ...this.value, entries, index: entries.length - 1 });
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Select the preceding application-known target.
|
|
594
|
+
* @returns a new load request, or undefined when unavailable.
|
|
595
|
+
*/
|
|
596
|
+
back() {
|
|
597
|
+
if (!this.canGoBack) return void 0;
|
|
598
|
+
const index = this.value.index - 1;
|
|
599
|
+
const target = this.value.entries[index];
|
|
600
|
+
return this.request(target, { ...this.value, index });
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Select the following application-known target.
|
|
604
|
+
* @returns a new load request, or undefined when unavailable.
|
|
605
|
+
*/
|
|
606
|
+
forward() {
|
|
607
|
+
if (!this.canGoForward) return void 0;
|
|
608
|
+
const index = this.value.index + 1;
|
|
609
|
+
const target = this.value.entries[index];
|
|
610
|
+
return this.request(target, { ...this.value, index });
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Start another load of the last application-known target.
|
|
614
|
+
* @returns a new load request, or undefined before the first target.
|
|
615
|
+
*/
|
|
616
|
+
reload() {
|
|
617
|
+
const target = _BrowserNavigation.current(this.value);
|
|
618
|
+
return target === void 0 ? void 0 : this.request(target, this.value);
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Record a frame load for its captured revision.
|
|
622
|
+
* @param revision - revision bound to the rendered frame.
|
|
623
|
+
*/
|
|
624
|
+
frameLoaded(revision) {
|
|
625
|
+
const navigation = this.value.navigation;
|
|
626
|
+
if (navigation.status === "empty" || navigation.revision !== revision) return;
|
|
627
|
+
if (navigation.status === "loading") {
|
|
628
|
+
this.value = { ...this.value, navigation: { status: "known", revision } };
|
|
629
|
+
} else if (navigation.status === "known") {
|
|
630
|
+
this.value = { ...this.value, navigation: { status: "unknown", revision } };
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
request(target, basis) {
|
|
634
|
+
const request = { revision: (this.value.request?.revision ?? 0) + 1, target };
|
|
635
|
+
this.value = {
|
|
636
|
+
...basis,
|
|
637
|
+
request,
|
|
638
|
+
navigation: { status: "loading", revision: request.revision },
|
|
639
|
+
failure: void 0
|
|
640
|
+
};
|
|
641
|
+
return request;
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/browser/IframeImpl.ts
|
|
646
|
+
var IframeImpl = class {
|
|
647
|
+
/** @param options - initial checkpoint and persistence writer. @param presentation - iframe DOM adapter. */
|
|
648
|
+
constructor(options, presentation) {
|
|
649
|
+
this.options = options;
|
|
650
|
+
this.presentation = presentation;
|
|
651
|
+
this.navigation = new BrowserNavigation(options.initial);
|
|
652
|
+
this.store = (0, import_dsh_client_store2.createSnapshotStore)({ ...emptyBrowserFrame(), sandboxEnabled: this.sandboxed });
|
|
653
|
+
}
|
|
654
|
+
options;
|
|
655
|
+
presentation;
|
|
656
|
+
sandbox = { setEnabled: (enabled) => {
|
|
657
|
+
this.setSandbox(enabled);
|
|
658
|
+
} };
|
|
659
|
+
navigation;
|
|
660
|
+
store;
|
|
661
|
+
sandboxed = true;
|
|
662
|
+
error;
|
|
663
|
+
disposed = false;
|
|
664
|
+
disposal;
|
|
665
|
+
/**
|
|
666
|
+
* Record a controlled load or a later navigation to an unreadable address.
|
|
667
|
+
* @param revision - document generation whose iframe emitted load.
|
|
668
|
+
*/
|
|
669
|
+
handleLoaded(revision) {
|
|
670
|
+
if (this.disposed) return;
|
|
671
|
+
this.navigation.frameLoaded(revision);
|
|
672
|
+
this.publish();
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Publish a failure only for the current document generation.
|
|
676
|
+
* @param revision - document generation whose iframe reported failure.
|
|
677
|
+
*/
|
|
678
|
+
handleLoadFailed(revision) {
|
|
679
|
+
if (this.disposed || this.navigation.snapshot.request?.revision !== revision) return;
|
|
680
|
+
this.error = { code: void 0, description: void 0 };
|
|
681
|
+
this.publish();
|
|
682
|
+
}
|
|
683
|
+
/** @returns immutable navigation state. */
|
|
684
|
+
getSnapshot = () => this.store.getSnapshot();
|
|
685
|
+
/** @param listener - state invalidation. @returns unsubscribe callback. */
|
|
686
|
+
subscribe = (listener) => this.store.subscribe(listener);
|
|
687
|
+
/** @param target - validated address; submitting the current address reloads it. */
|
|
688
|
+
loadUrl(target) {
|
|
689
|
+
if (this.disposed) return;
|
|
690
|
+
const request = BrowserNavigation.current(this.navigation.snapshot)?.url === target.url ? this.navigation.reload() : this.navigation.navigate(target);
|
|
691
|
+
this.load(request);
|
|
692
|
+
}
|
|
693
|
+
/** Move through application-known history while the iframe address remains known. */
|
|
694
|
+
goBack() {
|
|
695
|
+
if (!this.disposed) this.load(this.navigation.back());
|
|
696
|
+
}
|
|
697
|
+
/** Move through application-known history while the iframe address remains known. */
|
|
698
|
+
goForward() {
|
|
699
|
+
if (!this.disposed) this.load(this.navigation.forward());
|
|
700
|
+
}
|
|
701
|
+
/** Reload the last application-known address. */
|
|
702
|
+
reload() {
|
|
703
|
+
if (!this.disposed) this.load(this.navigation.reload());
|
|
704
|
+
}
|
|
705
|
+
/** @returns after the presentation has been removed; repeated calls join disposal. */
|
|
706
|
+
dispose() {
|
|
707
|
+
if (this.disposal !== void 0) return this.disposal;
|
|
708
|
+
this.disposed = true;
|
|
709
|
+
this.presentation.dispose();
|
|
710
|
+
this.disposal = Promise.resolve();
|
|
711
|
+
return this.disposal;
|
|
712
|
+
}
|
|
713
|
+
setSandbox(enabled) {
|
|
714
|
+
if (this.disposed || enabled === this.sandboxed) return;
|
|
715
|
+
this.sandboxed = enabled;
|
|
716
|
+
if (this.store.getSnapshot().target === void 0) {
|
|
717
|
+
this.store.set({ ...this.store.getSnapshot(), sandboxEnabled: enabled });
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
this.load(this.navigation.reload());
|
|
721
|
+
}
|
|
722
|
+
load(request) {
|
|
723
|
+
if (request === void 0) return;
|
|
724
|
+
this.error = void 0;
|
|
725
|
+
this.publish();
|
|
726
|
+
this.presentation.show({ target: request.target, revision: request.revision, sandboxed: this.sandboxed });
|
|
727
|
+
}
|
|
728
|
+
snapshot() {
|
|
729
|
+
const state = this.navigation.snapshot;
|
|
730
|
+
const target = BrowserNavigation.current(state);
|
|
731
|
+
return {
|
|
732
|
+
target,
|
|
733
|
+
address: target === void 0 ? "empty" : state.navigation.status === "unknown" ? "unknown" : "requested",
|
|
734
|
+
loading: state.navigation.status === "loading" && this.error === void 0,
|
|
735
|
+
canGoBack: this.navigation.canGoBack,
|
|
736
|
+
canGoForward: this.navigation.canGoForward,
|
|
737
|
+
error: this.error,
|
|
738
|
+
sandboxEnabled: this.sandboxed
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
publish() {
|
|
742
|
+
this.store.set(this.snapshot());
|
|
743
|
+
this.options.persist(this.navigation.snapshot);
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/annotation/protocol.ts
|
|
748
|
+
function object(value) {
|
|
749
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
750
|
+
}
|
|
751
|
+
function string(value, limit) {
|
|
752
|
+
return typeof value === "string" && value.length <= limit;
|
|
753
|
+
}
|
|
754
|
+
function number(value) {
|
|
755
|
+
return typeof value === "number" && Number.isFinite(value) && Math.abs(value) <= 1e7;
|
|
756
|
+
}
|
|
757
|
+
function rect(value) {
|
|
758
|
+
return object(value) && number(value.x) && number(value.y) && number(value.width) && number(value.height) && value.width >= 0 && value.height >= 0;
|
|
759
|
+
}
|
|
760
|
+
function isAnnotationResult(value) {
|
|
761
|
+
if (!object(value)) return false;
|
|
762
|
+
if (value.cancelled === true) return Object.keys(value).length === 1;
|
|
763
|
+
if (value.intent !== "comment" && value.intent !== "question" || !string(value.note, 1e3) || !value.note.trim() || !string(value.url, 8192) || !string(value.title, 300))
|
|
764
|
+
return false;
|
|
765
|
+
if (value.styleChanges !== void 0 && (!object(value.styleChanges) || Object.keys(value.styleChanges).length > 10 || !Object.entries(value.styleChanges).every(
|
|
766
|
+
([key, change]) => [
|
|
767
|
+
"color",
|
|
768
|
+
"background-color",
|
|
769
|
+
"opacity",
|
|
770
|
+
"font-family",
|
|
771
|
+
"font-size",
|
|
772
|
+
"font-weight",
|
|
773
|
+
"padding",
|
|
774
|
+
"margin",
|
|
775
|
+
"gap"
|
|
776
|
+
].includes(key) && object(change) && string(change.before, 1e3) && string(change.after, 1e3)
|
|
777
|
+
)))
|
|
778
|
+
return false;
|
|
779
|
+
if (value.screenshot !== void 0 && (!object(value.screenshot) || value.screenshot.mediaType !== "image/jpeg" || !string(value.screenshot.data, 4e6) || !/^[A-Za-z0-9+/]+={0,2}$/.test(value.screenshot.data) || !number(value.screenshot.width) || !number(value.screenshot.height) || value.screenshot.width <= 0 || value.screenshot.width > 1600 || value.screenshot.height <= 0 || value.screenshot.height > 1600))
|
|
780
|
+
return false;
|
|
781
|
+
try {
|
|
782
|
+
if (!["http:", "https:"].includes(new URL(value.url).protocol)) return false;
|
|
783
|
+
} catch {
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
if (!object(value.viewport) || !number(value.viewport.width) || !number(value.viewport.height) || value.viewport.width <= 0 || value.viewport.height <= 0)
|
|
787
|
+
return false;
|
|
788
|
+
const selection = value.selection;
|
|
789
|
+
if (!object(selection) || !["element", "region", "point"].includes(String(selection.kind)) || !rect(selection.rect) || !rect(selection.viewportRect) || !object(selection.scroll) || !number(selection.scroll.x) || !number(selection.scroll.y) || !Array.isArray(selection.candidates) || selection.candidates.length > 12)
|
|
790
|
+
return false;
|
|
791
|
+
if (selection.kind === "element" && selection.candidates.length !== 1) return false;
|
|
792
|
+
for (const item of selection.candidates) {
|
|
793
|
+
if (!object(item) || !string(item.selector, 8192) || !Array.isArray(item.shadowHosts) || item.shadowHosts.length > 32 || !item.shadowHosts.every((x) => string(x, 8192)) || item.matchCount !== 1 || !string(item.tag, 200) || !string(item.text, 400) || !rect(item.rect) || !object(item.styles) || Object.keys(item.styles).length > 16 || !Object.entries(item.styles).every(([key, val]) => string(key, 100) && string(val, 1e3)) || item.source !== void 0 && !string(item.source, 1e3))
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
796
|
+
try {
|
|
797
|
+
return JSON.stringify({ ...value, screenshot: void 0 }).length <= 64e3;
|
|
798
|
+
} catch {
|
|
799
|
+
return false;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/annotation/iframe-annotation.ts
|
|
804
|
+
function annotateIframe(frame, url, options, signal) {
|
|
805
|
+
if (signal.aborted) return Promise.resolve({ cancelled: true });
|
|
806
|
+
return new Promise((resolve, reject) => {
|
|
807
|
+
const channel = new MessageChannel();
|
|
808
|
+
let ended = false;
|
|
809
|
+
let ready = false;
|
|
810
|
+
const handshake = setTimeout(() => {
|
|
811
|
+
if (!ready) finish(void 0, new Error("annotation-unavailable"));
|
|
812
|
+
}, 3e3);
|
|
813
|
+
function finish(value, error) {
|
|
814
|
+
if (ended) return;
|
|
815
|
+
ended = true;
|
|
816
|
+
channel.port1.postMessage({ type: "cancel" });
|
|
817
|
+
channel.port1.close();
|
|
818
|
+
channel.port2.close();
|
|
819
|
+
clearTimeout(handshake);
|
|
820
|
+
signal.removeEventListener("abort", cancel);
|
|
821
|
+
frame.removeEventListener("load", cancel);
|
|
822
|
+
if (error) reject(error);
|
|
823
|
+
else resolve(value || { cancelled: true });
|
|
824
|
+
}
|
|
825
|
+
function cancel() {
|
|
826
|
+
finish({ cancelled: true });
|
|
827
|
+
}
|
|
828
|
+
signal.addEventListener("abort", cancel, { once: true });
|
|
829
|
+
frame.addEventListener("load", cancel, { once: true });
|
|
830
|
+
channel.port1.onmessage = (event) => {
|
|
831
|
+
if (event.data?.ready === true && !ready) {
|
|
832
|
+
ready = true;
|
|
833
|
+
clearTimeout(handshake);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
if (!isAnnotationResult(event.data)) {
|
|
837
|
+
finish(void 0, new Error("annotation-invalid"));
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
if (!("cancelled" in event.data) && event.data.url !== url) {
|
|
841
|
+
finish(void 0, new Error("annotation-page-changed"));
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
finish(event.data);
|
|
845
|
+
};
|
|
846
|
+
try {
|
|
847
|
+
if (!frame.contentWindow) {
|
|
848
|
+
finish(void 0, new Error("annotation-unavailable"));
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
frame.contentWindow.postMessage(
|
|
852
|
+
{ channel: "dsh-browser-annotation-v1", type: "start", options },
|
|
853
|
+
new URL(url).origin,
|
|
854
|
+
[channel.port2]
|
|
855
|
+
);
|
|
856
|
+
frame.focus();
|
|
857
|
+
} catch (error) {
|
|
858
|
+
finish(void 0, error instanceof Error ? error : new Error("annotation-unavailable"));
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/view/IframePresentation.ts
|
|
864
|
+
var WEB_BROWSER_SANDBOX = "allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox";
|
|
865
|
+
var IframePresentation = class {
|
|
866
|
+
/** @param events - provider-owned load and remount callbacks. */
|
|
867
|
+
constructor(events) {
|
|
868
|
+
this.events = events;
|
|
869
|
+
}
|
|
870
|
+
events;
|
|
871
|
+
host;
|
|
872
|
+
element;
|
|
873
|
+
document;
|
|
874
|
+
rendered = false;
|
|
875
|
+
/** @param url - controller-known URL. @param options - picker copy. @param signal - lifetime. @returns saved annotation or cancellation. */
|
|
876
|
+
annotate(url, options, signal) {
|
|
877
|
+
if (this.element === void 0) return Promise.reject(new Error("annotation-unavailable"));
|
|
878
|
+
return annotateIframe(this.element, url, options, signal);
|
|
879
|
+
}
|
|
880
|
+
/** @param viewportId - mounted placeholder. @returns removes only the iframe presentation. */
|
|
881
|
+
mount(viewportId) {
|
|
882
|
+
const host = document.getElementById(viewportId);
|
|
883
|
+
if (host === null) throw new Error("iframe presentation: viewport is not mounted");
|
|
884
|
+
this.element?.remove();
|
|
885
|
+
this.element = void 0;
|
|
886
|
+
this.host = host;
|
|
887
|
+
if (this.rendered) this.events.remounted();
|
|
888
|
+
else this.render();
|
|
889
|
+
return () => {
|
|
890
|
+
if (this.host !== host) return;
|
|
891
|
+
this.element?.remove();
|
|
892
|
+
this.element = void 0;
|
|
893
|
+
this.host = void 0;
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Retain the prepared document and render it when a container is mounted.
|
|
898
|
+
* @param value - prepared document and event generation.
|
|
899
|
+
*/
|
|
900
|
+
show(value) {
|
|
901
|
+
this.document = value;
|
|
902
|
+
this.render();
|
|
903
|
+
}
|
|
904
|
+
/** Remove the owned DOM and retained presentation data. */
|
|
905
|
+
dispose() {
|
|
906
|
+
this.element?.remove();
|
|
907
|
+
this.element = void 0;
|
|
908
|
+
this.host = void 0;
|
|
909
|
+
this.document = void 0;
|
|
910
|
+
}
|
|
911
|
+
render() {
|
|
912
|
+
const current = this.document;
|
|
913
|
+
const host = this.host;
|
|
914
|
+
if (current === void 0 || host === void 0) return;
|
|
915
|
+
this.element?.remove();
|
|
916
|
+
const element = document.createElement("iframe");
|
|
917
|
+
element.className = Browser_default.frame;
|
|
918
|
+
element.src = current.target.url;
|
|
919
|
+
element.title = current.target.title;
|
|
920
|
+
element.referrerPolicy = "no-referrer";
|
|
921
|
+
element.dataset.sidebarBrowserFrame = "iframe";
|
|
922
|
+
if (current.sandboxed) element.setAttribute("sandbox", WEB_BROWSER_SANDBOX);
|
|
923
|
+
element.addEventListener("load", () => {
|
|
924
|
+
if (this.element === element) this.events.loaded(current.revision);
|
|
925
|
+
});
|
|
926
|
+
element.addEventListener("error", () => {
|
|
927
|
+
if (this.element === element) this.events.failed(current.revision);
|
|
928
|
+
});
|
|
929
|
+
this.element = element;
|
|
930
|
+
this.rendered = true;
|
|
931
|
+
host.append(element);
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/pages.ts
|
|
936
|
+
function createIframePage(options) {
|
|
937
|
+
const presentation = new IframePresentation({
|
|
938
|
+
loaded: (revision) => {
|
|
939
|
+
frame.handleLoaded(revision);
|
|
940
|
+
},
|
|
941
|
+
failed: (revision) => {
|
|
942
|
+
frame.handleLoadFailed(revision);
|
|
943
|
+
},
|
|
944
|
+
remounted: () => {
|
|
945
|
+
frame.reload();
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
const frame = new IframeImpl(options, presentation);
|
|
949
|
+
return { frame, presentation };
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/electron/ElectronWebViewImpl.ts
|
|
953
|
+
var import_dsh_client_store3 = require("@deepseek-ai/dsh-client-store");
|
|
954
|
+
var ElectronWebViewImpl = class {
|
|
955
|
+
/**
|
|
956
|
+
* @param options - saved address, persistence and source-tab opening callback.
|
|
957
|
+
* @param bridge - main-process guest operations.
|
|
958
|
+
* @param workspace - resolves the storage account once for this frame lifetime.
|
|
959
|
+
* @param presentation - tag and Sidebar placement adapter.
|
|
960
|
+
*/
|
|
961
|
+
constructor(options, bridge, workspace, presentation) {
|
|
962
|
+
this.options = options;
|
|
963
|
+
this.bridge = bridge;
|
|
964
|
+
this.workspace = workspace;
|
|
965
|
+
this.presentation = presentation;
|
|
966
|
+
this.checkpoint = currentBrowserTarget(options.initial);
|
|
967
|
+
this.store = (0, import_dsh_client_store3.createSnapshotStore)(emptyBrowserFrame());
|
|
968
|
+
}
|
|
969
|
+
options;
|
|
970
|
+
bridge;
|
|
971
|
+
workspace;
|
|
972
|
+
presentation;
|
|
973
|
+
store;
|
|
974
|
+
lifetime = new AbortController();
|
|
975
|
+
guestLifetime;
|
|
976
|
+
element;
|
|
977
|
+
lease;
|
|
978
|
+
workspaceKey;
|
|
979
|
+
initializing;
|
|
980
|
+
ready = false;
|
|
981
|
+
pending;
|
|
982
|
+
revision = 0;
|
|
983
|
+
firstDocument = true;
|
|
984
|
+
checkpoint;
|
|
985
|
+
disposal;
|
|
986
|
+
attachment;
|
|
987
|
+
releases = /* @__PURE__ */ new Set();
|
|
988
|
+
/** @returns immutable carrier-neutral navigation state. */
|
|
989
|
+
getSnapshot = () => this.store.getSnapshot();
|
|
990
|
+
/** @param listener - state invalidation. @returns unsubscribe callback. */
|
|
991
|
+
subscribe = (listener) => this.store.subscribe(listener);
|
|
992
|
+
/** A physical mount may recreate a lost guest; ordinary Sidebar hiding never calls this. */
|
|
993
|
+
attach() {
|
|
994
|
+
if (this.lifetime.signal.aborted) return;
|
|
995
|
+
this.attachment = new AbortController();
|
|
996
|
+
this.pending ??= this.store.getSnapshot().target;
|
|
997
|
+
if (this.pending !== void 0) {
|
|
998
|
+
this.store.set({
|
|
999
|
+
...this.store.getSnapshot(),
|
|
1000
|
+
address: "requested",
|
|
1001
|
+
loading: true,
|
|
1002
|
+
canGoBack: false,
|
|
1003
|
+
canGoForward: false,
|
|
1004
|
+
error: void 0
|
|
1005
|
+
});
|
|
1006
|
+
this.initialize();
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
/** Invalidate pending attachment before the containing DOM is removed. */
|
|
1010
|
+
detach() {
|
|
1011
|
+
this.attachment?.abort();
|
|
1012
|
+
this.attachment = void 0;
|
|
1013
|
+
this.pending = this.store.getSnapshot().target;
|
|
1014
|
+
void this.dropGuest();
|
|
1015
|
+
}
|
|
1016
|
+
/** @param target - validated address, loaded without replacing the guest. */
|
|
1017
|
+
loadUrl(target) {
|
|
1018
|
+
if (this.lifetime.signal.aborted) return;
|
|
1019
|
+
const current = this.store.getSnapshot();
|
|
1020
|
+
if (this.ready && current.address === "observed" && current.target?.url === target.url) {
|
|
1021
|
+
this.reload();
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
this.revision++;
|
|
1025
|
+
this.pending = target;
|
|
1026
|
+
this.store.set({ ...current, target, address: "requested", loading: true, error: void 0 });
|
|
1027
|
+
this.persist(target);
|
|
1028
|
+
if (this.ready) this.loadPending();
|
|
1029
|
+
else this.initialize();
|
|
1030
|
+
}
|
|
1031
|
+
/** Move backward through Chromium history. */
|
|
1032
|
+
goBack() {
|
|
1033
|
+
if (this.store.getSnapshot().canGoBack) this.navigate("goBack");
|
|
1034
|
+
}
|
|
1035
|
+
/** Move forward through Chromium history. */
|
|
1036
|
+
goForward() {
|
|
1037
|
+
if (this.store.getSnapshot().canGoForward) this.navigate("goForward");
|
|
1038
|
+
}
|
|
1039
|
+
/** Reload the actual current page, or retry failed guest creation. */
|
|
1040
|
+
reload() {
|
|
1041
|
+
const current = this.store.getSnapshot();
|
|
1042
|
+
if (this.lifetime.signal.aborted || current.target === void 0) return;
|
|
1043
|
+
if (!this.ready || current.error !== void 0) {
|
|
1044
|
+
this.revision++;
|
|
1045
|
+
this.pending = current.target;
|
|
1046
|
+
this.store.set({ ...current, loading: true, error: void 0 });
|
|
1047
|
+
if (this.ready) this.loadPending();
|
|
1048
|
+
else this.initialize();
|
|
1049
|
+
} else this.navigate("reload");
|
|
1050
|
+
}
|
|
1051
|
+
/** @returns after pending initialization and the owned guest have been released. */
|
|
1052
|
+
dispose() {
|
|
1053
|
+
if (this.disposal !== void 0) return this.disposal;
|
|
1054
|
+
this.lifetime.abort();
|
|
1055
|
+
this.pending = void 0;
|
|
1056
|
+
this.disposal = Promise.all([this.dropGuest(), this.initializing]).then(() => Promise.all(this.releases)).then(() => {
|
|
1057
|
+
});
|
|
1058
|
+
this.presentation.dispose();
|
|
1059
|
+
return this.disposal;
|
|
1060
|
+
}
|
|
1061
|
+
navigate(command) {
|
|
1062
|
+
if (this.lifetime.signal.aborted || !this.ready || this.element === void 0) return;
|
|
1063
|
+
this.revision++;
|
|
1064
|
+
this.pending = void 0;
|
|
1065
|
+
this.store.set({ ...this.store.getSnapshot(), loading: true, error: void 0 });
|
|
1066
|
+
try {
|
|
1067
|
+
this.element[command]();
|
|
1068
|
+
} catch (error) {
|
|
1069
|
+
this.commandFailed(error);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
initialize() {
|
|
1073
|
+
const attachment = this.attachment;
|
|
1074
|
+
if (attachment === void 0 || this.initializing !== void 0 || this.element !== void 0 || this.lifetime.signal.aborted) return;
|
|
1075
|
+
const signal = AbortSignal.any([this.lifetime.signal, attachment.signal]);
|
|
1076
|
+
this.initializing = this.createGuest(signal).catch(async (error) => {
|
|
1077
|
+
await this.dropGuest();
|
|
1078
|
+
if (!signal.aborted) this.commandFailed(error);
|
|
1079
|
+
}).finally(() => {
|
|
1080
|
+
this.initializing = void 0;
|
|
1081
|
+
if (this.attachment !== attachment && this.pending !== void 0) this.initialize();
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
async createGuest(attachmentSignal) {
|
|
1085
|
+
this.workspaceKey ??= await this.workspace(attachmentSignal);
|
|
1086
|
+
if (attachmentSignal.aborted) return;
|
|
1087
|
+
const reservation = await this.bridge.acquire(this.workspaceKey);
|
|
1088
|
+
if (attachmentSignal.aborted) {
|
|
1089
|
+
await this.release(reservation.lease);
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
this.lease = reservation.lease;
|
|
1093
|
+
this.guestLifetime = new AbortController();
|
|
1094
|
+
const signal = AbortSignal.any([attachmentSignal, this.guestLifetime.signal]);
|
|
1095
|
+
const element = this.presentation.createElement(reservation);
|
|
1096
|
+
this.element = element;
|
|
1097
|
+
const unsubscribeOpen = this.bridge.onOpenRequested(reservation.lease, (url) => {
|
|
1098
|
+
if (this.element === element && !signal.aborted) this.options.openRequested(url);
|
|
1099
|
+
});
|
|
1100
|
+
signal.addEventListener("abort", unsubscribeOpen, { once: true });
|
|
1101
|
+
element.addEventListener("dom-ready", () => {
|
|
1102
|
+
this.ready = true;
|
|
1103
|
+
this.observe(this.store.getSnapshot().address !== "requested");
|
|
1104
|
+
this.loadPending();
|
|
1105
|
+
}, { signal });
|
|
1106
|
+
element.addEventListener("did-navigate", () => {
|
|
1107
|
+
this.observe(true);
|
|
1108
|
+
}, { signal });
|
|
1109
|
+
element.addEventListener("did-navigate-in-page", (event) => {
|
|
1110
|
+
if (event.isMainFrame) this.observe(true);
|
|
1111
|
+
}, { signal });
|
|
1112
|
+
element.addEventListener("did-start-navigation", (event) => {
|
|
1113
|
+
if (event.isMainFrame) {
|
|
1114
|
+
this.store.set({ ...this.store.getSnapshot(), loading: true, error: void 0 });
|
|
1115
|
+
}
|
|
1116
|
+
}, { signal });
|
|
1117
|
+
for (const name of ["did-start-loading", "did-stop-loading", "page-title-updated"]) {
|
|
1118
|
+
element.addEventListener(name, () => {
|
|
1119
|
+
this.observe(name === "page-title-updated" && this.store.getSnapshot().address === "observed");
|
|
1120
|
+
}, { signal });
|
|
1121
|
+
}
|
|
1122
|
+
element.addEventListener("did-fail-load", (event) => {
|
|
1123
|
+
const failure = event;
|
|
1124
|
+
if (failure.isMainFrame && failure.errorCode !== -3) {
|
|
1125
|
+
this.failed({ code: failure.errorCode, description: failure.errorDescription });
|
|
1126
|
+
}
|
|
1127
|
+
}, { signal });
|
|
1128
|
+
for (const name of ["render-process-gone", "destroyed"]) {
|
|
1129
|
+
element.addEventListener(name, () => {
|
|
1130
|
+
void this.dropGuest();
|
|
1131
|
+
this.failed();
|
|
1132
|
+
}, { signal });
|
|
1133
|
+
}
|
|
1134
|
+
this.presentation.present(element);
|
|
1135
|
+
}
|
|
1136
|
+
loadPending() {
|
|
1137
|
+
const target = this.pending;
|
|
1138
|
+
const element = this.element;
|
|
1139
|
+
if (!this.ready || target === void 0 || element === void 0) return;
|
|
1140
|
+
const revision = this.revision;
|
|
1141
|
+
this.pending = void 0;
|
|
1142
|
+
void element.loadURL(target.url).catch((error) => {
|
|
1143
|
+
if (this.element !== element || this.lifetime.signal.aborted || this.revision !== revision) return;
|
|
1144
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ERR_ABORTED") return;
|
|
1145
|
+
if (this.store.getSnapshot().error === void 0) this.commandFailed(error);
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
observe(committed) {
|
|
1149
|
+
if (!this.ready || this.element === void 0 || this.lifetime.signal.aborted) return;
|
|
1150
|
+
try {
|
|
1151
|
+
this.observeReady(this.element, committed);
|
|
1152
|
+
} catch (error) {
|
|
1153
|
+
this.commandFailed(error);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
observeReady(element, committed) {
|
|
1157
|
+
const current = this.store.getSnapshot();
|
|
1158
|
+
const parsed = committed ? parseBrowserAddress(element.getURL()) : void 0;
|
|
1159
|
+
if (parsed?.ok && this.firstDocument) {
|
|
1160
|
+
element.clearHistory();
|
|
1161
|
+
this.firstDocument = false;
|
|
1162
|
+
}
|
|
1163
|
+
let target = current.target;
|
|
1164
|
+
let address = current.address;
|
|
1165
|
+
if (parsed?.ok) {
|
|
1166
|
+
target = { ...parsed.target, title: element.getTitle() || parsed.target.title };
|
|
1167
|
+
address = "observed";
|
|
1168
|
+
this.presentation.show(target.title);
|
|
1169
|
+
}
|
|
1170
|
+
const loading = current.error === void 0 && element.isLoading();
|
|
1171
|
+
const canGoBack = element.canGoBack();
|
|
1172
|
+
const canGoForward = element.canGoForward();
|
|
1173
|
+
if (target?.url !== current.target?.url || target?.title !== current.target?.title || address !== current.address || loading !== current.loading || canGoBack !== current.canGoBack || canGoForward !== current.canGoForward) {
|
|
1174
|
+
this.store.set({ ...current, target, address, loading, canGoBack, canGoForward });
|
|
1175
|
+
}
|
|
1176
|
+
if (parsed?.ok && target !== void 0) this.persist(target);
|
|
1177
|
+
}
|
|
1178
|
+
persist(target) {
|
|
1179
|
+
if (target.url === this.checkpoint?.url && target.title === this.checkpoint.title) return;
|
|
1180
|
+
this.checkpoint = target;
|
|
1181
|
+
this.options.persist(browserAddressCheckpoint(target, this.revision));
|
|
1182
|
+
}
|
|
1183
|
+
failed(error = { code: void 0, description: void 0 }) {
|
|
1184
|
+
if (this.lifetime.signal.aborted) return;
|
|
1185
|
+
const current = this.store.getSnapshot();
|
|
1186
|
+
this.store.set({
|
|
1187
|
+
...current,
|
|
1188
|
+
loading: false,
|
|
1189
|
+
error,
|
|
1190
|
+
canGoBack: this.ready && current.canGoBack,
|
|
1191
|
+
canGoForward: this.ready && current.canGoForward
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
commandFailed(error) {
|
|
1195
|
+
console.error("Desktop browser operation failed", error);
|
|
1196
|
+
this.failed();
|
|
1197
|
+
}
|
|
1198
|
+
dropGuest() {
|
|
1199
|
+
this.guestLifetime?.abort();
|
|
1200
|
+
this.guestLifetime = void 0;
|
|
1201
|
+
this.presentation.clear();
|
|
1202
|
+
this.element = void 0;
|
|
1203
|
+
this.ready = false;
|
|
1204
|
+
this.firstDocument = true;
|
|
1205
|
+
const lease = this.lease;
|
|
1206
|
+
this.lease = void 0;
|
|
1207
|
+
return lease === void 0 ? Promise.resolve() : this.release(lease);
|
|
1208
|
+
}
|
|
1209
|
+
release(lease) {
|
|
1210
|
+
const released = this.bridge.release(lease).catch((error) => {
|
|
1211
|
+
console.error("Desktop browser guest release failed", error);
|
|
1212
|
+
}).finally(() => {
|
|
1213
|
+
this.releases.delete(released);
|
|
1214
|
+
});
|
|
1215
|
+
this.releases.add(released);
|
|
1216
|
+
return released;
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/annotation/page-inspector.ts
|
|
1221
|
+
function annotatePage(options) {
|
|
1222
|
+
window.__DSH_PAGE_ANNOTATION_CANCEL__?.();
|
|
1223
|
+
const doc = document;
|
|
1224
|
+
const originalUrl = location.href;
|
|
1225
|
+
const originalFocus = doc.activeElement;
|
|
1226
|
+
const root = doc.createElement("div");
|
|
1227
|
+
root.setAttribute("data-dsh-page-annotation", "");
|
|
1228
|
+
root.style.cssText = "position:fixed;inset:0;z-index:2147483647;pointer-events:none;";
|
|
1229
|
+
const shadow = root.attachShadow({ mode: "open" });
|
|
1230
|
+
const style = doc.createElement("style");
|
|
1231
|
+
style.textContent = `
|
|
1232
|
+
:host{all:initial;color-scheme:${options.dark ? "dark" : "light"}}
|
|
1233
|
+
*{box-sizing:border-box}
|
|
1234
|
+
.outline{position:fixed;pointer-events:none;border:2px solid #2979ff;border-radius:5px;background:#2979ff0c}
|
|
1235
|
+
.hint{position:fixed;top:12px;left:50%;transform:translateX(-50%);padding:8px 12px;border-radius:8px;background:#181a20;color:#fff;font:13px/20px system-ui;max-width:calc(100vw - 24px);pointer-events:none}
|
|
1236
|
+
.editor,.options{position:fixed;pointer-events:auto;border:0;background:${options.dark ? "#292a2d" : "#fff"};color:${options.dark ? "#ededee" : "#303338"};box-shadow:0 0 0 .5px ${options.dark ? "#55565b" : "#dcdfe3"},0 2px 8px #00000014;font:14px/22px system-ui}
|
|
1237
|
+
.editor{border-radius:26px;corner-shape:round;padding:7px 8px;width:min(384px,calc(100vw - 24px));overflow:hidden;max-height:calc(100vh - 24px);display:flex;flex-direction:column}
|
|
1238
|
+
.editor.expanded{width:min(420px,calc(100vw - 24px));border-radius:24px;padding:0}
|
|
1239
|
+
.expanded>.row{padding:12px 14px;flex:none}
|
|
1240
|
+
.expanded>.row>.save{display:none}
|
|
1241
|
+
.expanded textarea{max-height:min(66px,20vh)}
|
|
1242
|
+
.editor:focus-within{box-shadow:0 0 0 .5px ${options.dark ? "#85868b" : "#bfc4cc"},0 2px 8px #00000014}
|
|
1243
|
+
.row{display:flex;align-items:center;gap:10px}
|
|
1244
|
+
button,select,textarea{font:inherit;color:inherit}
|
|
1245
|
+
button{border:0;border-radius:8px;padding:6px 8px;background:transparent;cursor:pointer;flex:none}
|
|
1246
|
+
button:disabled{opacity:.4;cursor:default}
|
|
1247
|
+
button:hover:not(:disabled){background:${options.dark ? "#3b3c40" : "#f0f1f3"}}
|
|
1248
|
+
button:focus-visible,select:focus-visible{outline:2px solid #2979ff;outline-offset:2px}
|
|
1249
|
+
.options-toggle,.save{display:flex;align-items:center;justify-content:center;padding:0;width:30px;height:30px;border-radius:50%;corner-shape:round}
|
|
1250
|
+
.options-toggle{color:${options.dark ? "#bdc0c5" : "#737880"}}
|
|
1251
|
+
.save,.save:disabled{background:${options.dark ? "#797c82" : "#a2a5a9"};color:#fff;opacity:1}
|
|
1252
|
+
.save:not(:disabled){background:${options.dark ? "#d2d3d6" : "#777c83"};color:${options.dark ? "#242529" : "#fff"}}
|
|
1253
|
+
.save:hover:not(:disabled){background:${options.dark ? "#eeeff1" : "#60656d"}}
|
|
1254
|
+
textarea{display:block;flex:1;width:100%;min-width:0;height:22px;max-height:110px;resize:none;border:0;background:transparent;padding:0;margin:0;outline:0;line-height:22px;overflow-y:auto}
|
|
1255
|
+
textarea::placeholder{color:${options.dark ? "#aaaeb5" : "#888e96"};opacity:1}
|
|
1256
|
+
.options{position:static;box-shadow:none;flex:1;min-height:0;overflow:auto;font-size:14px;line-height:22px;scrollbar-width:thin}
|
|
1257
|
+
.target-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:7px 20px;background:${options.dark ? "#35363a" : "#f4f4f4"}}
|
|
1258
|
+
.target{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:${options.dark ? "#c9ccd1" : "#656b73"}}
|
|
1259
|
+
.target-controls{display:flex;gap:2px;flex:none}
|
|
1260
|
+
.target-controls button{font-size:13px;padding:0;width:24px;height:24px}
|
|
1261
|
+
.properties{padding:12px 20px;display:grid;gap:10px}
|
|
1262
|
+
.property{display:grid;grid-template-columns:minmax(90px,1fr) minmax(0,180px);align-items:center;gap:12px;color:${options.dark ? "#c9ccd1" : "#6e747b"}}
|
|
1263
|
+
.field{display:flex;align-items:center;gap:9px;border:.5px solid ${options.dark ? "#57595f" : "#dedfe2"};border-radius:13px;padding:7px 10px;min-width:0}
|
|
1264
|
+
.field input{font:13px/20px ui-monospace,monospace;background:transparent;color:inherit;border:0;outline:0;width:100%;min-width:0}
|
|
1265
|
+
.field:focus-within{border-color:${options.dark ? "#a2a5ac" : "#a5a9b1"}}
|
|
1266
|
+
.swatch{width:22px;height:22px;border-radius:7px;flex:none;box-shadow:inset 0 0 0 .5px #8886}
|
|
1267
|
+
.intent{display:flex;align-items:center;justify-content:space-between;padding:0 20px 12px;gap:8px;font-size:13px;color:${options.dark ? "#c9ccd1" : "#6e747b"}}
|
|
1268
|
+
select{font:inherit;border:.5px solid ${options.dark ? "#57595f" : "#dedfe2"};background:transparent;border-radius:10px;padding:5px 8px;max-width:160px}
|
|
1269
|
+
.footer{border-top:.5px solid ${options.dark ? "#44464b" : "#eee"};display:flex;align-items:center;justify-content:space-between;padding:10px 12px;flex:none}
|
|
1270
|
+
.footer>.cancel{border:.5px solid ${options.dark ? "#55565b" : "#e5e6e9"};border-radius:20px;padding:6px 12px;font-size:13px}
|
|
1271
|
+
.style-hint{font-size:12px;line-height:18px;margin:3px 0;color:${options.dark ? "#adb1b8" : "#8a9097"}}
|
|
1272
|
+
.error{color:${options.dark ? "#ffb0a9" : "#b82920"};font-size:12px;padding:3px 40px 1px}
|
|
1273
|
+
.hidden{display:none!important}
|
|
1274
|
+
`;
|
|
1275
|
+
const outline = doc.createElement("div");
|
|
1276
|
+
outline.className = "outline hidden";
|
|
1277
|
+
const hint = doc.createElement("div");
|
|
1278
|
+
hint.className = "hint";
|
|
1279
|
+
hint.textContent = options.copy.select;
|
|
1280
|
+
const editor = doc.createElement("form");
|
|
1281
|
+
editor.className = "editor hidden";
|
|
1282
|
+
editor.setAttribute("aria-label", options.copy.placeholder);
|
|
1283
|
+
const row = doc.createElement("div");
|
|
1284
|
+
row.className = "row";
|
|
1285
|
+
function icon(parts) {
|
|
1286
|
+
const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1287
|
+
for (const [name, value] of Object.entries({
|
|
1288
|
+
width: "18",
|
|
1289
|
+
height: "18",
|
|
1290
|
+
viewBox: "0 0 16 16",
|
|
1291
|
+
fill: "none",
|
|
1292
|
+
stroke: "currentColor",
|
|
1293
|
+
"stroke-width": "1.3",
|
|
1294
|
+
"stroke-linecap": "round",
|
|
1295
|
+
"stroke-linejoin": "round",
|
|
1296
|
+
"aria-hidden": "true"
|
|
1297
|
+
}))
|
|
1298
|
+
svg.setAttribute(name, value);
|
|
1299
|
+
for (const [tag, attributes] of parts) {
|
|
1300
|
+
const node = doc.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
1301
|
+
for (const [name, value] of Object.entries(attributes)) node.setAttribute(name, value);
|
|
1302
|
+
svg.append(node);
|
|
1303
|
+
}
|
|
1304
|
+
return svg;
|
|
1305
|
+
}
|
|
1306
|
+
const settings = doc.createElement("button");
|
|
1307
|
+
settings.type = "button";
|
|
1308
|
+
settings.className = "options-toggle";
|
|
1309
|
+
settings.title = options.copy.settings;
|
|
1310
|
+
settings.setAttribute("aria-label", options.copy.settings);
|
|
1311
|
+
settings.setAttribute("aria-expanded", "false");
|
|
1312
|
+
settings.setAttribute("aria-haspopup", "dialog");
|
|
1313
|
+
settings.append(
|
|
1314
|
+
icon([
|
|
1315
|
+
["path", { d: "M2.3 5h5.85M12.05 5h1.65" }],
|
|
1316
|
+
["circle", { cx: "9.95", cy: "5", r: "1.45" }],
|
|
1317
|
+
["path", { d: "M2.3 11h1.65M7.85 11h5.85" }],
|
|
1318
|
+
["circle", { cx: "5.75", cy: "11", r: "1.45" }]
|
|
1319
|
+
])
|
|
1320
|
+
);
|
|
1321
|
+
const optionsPanel = doc.createElement("section");
|
|
1322
|
+
optionsPanel.className = "options hidden";
|
|
1323
|
+
optionsPanel.setAttribute("role", "dialog");
|
|
1324
|
+
optionsPanel.setAttribute("aria-label", options.copy.settings);
|
|
1325
|
+
const intentRow = doc.createElement("div");
|
|
1326
|
+
intentRow.className = "intent";
|
|
1327
|
+
const intentLabel = doc.createElement("span");
|
|
1328
|
+
intentLabel.textContent = options.copy.comment + " / " + options.copy.question;
|
|
1329
|
+
const intent = doc.createElement("select");
|
|
1330
|
+
intent.setAttribute("aria-label", options.copy.comment + " / " + options.copy.question);
|
|
1331
|
+
for (const [value, label] of [
|
|
1332
|
+
["comment", options.copy.comment],
|
|
1333
|
+
["question", options.copy.question]
|
|
1334
|
+
]) {
|
|
1335
|
+
const option = doc.createElement("option");
|
|
1336
|
+
option.value = value;
|
|
1337
|
+
option.textContent = label;
|
|
1338
|
+
intent.append(option);
|
|
1339
|
+
}
|
|
1340
|
+
intent.value = options.intent;
|
|
1341
|
+
const input = doc.createElement("textarea");
|
|
1342
|
+
input.placeholder = options.copy.placeholder;
|
|
1343
|
+
input.setAttribute("aria-label", options.copy.placeholder);
|
|
1344
|
+
input.maxLength = 1e3;
|
|
1345
|
+
const save = doc.createElement("button");
|
|
1346
|
+
save.type = "submit";
|
|
1347
|
+
save.className = "save";
|
|
1348
|
+
save.disabled = true;
|
|
1349
|
+
save.append(
|
|
1350
|
+
icon([
|
|
1351
|
+
[
|
|
1352
|
+
"path",
|
|
1353
|
+
{ d: "M2.25 8.5L5.49732 11.7473C5.90519 12.1552 6.57263 12.1344 6.95426 11.7018L13.75 4" }
|
|
1354
|
+
]
|
|
1355
|
+
])
|
|
1356
|
+
);
|
|
1357
|
+
save.title = options.copy.save;
|
|
1358
|
+
save.setAttribute("aria-label", options.copy.save);
|
|
1359
|
+
row.append(settings, input, save);
|
|
1360
|
+
const targetLabel = doc.createElement("span");
|
|
1361
|
+
targetLabel.className = "target";
|
|
1362
|
+
const controls = doc.createElement("div");
|
|
1363
|
+
controls.className = "controls";
|
|
1364
|
+
function control(label, glyph) {
|
|
1365
|
+
const button = doc.createElement("button");
|
|
1366
|
+
button.type = "button";
|
|
1367
|
+
button.title = label;
|
|
1368
|
+
button.setAttribute("aria-label", label);
|
|
1369
|
+
button.textContent = glyph + " " + label;
|
|
1370
|
+
controls.append(button);
|
|
1371
|
+
return button;
|
|
1372
|
+
}
|
|
1373
|
+
const parent = control(options.copy.parent, "\u2191");
|
|
1374
|
+
const child = control(options.copy.child, "\u2193");
|
|
1375
|
+
const cancel = control(options.copy.cancel, "\xD7");
|
|
1376
|
+
const error = doc.createElement("div");
|
|
1377
|
+
error.className = "error hidden";
|
|
1378
|
+
error.setAttribute("role", "alert");
|
|
1379
|
+
intentRow.append(intentLabel, intent);
|
|
1380
|
+
const targetRow = doc.createElement("div");
|
|
1381
|
+
targetRow.className = "target-row";
|
|
1382
|
+
controls.className = "target-controls";
|
|
1383
|
+
cancel.remove();
|
|
1384
|
+
parent.textContent = "\u2191";
|
|
1385
|
+
child.textContent = "\u2193";
|
|
1386
|
+
targetRow.append(targetLabel, controls);
|
|
1387
|
+
const properties = doc.createElement("div");
|
|
1388
|
+
properties.className = "properties";
|
|
1389
|
+
const styleHint = doc.createElement("p");
|
|
1390
|
+
styleHint.className = "style-hint";
|
|
1391
|
+
const fields = /* @__PURE__ */ new Map();
|
|
1392
|
+
for (const [property, label] of [
|
|
1393
|
+
["color", options.copy.color],
|
|
1394
|
+
["background-color", options.copy.background],
|
|
1395
|
+
["opacity", options.copy.opacity],
|
|
1396
|
+
["font-family", options.copy.font],
|
|
1397
|
+
["font-size", options.copy.fontSize],
|
|
1398
|
+
["font-weight", options.copy.fontWeight],
|
|
1399
|
+
["gap", options.copy.spacing]
|
|
1400
|
+
]) {
|
|
1401
|
+
const item = doc.createElement("label");
|
|
1402
|
+
item.className = "property";
|
|
1403
|
+
const labelText = doc.createElement("span");
|
|
1404
|
+
labelText.textContent = label;
|
|
1405
|
+
const field = doc.createElement("div");
|
|
1406
|
+
field.className = "field";
|
|
1407
|
+
const value = doc.createElement("input");
|
|
1408
|
+
value.setAttribute("aria-label", label);
|
|
1409
|
+
value.dataset.property = property;
|
|
1410
|
+
value.maxLength = 1e3;
|
|
1411
|
+
let swatch;
|
|
1412
|
+
if (property === "color" || property === "background-color") {
|
|
1413
|
+
swatch = doc.createElement("span");
|
|
1414
|
+
swatch.className = "swatch";
|
|
1415
|
+
swatch.setAttribute("aria-hidden", "true");
|
|
1416
|
+
field.append(swatch);
|
|
1417
|
+
}
|
|
1418
|
+
field.append(value);
|
|
1419
|
+
item.append(labelText, field);
|
|
1420
|
+
properties.append(item);
|
|
1421
|
+
fields.set(property, { input: value, before: "", ...swatch ? { swatch } : {} });
|
|
1422
|
+
}
|
|
1423
|
+
properties.append(styleHint);
|
|
1424
|
+
const footer = doc.createElement("div");
|
|
1425
|
+
footer.className = "footer hidden";
|
|
1426
|
+
cancel.className = "cancel";
|
|
1427
|
+
cancel.textContent = options.copy.cancel;
|
|
1428
|
+
const expandedSave = save.cloneNode(true);
|
|
1429
|
+
footer.append(cancel, expandedSave);
|
|
1430
|
+
optionsPanel.append(targetRow, properties, intentRow);
|
|
1431
|
+
editor.append(row, optionsPanel, error, footer);
|
|
1432
|
+
shadow.append(style, outline, hint, editor);
|
|
1433
|
+
const surface = doc.createElement("div");
|
|
1434
|
+
surface.style.cssText = "position:fixed;inset:0;pointer-events:auto;cursor:crosshair;touch-action:none";
|
|
1435
|
+
shadow.prepend(surface);
|
|
1436
|
+
doc.documentElement.append(root);
|
|
1437
|
+
let target = null;
|
|
1438
|
+
let hovered = null;
|
|
1439
|
+
let selection = null;
|
|
1440
|
+
let gesture = null;
|
|
1441
|
+
let suspended = false;
|
|
1442
|
+
let suppressClick = false;
|
|
1443
|
+
const descendants = [];
|
|
1444
|
+
let animation = 0;
|
|
1445
|
+
let ended = false;
|
|
1446
|
+
let settle;
|
|
1447
|
+
const result = new Promise((resolve) => {
|
|
1448
|
+
settle = resolve;
|
|
1449
|
+
});
|
|
1450
|
+
const lifecycle = new AbortController();
|
|
1451
|
+
function changes() {
|
|
1452
|
+
if (selection?.kind !== "element") return {};
|
|
1453
|
+
return Object.fromEntries(
|
|
1454
|
+
[...fields].flatMap(([key, field]) => {
|
|
1455
|
+
let after = field.input.value.trim();
|
|
1456
|
+
if ((key === "font-size" || key === "gap") && /^\d+(\.\d+)?$/.test(after)) after += "px";
|
|
1457
|
+
return after === field.before ? [] : [[key, { before: field.before, after }]];
|
|
1458
|
+
})
|
|
1459
|
+
);
|
|
1460
|
+
}
|
|
1461
|
+
function refreshProperties(element) {
|
|
1462
|
+
const computed = element ? getComputedStyle(element) : null;
|
|
1463
|
+
for (const [key, field] of fields) {
|
|
1464
|
+
field.before = computed?.getPropertyValue(key) || "";
|
|
1465
|
+
field.input.value = field.before;
|
|
1466
|
+
field.input.disabled = !element;
|
|
1467
|
+
if (field.swatch) field.swatch.style.background = field.before;
|
|
1468
|
+
}
|
|
1469
|
+
properties.classList.toggle("area", !element);
|
|
1470
|
+
for (const field of fields.values())
|
|
1471
|
+
field.input.closest(".property")?.classList.toggle("hidden", !element);
|
|
1472
|
+
styleHint.textContent = element ? options.copy.styleHint : options.copy.elementOnly;
|
|
1473
|
+
}
|
|
1474
|
+
for (const [key, field] of fields) {
|
|
1475
|
+
field.input.addEventListener(
|
|
1476
|
+
"input",
|
|
1477
|
+
() => {
|
|
1478
|
+
if (field.swatch && CSS.supports(key, field.input.value))
|
|
1479
|
+
field.swatch.style.background = field.input.value;
|
|
1480
|
+
intent.value = "comment";
|
|
1481
|
+
error.classList.add("hidden");
|
|
1482
|
+
draw();
|
|
1483
|
+
},
|
|
1484
|
+
{ signal: lifecycle.signal }
|
|
1485
|
+
);
|
|
1486
|
+
field.input.addEventListener(
|
|
1487
|
+
"keydown",
|
|
1488
|
+
(event) => {
|
|
1489
|
+
if (event.key === "Enter" && !event.isComposing && event.keyCode !== 229) {
|
|
1490
|
+
event.preventDefault();
|
|
1491
|
+
event.stopPropagation();
|
|
1492
|
+
submit(event);
|
|
1493
|
+
}
|
|
1494
|
+
},
|
|
1495
|
+
{ signal: lifecycle.signal }
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
const observer = new ResizeObserver(draw);
|
|
1499
|
+
function finish(value) {
|
|
1500
|
+
if (ended) return;
|
|
1501
|
+
ended = true;
|
|
1502
|
+
lifecycle.abort();
|
|
1503
|
+
cancelAnimationFrame(animation);
|
|
1504
|
+
observer.disconnect();
|
|
1505
|
+
root.remove();
|
|
1506
|
+
if (window.__DSH_PAGE_ANNOTATION_CANCEL__ === dismiss)
|
|
1507
|
+
delete window.__DSH_PAGE_ANNOTATION_CANCEL__;
|
|
1508
|
+
if (originalFocus instanceof HTMLElement && originalFocus.isConnected)
|
|
1509
|
+
originalFocus.focus({ preventScroll: true });
|
|
1510
|
+
settle(value);
|
|
1511
|
+
}
|
|
1512
|
+
function dismiss() {
|
|
1513
|
+
finish({ cancelled: true });
|
|
1514
|
+
}
|
|
1515
|
+
window.__DSH_PAGE_ANNOTATION_CANCEL__ = dismiss;
|
|
1516
|
+
function showError(message) {
|
|
1517
|
+
error.textContent = message;
|
|
1518
|
+
error.classList.remove("hidden");
|
|
1519
|
+
}
|
|
1520
|
+
function valid(element) {
|
|
1521
|
+
return !!element && element !== doc.body && element !== doc.documentElement && !element.closest("[data-dsh-page-annotation]");
|
|
1522
|
+
}
|
|
1523
|
+
function hit(x, y) {
|
|
1524
|
+
surface.style.pointerEvents = "none";
|
|
1525
|
+
editor.style.pointerEvents = "none";
|
|
1526
|
+
let node = doc.elementFromPoint(x, y);
|
|
1527
|
+
while (node?.shadowRoot) {
|
|
1528
|
+
const inner = node.shadowRoot.elementFromPoint(x, y);
|
|
1529
|
+
if (!inner || inner === node) break;
|
|
1530
|
+
node = inner;
|
|
1531
|
+
}
|
|
1532
|
+
surface.style.pointerEvents = suspended ? "none" : "auto";
|
|
1533
|
+
editor.style.pointerEvents = "auto";
|
|
1534
|
+
return valid(node) ? node : null;
|
|
1535
|
+
}
|
|
1536
|
+
function rectInDocument(element) {
|
|
1537
|
+
const rect2 = element.getBoundingClientRect();
|
|
1538
|
+
return { x: rect2.x + scrollX, y: rect2.y + scrollY, width: rect2.width, height: rect2.height };
|
|
1539
|
+
}
|
|
1540
|
+
function gestureRect(x, y) {
|
|
1541
|
+
return {
|
|
1542
|
+
x: Math.min(gesture.x, x),
|
|
1543
|
+
y: Math.min(gesture.y, y),
|
|
1544
|
+
width: Math.max(1, Math.abs(x - gesture.x)),
|
|
1545
|
+
height: Math.max(1, Math.abs(y - gesture.y))
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
function selector(element) {
|
|
1549
|
+
const scope = element.getRootNode();
|
|
1550
|
+
if (element.id) {
|
|
1551
|
+
const id = "#" + CSS.escape(element.id);
|
|
1552
|
+
if (scope.querySelectorAll(id).length === 1) return id;
|
|
1553
|
+
}
|
|
1554
|
+
const pieces = [];
|
|
1555
|
+
let node = element;
|
|
1556
|
+
while (node) {
|
|
1557
|
+
let part = node.localName;
|
|
1558
|
+
const peers = node.parentElement ? Array.from(node.parentElement.children).filter((x) => x.localName === node.localName) : [];
|
|
1559
|
+
if (peers.length > 1) part += `:nth-of-type(${peers.indexOf(node) + 1})`;
|
|
1560
|
+
pieces.unshift(part);
|
|
1561
|
+
const path = pieces.join(" > ");
|
|
1562
|
+
if (scope.querySelectorAll(path).length === 1 && scope.querySelector(path) === element)
|
|
1563
|
+
return path;
|
|
1564
|
+
node = node.parentElement;
|
|
1565
|
+
}
|
|
1566
|
+
return pieces.join(" > ");
|
|
1567
|
+
}
|
|
1568
|
+
function locator(element) {
|
|
1569
|
+
const shadowHosts = [];
|
|
1570
|
+
let scope = element.getRootNode();
|
|
1571
|
+
while (scope instanceof ShadowRoot) {
|
|
1572
|
+
shadowHosts.unshift(selector(scope.host));
|
|
1573
|
+
scope = scope.host.getRootNode();
|
|
1574
|
+
}
|
|
1575
|
+
return {
|
|
1576
|
+
selector: selector(element),
|
|
1577
|
+
shadowHosts,
|
|
1578
|
+
matchCount: element.getRootNode().querySelectorAll(
|
|
1579
|
+
selector(element)
|
|
1580
|
+
).length
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
function draw() {
|
|
1584
|
+
if (ended) return;
|
|
1585
|
+
if (location.href !== originalUrl) {
|
|
1586
|
+
dismiss();
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
if (suspended) return;
|
|
1590
|
+
const node = target || hovered;
|
|
1591
|
+
if (selection?.kind === "element" && !target?.isConnected) {
|
|
1592
|
+
outline.classList.add("hidden");
|
|
1593
|
+
showError(options.copy.targetGone);
|
|
1594
|
+
save.disabled = expandedSave.disabled = true;
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
save.disabled = expandedSave.disabled = !selection || !input.value.trim() && !Object.keys(changes()).length;
|
|
1598
|
+
const documentRect = selection?.kind === "element" && target ? rectInDocument(target) : selection?.rect || (node?.isConnected ? rectInDocument(node) : null);
|
|
1599
|
+
if (!documentRect) {
|
|
1600
|
+
outline.classList.add("hidden");
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
const rect2 = {
|
|
1604
|
+
left: documentRect.x - scrollX,
|
|
1605
|
+
top: documentRect.y - scrollY,
|
|
1606
|
+
width: documentRect.width,
|
|
1607
|
+
height: documentRect.height,
|
|
1608
|
+
bottom: documentRect.y - scrollY + documentRect.height
|
|
1609
|
+
};
|
|
1610
|
+
outline.classList.remove("hidden");
|
|
1611
|
+
Object.assign(outline.style, {
|
|
1612
|
+
left: `${rect2.left - (selection?.kind === "point" ? 5 : 0)}px`,
|
|
1613
|
+
top: `${rect2.top - (selection?.kind === "point" ? 5 : 0)}px`,
|
|
1614
|
+
width: `${selection?.kind === "point" ? 10 : rect2.width}px`,
|
|
1615
|
+
height: `${selection?.kind === "point" ? 10 : rect2.height}px`,
|
|
1616
|
+
borderStyle: selection?.kind === "region" ? "dashed" : "solid"
|
|
1617
|
+
});
|
|
1618
|
+
if (editor.classList.contains("hidden")) return;
|
|
1619
|
+
const width = editor.getBoundingClientRect().width;
|
|
1620
|
+
const height = editor.getBoundingClientRect().height;
|
|
1621
|
+
const left = Math.max(12, Math.min(rect2.left, innerWidth - width - 12));
|
|
1622
|
+
const below = rect2.bottom + 8;
|
|
1623
|
+
const above = rect2.top - height - 8;
|
|
1624
|
+
const top = Math.max(
|
|
1625
|
+
12,
|
|
1626
|
+
Math.min(
|
|
1627
|
+
below + height <= innerHeight - 12 ? below : above >= 12 ? above : rect2.top + 8,
|
|
1628
|
+
innerHeight - height - 12
|
|
1629
|
+
)
|
|
1630
|
+
);
|
|
1631
|
+
Object.assign(editor.style, { left: `${left}px`, top: `${top}px` });
|
|
1632
|
+
}
|
|
1633
|
+
function setOptions(open, restoreFocus = false) {
|
|
1634
|
+
optionsPanel.classList.toggle("hidden", !open);
|
|
1635
|
+
editor.classList.toggle("expanded", open);
|
|
1636
|
+
footer.classList.toggle("hidden", !open);
|
|
1637
|
+
settings.setAttribute("aria-expanded", String(open));
|
|
1638
|
+
draw();
|
|
1639
|
+
if (open) settings.focus({ preventScroll: true });
|
|
1640
|
+
else if (restoreFocus) settings.focus({ preventScroll: true });
|
|
1641
|
+
}
|
|
1642
|
+
settings.addEventListener(
|
|
1643
|
+
"click",
|
|
1644
|
+
() => {
|
|
1645
|
+
setOptions(optionsPanel.classList.contains("hidden"));
|
|
1646
|
+
},
|
|
1647
|
+
{ signal: lifecycle.signal }
|
|
1648
|
+
);
|
|
1649
|
+
intent.addEventListener(
|
|
1650
|
+
"change",
|
|
1651
|
+
() => {
|
|
1652
|
+
setOptions(false);
|
|
1653
|
+
input.focus({ preventScroll: true });
|
|
1654
|
+
},
|
|
1655
|
+
{ signal: lifecycle.signal }
|
|
1656
|
+
);
|
|
1657
|
+
input.addEventListener(
|
|
1658
|
+
"input",
|
|
1659
|
+
() => {
|
|
1660
|
+
input.style.height = "22px";
|
|
1661
|
+
input.style.height = `${Math.min(110, input.scrollHeight)}px`;
|
|
1662
|
+
error.classList.add("hidden");
|
|
1663
|
+
draw();
|
|
1664
|
+
},
|
|
1665
|
+
{ signal: lifecycle.signal }
|
|
1666
|
+
);
|
|
1667
|
+
function track() {
|
|
1668
|
+
draw();
|
|
1669
|
+
if (!ended) animation = requestAnimationFrame(track);
|
|
1670
|
+
}
|
|
1671
|
+
function choose(element) {
|
|
1672
|
+
if (!valid(element)) return;
|
|
1673
|
+
observer.disconnect();
|
|
1674
|
+
target = element;
|
|
1675
|
+
selection = { kind: "element", rect: rectInDocument(element) };
|
|
1676
|
+
observer.observe(element);
|
|
1677
|
+
hint.classList.add("hidden");
|
|
1678
|
+
editor.classList.remove("hidden");
|
|
1679
|
+
error.classList.add("hidden");
|
|
1680
|
+
const where = locator(element);
|
|
1681
|
+
targetLabel.textContent = `<${element.localName}>`;
|
|
1682
|
+
targetLabel.title = [...where.shadowHosts, where.selector].join(" \u203A ");
|
|
1683
|
+
refreshProperties(element);
|
|
1684
|
+
parent.disabled = !valid(element.parentElement) && !(element.getRootNode() instanceof ShadowRoot);
|
|
1685
|
+
child.disabled = descendants.length === 0;
|
|
1686
|
+
draw();
|
|
1687
|
+
if (!animation) animation = requestAnimationFrame(track);
|
|
1688
|
+
input.focus({ preventScroll: true });
|
|
1689
|
+
}
|
|
1690
|
+
function chooseArea(kind, rect2) {
|
|
1691
|
+
observer.disconnect();
|
|
1692
|
+
target = null;
|
|
1693
|
+
selection = { kind, rect: rect2 };
|
|
1694
|
+
hint.classList.add("hidden");
|
|
1695
|
+
editor.classList.remove("hidden");
|
|
1696
|
+
error.classList.add("hidden");
|
|
1697
|
+
targetLabel.textContent = kind === "region" ? `${options.copy.region} \xB7 ${Math.round(rect2.width)} \xD7 ${Math.round(rect2.height)}` : options.copy.point;
|
|
1698
|
+
parent.disabled = child.disabled = true;
|
|
1699
|
+
targetLabel.title = "";
|
|
1700
|
+
refreshProperties(null);
|
|
1701
|
+
draw();
|
|
1702
|
+
if (!animation) animation = requestAnimationFrame(track);
|
|
1703
|
+
input.focus({ preventScroll: true });
|
|
1704
|
+
}
|
|
1705
|
+
function isOverlay(event) {
|
|
1706
|
+
return event.composedPath().includes(root);
|
|
1707
|
+
}
|
|
1708
|
+
window.addEventListener(
|
|
1709
|
+
"pointermove",
|
|
1710
|
+
(event) => {
|
|
1711
|
+
if (suspended || !editor.classList.contains("hidden")) return;
|
|
1712
|
+
if (gesture && gesture.id === event.pointerId) {
|
|
1713
|
+
gesture.dragging ||= Math.hypot(event.clientX - gesture.clientX, event.clientY - gesture.clientY) >= 5;
|
|
1714
|
+
if (gesture.dragging) {
|
|
1715
|
+
selection = {
|
|
1716
|
+
kind: "region",
|
|
1717
|
+
rect: gestureRect(event.clientX + scrollX, event.clientY + scrollY)
|
|
1718
|
+
};
|
|
1719
|
+
draw();
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
const node = hit(event.clientX, event.clientY);
|
|
1724
|
+
hovered = event.shiftKey && valid(node?.parentElement || null) ? node.parentElement : node;
|
|
1725
|
+
draw();
|
|
1726
|
+
},
|
|
1727
|
+
{ capture: true, signal: lifecycle.signal }
|
|
1728
|
+
);
|
|
1729
|
+
window.addEventListener(
|
|
1730
|
+
"pointerdown",
|
|
1731
|
+
(event) => {
|
|
1732
|
+
if (suspended) {
|
|
1733
|
+
suppressClick = false;
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
if (isOverlay(event) && !event.composedPath().includes(surface)) return;
|
|
1737
|
+
setOptions(false);
|
|
1738
|
+
event.preventDefault();
|
|
1739
|
+
event.stopImmediatePropagation();
|
|
1740
|
+
suppressClick = true;
|
|
1741
|
+
if (event.button !== 0 || !editor.classList.contains("hidden")) return;
|
|
1742
|
+
gesture = {
|
|
1743
|
+
id: event.pointerId,
|
|
1744
|
+
x: event.clientX + scrollX,
|
|
1745
|
+
y: event.clientY + scrollY,
|
|
1746
|
+
clientX: event.clientX,
|
|
1747
|
+
clientY: event.clientY,
|
|
1748
|
+
dragging: false
|
|
1749
|
+
};
|
|
1750
|
+
surface.setPointerCapture(event.pointerId);
|
|
1751
|
+
},
|
|
1752
|
+
{ capture: true, signal: lifecycle.signal }
|
|
1753
|
+
);
|
|
1754
|
+
window.addEventListener(
|
|
1755
|
+
"pointerup",
|
|
1756
|
+
(event) => {
|
|
1757
|
+
if (!gesture || gesture.id !== event.pointerId || suspended) return;
|
|
1758
|
+
event.preventDefault();
|
|
1759
|
+
event.stopImmediatePropagation();
|
|
1760
|
+
const current = gesture;
|
|
1761
|
+
const area = gestureRect(event.clientX + scrollX, event.clientY + scrollY);
|
|
1762
|
+
gesture = null;
|
|
1763
|
+
if (surface.hasPointerCapture(event.pointerId))
|
|
1764
|
+
surface.releasePointerCapture(event.pointerId);
|
|
1765
|
+
if (current.dragging || Math.hypot(event.clientX - current.clientX, event.clientY - current.clientY) >= 5)
|
|
1766
|
+
chooseArea("region", area);
|
|
1767
|
+
else {
|
|
1768
|
+
const node = hit(event.clientX, event.clientY);
|
|
1769
|
+
descendants.length = 0;
|
|
1770
|
+
if (node) choose(event.shiftKey && valid(node.parentElement) ? node.parentElement : node);
|
|
1771
|
+
else
|
|
1772
|
+
chooseArea("point", {
|
|
1773
|
+
x: event.clientX + scrollX,
|
|
1774
|
+
y: event.clientY + scrollY,
|
|
1775
|
+
width: 0,
|
|
1776
|
+
height: 0
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
},
|
|
1780
|
+
{ capture: true, signal: lifecycle.signal }
|
|
1781
|
+
);
|
|
1782
|
+
function abortGesture() {
|
|
1783
|
+
if (!gesture) return;
|
|
1784
|
+
if (surface.hasPointerCapture(gesture.id)) surface.releasePointerCapture(gesture.id);
|
|
1785
|
+
gesture = null;
|
|
1786
|
+
if (editor.classList.contains("hidden")) selection = null;
|
|
1787
|
+
draw();
|
|
1788
|
+
}
|
|
1789
|
+
window.addEventListener("pointercancel", abortGesture, {
|
|
1790
|
+
capture: true,
|
|
1791
|
+
signal: lifecycle.signal
|
|
1792
|
+
});
|
|
1793
|
+
window.addEventListener(
|
|
1794
|
+
"click",
|
|
1795
|
+
(event) => {
|
|
1796
|
+
if (isOverlay(event) && !event.composedPath().includes(surface)) return;
|
|
1797
|
+
if (suspended && !suppressClick) return;
|
|
1798
|
+
event.preventDefault();
|
|
1799
|
+
event.stopImmediatePropagation();
|
|
1800
|
+
suppressClick = false;
|
|
1801
|
+
},
|
|
1802
|
+
{ capture: true, signal: lifecycle.signal }
|
|
1803
|
+
);
|
|
1804
|
+
function resume() {
|
|
1805
|
+
if (!suspended) return;
|
|
1806
|
+
suspended = false;
|
|
1807
|
+
surface.style.pointerEvents = "auto";
|
|
1808
|
+
hint.textContent = options.copy.select;
|
|
1809
|
+
if (selection) {
|
|
1810
|
+
editor.classList.remove("hidden");
|
|
1811
|
+
hint.classList.add("hidden");
|
|
1812
|
+
} else hint.classList.remove("hidden");
|
|
1813
|
+
draw();
|
|
1814
|
+
}
|
|
1815
|
+
window.addEventListener(
|
|
1816
|
+
"keydown",
|
|
1817
|
+
(event) => {
|
|
1818
|
+
if (event.isComposing || event.keyCode === 229) return;
|
|
1819
|
+
if (event.key === "Escape") {
|
|
1820
|
+
event.preventDefault();
|
|
1821
|
+
event.stopImmediatePropagation();
|
|
1822
|
+
if (optionsPanel.classList.contains("hidden")) dismiss();
|
|
1823
|
+
else setOptions(false, true);
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
if (isOverlay(event) && !event.composedPath().includes(surface)) return;
|
|
1827
|
+
if (event.code === "Space" && !event.altKey && !event.ctrlKey && !event.metaKey) {
|
|
1828
|
+
event.preventDefault();
|
|
1829
|
+
event.stopImmediatePropagation();
|
|
1830
|
+
abortGesture();
|
|
1831
|
+
suspended = true;
|
|
1832
|
+
setOptions(false);
|
|
1833
|
+
surface.style.pointerEvents = "none";
|
|
1834
|
+
outline.classList.add("hidden");
|
|
1835
|
+
editor.classList.add("hidden");
|
|
1836
|
+
hint.classList.remove("hidden");
|
|
1837
|
+
hint.textContent = options.copy.interact;
|
|
1838
|
+
} else if (!suspended && !isOverlay(event)) {
|
|
1839
|
+
event.preventDefault();
|
|
1840
|
+
event.stopImmediatePropagation();
|
|
1841
|
+
}
|
|
1842
|
+
},
|
|
1843
|
+
{ capture: true, signal: lifecycle.signal }
|
|
1844
|
+
);
|
|
1845
|
+
window.addEventListener(
|
|
1846
|
+
"keyup",
|
|
1847
|
+
(event) => {
|
|
1848
|
+
if (event.code === "Space" && suspended) {
|
|
1849
|
+
event.preventDefault();
|
|
1850
|
+
event.stopImmediatePropagation();
|
|
1851
|
+
resume();
|
|
1852
|
+
}
|
|
1853
|
+
},
|
|
1854
|
+
{ capture: true, signal: lifecycle.signal }
|
|
1855
|
+
);
|
|
1856
|
+
window.addEventListener(
|
|
1857
|
+
"blur",
|
|
1858
|
+
() => {
|
|
1859
|
+
abortGesture();
|
|
1860
|
+
resume();
|
|
1861
|
+
},
|
|
1862
|
+
{ signal: lifecycle.signal }
|
|
1863
|
+
);
|
|
1864
|
+
doc.addEventListener(
|
|
1865
|
+
"visibilitychange",
|
|
1866
|
+
() => {
|
|
1867
|
+
if (doc.hidden) {
|
|
1868
|
+
abortGesture();
|
|
1869
|
+
resume();
|
|
1870
|
+
}
|
|
1871
|
+
},
|
|
1872
|
+
{ signal: lifecycle.signal }
|
|
1873
|
+
);
|
|
1874
|
+
window.addEventListener("pagehide", dismiss, { once: true, signal: lifecycle.signal });
|
|
1875
|
+
parent.addEventListener(
|
|
1876
|
+
"click",
|
|
1877
|
+
() => {
|
|
1878
|
+
if (!target) return;
|
|
1879
|
+
const scope = target.getRootNode();
|
|
1880
|
+
const node = target.parentElement || (scope instanceof ShadowRoot ? scope.host : null);
|
|
1881
|
+
if (valid(node)) {
|
|
1882
|
+
setOptions(false);
|
|
1883
|
+
descendants.push(target);
|
|
1884
|
+
choose(node);
|
|
1885
|
+
}
|
|
1886
|
+
},
|
|
1887
|
+
{ signal: lifecycle.signal }
|
|
1888
|
+
);
|
|
1889
|
+
child.addEventListener(
|
|
1890
|
+
"click",
|
|
1891
|
+
() => {
|
|
1892
|
+
const node = descendants.pop();
|
|
1893
|
+
if (node?.isConnected) {
|
|
1894
|
+
setOptions(false);
|
|
1895
|
+
choose(node);
|
|
1896
|
+
}
|
|
1897
|
+
},
|
|
1898
|
+
{ signal: lifecycle.signal }
|
|
1899
|
+
);
|
|
1900
|
+
cancel.addEventListener("click", dismiss, { signal: lifecycle.signal });
|
|
1901
|
+
function evidence(element) {
|
|
1902
|
+
const where = locator(element);
|
|
1903
|
+
const computed = getComputedStyle(element);
|
|
1904
|
+
const styles = {};
|
|
1905
|
+
for (const name of [
|
|
1906
|
+
"display",
|
|
1907
|
+
"position",
|
|
1908
|
+
"font-size",
|
|
1909
|
+
"font-family",
|
|
1910
|
+
"color",
|
|
1911
|
+
"background-color",
|
|
1912
|
+
"padding",
|
|
1913
|
+
"margin",
|
|
1914
|
+
"gap",
|
|
1915
|
+
"opacity",
|
|
1916
|
+
"font-weight"
|
|
1917
|
+
])
|
|
1918
|
+
styles[name] = computed.getPropertyValue(name).slice(0, 1e3);
|
|
1919
|
+
const source = element.closest("[data-web-annotator-source]")?.getAttribute("data-web-annotator-source")?.slice(0, 1e3);
|
|
1920
|
+
return {
|
|
1921
|
+
...where,
|
|
1922
|
+
tag: element.localName,
|
|
1923
|
+
text: (element instanceof HTMLElement ? element.innerText : element.textContent || "").replace(/\s+/g, " ").trim().slice(0, 400),
|
|
1924
|
+
rect: rectInDocument(element),
|
|
1925
|
+
styles,
|
|
1926
|
+
...source ? { source } : {}
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1929
|
+
function candidates(rect2) {
|
|
1930
|
+
const found = /* @__PURE__ */ new Set();
|
|
1931
|
+
for (let row2 = 0; row2 < 5; row2++)
|
|
1932
|
+
for (let col = 0; col < 5; col++) {
|
|
1933
|
+
const node = hit(
|
|
1934
|
+
rect2.x - scrollX + rect2.width * (col + 0.5) / 5,
|
|
1935
|
+
rect2.y - scrollY + rect2.height * (row2 + 0.5) / 5
|
|
1936
|
+
);
|
|
1937
|
+
if (node) found.add(node);
|
|
1938
|
+
}
|
|
1939
|
+
return [...found].slice(0, 12).map(evidence);
|
|
1940
|
+
}
|
|
1941
|
+
function submit(event) {
|
|
1942
|
+
event.preventDefault();
|
|
1943
|
+
if (!selection || selection.kind === "element" && !target?.isConnected || location.href !== originalUrl) {
|
|
1944
|
+
showError(options.copy.targetGone);
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
const styleChanges = changes();
|
|
1948
|
+
for (const [property, { after }] of Object.entries(styleChanges)) {
|
|
1949
|
+
if (!CSS.supports(property, after) || property === "opacity" && !(Number(after) >= 0 && Number(after) <= 1)) {
|
|
1950
|
+
showError(options.copy.invalidStyle);
|
|
1951
|
+
setOptions(true);
|
|
1952
|
+
fields.get(property)?.input.focus();
|
|
1953
|
+
return;
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
if (!input.value.trim() && !Object.keys(styleChanges).length) {
|
|
1957
|
+
showError(options.copy.empty);
|
|
1958
|
+
input.focus();
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
const rect2 = selection.kind === "element" ? rectInDocument(target) : selection.rect;
|
|
1962
|
+
const items = selection.kind === "element" ? [evidence(target)] : candidates(rect2);
|
|
1963
|
+
finish({
|
|
1964
|
+
intent: intent.value === "question" ? "question" : "comment",
|
|
1965
|
+
note: input.value.trim() || options.copy.applyStyles,
|
|
1966
|
+
...Object.keys(styleChanges).length ? { styleChanges } : {},
|
|
1967
|
+
url: originalUrl,
|
|
1968
|
+
title: doc.title.slice(0, 300),
|
|
1969
|
+
viewport: { width: innerWidth, height: innerHeight },
|
|
1970
|
+
selection: {
|
|
1971
|
+
kind: selection.kind,
|
|
1972
|
+
rect: rect2,
|
|
1973
|
+
viewportRect: { ...rect2, x: rect2.x - scrollX, y: rect2.y - scrollY },
|
|
1974
|
+
scroll: { x: scrollX, y: scrollY },
|
|
1975
|
+
candidates: items
|
|
1976
|
+
}
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
editor.addEventListener("submit", submit, { signal: lifecycle.signal });
|
|
1980
|
+
input.addEventListener(
|
|
1981
|
+
"keydown",
|
|
1982
|
+
(event) => {
|
|
1983
|
+
if (event.key === "Enter" && !event.shiftKey && !event.isComposing && event.keyCode !== 229) {
|
|
1984
|
+
event.preventDefault();
|
|
1985
|
+
event.stopPropagation();
|
|
1986
|
+
submit(event);
|
|
1987
|
+
}
|
|
1988
|
+
},
|
|
1989
|
+
{ signal: lifecycle.signal }
|
|
1990
|
+
);
|
|
1991
|
+
animation = requestAnimationFrame(track);
|
|
1992
|
+
return result;
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/annotation/screenshot.ts
|
|
1996
|
+
async function markScreenshot(dataUrl, annotation) {
|
|
1997
|
+
const picture = new Image();
|
|
1998
|
+
picture.src = dataUrl;
|
|
1999
|
+
await picture.decode();
|
|
2000
|
+
if (!picture.naturalWidth || !picture.naturalHeight) throw new Error("annotation-image-failed");
|
|
2001
|
+
const scale = Math.min(1, 1600 / Math.max(picture.naturalWidth, picture.naturalHeight));
|
|
2002
|
+
const canvas = document.createElement("canvas");
|
|
2003
|
+
canvas.width = Math.round(picture.naturalWidth * scale);
|
|
2004
|
+
canvas.height = Math.round(picture.naturalHeight * scale);
|
|
2005
|
+
const context = canvas.getContext("2d");
|
|
2006
|
+
if (!context) throw new Error("annotation-image-failed");
|
|
2007
|
+
context.drawImage(picture, 0, 0, canvas.width, canvas.height);
|
|
2008
|
+
const sx = canvas.width / annotation.viewport.width;
|
|
2009
|
+
const sy = canvas.height / annotation.viewport.height;
|
|
2010
|
+
const area = annotation.selection.viewportRect;
|
|
2011
|
+
context.strokeStyle = "#1683ff";
|
|
2012
|
+
context.fillStyle = "#1683ff";
|
|
2013
|
+
context.lineWidth = 2;
|
|
2014
|
+
const x = area.x * sx, y = area.y * sy;
|
|
2015
|
+
if (annotation.selection.kind === "point") {
|
|
2016
|
+
if (x < 0 || y < 0 || x > canvas.width || y > canvas.height)
|
|
2017
|
+
throw new Error("annotation-image-failed");
|
|
2018
|
+
context.beginPath();
|
|
2019
|
+
context.arc(x, y, 7, 0, Math.PI * 2);
|
|
2020
|
+
context.stroke();
|
|
2021
|
+
} else {
|
|
2022
|
+
const left = Math.max(1, x), top = Math.max(1, y);
|
|
2023
|
+
const right = Math.min(canvas.width - 1, x + area.width * sx);
|
|
2024
|
+
const bottom = Math.min(canvas.height - 1, y + area.height * sy);
|
|
2025
|
+
if (right <= left || bottom <= top) throw new Error("annotation-image-failed");
|
|
2026
|
+
context.strokeRect(left, top, right - left, bottom - top);
|
|
2027
|
+
}
|
|
2028
|
+
const data = canvas.toDataURL("image/jpeg", 0.8).split(",")[1];
|
|
2029
|
+
if (data.length > 4e6) throw new Error("annotation-image-failed");
|
|
2030
|
+
return { mediaType: "image/jpeg", data, width: canvas.width, height: canvas.height };
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/electron/ElectronWebviewPresentation.ts
|
|
2034
|
+
var ElectronWebviewPresentation = class {
|
|
2035
|
+
constructor(events) {
|
|
2036
|
+
this.events = events;
|
|
2037
|
+
}
|
|
2038
|
+
events;
|
|
2039
|
+
element;
|
|
2040
|
+
host;
|
|
2041
|
+
/** @param url - controller-known page. @param options - picker configuration. @param signal - lifetime. @returns bounded guest evidence. */
|
|
2042
|
+
async annotate(url, options, signal) {
|
|
2043
|
+
const element = this.element;
|
|
2044
|
+
if (signal.aborted) return { cancelled: true };
|
|
2045
|
+
if (element === void 0 || element.getURL() !== url) throw new Error("annotation-unavailable");
|
|
2046
|
+
const cancel = () => {
|
|
2047
|
+
void element.executeJavaScript("window.__DSH_PAGE_ANNOTATION_CANCEL__?.()").catch(() => {
|
|
2048
|
+
});
|
|
2049
|
+
};
|
|
2050
|
+
signal.addEventListener("abort", cancel, { once: true });
|
|
2051
|
+
const navigation = () => {
|
|
2052
|
+
cancel();
|
|
2053
|
+
};
|
|
2054
|
+
element.addEventListener("did-start-navigation", navigation);
|
|
2055
|
+
try {
|
|
2056
|
+
element.focus();
|
|
2057
|
+
const result = await element.executeJavaScript(`(${annotatePage.toString()})(${JSON.stringify(options)})`);
|
|
2058
|
+
if (signal.aborted || this.element !== element) return { cancelled: true };
|
|
2059
|
+
if (!isAnnotationResult(result)) throw new Error("annotation-invalid");
|
|
2060
|
+
if (!("cancelled" in result) && (result.url !== url || element.getURL() !== url)) throw new Error("annotation-page-changed");
|
|
2061
|
+
if (!("cancelled" in result)) {
|
|
2062
|
+
try {
|
|
2063
|
+
const image = await element.capturePage();
|
|
2064
|
+
const screenshot = await markScreenshot(image.toDataURL(), result);
|
|
2065
|
+
if (signal.aborted || this.element !== element) return { cancelled: true };
|
|
2066
|
+
if (element.getURL() !== url || element.isLoading()) throw new Error("annotation-page-changed");
|
|
2067
|
+
const view = await element.executeJavaScript("({width:innerWidth,height:innerHeight,x:scrollX,y:scrollY})");
|
|
2068
|
+
if (typeof view !== "object" || view === null || !("width" in view) || !("height" in view) || !("x" in view) || !("y" in view) || view.width !== result.viewport.width || view.height !== result.viewport.height || view.x !== result.selection.scroll.x || view.y !== result.selection.scroll.y) return result;
|
|
2069
|
+
return { ...result, screenshot };
|
|
2070
|
+
} catch (error) {
|
|
2071
|
+
if (signal.aborted || this.element !== element) return { cancelled: true };
|
|
2072
|
+
if (element.getURL() !== url || element.isLoading()) throw new Error("annotation-page-changed");
|
|
2073
|
+
return result;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
return result;
|
|
2077
|
+
} finally {
|
|
2078
|
+
signal.removeEventListener("abort", cancel);
|
|
2079
|
+
element.removeEventListener("did-start-navigation", navigation);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
/** @param viewportId - committed content container. @returns ends attachment and releases its guest. */
|
|
2083
|
+
mount(viewportId) {
|
|
2084
|
+
const host = document.getElementById(viewportId);
|
|
2085
|
+
if (host === null) throw new Error("Electron presentation: content container is not mounted");
|
|
2086
|
+
if (this.host !== void 0) this.events.unmounted();
|
|
2087
|
+
this.host = host;
|
|
2088
|
+
this.events.mounted();
|
|
2089
|
+
return () => {
|
|
2090
|
+
if (this.host !== host) return;
|
|
2091
|
+
this.events.unmounted();
|
|
2092
|
+
this.host = void 0;
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
/**
|
|
2096
|
+
* Configure a detached webview for an approved guest reservation.
|
|
2097
|
+
* @param reservation - main-approved partition and bootstrap lease.
|
|
2098
|
+
* @returns a detached, configured webview.
|
|
2099
|
+
*/
|
|
2100
|
+
createElement(reservation) {
|
|
2101
|
+
const element = document.createElement("webview");
|
|
2102
|
+
element.className = Browser_default.webview;
|
|
2103
|
+
element.dataset.sidebarBrowserFrame = "webview";
|
|
2104
|
+
element.setAttribute("name", reservation.lease);
|
|
2105
|
+
element.setAttribute("partition", reservation.partition);
|
|
2106
|
+
element.setAttribute("allowpopups", "");
|
|
2107
|
+
element.setAttribute("src", "about:blank#" + reservation.lease);
|
|
2108
|
+
return element;
|
|
2109
|
+
}
|
|
2110
|
+
/**
|
|
2111
|
+
* Attach a prepared guest, removing any previous guest DOM from the container.
|
|
2112
|
+
* @param element - configured guest with its navigation listeners already installed.
|
|
2113
|
+
* @throws when no content container is mounted.
|
|
2114
|
+
*/
|
|
2115
|
+
present(element) {
|
|
2116
|
+
if (this.host === void 0) throw new Error("Electron presentation: cannot attach without a content container");
|
|
2117
|
+
this.clear();
|
|
2118
|
+
this.element = element;
|
|
2119
|
+
this.host.append(element);
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Set the guest element's accessible label.
|
|
2123
|
+
* @param title - observed document title.
|
|
2124
|
+
*/
|
|
2125
|
+
show(title) {
|
|
2126
|
+
this.element?.setAttribute("aria-label", title);
|
|
2127
|
+
}
|
|
2128
|
+
/** Destroy only the guest DOM; a replacement may use the same mounted container. */
|
|
2129
|
+
clear() {
|
|
2130
|
+
this.element?.remove();
|
|
2131
|
+
this.element = void 0;
|
|
2132
|
+
}
|
|
2133
|
+
/** Destroy the guest DOM and release its content container. */
|
|
2134
|
+
dispose() {
|
|
2135
|
+
this.clear();
|
|
2136
|
+
this.host = void 0;
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
|
|
2140
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/electron/pages.ts
|
|
2141
|
+
function createElectronPage(options, bridge, workspace) {
|
|
2142
|
+
const presentation = new ElectronWebviewPresentation({
|
|
2143
|
+
mounted: () => {
|
|
2144
|
+
frame.attach();
|
|
2145
|
+
},
|
|
2146
|
+
unmounted: () => {
|
|
2147
|
+
frame.detach();
|
|
2148
|
+
}
|
|
2149
|
+
});
|
|
2150
|
+
const frame = new ElectronWebViewImpl(options, bridge, workspace, presentation);
|
|
2151
|
+
return { frame, presentation };
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/electron/workspace.ts
|
|
2155
|
+
async function browserWorkspace(source, sessionId, signal) {
|
|
2156
|
+
signal.throwIfAborted();
|
|
2157
|
+
if (source.getSnapshot().phase !== "ready") {
|
|
2158
|
+
await new Promise((resolve, reject) => {
|
|
2159
|
+
const abort = () => {
|
|
2160
|
+
stop();
|
|
2161
|
+
const reason = signal.reason;
|
|
2162
|
+
reject(reason);
|
|
2163
|
+
};
|
|
2164
|
+
const stop = source.subscribe(() => {
|
|
2165
|
+
if (source.getSnapshot().phase !== "ready") return;
|
|
2166
|
+
stop();
|
|
2167
|
+
signal.removeEventListener("abort", abort);
|
|
2168
|
+
resolve();
|
|
2169
|
+
});
|
|
2170
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
2173
|
+
signal.throwIfAborted();
|
|
2174
|
+
const workspace = source.getSnapshot().items.find((item) => item.sessionIds.some((id) => id === sessionId));
|
|
2175
|
+
return workspace === void 0 ? `session:${sessionId}` : `cwd:${workspace.path}`;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/definition.tsx
|
|
2179
|
+
var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
2180
|
+
var BROWSER_KIND = "browser";
|
|
2181
|
+
var BROWSER_ID = "@deepseek-ai/dsh-client-ui-sidebar-browser";
|
|
2182
|
+
function browserDefinition(t) {
|
|
2183
|
+
return {
|
|
2184
|
+
id: BROWSER_ID,
|
|
2185
|
+
kind: BROWSER_KIND,
|
|
2186
|
+
multiple: true,
|
|
2187
|
+
priority: "builtin",
|
|
2188
|
+
title: () => t("type.label"),
|
|
2189
|
+
guide: [{
|
|
2190
|
+
id: "new",
|
|
2191
|
+
commandId: "browser.new",
|
|
2192
|
+
order: 30,
|
|
2193
|
+
title: () => t("guide.title"),
|
|
2194
|
+
description: () => t("guide.description"),
|
|
2195
|
+
icon: import_dsh_client_ui_primitives3.GuideArtworkBrowser
|
|
2196
|
+
}]
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/locales.ts
|
|
2201
|
+
var zh = {
|
|
2202
|
+
"type.label": "\u6D4F\u89C8\u5668",
|
|
2203
|
+
"guide.title": "\u6D4F\u89C8\u5668",
|
|
2204
|
+
"guide.description": "\u6D4F\u89C8\u7F51\u9875",
|
|
2205
|
+
"shortcut.noSession": "\u8BF7\u5148\u6253\u5F00\u4E00\u4E2A\u4F1A\u8BDD",
|
|
2206
|
+
"address.placeholder": "\u8F93\u5165 HTTP(S) \u5730\u5740",
|
|
2207
|
+
"address.changed": "URL \u5DF2\u53D8\u5316",
|
|
2208
|
+
back: "\u540E\u9000",
|
|
2209
|
+
forward: "\u524D\u8FDB",
|
|
2210
|
+
reload: "\u5237\u65B0",
|
|
2211
|
+
go: "\u524D\u5F80",
|
|
2212
|
+
external: "\u5728\u7CFB\u7EDF\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00",
|
|
2213
|
+
"sandbox.disable": "\u5173\u95ED\u6C99\u7BB1\u9650\u5236",
|
|
2214
|
+
"sandbox.enable": "\u6062\u590D\u6C99\u7BB1\u9650\u5236",
|
|
2215
|
+
"sandbox.warning": "\u6C99\u7BB1\u9650\u5236\u5DF2\u5173\u95ED\uFF1B\u9875\u9762\u53EF\u4EE5\u5BFC\u822A\u9876\u5C42\u5E94\u7528\uFF0C\u5E76\u4F7F\u7528\u4E0B\u8F7D\u3001\u6A21\u6001\u5BF9\u8BDD\u6846\u4E0E\u8F93\u5165\u9501\u5B9A\u3002",
|
|
2216
|
+
start: "\u8F93\u5165 HTTP(S) \u5730\u5740\u5F00\u59CB\u6D4F\u89C8",
|
|
2217
|
+
loading: "\u6B63\u5728\u6253\u5F00\u2026",
|
|
2218
|
+
"restore.previous": "\u4E0A\u6B21\u6253\u5F00",
|
|
2219
|
+
"restore.action": "\u6062\u590D\u9875\u9762",
|
|
2220
|
+
"error.empty": "\u8BF7\u8F93\u5165\u5730\u5740\u3002",
|
|
2221
|
+
"error.invalid": "\u8FD9\u4E2A\u5730\u5740\u65E0\u6548\u6216\u8FC7\u957F\u3002",
|
|
2222
|
+
"error.protocol": "\u53EA\u652F\u6301 HTTP \u548C HTTPS \u5730\u5740\uFF1B\u672C\u5730\u6587\u4EF6\u8BF7\u4F7F\u7528\u6587\u6863\u9884\u89C8\u3002",
|
|
2223
|
+
"error.credentials": "\u5730\u5740\u4E0D\u80FD\u5305\u542B\u7528\u6237\u540D\u6216\u5BC6\u7801\u3002",
|
|
2224
|
+
"error.application-origin": "\u4E0D\u80FD\u5728\u5D4C\u5165\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00 DSH \u5E94\u7528\u81EA\u8EAB\u3002",
|
|
2225
|
+
"load.failed": "\u9875\u9762\u52A0\u8F7D\u5931\u8D25\uFF1B\u8BF7\u5237\u65B0\u91CD\u8BD5\u6216\u5728\u7CFB\u7EDF\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u3002",
|
|
2226
|
+
"load.failed.detail": "\u9875\u9762\u52A0\u8F7D\u5931\u8D25 ({code}): {description}",
|
|
2227
|
+
"address.unknown": "\u9875\u9762\u5DF2\u8DF3\u8F6C\uFF1B\u5F53\u524D\u8F7D\u4F53\u65E0\u6CD5\u8BFB\u53D6\u65B0\u7684 URL\u3002"
|
|
2228
|
+
};
|
|
2229
|
+
var en = {
|
|
2230
|
+
"type.label": "Browser",
|
|
2231
|
+
"guide.title": "Browser",
|
|
2232
|
+
"guide.description": "Browse web pages",
|
|
2233
|
+
"shortcut.noSession": "Open a session first",
|
|
2234
|
+
"address.placeholder": "Enter an HTTP(S) address",
|
|
2235
|
+
"address.changed": "URL changed",
|
|
2236
|
+
back: "Back",
|
|
2237
|
+
forward: "Forward",
|
|
2238
|
+
reload: "Reload",
|
|
2239
|
+
go: "Go",
|
|
2240
|
+
external: "Open in system browser",
|
|
2241
|
+
"sandbox.disable": "Disable sandbox restrictions",
|
|
2242
|
+
"sandbox.enable": "Restore sandbox restrictions",
|
|
2243
|
+
"sandbox.warning": "Sandbox restrictions are disabled; the page can navigate the top-level app and use downloads, modal dialogs, and input locks.",
|
|
2244
|
+
start: "Enter an HTTP(S) address to start browsing",
|
|
2245
|
+
loading: "Opening\u2026",
|
|
2246
|
+
"restore.previous": "Previously opened",
|
|
2247
|
+
"restore.action": "Restore page",
|
|
2248
|
+
"error.empty": "Enter an address.",
|
|
2249
|
+
"error.invalid": "That address is invalid or too long.",
|
|
2250
|
+
"error.protocol": "Only HTTP and HTTPS addresses are supported; use Document Preview for local files.",
|
|
2251
|
+
"error.credentials": "Addresses cannot contain a username or password.",
|
|
2252
|
+
"error.application-origin": "The embedded browser cannot open the DSH application itself.",
|
|
2253
|
+
"load.failed": "The page could not load; reload or open it in the system browser.",
|
|
2254
|
+
"load.failed.detail": "Page load failed ({code}): {description}",
|
|
2255
|
+
"address.unknown": "The page navigated; this carrier cannot read its new URL."
|
|
2256
|
+
};
|
|
2257
|
+
|
|
2258
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/browser/store.ts
|
|
2259
|
+
var import_dsh_client_store4 = require("@deepseek-ai/dsh-client-store");
|
|
2260
|
+
function createBrowserStore() {
|
|
2261
|
+
return (0, import_dsh_client_store4.defineStore)({
|
|
2262
|
+
init: () => ({ byTab: {} }),
|
|
2263
|
+
persist: "dsh.sidebar-browser.v1",
|
|
2264
|
+
actions: {
|
|
2265
|
+
replace: (draft, tabId, state) => {
|
|
2266
|
+
draft.byTab[tabId] = state;
|
|
2267
|
+
},
|
|
2268
|
+
forget: (draft, tabId) => {
|
|
2269
|
+
const byTab = {};
|
|
2270
|
+
for (const [id, state] of Object.entries(draft.byTab)) {
|
|
2271
|
+
if (id !== tabId) byTab[id] = state;
|
|
2272
|
+
}
|
|
2273
|
+
draft.byTab = byTab;
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
});
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
// .host-source/packages/client/ui-sidebar-browser/src/client/index.ts
|
|
2280
|
+
var inject = ["slots", "locale", "sidebarRight", "sidebarRightTabs"];
|
|
2281
|
+
function apply(ctx) {
|
|
2282
|
+
ctx.effect(cssDispose);
|
|
2283
|
+
const namespace = "sidebarBrowser";
|
|
2284
|
+
const t = ctx.locale.bind(namespace);
|
|
2285
|
+
ctx.inject(["shortcuts"], (ctx2) => {
|
|
2286
|
+
ctx2.effect(() => ctx2.shortcuts.register({
|
|
2287
|
+
id: "browser.new",
|
|
2288
|
+
label: () => t("guide.title"),
|
|
2289
|
+
aliases: ["browser", "new browser tab"],
|
|
2290
|
+
defaults: {
|
|
2291
|
+
"desktop:macos": { code: "KeyT", modifiers: ["primary"] },
|
|
2292
|
+
"desktop:windows": { code: "KeyT", modifiers: ["primary"] },
|
|
2293
|
+
"desktop:linux": { code: "KeyT", modifiers: ["primary"] },
|
|
2294
|
+
"web:macos": { code: "KeyT", modifiers: ["primary", "alt"] },
|
|
2295
|
+
"web:windows": { code: "KeyT", modifiers: ["primary", "alt"] }
|
|
2296
|
+
},
|
|
2297
|
+
// Each tab plugin owns its command's availability, localized refusal, and tab kind.
|
|
2298
|
+
/* jscpd:ignore-start */
|
|
2299
|
+
regions: ["page", "editable", "terminal"],
|
|
2300
|
+
modals: [],
|
|
2301
|
+
resolve: ({ target: element }) => {
|
|
2302
|
+
const target = ctx2.sidebarRight.commandTarget(element);
|
|
2303
|
+
if (target === void 0) return { status: "blocked", reason: t("shortcut.noSession") };
|
|
2304
|
+
return { status: "handled", run: () => {
|
|
2305
|
+
ctx2.sidebarRight.openTabFromTarget("browser", target);
|
|
2306
|
+
} };
|
|
2307
|
+
}
|
|
2308
|
+
/* jscpd:ignore-end */
|
|
2309
|
+
}), "ui-sidebar-browser: shortcut");
|
|
2310
|
+
});
|
|
2311
|
+
const store = createBrowserStore();
|
|
2312
|
+
const openTabs = ctx.sidebarRight.openTabs;
|
|
2313
|
+
const carrier = globalThis.dshDesktop;
|
|
2314
|
+
const desktop = carrier?.protocolVersion === 1 ? carrier.browser : void 0;
|
|
2315
|
+
ctx.effect(() => ctx.locale.register(namespace, { zh, en }), "ui-sidebar-browser.copy");
|
|
2316
|
+
ctx.effect(() => ctx.sidebarRightTabs.register({ ...browserDefinition(t), keepMounted: desktop !== void 0 }), "ui-sidebar-browser.type");
|
|
2317
|
+
const installFrames = (scope, factory) => {
|
|
2318
|
+
const controllers = /* @__PURE__ */ new Map();
|
|
2319
|
+
scope.effect(() => async () => {
|
|
2320
|
+
const pending = [...controllers.values()].map((controller) => controller.dispose());
|
|
2321
|
+
controllers.clear();
|
|
2322
|
+
await Promise.all(pending);
|
|
2323
|
+
}, "ui-sidebar-browser.frames");
|
|
2324
|
+
scope.effect(() => scope.slots.inject("sidebar.right.pane.tab", () => scope.slots.register({
|
|
2325
|
+
name: "sidebar.right.pane.tab",
|
|
2326
|
+
key: BROWSER_ID,
|
|
2327
|
+
locale: namespace,
|
|
2328
|
+
store,
|
|
2329
|
+
children: {
|
|
2330
|
+
"sidebar.right.tab.browser.toolbar": { kind: "list", scope: "session" },
|
|
2331
|
+
"sidebar.right.tab.browser.annotations": { kind: "list", scope: "session" }
|
|
2332
|
+
},
|
|
2333
|
+
inject: (sessionId, actions) => {
|
|
2334
|
+
const existing = controllers.get(sessionId);
|
|
2335
|
+
if (existing !== void 0) {
|
|
2336
|
+
existing.rebind(actions);
|
|
2337
|
+
return existing;
|
|
2338
|
+
}
|
|
2339
|
+
const controller = createBrowserControllers(actions, factory(sessionId), (tabId) => openTabs.getSnapshot().some((tab) => tab.sessionId === sessionId && tab.tabId === tabId));
|
|
2340
|
+
controllers.set(sessionId, controller);
|
|
2341
|
+
return controller;
|
|
2342
|
+
}
|
|
2343
|
+
}, BrowserBody)), "ui-sidebar-browser.body");
|
|
2344
|
+
};
|
|
2345
|
+
if (desktop === void 0) installFrames(ctx, () => createIframePage);
|
|
2346
|
+
else ctx.inject(["workspaces"], (scope) => {
|
|
2347
|
+
installFrames(scope, (sessionId) => (options) => createElectronPage(
|
|
2348
|
+
options,
|
|
2349
|
+
desktop,
|
|
2350
|
+
(signal) => browserWorkspace(scope.workspaces.list, sessionId, signal)
|
|
2351
|
+
));
|
|
2352
|
+
});
|
|
2353
|
+
ctx.effect(() => ctx.slots.inject("sidebar.right.pane.tab.title", () => ctx.slots.register({
|
|
2354
|
+
name: "sidebar.right.pane.tab.title",
|
|
2355
|
+
key: BROWSER_ID,
|
|
2356
|
+
store
|
|
2357
|
+
}, BrowserTitle)), "ui-sidebar-browser.title");
|
|
2358
|
+
}
|
|
2359
|
+
return module.exports; } });
|
|
2360
|
+
//# sourceMappingURL=client.js.map
|