@opentabs-dev/browser-extension 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/background-log-state.d.ts +10 -0
- package/dist/background-log-state.d.ts.map +1 -0
- package/dist/background-log-state.js +11 -0
- package/dist/background-log-state.js.map +1 -0
- package/dist/background-message-handlers.d.ts +41 -0
- package/dist/background-message-handlers.d.ts.map +1 -0
- package/dist/background-message-handlers.js +214 -0
- package/dist/background-message-handlers.js.map +1 -0
- package/dist/background.d.ts +2 -0
- package/dist/background.d.ts.map +1 -0
- package/dist/background.js +3780 -0
- package/dist/background.js.map +1 -0
- package/dist/bg-log-state.d.ts +10 -0
- package/dist/bg-log-state.d.ts.map +1 -0
- package/dist/bg-log-state.js +11 -0
- package/dist/bg-log-state.js.map +1 -0
- package/dist/browser-commands/content-commands.d.ts +25 -0
- package/dist/browser-commands/content-commands.d.ts.map +1 -0
- package/dist/browser-commands/content-commands.js +166 -0
- package/dist/browser-commands/content-commands.js.map +1 -0
- package/dist/browser-commands/cookie-commands.d.ts +14 -0
- package/dist/browser-commands/cookie-commands.d.ts.map +1 -0
- package/dist/browser-commands/cookie-commands.js +99 -0
- package/dist/browser-commands/cookie-commands.js.map +1 -0
- package/dist/browser-commands/extension-commands.d.ts +12 -0
- package/dist/browser-commands/extension-commands.d.ts.map +1 -0
- package/dist/browser-commands/extension-commands.js +386 -0
- package/dist/browser-commands/extension-commands.js.map +1 -0
- package/dist/browser-commands/helpers.d.ts +35 -0
- package/dist/browser-commands/helpers.d.ts.map +1 -0
- package/dist/browser-commands/helpers.js +121 -0
- package/dist/browser-commands/helpers.js.map +1 -0
- package/dist/browser-commands/index.d.ts +11 -0
- package/dist/browser-commands/index.d.ts.map +1 -0
- package/dist/browser-commands/index.js +10 -0
- package/dist/browser-commands/index.js.map +1 -0
- package/dist/browser-commands/interaction-commands.d.ts +23 -0
- package/dist/browser-commands/interaction-commands.d.ts.map +1 -0
- package/dist/browser-commands/interaction-commands.js +353 -0
- package/dist/browser-commands/interaction-commands.js.map +1 -0
- package/dist/browser-commands/key-press-command.d.ts +2 -0
- package/dist/browser-commands/key-press-command.d.ts.map +1 -0
- package/dist/browser-commands/key-press-command.js +144 -0
- package/dist/browser-commands/key-press-command.js.map +1 -0
- package/dist/browser-commands/network-commands.d.ts +6 -0
- package/dist/browser-commands/network-commands.d.ts.map +1 -0
- package/dist/browser-commands/network-commands.js +69 -0
- package/dist/browser-commands/network-commands.js.map +1 -0
- package/dist/browser-commands/resource-commands.d.ts +37 -0
- package/dist/browser-commands/resource-commands.d.ts.map +1 -0
- package/dist/browser-commands/resource-commands.js +153 -0
- package/dist/browser-commands/resource-commands.js.map +1 -0
- package/dist/browser-commands/scroll-command.d.ts +2 -0
- package/dist/browser-commands/scroll-command.d.ts.map +1 -0
- package/dist/browser-commands/scroll-command.js +133 -0
- package/dist/browser-commands/scroll-command.js.map +1 -0
- package/dist/browser-commands/tab-commands.d.ts +33 -0
- package/dist/browser-commands/tab-commands.d.ts.map +1 -0
- package/dist/browser-commands/tab-commands.js +121 -0
- package/dist/browser-commands/tab-commands.js.map +1 -0
- package/dist/browser-commands.d.ts +36 -0
- package/dist/browser-commands.d.ts.map +1 -0
- package/dist/browser-commands.js +1931 -0
- package/dist/browser-commands.js.map +1 -0
- package/dist/confirmation-badge.d.ts +17 -0
- package/dist/confirmation-badge.d.ts.map +1 -0
- package/dist/confirmation-badge.js +64 -0
- package/dist/confirmation-badge.js.map +1 -0
- package/dist/constants.d.ts +79 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +79 -0
- package/dist/constants.js.map +1 -0
- package/dist/dispatch-helpers.d.ts +61 -0
- package/dist/dispatch-helpers.d.ts.map +1 -0
- package/dist/dispatch-helpers.js +149 -0
- package/dist/dispatch-helpers.js.map +1 -0
- package/dist/extension-messages.d.ts +146 -0
- package/dist/extension-messages.d.ts.map +1 -0
- package/dist/extension-messages.js +2 -0
- package/dist/extension-messages.js.map +1 -0
- package/dist/iife-injection.d.ts +55 -0
- package/dist/iife-injection.d.ts.map +1 -0
- package/dist/iife-injection.js +474 -0
- package/dist/iife-injection.js.map +1 -0
- package/dist/json-rpc-errors.d.ts +8 -0
- package/dist/json-rpc-errors.d.ts.map +1 -0
- package/dist/json-rpc-errors.js +8 -0
- package/dist/json-rpc-errors.js.map +1 -0
- package/dist/known-methods.d.ts +19 -0
- package/dist/known-methods.d.ts.map +1 -0
- package/dist/known-methods.js +68 -0
- package/dist/known-methods.js.map +1 -0
- package/dist/log-collector.d.ts +45 -0
- package/dist/log-collector.d.ts.map +1 -0
- package/dist/log-collector.js +99 -0
- package/dist/log-collector.js.map +1 -0
- package/dist/message-router.d.ts +28 -0
- package/dist/message-router.d.ts.map +1 -0
- package/dist/message-router.js +367 -0
- package/dist/message-router.js.map +1 -0
- package/dist/messaging.d.ts +15 -0
- package/dist/messaging.d.ts.map +1 -0
- package/dist/messaging.js +41 -0
- package/dist/messaging.js.map +1 -0
- package/dist/network-capture.d.ts +56 -0
- package/dist/network-capture.d.ts.map +1 -0
- package/dist/network-capture.js +374 -0
- package/dist/network-capture.js.map +1 -0
- package/dist/offscreen/index.d.ts +16 -0
- package/dist/offscreen/index.d.ts.map +1 -0
- package/dist/offscreen/index.js +549 -0
- package/dist/offscreen/index.js.map +1 -0
- package/dist/plugin-storage.d.ts +19 -0
- package/dist/plugin-storage.d.ts.map +1 -0
- package/dist/plugin-storage.js +100 -0
- package/dist/plugin-storage.js.map +1 -0
- package/dist/rate-limiter.d.ts +18 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +53 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/resource-prompt-dispatch.d.ts +14 -0
- package/dist/resource-prompt-dispatch.d.ts.map +1 -0
- package/dist/resource-prompt-dispatch.js +195 -0
- package/dist/resource-prompt-dispatch.js.map +1 -0
- package/dist/sanitize-error.d.ts +8 -0
- package/dist/sanitize-error.d.ts.map +1 -0
- package/dist/sanitize-error.js +25 -0
- package/dist/sanitize-error.js.map +1 -0
- package/dist/sanitize-svg.d.ts +20 -0
- package/dist/sanitize-svg.d.ts.map +1 -0
- package/dist/sanitize-svg.js +296 -0
- package/dist/sanitize-svg.js.map +1 -0
- package/dist/side-panel/App.d.ts +3 -0
- package/dist/side-panel/App.d.ts.map +1 -0
- package/dist/side-panel/App.js +147 -0
- package/dist/side-panel/App.js.map +1 -0
- package/dist/side-panel/bridge.d.ts +50 -0
- package/dist/side-panel/bridge.d.ts.map +1 -0
- package/dist/side-panel/bridge.js +113 -0
- package/dist/side-panel/bridge.js.map +1 -0
- package/dist/side-panel/components/ConfirmationDialog.d.ts +16 -0
- package/dist/side-panel/components/ConfirmationDialog.d.ts.map +1 -0
- package/dist/side-panel/components/ConfirmationDialog.js +39 -0
- package/dist/side-panel/components/ConfirmationDialog.js.map +1 -0
- package/dist/side-panel/components/EmptyStates.d.ts +8 -0
- package/dist/side-panel/components/EmptyStates.d.ts.map +1 -0
- package/dist/side-panel/components/EmptyStates.js +40 -0
- package/dist/side-panel/components/EmptyStates.js.map +1 -0
- package/dist/side-panel/components/ErrorBoundary.d.ts +23 -0
- package/dist/side-panel/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/side-panel/components/ErrorBoundary.js +28 -0
- package/dist/side-panel/components/ErrorBoundary.js.map +1 -0
- package/dist/side-panel/components/FailedPluginCard.d.ts +6 -0
- package/dist/side-panel/components/FailedPluginCard.d.ts.map +1 -0
- package/dist/side-panel/components/FailedPluginCard.js +9 -0
- package/dist/side-panel/components/FailedPluginCard.js.map +1 -0
- package/dist/side-panel/components/Footer.d.ts +3 -0
- package/dist/side-panel/components/Footer.d.ts.map +1 -0
- package/dist/side-panel/components/Footer.js +32 -0
- package/dist/side-panel/components/Footer.js.map +1 -0
- package/dist/side-panel/components/Header.d.ts +5 -0
- package/dist/side-panel/components/Header.d.ts.map +1 -0
- package/dist/side-panel/components/Header.js +6 -0
- package/dist/side-panel/components/Header.js.map +1 -0
- package/dist/side-panel/components/PluginCard.d.ts +10 -0
- package/dist/side-panel/components/PluginCard.d.ts.map +1 -0
- package/dist/side-panel/components/PluginCard.js +50 -0
- package/dist/side-panel/components/PluginCard.js.map +1 -0
- package/dist/side-panel/components/PluginIcon.d.ts +21 -0
- package/dist/side-panel/components/PluginIcon.d.ts.map +1 -0
- package/dist/side-panel/components/PluginIcon.js +48 -0
- package/dist/side-panel/components/PluginIcon.js.map +1 -0
- package/dist/side-panel/components/PluginList.d.ts +11 -0
- package/dist/side-panel/components/PluginList.d.ts.map +1 -0
- package/dist/side-panel/components/PluginList.js +17 -0
- package/dist/side-panel/components/PluginList.js.map +1 -0
- package/dist/side-panel/components/ToolIcon.d.ts +7 -0
- package/dist/side-panel/components/ToolIcon.d.ts.map +1 -0
- package/dist/side-panel/components/ToolIcon.js +8 -0
- package/dist/side-panel/components/ToolIcon.js.map +1 -0
- package/dist/side-panel/components/ToolRow.d.ts +11 -0
- package/dist/side-panel/components/ToolRow.d.ts.map +1 -0
- package/dist/side-panel/components/ToolRow.js +8 -0
- package/dist/side-panel/components/ToolRow.js.map +1 -0
- package/dist/side-panel/components/VersionMismatchBanner.d.ts +3 -0
- package/dist/side-panel/components/VersionMismatchBanner.d.ts.map +1 -0
- package/dist/side-panel/components/VersionMismatchBanner.js +5 -0
- package/dist/side-panel/components/VersionMismatchBanner.js.map +1 -0
- package/dist/side-panel/components/retro/Accordion.d.ts +8 -0
- package/dist/side-panel/components/retro/Accordion.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Accordion.js +15 -0
- package/dist/side-panel/components/retro/Accordion.js.map +1 -0
- package/dist/side-panel/components/retro/Alert.d.ts +25 -0
- package/dist/side-panel/components/retro/Alert.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Alert.js +33 -0
- package/dist/side-panel/components/retro/Alert.js.map +1 -0
- package/dist/side-panel/components/retro/Badge.d.ts +15 -0
- package/dist/side-panel/components/retro/Badge.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Badge.js +23 -0
- package/dist/side-panel/components/retro/Badge.js.map +1 -0
- package/dist/side-panel/components/retro/Button.d.ts +13 -0
- package/dist/side-panel/components/retro/Button.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Button.js +33 -0
- package/dist/side-panel/components/retro/Button.js.map +1 -0
- package/dist/side-panel/components/retro/Empty.d.ts +31 -0
- package/dist/side-panel/components/retro/Empty.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Empty.js +25 -0
- package/dist/side-panel/components/retro/Empty.js.map +1 -0
- package/dist/side-panel/components/retro/Input.d.ts +8 -0
- package/dist/side-panel/components/retro/Input.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Input.js +7 -0
- package/dist/side-panel/components/retro/Input.js.map +1 -0
- package/dist/side-panel/components/retro/Loader.d.ts +14 -0
- package/dist/side-panel/components/retro/Loader.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Loader.js +30 -0
- package/dist/side-panel/components/retro/Loader.js.map +1 -0
- package/dist/side-panel/components/retro/Menu.d.ts +9 -0
- package/dist/side-panel/components/retro/Menu.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Menu.js +17 -0
- package/dist/side-panel/components/retro/Menu.js.map +1 -0
- package/dist/side-panel/components/retro/NumberStepper.d.ts +18 -0
- package/dist/side-panel/components/retro/NumberStepper.d.ts.map +1 -0
- package/dist/side-panel/components/retro/NumberStepper.js +38 -0
- package/dist/side-panel/components/retro/NumberStepper.js.map +1 -0
- package/dist/side-panel/components/retro/Progress.d.ts +9 -0
- package/dist/side-panel/components/retro/Progress.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Progress.js +6 -0
- package/dist/side-panel/components/retro/Progress.js.map +1 -0
- package/dist/side-panel/components/retro/Switch.d.ts +4 -0
- package/dist/side-panel/components/retro/Switch.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Switch.js +6 -0
- package/dist/side-panel/components/retro/Switch.js.map +1 -0
- package/dist/side-panel/components/retro/Text.d.ts +11 -0
- package/dist/side-panel/components/retro/Text.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Text.js +28 -0
- package/dist/side-panel/components/retro/Text.js.map +1 -0
- package/dist/side-panel/components/retro/Tooltip.d.ts +12 -0
- package/dist/side-panel/components/retro/Tooltip.d.ts.map +1 -0
- package/dist/side-panel/components/retro/Tooltip.js +32 -0
- package/dist/side-panel/components/retro/Tooltip.js.map +1 -0
- package/dist/side-panel/constants.d.ts +5 -0
- package/dist/side-panel/constants.d.ts.map +1 -0
- package/dist/side-panel/constants.js +5 -0
- package/dist/side-panel/constants.js.map +1 -0
- package/dist/side-panel/hooks/useServerNotifications.d.ts +21 -0
- package/dist/side-panel/hooks/useServerNotifications.d.ts.map +1 -0
- package/dist/side-panel/hooks/useServerNotifications.js +93 -0
- package/dist/side-panel/hooks/useServerNotifications.js.map +1 -0
- package/dist/side-panel/hooks/useTheme.d.ts +8 -0
- package/dist/side-panel/hooks/useTheme.d.ts.map +1 -0
- package/dist/side-panel/hooks/useTheme.js +30 -0
- package/dist/side-panel/hooks/useTheme.js.map +1 -0
- package/dist/side-panel/index.d.ts +2 -0
- package/dist/side-panel/index.d.ts.map +1 -0
- package/dist/side-panel/index.js +12 -0
- package/dist/side-panel/index.js.map +1 -0
- package/dist/side-panel/lib/cn.d.ts +3 -0
- package/dist/side-panel/lib/cn.d.ts.map +1 -0
- package/dist/side-panel/lib/cn.js +4 -0
- package/dist/side-panel/lib/cn.js.map +1 -0
- package/dist/side-panel/lib/utils.d.ts +3 -0
- package/dist/side-panel/lib/utils.d.ts.map +1 -0
- package/dist/side-panel/lib/utils.js +4 -0
- package/dist/side-panel/lib/utils.js.map +1 -0
- package/dist/side-panel/side-panel.js +78034 -0
- package/dist/side-panel/styles.css +2 -0
- package/dist/side-panel-state.d.ts +11 -0
- package/dist/side-panel-state.d.ts.map +1 -0
- package/dist/side-panel-state.js +38 -0
- package/dist/side-panel-state.js.map +1 -0
- package/dist/side-panel-toggle.d.ts +3 -0
- package/dist/side-panel-toggle.d.ts.map +1 -0
- package/dist/side-panel-toggle.js +31 -0
- package/dist/side-panel-toggle.js.map +1 -0
- package/dist/tab-matching.d.ts +51 -0
- package/dist/tab-matching.d.ts.map +1 -0
- package/dist/tab-matching.js +160 -0
- package/dist/tab-matching.js.map +1 -0
- package/dist/tab-state.d.ts +56 -0
- package/dist/tab-state.d.ts.map +1 -0
- package/dist/tab-state.js +282 -0
- package/dist/tab-state.js.map +1 -0
- package/dist/tool-dispatch.d.ts +19 -0
- package/dist/tool-dispatch.d.ts.map +1 -0
- package/dist/tool-dispatch.js +336 -0
- package/dist/tool-dispatch.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/icons/icon-128.png +0 -0
- package/icons/icon-16.png +0 -0
- package/icons/icon-32.png +0 -0
- package/icons/icon-48.png +0 -0
- package/icons/icon.svg +6 -0
- package/manifest.json +38 -0
- package/offscreen/offscreen.html +10 -0
- package/package.json +49 -0
- package/side-panel/dark-mode.js +29 -0
- package/side-panel/fonts/archivo-black-latin.woff2 +0 -0
- package/side-panel/fonts/space-grotesk-latin.woff2 +0 -0
- package/side-panel/fonts/space-mono-400-latin.woff2 +0 -0
- package/side-panel/fonts/space-mono-700-latin.woff2 +0 -0
- package/side-panel/fonts.css +34 -0
- package/side-panel/side-panel.html +16 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG sanitizer for plugin icons rendered via dangerouslySetInnerHTML.
|
|
3
|
+
*
|
|
4
|
+
* Uses an element/attribute allowlist approach — only known-safe SVG elements
|
|
5
|
+
* and attributes are preserved. Everything else is stripped. This prevents XSS
|
|
6
|
+
* vectors including <script>, <foreignObject>, event handler attributes,
|
|
7
|
+
* javascript: URIs, and CSS-based attacks.
|
|
8
|
+
*
|
|
9
|
+
* Runs in the side panel context (browser DOM available) and is also testable
|
|
10
|
+
* in Bun's test runner (no DOM dependency — pure string processing).
|
|
11
|
+
*/
|
|
12
|
+
// Elements allowed in plugin SVG icons (covers common icon shapes and styling)
|
|
13
|
+
const ALLOWED_ELEMENTS = new Set([
|
|
14
|
+
'svg',
|
|
15
|
+
'path',
|
|
16
|
+
'circle',
|
|
17
|
+
'ellipse',
|
|
18
|
+
'rect',
|
|
19
|
+
'line',
|
|
20
|
+
'polyline',
|
|
21
|
+
'polygon',
|
|
22
|
+
'g',
|
|
23
|
+
'defs',
|
|
24
|
+
'clippath',
|
|
25
|
+
'mask',
|
|
26
|
+
'lineargradient',
|
|
27
|
+
'radialgradient',
|
|
28
|
+
'stop',
|
|
29
|
+
'text',
|
|
30
|
+
'tspan',
|
|
31
|
+
'use',
|
|
32
|
+
'symbol',
|
|
33
|
+
'title',
|
|
34
|
+
'desc',
|
|
35
|
+
]);
|
|
36
|
+
// Attributes allowed on SVG elements (covers geometry, styling, transforms)
|
|
37
|
+
const ALLOWED_ATTRIBUTES = new Set([
|
|
38
|
+
// Core SVG attributes
|
|
39
|
+
'viewbox',
|
|
40
|
+
'xmlns',
|
|
41
|
+
'xmlns:xlink',
|
|
42
|
+
'version',
|
|
43
|
+
'id',
|
|
44
|
+
'class',
|
|
45
|
+
// Geometry
|
|
46
|
+
'd',
|
|
47
|
+
'cx',
|
|
48
|
+
'cy',
|
|
49
|
+
'r',
|
|
50
|
+
'rx',
|
|
51
|
+
'ry',
|
|
52
|
+
'x',
|
|
53
|
+
'x1',
|
|
54
|
+
'x2',
|
|
55
|
+
'y',
|
|
56
|
+
'y1',
|
|
57
|
+
'y2',
|
|
58
|
+
'width',
|
|
59
|
+
'height',
|
|
60
|
+
'points',
|
|
61
|
+
// Presentation
|
|
62
|
+
'fill',
|
|
63
|
+
'fill-opacity',
|
|
64
|
+
'fill-rule',
|
|
65
|
+
'stroke',
|
|
66
|
+
'stroke-dasharray',
|
|
67
|
+
'stroke-dashoffset',
|
|
68
|
+
'stroke-linecap',
|
|
69
|
+
'stroke-linejoin',
|
|
70
|
+
'stroke-miterlimit',
|
|
71
|
+
'stroke-opacity',
|
|
72
|
+
'stroke-width',
|
|
73
|
+
'opacity',
|
|
74
|
+
'color',
|
|
75
|
+
'display',
|
|
76
|
+
'visibility',
|
|
77
|
+
// Text
|
|
78
|
+
'font-family',
|
|
79
|
+
'font-size',
|
|
80
|
+
'font-style',
|
|
81
|
+
'font-variant',
|
|
82
|
+
'font-weight',
|
|
83
|
+
'text-anchor',
|
|
84
|
+
'text-decoration',
|
|
85
|
+
'dominant-baseline',
|
|
86
|
+
'alignment-baseline',
|
|
87
|
+
'letter-spacing',
|
|
88
|
+
'word-spacing',
|
|
89
|
+
'dx',
|
|
90
|
+
'dy',
|
|
91
|
+
'rotate',
|
|
92
|
+
'textlength',
|
|
93
|
+
'lengthadjust',
|
|
94
|
+
// Transform and clipping
|
|
95
|
+
'transform',
|
|
96
|
+
'clip-path',
|
|
97
|
+
'clip-rule',
|
|
98
|
+
'mask',
|
|
99
|
+
// Gradient-specific
|
|
100
|
+
'offset',
|
|
101
|
+
'stop-color',
|
|
102
|
+
'stop-opacity',
|
|
103
|
+
'gradientunits',
|
|
104
|
+
'gradienttransform',
|
|
105
|
+
'spreadmethod',
|
|
106
|
+
// Use/href (validated separately for safe values)
|
|
107
|
+
'href',
|
|
108
|
+
'xlink:href',
|
|
109
|
+
// Overflow/aspect
|
|
110
|
+
'overflow',
|
|
111
|
+
'preserveaspectratio',
|
|
112
|
+
// Marker
|
|
113
|
+
'marker-start',
|
|
114
|
+
'marker-mid',
|
|
115
|
+
'marker-end',
|
|
116
|
+
// Filter (references only)
|
|
117
|
+
'filter',
|
|
118
|
+
'flood-color',
|
|
119
|
+
'flood-opacity',
|
|
120
|
+
// Style (validated separately for safe values)
|
|
121
|
+
'style',
|
|
122
|
+
]);
|
|
123
|
+
/** Returns true if a URI value is a safe internal reference (e.g., #id, url(#id)). */
|
|
124
|
+
const isSafeUri = (value) => {
|
|
125
|
+
const trimmed = value.trim();
|
|
126
|
+
// Internal fragment references are safe
|
|
127
|
+
if (trimmed.startsWith('#'))
|
|
128
|
+
return true;
|
|
129
|
+
// Empty string is safe
|
|
130
|
+
if (trimmed === '')
|
|
131
|
+
return true;
|
|
132
|
+
// Block javascript:, data:, and any other protocol URIs
|
|
133
|
+
return false;
|
|
134
|
+
};
|
|
135
|
+
/** Returns true if a style attribute value contains only safe CSS properties. */
|
|
136
|
+
const isSafeStyle = (value) => {
|
|
137
|
+
// Block any url() references except #fragment
|
|
138
|
+
const urlPattern = /url\s*\(\s*(?:["']?\s*)(.*?)(?:\s*["']?\s*)\)/gi;
|
|
139
|
+
let urlMatch;
|
|
140
|
+
while ((urlMatch = urlPattern.exec(value)) !== null) {
|
|
141
|
+
const url = (urlMatch[1] ?? '').trim();
|
|
142
|
+
if (!url.startsWith('#'))
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
// Block expression(), -moz-binding, and behavior CSS properties
|
|
146
|
+
if (/expression\s*\(/i.test(value))
|
|
147
|
+
return false;
|
|
148
|
+
if (/-moz-binding/i.test(value))
|
|
149
|
+
return false;
|
|
150
|
+
if (/behavior\s*:/i.test(value))
|
|
151
|
+
return false;
|
|
152
|
+
// Block javascript: in any property value
|
|
153
|
+
if (/javascript\s*:/i.test(value))
|
|
154
|
+
return false;
|
|
155
|
+
return true;
|
|
156
|
+
};
|
|
157
|
+
/** Parse an attribute string like `key="value"` or `key='value'` or `key` */
|
|
158
|
+
const parseAttributes = (attrString) => {
|
|
159
|
+
const attrs = [];
|
|
160
|
+
// Match: name="value", name='value', or standalone name
|
|
161
|
+
const attrRegex = /([a-z][a-z0-9:_-]*)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'))?/gi;
|
|
162
|
+
let match;
|
|
163
|
+
while ((match = attrRegex.exec(attrString)) !== null) {
|
|
164
|
+
const name = match[1] ?? '';
|
|
165
|
+
const value = match[2] ?? match[3] ?? '';
|
|
166
|
+
attrs.push({ name, value, raw: match[0] });
|
|
167
|
+
}
|
|
168
|
+
return attrs;
|
|
169
|
+
};
|
|
170
|
+
/** Tokenize SVG markup into tags and text */
|
|
171
|
+
const tokenize = (svg) => {
|
|
172
|
+
const tokens = [];
|
|
173
|
+
// Match opening/closing/self-closing tags and comments
|
|
174
|
+
const tagRegex = /<!--[\s\S]*?-->|<\/?([a-z][a-z0-9]*)((?:\s+[a-z][a-z0-9:_-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*)\s*(\/?)>/gi;
|
|
175
|
+
let lastIndex = 0;
|
|
176
|
+
let tagMatch;
|
|
177
|
+
while ((tagMatch = tagRegex.exec(svg)) !== null) {
|
|
178
|
+
// Text content before this tag
|
|
179
|
+
if (tagMatch.index > lastIndex) {
|
|
180
|
+
tokens.push({ type: 'text', content: svg.slice(lastIndex, tagMatch.index) });
|
|
181
|
+
}
|
|
182
|
+
const raw = tagMatch[0];
|
|
183
|
+
// Skip comments entirely
|
|
184
|
+
if (raw.startsWith('<!--')) {
|
|
185
|
+
lastIndex = tagRegex.lastIndex;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const tagName = tagMatch[1] ?? '';
|
|
189
|
+
const attrString = tagMatch[2] ?? '';
|
|
190
|
+
const selfClosing = tagMatch[3] === '/';
|
|
191
|
+
const isClosing = raw.startsWith('</');
|
|
192
|
+
if (isClosing) {
|
|
193
|
+
tokens.push({ type: 'close', raw, tagName, attributes: [] });
|
|
194
|
+
}
|
|
195
|
+
else if (selfClosing) {
|
|
196
|
+
tokens.push({ type: 'self-closing', raw, tagName, attributes: parseAttributes(attrString) });
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
tokens.push({ type: 'open', raw, tagName, attributes: parseAttributes(attrString) });
|
|
200
|
+
}
|
|
201
|
+
lastIndex = tagRegex.lastIndex;
|
|
202
|
+
}
|
|
203
|
+
// Trailing text
|
|
204
|
+
if (lastIndex < svg.length) {
|
|
205
|
+
tokens.push({ type: 'text', content: svg.slice(lastIndex) });
|
|
206
|
+
}
|
|
207
|
+
return tokens;
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Reconstruct an SVG tag from allowed attributes only.
|
|
211
|
+
* Returns the sanitized tag string.
|
|
212
|
+
*/
|
|
213
|
+
const buildTag = (tagName, attributes, selfClosing) => {
|
|
214
|
+
const parts = [tagName];
|
|
215
|
+
for (const attr of attributes) {
|
|
216
|
+
// Double-quote the value, escaping any embedded quotes
|
|
217
|
+
const escapedValue = attr.value.replace(/&/g, '&').replace(/"/g, '"');
|
|
218
|
+
parts.push(`${attr.name}="${escapedValue}"`);
|
|
219
|
+
}
|
|
220
|
+
const inner = parts.join(' ');
|
|
221
|
+
return selfClosing ? `<${inner}/>` : `<${inner}>`;
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Sanitize an SVG string by stripping all elements and attributes not on the allowlist.
|
|
225
|
+
*
|
|
226
|
+
* Returns the sanitized SVG string safe for use with dangerouslySetInnerHTML.
|
|
227
|
+
* Returns an empty string for non-string or empty input.
|
|
228
|
+
*/
|
|
229
|
+
const sanitizeSvg = (svg) => {
|
|
230
|
+
if (!svg || typeof svg !== 'string')
|
|
231
|
+
return '';
|
|
232
|
+
const tokens = tokenize(svg);
|
|
233
|
+
const result = [];
|
|
234
|
+
// Track which elements are currently stripped (to strip their children too)
|
|
235
|
+
let strippedDepth = 0;
|
|
236
|
+
for (const token of tokens) {
|
|
237
|
+
if (token.type === 'text') {
|
|
238
|
+
if (strippedDepth === 0) {
|
|
239
|
+
result.push(token.content);
|
|
240
|
+
}
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const tagNameLower = token.tagName.toLowerCase();
|
|
244
|
+
if (token.type === 'close') {
|
|
245
|
+
if (strippedDepth > 0) {
|
|
246
|
+
strippedDepth--;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (ALLOWED_ELEMENTS.has(tagNameLower)) {
|
|
250
|
+
result.push(`</${token.tagName}>`);
|
|
251
|
+
}
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
// Open or self-closing tag
|
|
255
|
+
if (!ALLOWED_ELEMENTS.has(tagNameLower)) {
|
|
256
|
+
if (token.type === 'open') {
|
|
257
|
+
strippedDepth++;
|
|
258
|
+
}
|
|
259
|
+
// Self-closing disallowed tags are just dropped
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (strippedDepth > 0) {
|
|
263
|
+
// Inside a stripped subtree — skip this element and its children
|
|
264
|
+
if (token.type === 'open') {
|
|
265
|
+
strippedDepth++;
|
|
266
|
+
}
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
// Filter attributes
|
|
270
|
+
const safeAttrs = [];
|
|
271
|
+
for (const attr of token.attributes) {
|
|
272
|
+
const attrNameLower = attr.name.toLowerCase();
|
|
273
|
+
// Skip any event handler attributes (on*)
|
|
274
|
+
if (attrNameLower.startsWith('on'))
|
|
275
|
+
continue;
|
|
276
|
+
// Skip attributes not on the allowlist
|
|
277
|
+
if (!ALLOWED_ATTRIBUTES.has(attrNameLower))
|
|
278
|
+
continue;
|
|
279
|
+
// Validate href/xlink:href — only allow internal fragment references
|
|
280
|
+
if (attrNameLower === 'href' || attrNameLower === 'xlink:href') {
|
|
281
|
+
if (!isSafeUri(attr.value))
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
// Validate style attribute content
|
|
285
|
+
if (attrNameLower === 'style') {
|
|
286
|
+
if (!isSafeStyle(attr.value))
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
safeAttrs.push({ name: attr.name, value: attr.value });
|
|
290
|
+
}
|
|
291
|
+
result.push(buildTag(token.tagName, safeAttrs, token.type === 'self-closing'));
|
|
292
|
+
}
|
|
293
|
+
return result.join('');
|
|
294
|
+
};
|
|
295
|
+
export { sanitizeSvg };
|
|
296
|
+
//# sourceMappingURL=sanitize-svg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize-svg.js","sourceRoot":"","sources":["../src/sanitize-svg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/side-panel/App.tsx"],"names":[],"mappings":"AAqBA,QAAA,MAAM,GAAG,+CAqNR,CAAC;AAEF,OAAO,EAAE,GAAG,EAAE,CAAC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { getConnectionState, fetchConfigState, handleServerResponse, rejectAllPending, sendConfirmationResponse, } from './bridge.js';
|
|
3
|
+
import { ConfirmationDialog } from './components/ConfirmationDialog.js';
|
|
4
|
+
import { DisconnectedState, NoPluginsState, LoadingState } from './components/EmptyStates.js';
|
|
5
|
+
import { Footer } from './components/Footer.js';
|
|
6
|
+
import { PluginList } from './components/PluginList.js';
|
|
7
|
+
import { Input } from './components/retro/Input.js';
|
|
8
|
+
import { Tooltip } from './components/retro/Tooltip.js';
|
|
9
|
+
import { useServerNotifications } from './hooks/useServerNotifications.js';
|
|
10
|
+
import { Search, X } from 'lucide-react';
|
|
11
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
12
|
+
const App = () => {
|
|
13
|
+
const [connected, setConnected] = useState(false);
|
|
14
|
+
const [disconnectReason, setDisconnectReason] = useState();
|
|
15
|
+
const [plugins, setPlugins] = useState([]);
|
|
16
|
+
const [failedPlugins, setFailedPlugins] = useState([]);
|
|
17
|
+
const [loading, setLoading] = useState(true);
|
|
18
|
+
const [activeTools, setActiveTools] = useState(new Set());
|
|
19
|
+
const [toolFilter, setToolFilter] = useState('');
|
|
20
|
+
const [pendingConfirmations, setPendingConfirmations] = useState([]);
|
|
21
|
+
const lastFetchRef = useRef(0);
|
|
22
|
+
const pendingTabStates = useRef(new Map());
|
|
23
|
+
const connectedRef = useRef(connected);
|
|
24
|
+
const loadingRef = useRef(loading);
|
|
25
|
+
const pluginsRef = useRef(plugins);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
connectedRef.current = connected;
|
|
28
|
+
loadingRef.current = loading;
|
|
29
|
+
pluginsRef.current = plugins;
|
|
30
|
+
}, [connected, loading, plugins]);
|
|
31
|
+
const loadPlugins = useCallback(() => {
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
if (now - lastFetchRef.current < 200)
|
|
34
|
+
return;
|
|
35
|
+
lastFetchRef.current = now;
|
|
36
|
+
fetchConfigState()
|
|
37
|
+
.then(result => {
|
|
38
|
+
let updatedPlugins = result.plugins;
|
|
39
|
+
if (pendingTabStates.current.size > 0) {
|
|
40
|
+
updatedPlugins = updatedPlugins.map(p => {
|
|
41
|
+
const buffered = pendingTabStates.current.get(p.name);
|
|
42
|
+
return buffered ? { ...p, tabState: buffered } : p;
|
|
43
|
+
});
|
|
44
|
+
pendingTabStates.current.clear();
|
|
45
|
+
}
|
|
46
|
+
setPlugins(updatedPlugins);
|
|
47
|
+
setFailedPlugins(result.failedPlugins);
|
|
48
|
+
})
|
|
49
|
+
.catch(() => {
|
|
50
|
+
// Server may not be ready yet
|
|
51
|
+
});
|
|
52
|
+
}, []);
|
|
53
|
+
const { handleNotification, clearConfirmationTimeout } = useServerNotifications({
|
|
54
|
+
setPlugins,
|
|
55
|
+
setActiveTools,
|
|
56
|
+
setPendingConfirmations,
|
|
57
|
+
pendingTabStates,
|
|
58
|
+
});
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
void getConnectionState().then(result => {
|
|
61
|
+
setConnected(result.connected);
|
|
62
|
+
setDisconnectReason(result.disconnectReason);
|
|
63
|
+
if (result.connected) {
|
|
64
|
+
loadPlugins();
|
|
65
|
+
}
|
|
66
|
+
setLoading(false);
|
|
67
|
+
});
|
|
68
|
+
const listener = (message, _sender, sendResponse) => {
|
|
69
|
+
if (message.type === 'sp:getState') {
|
|
70
|
+
const rootEl = document.getElementById('root');
|
|
71
|
+
const html = rootEl ? rootEl.innerHTML.slice(0, 50000) : '';
|
|
72
|
+
const currentPlugins = pluginsRef.current;
|
|
73
|
+
sendResponse({
|
|
74
|
+
state: {
|
|
75
|
+
connected: connectedRef.current,
|
|
76
|
+
loading: loadingRef.current,
|
|
77
|
+
pluginCount: currentPlugins.length,
|
|
78
|
+
plugins: currentPlugins.map(p => ({ name: p.name, tabState: p.tabState })),
|
|
79
|
+
},
|
|
80
|
+
html,
|
|
81
|
+
});
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
if (message.type === 'sp:connectionState') {
|
|
85
|
+
const isConnected = message.data.connected;
|
|
86
|
+
setConnected(isConnected);
|
|
87
|
+
setDisconnectReason(isConnected ? undefined : message.data.disconnectReason);
|
|
88
|
+
if (isConnected) {
|
|
89
|
+
loadPlugins();
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
setPlugins([]);
|
|
93
|
+
setFailedPlugins([]);
|
|
94
|
+
setActiveTools(new Set());
|
|
95
|
+
setPendingConfirmations([]);
|
|
96
|
+
rejectAllPending();
|
|
97
|
+
}
|
|
98
|
+
sendResponse({ ok: true });
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (message.type === 'sp:serverMessage') {
|
|
102
|
+
const data = message.data;
|
|
103
|
+
if (handleServerResponse(data)) {
|
|
104
|
+
sendResponse({ ok: true });
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
if (data.method === 'plugins.changed') {
|
|
108
|
+
loadPlugins();
|
|
109
|
+
sendResponse({ ok: true });
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
handleNotification(data);
|
|
113
|
+
sendResponse({ ok: true });
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
if (message.type === 'ws:message') {
|
|
117
|
+
const wsData = message.data;
|
|
118
|
+
if (wsData?.method === 'sync.full') {
|
|
119
|
+
loadPlugins();
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
};
|
|
125
|
+
chrome.runtime.onMessage.addListener(listener);
|
|
126
|
+
return () => chrome.runtime.onMessage.removeListener(listener);
|
|
127
|
+
}, [loadPlugins, handleNotification]);
|
|
128
|
+
const handleConfirmationRespond = useCallback((id, decision, scope) => {
|
|
129
|
+
clearConfirmationTimeout(id);
|
|
130
|
+
sendConfirmationResponse(id, decision, scope);
|
|
131
|
+
setPendingConfirmations(prev => prev.filter(c => c.id !== id));
|
|
132
|
+
}, [clearConfirmationTimeout]);
|
|
133
|
+
const handleDenyAll = useCallback(() => {
|
|
134
|
+
for (const c of pendingConfirmations) {
|
|
135
|
+
clearConfirmationTimeout(c.id);
|
|
136
|
+
sendConfirmationResponse(c.id, 'deny');
|
|
137
|
+
}
|
|
138
|
+
setPendingConfirmations([]);
|
|
139
|
+
}, [pendingConfirmations, clearConfirmationTimeout]);
|
|
140
|
+
const totalTools = plugins.reduce((sum, p) => sum + p.tools.length, 0);
|
|
141
|
+
const hasContent = plugins.length > 0 || failedPlugins.length > 0;
|
|
142
|
+
const showPlugins = !loading && connected && hasContent;
|
|
143
|
+
const showSearchBar = connected && !loading && totalTools > 5;
|
|
144
|
+
return (_jsx(Tooltip.Provider, { children: _jsxs("div", { className: "text-foreground flex min-h-screen flex-col", children: [connected && pendingConfirmations.length > 0 && (_jsx(ConfirmationDialog, { confirmations: pendingConfirmations, onRespond: handleConfirmationRespond, onDenyAll: handleDenyAll })), showSearchBar && (_jsx("div", { className: "pt-4 pr-5 pb-2 pl-4", children: _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "text-muted-foreground pointer-events-none absolute top-1/2 left-2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { value: toolFilter, onChange: e => setToolFilter(e.target.value), placeholder: "Filter tools...", className: "pr-8 pl-9" }), toolFilter && (_jsx("button", { type: "button", onClick: () => setToolFilter(''), className: "text-muted-foreground hover:text-foreground absolute top-1/2 right-2 -translate-y-1/2 cursor-pointer", children: _jsx(X, { className: "h-4 w-4" }) }))] }) })), _jsx("main", { className: `flex-1 pr-5 pb-2 pl-4 ${showSearchBar ? 'pt-2' : 'pt-4'} ${showPlugins ? '' : 'flex items-center justify-center'}`, children: loading ? (_jsx(LoadingState, {})) : !connected ? (_jsx(DisconnectedState, { reason: disconnectReason })) : !hasContent ? (_jsx(NoPluginsState, {})) : (_jsx(PluginList, { plugins: plugins, failedPlugins: failedPlugins, activeTools: activeTools, setPlugins: setPlugins, toolFilter: toolFilter })) }), _jsx(Footer, {})] }) }));
|
|
145
|
+
};
|
|
146
|
+
export { App };
|
|
147
|
+
//# sourceMappingURL=App.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"App.js","sourceRoot":"","sources":["../../src/side-panel/App.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge for side panel ↔ background script ↔ MCP server communication.
|
|
3
|
+
*
|
|
4
|
+
* Uses chrome.runtime.sendMessage with bg:* message types to relay JSON-RPC
|
|
5
|
+
* requests to the MCP server. Responses return asynchronously via sp:serverMessage
|
|
6
|
+
* and are correlated by request ID using a pending-request map.
|
|
7
|
+
*
|
|
8
|
+
* Each pending request has a 30-second timeout to prevent hanging promises when
|
|
9
|
+
* the server disconnects between send and response. All pending requests are
|
|
10
|
+
* also rejected on WebSocket disconnect (sp:connectionState connected=false).
|
|
11
|
+
*/
|
|
12
|
+
import type { DisconnectReason } from '../extension-messages.js';
|
|
13
|
+
import type { ConfigStateFailedPlugin, ConfigStatePlugin, ConfigStateResult } from '@opentabs-dev/shared';
|
|
14
|
+
/** Plugin state as displayed in the side panel (matches config.getState response) */
|
|
15
|
+
type PluginState = ConfigStatePlugin;
|
|
16
|
+
/** Failed plugin state as displayed in the side panel */
|
|
17
|
+
type FailedPluginState = ConfigStateFailedPlugin;
|
|
18
|
+
/** Returns true if a tool's displayName, name, or description matches the filter string */
|
|
19
|
+
declare const matchesTool: (tool: PluginState["tools"][number], filterLower: string) => boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Reject all pending requests immediately.
|
|
22
|
+
* Called on WebSocket disconnect (sp:connectionState connected=false) so the
|
|
23
|
+
* side panel gets fast errors instead of waiting for individual 30s timeouts.
|
|
24
|
+
*/
|
|
25
|
+
declare const rejectAllPending: () => void;
|
|
26
|
+
/**
|
|
27
|
+
* Handle a server message forwarded by the background script.
|
|
28
|
+
* If the message is a response (has id, no method), resolve the matching
|
|
29
|
+
* pending request and return true. Otherwise return false so the caller
|
|
30
|
+
* can handle it as a notification.
|
|
31
|
+
*/
|
|
32
|
+
declare const handleServerResponse: (data: Record<string, unknown>) => boolean;
|
|
33
|
+
/** Result from querying the background script for WebSocket connection state */
|
|
34
|
+
interface ConnectionStateResult {
|
|
35
|
+
connected: boolean;
|
|
36
|
+
disconnectReason?: DisconnectReason;
|
|
37
|
+
}
|
|
38
|
+
/** Query the background script for WebSocket connection state */
|
|
39
|
+
declare const getConnectionState: () => Promise<ConnectionStateResult>;
|
|
40
|
+
/** Request full state from MCP server via config.getState */
|
|
41
|
+
declare const fetchConfigState: () => Promise<ConfigStateResult>;
|
|
42
|
+
/** Toggle a single tool's enabled state */
|
|
43
|
+
declare const setToolEnabled: (plugin: string, tool: string, enabled: boolean) => Promise<unknown>;
|
|
44
|
+
/** Toggle all tools for a plugin */
|
|
45
|
+
declare const setAllToolsEnabled: (plugin: string, enabled: boolean) => Promise<unknown>;
|
|
46
|
+
/** Send a confirmation response to the MCP server via the background script (fire-and-forget) */
|
|
47
|
+
declare const sendConfirmationResponse: (id: string, decision: "allow_once" | "allow_always" | "deny", scope?: "tool_domain" | "tool_all" | "domain_all") => void;
|
|
48
|
+
export type { FailedPluginState, PluginState };
|
|
49
|
+
export { getConnectionState, fetchConfigState, matchesTool, setToolEnabled, setAllToolsEnabled, handleServerResponse, rejectAllPending, sendConfirmationResponse, };
|
|
50
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/side-panel/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE1G,qFAAqF;AACrF,KAAK,WAAW,GAAG,iBAAiB,CAAC;AAErC,yDAAyD;AACzD,KAAK,iBAAiB,GAAG,uBAAuB,CAAC;AAEjD,2FAA2F;AAC3F,QAAA,MAAM,WAAW,GAAI,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,aAAa,MAAM,KAAG,OAGzB,CAAC;AAevD;;;;GAIG;AACH,QAAA,MAAM,gBAAgB,QAAO,IAM5B,CAAC;AA8BF;;;;;GAKG;AACH,QAAA,MAAM,oBAAoB,GAAI,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,OAmB7D,CAAC;AAEF,gFAAgF;AAChF,UAAU,qBAAqB;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED,iEAAiE;AACjE,QAAA,MAAM,kBAAkB,QAAO,OAAO,CAAC,qBAAqB,CAexD,CAAC;AAEL,6DAA6D;AAC7D,QAAA,MAAM,gBAAgB,QAA2C,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE5F,2CAA2C;AAC3C,QAAA,MAAM,cAAc,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,EAAE,SAAS,OAAO,KAAG,OAAO,CAAC,OAAO,CACvB,CAAC;AAElE,oCAAoC;AACpC,QAAA,MAAM,kBAAkB,GAAI,QAAQ,MAAM,EAAE,SAAS,OAAO,KAAG,OAAO,CAAC,OAAO,CACf,CAAC;AAEhE,iGAAiG;AACjG,QAAA,MAAM,wBAAwB,GAC5B,IAAI,MAAM,EACV,UAAU,YAAY,GAAG,cAAc,GAAG,MAAM,EAChD,QAAQ,aAAa,GAAG,UAAU,GAAG,YAAY,KAChD,IASF,CAAC;AAEF,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAC;AAC/C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,wBAAwB,GACzB,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge for side panel ↔ background script ↔ MCP server communication.
|
|
3
|
+
*
|
|
4
|
+
* Uses chrome.runtime.sendMessage with bg:* message types to relay JSON-RPC
|
|
5
|
+
* requests to the MCP server. Responses return asynchronously via sp:serverMessage
|
|
6
|
+
* and are correlated by request ID using a pending-request map.
|
|
7
|
+
*
|
|
8
|
+
* Each pending request has a 30-second timeout to prevent hanging promises when
|
|
9
|
+
* the server disconnects between send and response. All pending requests are
|
|
10
|
+
* also rejected on WebSocket disconnect (sp:connectionState connected=false).
|
|
11
|
+
*/
|
|
12
|
+
/** Returns true if a tool's displayName, name, or description matches the filter string */
|
|
13
|
+
const matchesTool = (tool, filterLower) => tool.displayName.toLowerCase().includes(filterLower) ||
|
|
14
|
+
tool.name.toLowerCase().includes(filterLower) ||
|
|
15
|
+
tool.description.toLowerCase().includes(filterLower);
|
|
16
|
+
/** Timeout for pending JSON-RPC requests relayed through the background script (ms) */
|
|
17
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
18
|
+
/** Map of request ID → pending request. Responses are matched by ID. */
|
|
19
|
+
const pendingRequests = new Map();
|
|
20
|
+
/**
|
|
21
|
+
* Reject all pending requests immediately.
|
|
22
|
+
* Called on WebSocket disconnect (sp:connectionState connected=false) so the
|
|
23
|
+
* side panel gets fast errors instead of waiting for individual 30s timeouts.
|
|
24
|
+
*/
|
|
25
|
+
const rejectAllPending = () => {
|
|
26
|
+
for (const [id, pending] of pendingRequests) {
|
|
27
|
+
pendingRequests.delete(id);
|
|
28
|
+
clearTimeout(pending.timerId);
|
|
29
|
+
pending.reject(new Error('Server disconnected'));
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Send a JSON-RPC request to the MCP server via the background script.
|
|
34
|
+
* Returns a promise that resolves with the MCP server's response (not the
|
|
35
|
+
* background script's ack). Call handleServerResponse() when sp:serverMessage
|
|
36
|
+
* arrives to resolve pending requests.
|
|
37
|
+
*/
|
|
38
|
+
const sendRequest = (method, params = {}) => {
|
|
39
|
+
const id = crypto.randomUUID();
|
|
40
|
+
const data = { jsonrpc: '2.0', method, params, id };
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const timerId = setTimeout(() => {
|
|
43
|
+
if (pendingRequests.has(id)) {
|
|
44
|
+
pendingRequests.delete(id);
|
|
45
|
+
reject(new Error(`Request ${method} timed out after ${REQUEST_TIMEOUT_MS}ms`));
|
|
46
|
+
}
|
|
47
|
+
}, REQUEST_TIMEOUT_MS);
|
|
48
|
+
pendingRequests.set(id, { resolve, reject, timerId });
|
|
49
|
+
chrome.runtime.sendMessage({ type: 'bg:send', data }).catch((err) => {
|
|
50
|
+
clearTimeout(timerId);
|
|
51
|
+
pendingRequests.delete(id);
|
|
52
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Handle a server message forwarded by the background script.
|
|
58
|
+
* If the message is a response (has id, no method), resolve the matching
|
|
59
|
+
* pending request and return true. Otherwise return false so the caller
|
|
60
|
+
* can handle it as a notification.
|
|
61
|
+
*/
|
|
62
|
+
const handleServerResponse = (data) => {
|
|
63
|
+
const rawId = data.id;
|
|
64
|
+
if (rawId === undefined || rawId === null || data.method)
|
|
65
|
+
return false;
|
|
66
|
+
const id = String(rawId);
|
|
67
|
+
const pending = pendingRequests.get(id);
|
|
68
|
+
if (!pending)
|
|
69
|
+
return false;
|
|
70
|
+
pendingRequests.delete(id);
|
|
71
|
+
clearTimeout(pending.timerId);
|
|
72
|
+
if ('error' in data) {
|
|
73
|
+
const err = data.error;
|
|
74
|
+
pending.reject(new Error(err.message ?? 'Unknown server error'));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
pending.resolve(data.result);
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
};
|
|
81
|
+
/** Query the background script for WebSocket connection state */
|
|
82
|
+
const getConnectionState = () => new Promise(resolve => {
|
|
83
|
+
chrome.runtime.sendMessage({ type: 'bg:getConnectionState' }, (response) => {
|
|
84
|
+
if (chrome.runtime.lastError) {
|
|
85
|
+
resolve({ connected: false });
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
resolve({
|
|
89
|
+
connected: response?.connected === true,
|
|
90
|
+
disconnectReason: response?.disconnectReason,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
/** Request full state from MCP server via config.getState */
|
|
96
|
+
const fetchConfigState = () => sendRequest('config.getState');
|
|
97
|
+
/** Toggle a single tool's enabled state */
|
|
98
|
+
const setToolEnabled = (plugin, tool, enabled) => sendRequest('config.setToolEnabled', { plugin, tool, enabled });
|
|
99
|
+
/** Toggle all tools for a plugin */
|
|
100
|
+
const setAllToolsEnabled = (plugin, enabled) => sendRequest('config.setAllToolsEnabled', { plugin, enabled });
|
|
101
|
+
/** Send a confirmation response to the MCP server via the background script (fire-and-forget) */
|
|
102
|
+
const sendConfirmationResponse = (id, decision, scope) => {
|
|
103
|
+
chrome.runtime
|
|
104
|
+
.sendMessage({
|
|
105
|
+
type: 'sp:confirmationResponse',
|
|
106
|
+
data: { id, decision, ...(scope ? { scope } : {}) },
|
|
107
|
+
})
|
|
108
|
+
.catch((err) => {
|
|
109
|
+
console.warn('[opentabs:side-panel] Failed to send confirmation response:', err);
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
export { getConnectionState, fetchConfigState, matchesTool, setToolEnabled, setAllToolsEnabled, handleServerResponse, rejectAllPending, sendConfirmationResponse, };
|
|
113
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/side-panel/bridge.ts"],"names":[],"mappings":"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"}
|