@dxos/plugin-crx 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +105 -0
- package/PLUGIN.mdl +599 -0
- package/README.md +25 -0
- package/dist/lib/CrxCapabilities.mjs +21 -0
- package/dist/lib/CrxCapabilities.mjs.map +1 -0
- package/dist/lib/CrxEvents.mjs +15 -0
- package/dist/lib/CrxEvents.mjs.map +1 -0
- package/dist/lib/CrxOperation.mjs +50 -0
- package/dist/lib/CrxOperation.mjs.map +1 -0
- package/dist/lib/CrxPlugin.mjs +16 -0
- package/dist/lib/CrxPlugin.mjs.map +1 -0
- package/dist/lib/PageAction.mjs +35 -0
- package/dist/lib/PageAction.mjs.map +1 -0
- package/dist/lib/Settings.mjs +84 -0
- package/dist/lib/Settings.mjs.map +1 -0
- package/dist/lib/capabilities.mjs +41 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/capabilities.node.mjs +32 -0
- package/dist/lib/capabilities.node.mjs.map +1 -0
- package/dist/lib/capabilities.workerd.mjs +32 -0
- package/dist/lib/capabilities.workerd.mjs.map +1 -0
- package/dist/lib/chunk-add-note-from-snapshot.mjs +15 -0
- package/dist/lib/chunk-add-note-from-snapshot.mjs.map +1 -0
- package/dist/lib/chunk-add-organization-from-snapshot.mjs +15 -0
- package/dist/lib/chunk-add-organization-from-snapshot.mjs.map +1 -0
- package/dist/lib/chunk-add-person-from-snapshot.mjs +15 -0
- package/dist/lib/chunk-add-person-from-snapshot.mjs.map +1 -0
- package/dist/lib/chunk-install-page-actions.mjs +284 -0
- package/dist/lib/chunk-install-page-actions.mjs.map +1 -0
- package/dist/lib/chunk-mapping.mjs +81 -0
- package/dist/lib/chunk-mapping.mjs.map +1 -0
- package/dist/lib/chunk-operation-handler.mjs +12 -0
- package/dist/lib/chunk-operation-handler.mjs.map +1 -0
- package/dist/lib/chunk-page-action-provider.mjs +41 -0
- package/dist/lib/chunk-page-action-provider.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +18 -0
- package/dist/lib/chunk-react-surface.mjs.map +1 -0
- package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
- package/dist/lib/chunk-settings.mjs +28 -0
- package/dist/lib/chunk-settings.mjs.map +1 -0
- package/dist/lib/containers.mjs +80 -0
- package/dist/lib/containers.mjs.map +1 -0
- package/dist/lib/index.mjs +4 -0
- package/dist/lib/meta.mjs +34 -0
- package/dist/lib/meta.mjs.map +1 -0
- package/dist/lib/operations.mjs +13 -0
- package/dist/lib/operations.mjs.map +1 -0
- package/dist/lib/plugin.mjs +9 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/translations.mjs +22 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +6 -0
- package/dist/lib/util.mjs +63 -0
- package/dist/lib/util.mjs.map +1 -0
- package/dist/types/dx.config.d.ts +28 -0
- package/dist/types/dx.config.d.ts.map +1 -0
- package/dist/types/src/CrxPlugin.d.ts +37 -0
- package/dist/types/src/CrxPlugin.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/node.d.ts +9 -0
- package/dist/types/src/capabilities/gen/node.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts +9 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +9 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/install-page-actions.d.ts +5 -0
- package/dist/types/src/capabilities/install-page-actions.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +5 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/page-action-provider.d.ts +5 -0
- package/dist/types/src/capabilities/page-action-provider.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/settings.d.ts +10 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/containers/CrxSettings/CrxSettings.d.ts +10 -0
- package/dist/types/src/containers/CrxSettings/CrxSettings.d.ts.map +1 -0
- package/dist/types/src/containers/CrxSettings/CrxSettings.stories.d.ts +25 -0
- package/dist/types/src/containers/CrxSettings/CrxSettings.stories.d.ts.map +1 -0
- package/dist/types/src/containers/CrxSettings/index.d.ts +2 -0
- package/dist/types/src/containers/CrxSettings/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +2 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/mapping.d.ts +24 -0
- package/dist/types/src/mapping.d.ts.map +1 -0
- package/dist/types/src/mapping.test.d.ts +2 -0
- package/dist/types/src/mapping.test.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +33 -0
- package/dist/types/src/meta.d.ts.map +1 -0
- package/dist/types/src/operations/add-note-from-snapshot.d.ts +5 -0
- package/dist/types/src/operations/add-note-from-snapshot.d.ts.map +1 -0
- package/dist/types/src/operations/add-organization-from-snapshot.d.ts +5 -0
- package/dist/types/src/operations/add-organization-from-snapshot.d.ts.map +1 -0
- package/dist/types/src/operations/add-person-from-snapshot.d.ts +5 -0
- package/dist/types/src/operations/add-person-from-snapshot.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/page-actions.d.ts +39 -0
- package/dist/types/src/page-actions.d.ts.map +1 -0
- package/dist/types/src/page-actions.test.d.ts +2 -0
- package/dist/types/src/page-actions.test.d.ts.map +1 -0
- package/dist/types/src/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/stories/Testbench.stories.d.ts +8 -0
- package/dist/types/src/stories/Testbench.stories.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +22 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/types/CrxCapabilities.d.ts +12 -0
- package/dist/types/src/types/CrxCapabilities.d.ts.map +1 -0
- package/dist/types/src/types/CrxEvents.d.ts +8 -0
- package/dist/types/src/types/CrxEvents.d.ts.map +1 -0
- package/dist/types/src/types/CrxOperation.d.ts +108 -0
- package/dist/types/src/types/CrxOperation.d.ts.map +1 -0
- package/dist/types/src/types/PageAction.d.ts +153 -0
- package/dist/types/src/types/PageAction.d.ts.map +1 -0
- package/dist/types/src/types/Settings.d.ts +25 -0
- package/dist/types/src/types/Settings.d.ts.map +1 -0
- package/dist/types/src/types/Settings.test.d.ts +2 -0
- package/dist/types/src/types/Settings.test.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +6 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -0
- package/dist/types/src/util/pingExtension.d.ts +15 -0
- package/dist/types/src/util/pingExtension.d.ts.map +1 -0
- package/dist/types/src/util/pingExtension.test.d.ts +2 -0
- package/dist/types/src/util/pingExtension.test.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/dx.config.ts +32 -0
- package/package.json +162 -0
- package/src/CrxPlugin.ts +15 -0
- package/src/capabilities/gen/node.ts +43 -0
- package/src/capabilities/gen/workerd.ts +43 -0
- package/src/capabilities/index.ts +47 -0
- package/src/capabilities/install-page-actions.ts +64 -0
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/page-action-provider.ts +47 -0
- package/src/capabilities/react-surface.ts +26 -0
- package/src/capabilities/settings.ts +36 -0
- package/src/containers/CrxSettings/CrxSettings.stories.tsx +99 -0
- package/src/containers/CrxSettings/CrxSettings.tsx +100 -0
- package/src/containers/CrxSettings/index.ts +5 -0
- package/src/containers/index.ts +5 -0
- package/src/index.ts +7 -0
- package/src/mapping.test.ts +87 -0
- package/src/mapping.ts +101 -0
- package/src/meta.ts +9 -0
- package/src/operations/add-note-from-snapshot.ts +24 -0
- package/src/operations/add-organization-from-snapshot.ts +29 -0
- package/src/operations/add-person-from-snapshot.ts +29 -0
- package/src/operations/index.ts +16 -0
- package/src/page-actions.test.ts +164 -0
- package/src/page-actions.ts +171 -0
- package/src/plugin.tsx +29 -0
- package/src/stories/Testbench.stories.tsx +45 -0
- package/src/translations.ts +31 -0
- package/src/types/CrxCapabilities.ts +26 -0
- package/src/types/CrxEvents.ts +16 -0
- package/src/types/CrxOperation.ts +54 -0
- package/src/types/PageAction.ts +57 -0
- package/src/types/Settings.test.ts +30 -0
- package/src/types/Settings.ts +101 -0
- package/src/types/index.ts +9 -0
- package/src/util/index.ts +5 -0
- package/src/util/pingExtension.test.ts +69 -0
- package/src/util/pingExtension.ts +83 -0
- package/src/vite-env.d.ts +10 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, ALv2 Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-Apache-2.0
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 DXOS
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the Apache License, Version 2.0 that is effective on the second anniversary of
|
|
91
|
+
the date we make the Software available. On or after that date, you may use the
|
|
92
|
+
Software under the Apache License, Version 2.0, in which case the following
|
|
93
|
+
will apply:
|
|
94
|
+
|
|
95
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
96
|
+
this file except in compliance with the License.
|
|
97
|
+
|
|
98
|
+
You may obtain a copy of the License at
|
|
99
|
+
|
|
100
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
101
|
+
|
|
102
|
+
Unless required by applicable law or agreed to in writing, software distributed
|
|
103
|
+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
104
|
+
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
105
|
+
specific language governing permissions and limitations under the License.
|
package/PLUGIN.mdl
ADDED
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: org.dxos.plugin.crx
|
|
3
|
+
name: CrxPlugin
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Coordinates with the `@dxos/composer-crx` browser extension. Owns the Composer
|
|
8
|
+
side of the page-actions bridge: plugins contribute page actions (serializable
|
|
9
|
+
descriptor + target operation) under a plugin-scoped capability; the extension
|
|
10
|
+
caches the descriptors in its registry and invokes them over same-origin
|
|
11
|
+
`window` `CustomEvent`s. This plugin answers registry-list requests, validates
|
|
12
|
+
invoke requests, resolves the active space, executes the target operation, and
|
|
13
|
+
surfaces outcomes as toasts. It also contributes three picker actions
|
|
14
|
+
(Person, Organization, Note) whose operations map a page snapshot to an ECHO
|
|
15
|
+
object, and owns the user-facing settings surface — including the render-proxy
|
|
16
|
+
settings (absorbed from the retired plugin-extension) for the extension's
|
|
17
|
+
page-rendering facility.
|
|
18
|
+
|
|
19
|
+
The extension owns the DOM picker, popup toolbar, extractors, and bridge
|
|
20
|
+
sender; this plugin owns envelope validation, snapshot → ECHO object mapping,
|
|
21
|
+
and user feedback (toast). The extension keeps a hand-validated mirror of the
|
|
22
|
+
wire types at `packages/apps/composer-crx/src/page-actions/types.ts` which
|
|
23
|
+
must be updated in lockstep.
|
|
24
|
+
|
|
25
|
+
## Extensions
|
|
26
|
+
|
|
27
|
+
The following extension dialects are used in this document.
|
|
28
|
+
Each extension is defined in the Appendix or resolved via its URI.
|
|
29
|
+
|
|
30
|
+
| Term | URI |
|
|
31
|
+
|--------|--------------------------------|
|
|
32
|
+
| `type` | `org.dxos.mdl.type@1.0` |
|
|
33
|
+
| `feat` | `org.dxos.mdl.feat@1.0` |
|
|
34
|
+
| `scenario` | `org.dxos.mdl.scenario@1.0` |
|
|
35
|
+
| `test` | `org.dxos.mdl.test@1.0` |
|
|
36
|
+
|
|
37
|
+
## Types
|
|
38
|
+
|
|
39
|
+
```mdl
|
|
40
|
+
type Settings
|
|
41
|
+
desc: |
|
|
42
|
+
User preferences for extension ↔ Composer coordination, persisted via the
|
|
43
|
+
app-framework settings store. Only user-set values are stored; defaults
|
|
44
|
+
are applied by consumers when reading the atom. All fields are optional.
|
|
45
|
+
The fields are a configuration surface; enforcement is owned by whichever
|
|
46
|
+
consumer reads the atom.
|
|
47
|
+
fields:
|
|
48
|
+
enabled?: boolean # default: true. "Accept extension actions" preference.
|
|
49
|
+
autoOpenAfterClip?: boolean # default: false. "Open after action" preference.
|
|
50
|
+
renderProxyEnabled?: boolean # default: true. "Use extension to render pages" preference.
|
|
51
|
+
renderTimeout?: number # default: 20000. Max ms to wait for a render.
|
|
52
|
+
renderActiveTab?: boolean # default: false. Render in a focused tab.
|
|
53
|
+
developerMode?: boolean # default: false. Verbose logging / previews.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```mdl
|
|
57
|
+
type Source
|
|
58
|
+
fields:
|
|
59
|
+
url: string
|
|
60
|
+
title: string
|
|
61
|
+
favicon?: string
|
|
62
|
+
clippedAt: string # ISO timestamp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```mdl
|
|
66
|
+
type Selection
|
|
67
|
+
fields:
|
|
68
|
+
text: string # innerText of the picked subtree
|
|
69
|
+
html?: string # outerHTML, possibly truncated
|
|
70
|
+
htmlTruncated?: boolean
|
|
71
|
+
rect?: { x: number, y: number, width: number, height: number }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```mdl
|
|
75
|
+
type Hints
|
|
76
|
+
desc: |
|
|
77
|
+
Cheap signals harvested from the whole page at capture time. The mappings
|
|
78
|
+
use a subset (h1, og:title, og:description, og:image) as fallbacks; a
|
|
79
|
+
future agent stage may interpret all of them.
|
|
80
|
+
fields:
|
|
81
|
+
ogTitle?: string
|
|
82
|
+
ogDescription?: string
|
|
83
|
+
ogImage?: string
|
|
84
|
+
jsonLd?: unknown[]
|
|
85
|
+
h1?: string
|
|
86
|
+
firstImage?: string
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```mdl
|
|
90
|
+
type Snapshot
|
|
91
|
+
desc: |
|
|
92
|
+
Generic page capture produced by the extension's `snapshot` extractor.
|
|
93
|
+
Travels as the `inputs` of an invoke request.
|
|
94
|
+
fields:
|
|
95
|
+
source: Source
|
|
96
|
+
selection?: Selection
|
|
97
|
+
hints?: Hints
|
|
98
|
+
imageData?: string # downscaled primary-image thumbnail (data URL),
|
|
99
|
+
# captured by the extension's background worker
|
|
100
|
+
html?: string
|
|
101
|
+
htmlTruncated?: boolean
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```mdl
|
|
105
|
+
type PageActionDescriptor
|
|
106
|
+
desc: |
|
|
107
|
+
Serializable descriptor for a plugin-contributed page action, synced to
|
|
108
|
+
the extension's registry cache. Invocation is correlated by `id`.
|
|
109
|
+
fields:
|
|
110
|
+
id: string # stable plugin-scoped action id
|
|
111
|
+
label: string # human-readable action name
|
|
112
|
+
icon: string # phosphor icon identifier
|
|
113
|
+
urlPatterns: string[] # match patterns the action applies to
|
|
114
|
+
predicate?: { exists: string } # lazy DOM condition (CSS selector)
|
|
115
|
+
extractor: { name: string, params?: unknown } # extension-bundled extractor
|
|
116
|
+
contexts: string[] # surfaces: popup | page | selection | link | picker
|
|
117
|
+
operation: string # operation key, display/diagnostics only
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```mdl
|
|
121
|
+
type PageAction
|
|
122
|
+
desc: |
|
|
123
|
+
A live page-action contribution: the descriptor fields plus the actual
|
|
124
|
+
operation definition (serialized to its key string when listed). The
|
|
125
|
+
target operation must accept `{ snapshot: Snapshot, target: Database }`
|
|
126
|
+
and return `{ id: string }`.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```mdl
|
|
130
|
+
type ListRequest
|
|
131
|
+
desc: |
|
|
132
|
+
Sent by the extension as `composer:page-actions:list`; acked as
|
|
133
|
+
`composer:page-actions:list:ack`.
|
|
134
|
+
fields:
|
|
135
|
+
version: number # runtime check: only 1 is supported
|
|
136
|
+
id: string # correlation id, echoed in the ack
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```mdl
|
|
140
|
+
type ListAck
|
|
141
|
+
fields:
|
|
142
|
+
version: number # always 1
|
|
143
|
+
id: string
|
|
144
|
+
ok: boolean
|
|
145
|
+
actions?: PageActionDescriptor[] # when ok = true
|
|
146
|
+
error?: string # stable error code when ok = false
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
```mdl
|
|
150
|
+
type InvokeRequest
|
|
151
|
+
desc: |
|
|
152
|
+
Sent by the extension as `composer:page-action:invoke`; acked as
|
|
153
|
+
`composer:page-action:invoke:ack`. Decoded in two passes: a loose
|
|
154
|
+
envelope ({ version, id? }) first, so newer versions yield
|
|
155
|
+
`unsupportedVersion` rather than the generic `invalidPayload` and
|
|
156
|
+
malformed payloads can still echo the request id.
|
|
157
|
+
fields:
|
|
158
|
+
version: number # runtime check: only 1 is supported
|
|
159
|
+
id: string # correlation id, echoed in the ack
|
|
160
|
+
actionId: string # id of the registered action to invoke
|
|
161
|
+
page: { url: string, title: string, favicon?: string }
|
|
162
|
+
inputs: unknown # extractor output; `snapshot` yields Snapshot
|
|
163
|
+
invokedFrom: popup | contextMenu | picker
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```mdl
|
|
167
|
+
type InvokeAck
|
|
168
|
+
fields:
|
|
169
|
+
version: number # always 1
|
|
170
|
+
id: string
|
|
171
|
+
ok: boolean
|
|
172
|
+
objectId?: string # ID of the created object when ok = true
|
|
173
|
+
error?: string # stable error code when ok = false:
|
|
174
|
+
# invalidPayload | unsupportedVersion |
|
|
175
|
+
# unknownAction | noSpace | operationFailed
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Features
|
|
179
|
+
|
|
180
|
+
```mdl
|
|
181
|
+
feat F-1: Settings Capability
|
|
182
|
+
|
|
183
|
+
req F-1.1:
|
|
184
|
+
when: plugin is activated
|
|
185
|
+
then: |
|
|
186
|
+
a single `Settings` atom is registered under a plugin-scoped capability
|
|
187
|
+
and contributed to `AppCapabilities.Settings` so the standard settings
|
|
188
|
+
UI surfaces it.
|
|
189
|
+
|
|
190
|
+
req F-1.2:
|
|
191
|
+
when: another plugin reads the capability
|
|
192
|
+
then: it receives the current Settings with defaults applied
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```mdl
|
|
196
|
+
feat F-2: Settings Surface
|
|
197
|
+
|
|
198
|
+
req F-2.1:
|
|
199
|
+
when: the app-framework settings panel renders the CRX section
|
|
200
|
+
then: |
|
|
201
|
+
a schema-driven form is shown covering all Settings fields, wired to
|
|
202
|
+
the Settings atom and persisted on change.
|
|
203
|
+
|
|
204
|
+
req F-2.2:
|
|
205
|
+
when: the user presses the Test connection button
|
|
206
|
+
then: |
|
|
207
|
+
a ping round-trips over the page → content-script → background path and
|
|
208
|
+
reports the extension's name and version on success, or the failure
|
|
209
|
+
reason otherwise.
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```mdl
|
|
213
|
+
feat F-2c: Settings Enforcement
|
|
214
|
+
|
|
215
|
+
req F-2c.1:
|
|
216
|
+
when: `enabled` is false and a page-action invoke arrives
|
|
217
|
+
then: |
|
|
218
|
+
no object is created; the request is still acked (so the extension does
|
|
219
|
+
not time out) with the stable error code `disabled`
|
|
220
|
+
|
|
221
|
+
req F-2c.2:
|
|
222
|
+
when: `autoOpenAfterClip` is true and a page action succeeds
|
|
223
|
+
then: the created object is opened via LayoutOperation.Open
|
|
224
|
+
|
|
225
|
+
req F-2c.3:
|
|
226
|
+
when: `autoOpenAfterClip` is false (default)
|
|
227
|
+
then: focus is not moved; only the success toast is shown
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```mdl
|
|
231
|
+
feat F-3: Page Action Contributions
|
|
232
|
+
desc: |
|
|
233
|
+
Plugins contribute `PageAction[]` under the plugin-scoped
|
|
234
|
+
`CrxCapabilities.PageAction` capability. Other plugins contribute their
|
|
235
|
+
own actions the same way (e.g. plugin-bookmarks contributes
|
|
236
|
+
"Add bookmark").
|
|
237
|
+
|
|
238
|
+
req F-3.1:
|
|
239
|
+
when: plugin is activated
|
|
240
|
+
then: |
|
|
241
|
+
three picker-only actions are contributed — Person, Organization, and
|
|
242
|
+
Note — each matching `http(s)://*/*`, using the extension-bundled
|
|
243
|
+
`snapshot` extractor, and targeting the corresponding
|
|
244
|
+
AddPersonFromSnapshot / AddOrganizationFromSnapshot /
|
|
245
|
+
AddNoteFromSnapshot operation.
|
|
246
|
+
|
|
247
|
+
req F-3.2:
|
|
248
|
+
when: the extension lists actions
|
|
249
|
+
then: |
|
|
250
|
+
contributions are resolved per-request, so actions from
|
|
251
|
+
late-activating plugins are included.
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
```mdl
|
|
255
|
+
feat F-4: Registry Listing
|
|
256
|
+
|
|
257
|
+
req F-4.1:
|
|
258
|
+
when: plugin is activated
|
|
259
|
+
then: |
|
|
260
|
+
window event listeners for `composer:page-actions:list` and
|
|
261
|
+
`composer:page-action:invoke` are installed, and a
|
|
262
|
+
`composer:page-actions:ready` event is dispatched so the extension
|
|
263
|
+
relay can refresh its registry even when Composer boots after the
|
|
264
|
+
content script has already installed.
|
|
265
|
+
|
|
266
|
+
req F-4.2:
|
|
267
|
+
when: a valid list request arrives
|
|
268
|
+
then: |
|
|
269
|
+
the ack carries the currently contributed actions as serializable
|
|
270
|
+
descriptors, with `operation` serialized to the operation's key string.
|
|
271
|
+
|
|
272
|
+
req F-4.3:
|
|
273
|
+
when: a list request fails schema decoding
|
|
274
|
+
then: |
|
|
275
|
+
the ack is { ok: false, error: 'invalidPayload' } with a best-effort
|
|
276
|
+
echo of the request id so the extension can correlate the failure.
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
```mdl
|
|
280
|
+
feat F-5: Invoke Validation & Dispatch
|
|
281
|
+
|
|
282
|
+
req F-5.1:
|
|
283
|
+
when: an invoke request's envelope fails decoding
|
|
284
|
+
then: ack is { ok: false, error: 'invalidPayload' } and nothing is invoked
|
|
285
|
+
|
|
286
|
+
req F-5.2:
|
|
287
|
+
when: the envelope has a version other than 1
|
|
288
|
+
then: |
|
|
289
|
+
ack is { ok: false, error: 'unsupportedVersion' } with the request id
|
|
290
|
+
echoed when present.
|
|
291
|
+
|
|
292
|
+
req F-5.3:
|
|
293
|
+
when: the strict payload fails decoding
|
|
294
|
+
then: ack is { ok: false, error: 'invalidPayload' }
|
|
295
|
+
|
|
296
|
+
req F-5.4:
|
|
297
|
+
when: `actionId` matches no registered action
|
|
298
|
+
then: ack is { ok: false, error: 'unknownAction' }
|
|
299
|
+
|
|
300
|
+
req F-5.5:
|
|
301
|
+
when: the active space cannot be determined
|
|
302
|
+
then: nothing is invoked; ack is { ok: false, error: 'noSpace' }
|
|
303
|
+
|
|
304
|
+
req F-5.6:
|
|
305
|
+
when: the request resolves to an action and an active space
|
|
306
|
+
then: |
|
|
307
|
+
the action's operation is invoked with
|
|
308
|
+
{ snapshot: inputs, target: activeSpace.db }; on success the ack is
|
|
309
|
+
{ ok: true, objectId } carrying the created object's id.
|
|
310
|
+
|
|
311
|
+
req F-5.7:
|
|
312
|
+
when: the operation returns an error or throws
|
|
313
|
+
then: ack is { ok: false, error: 'operationFailed' }
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
```mdl
|
|
317
|
+
feat F-6: Snapshot → Object Mapping
|
|
318
|
+
desc: |
|
|
319
|
+
Each contributed operation maps a Snapshot to an ECHO object and adds it
|
|
320
|
+
to the target database via SpaceOperation.AddObject.
|
|
321
|
+
|
|
322
|
+
req F-6.1:
|
|
323
|
+
when: AddPersonFromSnapshot is invoked
|
|
324
|
+
then: |
|
|
325
|
+
a Person is created with best-effort fields:
|
|
326
|
+
fullName ← hints.h1 ?? hints.ogTitle ?? firstLine(selection.text)
|
|
327
|
+
image ← hints.ogImage
|
|
328
|
+
notes ← selection.text (truncated to 4000 chars)
|
|
329
|
+
urls ← [{ label: source.title || source.url, value: source.url }]
|
|
330
|
+
|
|
331
|
+
req F-6.2:
|
|
332
|
+
when: AddOrganizationFromSnapshot is invoked
|
|
333
|
+
then: |
|
|
334
|
+
an Organization is created with best-effort fields:
|
|
335
|
+
name ← hints.ogTitle ?? hints.h1 ?? firstLine(selection.text)
|
|
336
|
+
description ← hints.ogDescription ?? selection.text (truncated)
|
|
337
|
+
image ← hints.ogImage
|
|
338
|
+
website ← source.url
|
|
339
|
+
|
|
340
|
+
req F-6.3:
|
|
341
|
+
when: AddNoteFromSnapshot is invoked
|
|
342
|
+
then: |
|
|
343
|
+
a Markdown document is created with a small prelude (title, source
|
|
344
|
+
link, timestamp) followed by the selection's rendered text (truncated
|
|
345
|
+
to 50000 chars). The title falls back through
|
|
346
|
+
hints.h1 ?? hints.ogTitle ?? firstLine(selection.text) ?? source.title.
|
|
347
|
+
|
|
348
|
+
req F-6.4:
|
|
349
|
+
when: there is no name/title candidate
|
|
350
|
+
then: the object is still created; empty name is acceptable
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
```mdl
|
|
354
|
+
feat F-7: User Feedback
|
|
355
|
+
|
|
356
|
+
req F-7.1:
|
|
357
|
+
when: an invoke ack is produced with ok = true
|
|
358
|
+
then: a success toast is shown
|
|
359
|
+
|
|
360
|
+
req F-7.2:
|
|
361
|
+
when: an invoke ack is produced with ok = false
|
|
362
|
+
then: an error toast is shown naming the stable error code
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```mdl
|
|
366
|
+
feat F-8: Page Actions Registry & Invocation
|
|
367
|
+
desc: |
|
|
368
|
+
Plugins contribute page actions (serializable descriptor + target
|
|
369
|
+
operation) under a plugin-scoped capability. This plugin answers the
|
|
370
|
+
extension's registry-list requests (`composer:page-actions:list`) with
|
|
371
|
+
descriptors and executes invoke requests (`composer:page-action:invoke`)
|
|
372
|
+
by resolving the active space and invoking the target operation with
|
|
373
|
+
`{ snapshot, target }`. Every request is acked with stable error codes
|
|
374
|
+
(invalidPayload, unsupportedVersion, disabled, unknownAction, noSpace,
|
|
375
|
+
operationFailed); outcomes surface as toasts.
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Acceptance
|
|
379
|
+
|
|
380
|
+
```mdl
|
|
381
|
+
scenario T-1: Settings atom registered
|
|
382
|
+
given: plugin activated
|
|
383
|
+
then:
|
|
384
|
+
- the Settings capability is available via Capability.get
|
|
385
|
+
- the default atom value is { enabled: true, autoOpenAfterClip: false,
|
|
386
|
+
renderProxyEnabled: true, renderTimeout: 20000, renderActiveTab: false,
|
|
387
|
+
developerMode: false }
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
```mdl
|
|
391
|
+
scenario T-2: Toggling enabled persists
|
|
392
|
+
given: settings surface rendered
|
|
393
|
+
when: user toggles the `enabled` switch off
|
|
394
|
+
then: subsequent reads of the atom observe enabled === false
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
```mdl
|
|
398
|
+
scenario T-3: List request returns descriptors
|
|
399
|
+
given: contributed picker actions (Person, Organization, Note)
|
|
400
|
+
when: handleListEvent receives { version: 1, id: 'r1' }
|
|
401
|
+
then:
|
|
402
|
+
- ack is { ok: true, id: 'r1' }
|
|
403
|
+
- ack.actions contains one descriptor per action with `operation`
|
|
404
|
+
serialized to the operation key string
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
```mdl
|
|
408
|
+
scenario T-4: Invalid list request is rejected
|
|
409
|
+
given: page-actions listeners installed
|
|
410
|
+
when: handleListEvent receives a malformed detail (missing id)
|
|
411
|
+
then: ack is { ok: false, error: 'invalidPayload' }
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
```mdl
|
|
415
|
+
scenario T-5: Valid person invoke creates Person
|
|
416
|
+
given: a registered add-person action and an active space
|
|
417
|
+
when: |
|
|
418
|
+
handleInvokeEvent receives { version: 1, actionId: <add-person>,
|
|
419
|
+
inputs: snapshot } with snapshot.hints.h1 = 'Jane Doe'
|
|
420
|
+
then:
|
|
421
|
+
- a Person is created with fullName = 'Jane Doe'
|
|
422
|
+
- ack is { ok: true, objectId: <non-empty> }
|
|
423
|
+
- a success toast is shown
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
```mdl
|
|
427
|
+
scenario T-6: Organization mapping falls back to og:title
|
|
428
|
+
given: a snapshot with hints.ogTitle = 'DXOS' and hints.h1 undefined
|
|
429
|
+
when: AddOrganizationFromSnapshot is invoked
|
|
430
|
+
then:
|
|
431
|
+
- Organization.name === 'DXOS'
|
|
432
|
+
- Organization.website === source.url
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
```mdl
|
|
436
|
+
scenario T-7: Missing name is allowed
|
|
437
|
+
given: a snapshot with no hints and empty selection text
|
|
438
|
+
when: the add-person action is invoked
|
|
439
|
+
then:
|
|
440
|
+
- a Person is created (empty fullName)
|
|
441
|
+
- ack is { ok: true, objectId: <non-empty> }
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
```mdl
|
|
445
|
+
scenario T-8: Unsupported envelope version
|
|
446
|
+
given: page-actions listeners installed
|
|
447
|
+
when: handleInvokeEvent receives { version: 2, id: 'r2', ... }
|
|
448
|
+
then:
|
|
449
|
+
- nothing is invoked
|
|
450
|
+
- ack is { ok: false, id: 'r2', error: 'unsupportedVersion' }
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
```mdl
|
|
454
|
+
scenario T-9: Invalid invoke payload is rejected
|
|
455
|
+
given: page-actions listeners installed
|
|
456
|
+
when: handleInvokeEvent receives a version-1 detail missing `actionId`
|
|
457
|
+
then:
|
|
458
|
+
- nothing is invoked
|
|
459
|
+
- ack is { ok: false, error: 'invalidPayload' } with the envelope id echoed
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
```mdl
|
|
463
|
+
scenario T-10: Unknown action
|
|
464
|
+
given: no action registered with id 'nope'
|
|
465
|
+
when: handleInvokeEvent receives a valid request with actionId = 'nope'
|
|
466
|
+
then: ack is { ok: false, error: 'unknownAction' }
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
```mdl
|
|
470
|
+
scenario T-11: No active space
|
|
471
|
+
given: the active space cannot be resolved
|
|
472
|
+
when: a valid invoke request arrives
|
|
473
|
+
then:
|
|
474
|
+
- nothing is invoked
|
|
475
|
+
- ack is { ok: false, error: 'noSpace' }
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
```mdl
|
|
479
|
+
scenario T-12: Operation failure
|
|
480
|
+
given: the target operation returns an error (or throws)
|
|
481
|
+
when: a valid invoke request arrives
|
|
482
|
+
then:
|
|
483
|
+
- ack is { ok: false, error: 'operationFailed' }
|
|
484
|
+
- an error toast is shown
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
## QA
|
|
490
|
+
|
|
491
|
+
Executable tests. Each step carries a human instruction (`do`) and, where the step maps to an
|
|
492
|
+
invocable operation, an agent binding (`invoke`). Report failures by test and step number.
|
|
493
|
+
|
|
494
|
+
Note: this document declares no `op` blocks, so the operations below are referenced by their full
|
|
495
|
+
runtime key rather than `[op:...]` — see `src/types/CrxOperation.ts`.
|
|
496
|
+
|
|
497
|
+
```mdl
|
|
498
|
+
test QA-1: Add a Person from a page snapshot
|
|
499
|
+
desc: |
|
|
500
|
+
The core snapshot → object mapping (F-6.1), standing in for the browser extension's picker,
|
|
501
|
+
which an agent cannot drive — the extension is a separate, unautomatable browser process. This
|
|
502
|
+
test invokes the target operation directly with a synthetic snapshot, exactly as the plugin's
|
|
503
|
+
own invoke handler does after resolving `{ snapshot, target }` (F-5.6).
|
|
504
|
+
actors: agent
|
|
505
|
+
covers: [F-3.1, F-5.6, F-6.1, T-5]
|
|
506
|
+
status: unverified
|
|
507
|
+
given:
|
|
508
|
+
- an identity exists
|
|
509
|
+
- space: a space is open
|
|
510
|
+
- plugin-crx is enabled — it is OFF by default (not in `getDefaults` and not in the dev-only
|
|
511
|
+
default set; see `packages/apps/composer-app/src/plugin-defs.tsx`)
|
|
512
|
+
- no Person named "QA Snapshot Person" exists — ABORT rather than reusing one this test did
|
|
513
|
+
not create
|
|
514
|
+
steps:
|
|
515
|
+
- name: Add the person from a snapshot
|
|
516
|
+
do: |
|
|
517
|
+
(Real path, not automatable here: with the extension installed, open the popup on a page
|
|
518
|
+
and choose the "Person" picker action, then select the page region to capture.)
|
|
519
|
+
invoke: |
|
|
520
|
+
org.dxos.operation.crx.addPersonFromSnapshot {
|
|
521
|
+
snapshot: { source: { url: "https://example.com/qa", title: "QA Page", clippedAt: "2026-08-27T00:00:00.000Z" },
|
|
522
|
+
hints: { h1: "QA Snapshot Person" } },
|
|
523
|
+
target: $given.space.db
|
|
524
|
+
}
|
|
525
|
+
capture: created
|
|
526
|
+
expect: a Person named "QA Snapshot Person" now exists in the space
|
|
527
|
+
assert: return !!$created.id
|
|
528
|
+
after:
|
|
529
|
+
- name: Delete the Person
|
|
530
|
+
do: Right-click "QA Snapshot Person" in the navtree and choose Delete.
|
|
531
|
+
invoke: org.dxos.operation.space.removeObjects { objects: [{ "/": "echo://$given.space.id/" + $created.id }], target: $given.space }
|
|
532
|
+
expect: the row leaves the navtree
|
|
533
|
+
note: |
|
|
534
|
+
`addPersonFromSnapshot` returns a bare `id`, not a live ref, so the DXN envelope is built
|
|
535
|
+
by hand for the removal call — see Rule 4 in `op@1.1`'s parent doc about resolving by the
|
|
536
|
+
invoker path rather than guessing a shortcut.
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
```mdl
|
|
540
|
+
test QA-2: Accept-extension-actions toggle gates invocation
|
|
541
|
+
desc: |
|
|
542
|
+
The settings enforcement path (F-2c.1) — no operation exists for the settings atom itself
|
|
543
|
+
(it is a plain app-framework settings store), so this step is human-only.
|
|
544
|
+
actors: human
|
|
545
|
+
covers: [F-1.1, F-2.1, F-2c.1]
|
|
546
|
+
status: unverified
|
|
547
|
+
given:
|
|
548
|
+
- an identity exists
|
|
549
|
+
- plugin-crx is enabled — it is OFF by default (see QA-1)
|
|
550
|
+
- the Settings panel's CRX section is reachable
|
|
551
|
+
steps:
|
|
552
|
+
- name: Disable "Accept extension actions"
|
|
553
|
+
do: Open Settings, find the CRX section, and toggle "Accept extension actions" off.
|
|
554
|
+
expect: the toggle shows off and the preference persists across a reload
|
|
555
|
+
note: |
|
|
556
|
+
NO `invoke` — the Settings atom has no operation binding; per F-2c.1, an invoke request
|
|
557
|
+
that arrives while this is off is still acked (error `disabled`), which requires the
|
|
558
|
+
extension itself to exercise and cannot be driven from the debug port alone.
|
|
559
|
+
after:
|
|
560
|
+
- name: Re-enable "Accept extension actions"
|
|
561
|
+
do: Toggle "Accept extension actions" back on.
|
|
562
|
+
expect: the toggle shows on again
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## Appendix: Extension Definitions
|
|
568
|
+
|
|
569
|
+
```mdl
|
|
570
|
+
ext type
|
|
571
|
+
uri: org.dxos.mdl.type@1.0
|
|
572
|
+
desc: A named data structure with typed fields and optional literals.
|
|
573
|
+
fields:
|
|
574
|
+
desc?: Prose
|
|
575
|
+
fields?: FieldMap
|
|
576
|
+
literals?: UnionList
|
|
577
|
+
extends?: TypeRef[]
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
```mdl
|
|
581
|
+
ext feat
|
|
582
|
+
uri: org.dxos.mdl.feat@1.0
|
|
583
|
+
desc: A named feature grouping one or more requirements.
|
|
584
|
+
fields:
|
|
585
|
+
desc?: Prose
|
|
586
|
+
req: RequirementList
|
|
587
|
+
nesting: self
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
```mdl
|
|
591
|
+
ext scenario
|
|
592
|
+
uri: org.dxos.mdl.scenario@1.0
|
|
593
|
+
desc: An acceptance scenario expressed as given / when / then steps.
|
|
594
|
+
fields:
|
|
595
|
+
given?: Step | Step[]
|
|
596
|
+
when?: Step | Step[]
|
|
597
|
+
then: Step | Step[]
|
|
598
|
+
tags?: TagList
|
|
599
|
+
```
|