@medeo-plugin/sdk 0.0.0-stage → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -2
- package/dist/generated.d.ts +497 -0
- package/dist/generated.js +1 -0
- package/dist/index.d.ts +891 -0
- package/dist/index.js +424 -0
- package/dist/react.d.ts +46 -0
- package/dist/react.js +44 -0
- package/dist/registry.d.ts +1132 -0
- package/dist/registry.js +172 -0
- package/dist/rpc.d.ts +28 -0
- package/dist/rpc.js +172 -0
- package/dist/testing.d.ts +28 -0
- package/dist/testing.js +161 -0
- package/dist/ui/styles.d.ts +4 -0
- package/dist/ui/styles.js +31 -0
- package/dist/ui.d.ts +50 -0
- package/dist/ui.js +115 -0
- package/package.json +66 -4
- package/src/generated.ts +158 -0
- package/src/index.ts +468 -0
- package/src/react.ts +50 -0
- package/src/registry.ts +173 -0
- package/src/rpc.ts +136 -0
- package/src/testing.ts +144 -0
- package/src/ui/styles.ts +31 -0
- package/src/ui.ts +102 -0
package/dist/registry.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Generated by platform-core from the capability registry; do not edit.
|
|
2
|
+
export const METHODS = {
|
|
3
|
+
"capabilities.list": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
4
|
+
"capabilities.labels": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
5
|
+
"providers.list": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
6
|
+
"permissions.request": { permission: null, provider: null, ui: true, confirm: true, adapter: "host" },
|
|
7
|
+
"i18n.state": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
8
|
+
"i18n.setLocale": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
9
|
+
"i18n.t": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
10
|
+
"i18n.catalog": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
11
|
+
"operations.state": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
12
|
+
"operations.list": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
13
|
+
"operations.cancel": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
14
|
+
"operations.release": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
15
|
+
"config.get": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
16
|
+
"config.set": { permission: null, provider: null, ui: false, confirm: false, adapter: "host" },
|
|
17
|
+
"storage.get": { permission: "storage", provider: "medeo.storage", ui: false, confirm: false, adapter: "host" },
|
|
18
|
+
"storage.set": { permission: "storage", provider: "medeo.storage", ui: false, confirm: false, adapter: "host" },
|
|
19
|
+
"storage.remove": { permission: "storage", provider: "medeo.storage", ui: false, confirm: false, adapter: "host" },
|
|
20
|
+
"creation.open": { permission: "creation_open", provider: "medeo.creation", ui: false, confirm: false, adapter: "host" },
|
|
21
|
+
"resources.choose_directories": { permission: "file_access", provider: "medeo.workspace", ui: true, confirm: false, adapter: "host" },
|
|
22
|
+
"resources.request_directory": { permission: "file_access", provider: "medeo.workspace", ui: true, confirm: true, adapter: "host" },
|
|
23
|
+
"resources.list": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
24
|
+
"resources.revoke": { permission: "file_access", provider: "medeo.workspace", ui: true, confirm: false, adapter: "host" },
|
|
25
|
+
"files.roots": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
26
|
+
"files.mkdir": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
27
|
+
"files.history": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
28
|
+
"files.history_page": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
29
|
+
"files.backup_preview": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
30
|
+
"files.archive_backup": { permission: "file_access", provider: "medeo.workspace", ui: true, confirm: true, adapter: "host" },
|
|
31
|
+
"files.forget_preview": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
32
|
+
"files.forget_record": { permission: "file_access", provider: "medeo.workspace", ui: true, confirm: true, adapter: "host" },
|
|
33
|
+
"files.restore": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
34
|
+
"files.list": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
35
|
+
"files.stat": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
36
|
+
"files.read_text": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
37
|
+
"files.write_text": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
38
|
+
"files.move": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
39
|
+
"files.trash": { permission: "file_access", provider: "medeo.workspace", ui: true, confirm: true, adapter: "host" },
|
|
40
|
+
"files.trash_preview": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
41
|
+
"files.discard_preview": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
42
|
+
"files.watch": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
43
|
+
"files.events": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
44
|
+
"files.watches": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
45
|
+
"files.unwatch": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
46
|
+
"files.read_link": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
47
|
+
"files.measure": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
48
|
+
"files.list_tree": { permission: "file_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
49
|
+
"files.trash_direct": { permission: "file_full_access", provider: "medeo.workspace", ui: false, confirm: false, adapter: "host" },
|
|
50
|
+
"network.request": { permission: "network", provider: "medeo.network", ui: false, confirm: false, adapter: "host" },
|
|
51
|
+
"network.read": { permission: "network", provider: "medeo.network", ui: false, confirm: false, adapter: "host" },
|
|
52
|
+
"network.origins": { permission: "network", provider: "medeo.network", ui: false, confirm: false, adapter: "host" },
|
|
53
|
+
"network.request_origin": { permission: "network", provider: "medeo.network", ui: true, confirm: true, adapter: "host" },
|
|
54
|
+
"network.revoke_origin": { permission: "network", provider: "medeo.network", ui: true, confirm: false, adapter: "host" },
|
|
55
|
+
"agent.models": { permission: "agent_api", provider: "medeo.agent", ui: false, confirm: false, adapter: "host" },
|
|
56
|
+
"agent.start": { permission: "agent_api", provider: "medeo.agent", ui: false, confirm: false, adapter: "host" },
|
|
57
|
+
"clipboard.read": { permission: "clipboard_read", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
58
|
+
"clipboard.write": { permission: "clipboard_write", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
59
|
+
"capture.status": { permission: "screen_capture", provider: "medeo.capture", ui: false, confirm: false, adapter: "host" },
|
|
60
|
+
"capture.sources": { permission: "screen_capture", provider: "medeo.capture", ui: true, confirm: false, adapter: "host" },
|
|
61
|
+
"capture.start": { permission: "screen_capture", provider: "medeo.capture", ui: true, confirm: true, adapter: "host" },
|
|
62
|
+
"capture.state": { permission: "screen_capture", provider: "medeo.capture", ui: false, confirm: false, adapter: "host" },
|
|
63
|
+
"capture.stop": { permission: "screen_capture", provider: "medeo.capture", ui: false, confirm: false, adapter: "host" },
|
|
64
|
+
"capture.cancel": { permission: "screen_capture", provider: "medeo.capture", ui: false, confirm: false, adapter: "host" },
|
|
65
|
+
"capture.open": { permission: "screen_capture", provider: "medeo.capture", ui: true, confirm: false, adapter: "electron" },
|
|
66
|
+
"capture.recover": { permission: "screen_capture", provider: "medeo.capture", ui: true, confirm: false, adapter: "host" },
|
|
67
|
+
"camera.status": { permission: "camera", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
68
|
+
"camera.start": { permission: "camera", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
69
|
+
"camera.stop": { permission: "camera", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
70
|
+
"camera.state": { permission: "camera", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
71
|
+
"camera.read": { permission: "camera", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
72
|
+
"microphone.status": { permission: "microphone", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
73
|
+
"microphone.start": { permission: "microphone", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
74
|
+
"microphone.stop": { permission: "microphone", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
75
|
+
"microphone.state": { permission: "microphone", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
76
|
+
"microphone.read": { permission: "microphone", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
77
|
+
"system_audio.status": { permission: "system_audio", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
78
|
+
"system_audio.start": { permission: "system_audio", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
79
|
+
"system_audio.stop": { permission: "system_audio", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
80
|
+
"system_audio.state": { permission: "system_audio", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
81
|
+
"system_audio.read": { permission: "system_audio", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
82
|
+
"applications.list": { permission: "applications", provider: "medeo.applications", ui: false, confirm: false, adapter: "host" },
|
|
83
|
+
"applications.launch": { permission: "applications", provider: "medeo.applications", ui: false, confirm: false, adapter: "host" },
|
|
84
|
+
"applications.state": { permission: "applications", provider: "medeo.applications", ui: false, confirm: false, adapter: "host" },
|
|
85
|
+
"shortcuts.register": { permission: "global_shortcuts", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
86
|
+
"shortcuts.unregister": { permission: "global_shortcuts", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
87
|
+
"shortcuts.events": { permission: "global_shortcuts", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
88
|
+
"shortcuts.list": { permission: "global_shortcuts", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
89
|
+
"notifications.show": { permission: "notifications", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
90
|
+
"notifications.close": { permission: "notifications", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
91
|
+
"tray.set": { permission: "tray", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
92
|
+
"tray.remove": { permission: "tray", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
93
|
+
"tray.events": { permission: "tray", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
94
|
+
"windows.open": { permission: "app_windows", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
95
|
+
"windows.close": { permission: "app_windows", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
96
|
+
"windows.focus": { permission: "app_windows", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
97
|
+
"windows.hide": { permission: "app_windows", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
98
|
+
"windows.list": { permission: "app_windows", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
99
|
+
"accessibility.status": { permission: "accessibility", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
100
|
+
"accessibility.request": { permission: "accessibility", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
101
|
+
"accessibility.windows": { permission: "accessibility", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
102
|
+
"accessibility.focus": { permission: "accessibility", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
103
|
+
"accessibility.selected_text": { permission: "accessibility", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
104
|
+
"accessibility.insert_text": { permission: "accessibility", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
105
|
+
"input_method.status": { permission: "input_method", provider: "medeo.input-method", ui: false, confirm: false, adapter: "host" },
|
|
106
|
+
"input_method.configure": { permission: "input_method", provider: "medeo.input-method", ui: false, confirm: false, adapter: "host" },
|
|
107
|
+
"input_method.enable": { permission: "input_method", provider: "medeo.input-method", ui: true, confirm: false, adapter: "host" },
|
|
108
|
+
"input_method.disable": { permission: "input_method", provider: "medeo.input-method", ui: false, confirm: false, adapter: "host" },
|
|
109
|
+
"input_method.events": { permission: "input_method", provider: "medeo.input-method", ui: false, confirm: false, adapter: "host" },
|
|
110
|
+
"input_method.respond": { permission: "input_method", provider: "medeo.input-method", ui: false, confirm: false, adapter: "host" },
|
|
111
|
+
"input_method.system": { permission: "input_method", provider: "medeo.input-method", ui: false, confirm: false, adapter: "host" },
|
|
112
|
+
"input_method.setup": { permission: "input_method", provider: "medeo.input-method", ui: true, confirm: true, adapter: "host" },
|
|
113
|
+
"input_method.remove": { permission: "input_method", provider: "medeo.input-method", ui: true, confirm: true, adapter: "host" },
|
|
114
|
+
"input_method.settings": { permission: "input_method", provider: "medeo.input-method", ui: true, confirm: false, adapter: "host" },
|
|
115
|
+
"external.open": { permission: "external_open", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
116
|
+
"browser.open": { permission: "browser_view", provider: "medeo.desktop", ui: true, confirm: false, adapter: "electron" },
|
|
117
|
+
"browser.close": { permission: "browser_view", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
118
|
+
"browser.state": { permission: "browser_view", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
119
|
+
"system.info": { permission: "system_info", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
120
|
+
"system.running": { permission: "system_info", provider: "medeo.desktop", ui: false, confirm: false, adapter: "host" },
|
|
121
|
+
"secrets.get": { permission: "secrets", provider: "medeo.secrets", ui: false, confirm: false, adapter: "host" },
|
|
122
|
+
"secrets.set": { permission: "secrets", provider: "medeo.secrets", ui: false, confirm: false, adapter: "host" },
|
|
123
|
+
"secrets.remove": { permission: "secrets", provider: "medeo.secrets", ui: false, confirm: false, adapter: "host" },
|
|
124
|
+
"secrets.result": { permission: "secrets", provider: "medeo.secrets", ui: false, confirm: false, adapter: "host" },
|
|
125
|
+
"background.schedule": { permission: "background_tasks", provider: "medeo.background", ui: false, confirm: false, adapter: "host" },
|
|
126
|
+
"background.list": { permission: "background_tasks", provider: "medeo.background", ui: false, confirm: false, adapter: "host" },
|
|
127
|
+
"background.cancel": { permission: "background_tasks", provider: "medeo.background", ui: false, confirm: false, adapter: "host" },
|
|
128
|
+
"background.events": { permission: "background_tasks", provider: "medeo.background", ui: false, confirm: false, adapter: "host" },
|
|
129
|
+
"power.start": { permission: "power", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
130
|
+
"power.stop": { permission: "power", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
131
|
+
"power.state": { permission: "power", provider: "medeo.desktop", ui: false, confirm: false, adapter: "electron" },
|
|
132
|
+
"process.start": { permission: "process_spawn", provider: "medeo.process", ui: false, confirm: false, adapter: "host" },
|
|
133
|
+
"process.state": { permission: "process_spawn", provider: "medeo.process", ui: false, confirm: false, adapter: "host" },
|
|
134
|
+
"process.write": { permission: "process_spawn", provider: "medeo.process", ui: false, confirm: false, adapter: "host" },
|
|
135
|
+
"process.stop": { permission: "process_spawn", provider: "medeo.process", ui: false, confirm: false, adapter: "host" },
|
|
136
|
+
"system.trash": { permission: "node", provider: "medeo.node", ui: false, confirm: false, adapter: "electron" },
|
|
137
|
+
"approvals.request": { permission: "approvals", provider: "medeo.approvals", ui: true, confirm: true, adapter: "host" },
|
|
138
|
+
"approvals.rules": { permission: "approvals", provider: "medeo.approvals", ui: false, confirm: false, adapter: "host" },
|
|
139
|
+
"approvals.forget": { permission: "approvals", provider: "medeo.approvals", ui: false, confirm: false, adapter: "host" },
|
|
140
|
+
"server.start": { permission: "local_server", provider: "medeo.local-server", ui: false, confirm: false, adapter: "host" },
|
|
141
|
+
"server.state": { permission: "local_server", provider: "medeo.local-server", ui: false, confirm: false, adapter: "host" },
|
|
142
|
+
"server.stop": { permission: "local_server", provider: "medeo.local-server", ui: false, confirm: false, adapter: "host" },
|
|
143
|
+
"server.events": { permission: "local_server", provider: "medeo.local-server", ui: false, confirm: false, adapter: "host" },
|
|
144
|
+
"server.respond": { permission: "local_server", provider: "medeo.local-server", ui: false, confirm: false, adapter: "host" },
|
|
145
|
+
"printing.print": { permission: "printing", provider: "medeo.desktop", ui: true, confirm: false, adapter: "electron" },
|
|
146
|
+
"contacts.status": { permission: "contacts", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
147
|
+
"contacts.request": { permission: "contacts", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
148
|
+
"contacts.list": { permission: "contacts", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
149
|
+
"contacts.save": { permission: "contacts", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
150
|
+
"calendar.status": { permission: "calendar", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
151
|
+
"calendar.request": { permission: "calendar", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
152
|
+
"calendar.list": { permission: "calendar", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
153
|
+
"calendar.save": { permission: "calendar", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
154
|
+
"location.status": { permission: "location", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
155
|
+
"location.request": { permission: "location", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
156
|
+
"location.current": { permission: "location", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
157
|
+
"devices.choose": { permission: "device_access", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
158
|
+
"devices.open": { permission: "device_access", provider: "medeo.system-services", ui: true, confirm: false, adapter: "host" },
|
|
159
|
+
"devices.read": { permission: "device_access", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
160
|
+
"devices.write": { permission: "device_access", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
161
|
+
"devices.close": { permission: "device_access", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
162
|
+
"devices.list": { permission: "device_access", provider: "medeo.system-services", ui: false, confirm: false, adapter: "host" },
|
|
163
|
+
};
|
|
164
|
+
export const LIMITS = {
|
|
165
|
+
notifications: 16,
|
|
166
|
+
power: 8,
|
|
167
|
+
shortcuts: 16,
|
|
168
|
+
windows: 8,
|
|
169
|
+
browser_windows: 4,
|
|
170
|
+
media_streams: 4,
|
|
171
|
+
queued_events: 64,
|
|
172
|
+
};
|
package/dist/rpc.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Disposable } from './index.ts';
|
|
2
|
+
/** A transport is injectable for offline tests; the default is this plugin's own channel. */
|
|
3
|
+
export interface RpcTransport {
|
|
4
|
+
post(message: unknown): Promise<void>;
|
|
5
|
+
onMessage(listener: (message: unknown) => void): () => void;
|
|
6
|
+
}
|
|
7
|
+
export interface RpcOptions {
|
|
8
|
+
transport?: RpcTransport;
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
type Handler = (...args: any[]) => unknown;
|
|
13
|
+
export type WorkerApiClient<T> = {
|
|
14
|
+
[K in keyof T]: T[K] extends (...args: infer A) => infer R ? (...args: A) => Promise<Awaited<R>> : never;
|
|
15
|
+
} & {
|
|
16
|
+
$dispose(): void;
|
|
17
|
+
};
|
|
18
|
+
/** Register in activate, then push the returned disposable into context.subscriptions.
|
|
19
|
+
* Requests only reach this plugin's worker; this is not a cross-plugin service API.
|
|
20
|
+
*/
|
|
21
|
+
export declare function defineWorkerApi<T extends {
|
|
22
|
+
[K in keyof T]: Handler;
|
|
23
|
+
}>(handlers: T, options?: Pick<RpcOptions, 'transport'>): Disposable;
|
|
24
|
+
/** Create once per window, dispose on unmount. Timeout/disposal cancels waiting,
|
|
25
|
+
* not a worker side effect that already started. All arguments/results are JSON.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useWorkerApi<T>(options?: RpcOptions): WorkerApiClient<T>;
|
|
28
|
+
export {};
|
package/dist/rpc.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { channel } from "./index.js";
|
|
2
|
+
const PROTOCOL = 'medeo.rpc.v1';
|
|
3
|
+
const MAX_PENDING = 64;
|
|
4
|
+
const MAX_BYTES = 60 * 1024;
|
|
5
|
+
function packet(value) {
|
|
6
|
+
if (!value || typeof value !== 'object')
|
|
7
|
+
return false;
|
|
8
|
+
const p = value;
|
|
9
|
+
return p.protocol === PROTOCOL && ['request', 'result', 'error'].includes(p.kind) && typeof p.client === 'string' && p.client.length <= 80 && typeof p.id === 'string' && p.id.length <= 80;
|
|
10
|
+
}
|
|
11
|
+
/** Reject values JSON would silently drop or change, rather than corrupting an RPC argument. */
|
|
12
|
+
function checked(value) {
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
function visit(v) {
|
|
15
|
+
if (v === null || typeof v === 'string' || typeof v === 'boolean')
|
|
16
|
+
return;
|
|
17
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
18
|
+
return;
|
|
19
|
+
if (!v || typeof v !== 'object' || (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype && Object.getPrototypeOf(v) !== null) || seen.has(v))
|
|
20
|
+
throw new Error('rpc_invalid_json');
|
|
21
|
+
seen.add(v);
|
|
22
|
+
const array = Array.isArray(v);
|
|
23
|
+
if (array && Object.keys(v).length !== v.length)
|
|
24
|
+
throw new Error('rpc_invalid_json');
|
|
25
|
+
for (const key of Reflect.ownKeys(v)) {
|
|
26
|
+
if (array && key === 'length')
|
|
27
|
+
continue;
|
|
28
|
+
const descriptor = Object.getOwnPropertyDescriptor(v, key);
|
|
29
|
+
if (typeof key !== 'string' || !descriptor.enumerable || !('value' in descriptor) ||
|
|
30
|
+
(array && (!/^(0|[1-9]\d*)$/.test(key) || Number(key) >= v.length)))
|
|
31
|
+
throw new Error('rpc_invalid_json');
|
|
32
|
+
visit(descriptor.value);
|
|
33
|
+
}
|
|
34
|
+
seen.delete(v);
|
|
35
|
+
}
|
|
36
|
+
visit(value);
|
|
37
|
+
const json = JSON.stringify(value);
|
|
38
|
+
if (new TextEncoder().encode(json).length > MAX_BYTES)
|
|
39
|
+
throw new Error('rpc_message_too_large');
|
|
40
|
+
return JSON.parse(json);
|
|
41
|
+
}
|
|
42
|
+
/** Register in activate, then push the returned disposable into context.subscriptions.
|
|
43
|
+
* Requests only reach this plugin's worker; this is not a cross-plugin service API.
|
|
44
|
+
*/
|
|
45
|
+
export function defineWorkerApi(handlers, options = {}) {
|
|
46
|
+
for (const name of ['then', '$dispose']) {
|
|
47
|
+
if (Object.hasOwn(handlers, name))
|
|
48
|
+
throw new Error(`rpc_reserved_method: ${name}`);
|
|
49
|
+
}
|
|
50
|
+
const transport = options.transport ?? channel;
|
|
51
|
+
const active = new Set();
|
|
52
|
+
let stopped = false;
|
|
53
|
+
const respond = (p) => {
|
|
54
|
+
if (stopped)
|
|
55
|
+
return;
|
|
56
|
+
const message = checked(p);
|
|
57
|
+
void Promise.resolve().then(() => { if (!stopped)
|
|
58
|
+
return transport.post(message); }).catch(() => { });
|
|
59
|
+
};
|
|
60
|
+
const off = transport.onMessage(message => {
|
|
61
|
+
if (stopped || !packet(message) || message.kind !== 'request')
|
|
62
|
+
return;
|
|
63
|
+
const key = JSON.stringify([message.client, message.id]);
|
|
64
|
+
if (active.has(key))
|
|
65
|
+
return;
|
|
66
|
+
const reply = { protocol: PROTOCOL, client: message.client, id: message.id };
|
|
67
|
+
if (active.size >= MAX_PENDING) {
|
|
68
|
+
respond({ ...reply, kind: 'error', error: 'rpc_busy' });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (typeof message.method !== 'string' || !Object.hasOwn(handlers, message.method) || typeof handlers[message.method] !== 'function' || !Array.isArray(message.args)) {
|
|
72
|
+
respond({ ...reply, kind: 'error', error: 'rpc_method_not_found' });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
active.add(key);
|
|
76
|
+
void Promise.resolve().then(() => {
|
|
77
|
+
if (stopped)
|
|
78
|
+
return;
|
|
79
|
+
const request = checked(message);
|
|
80
|
+
return handlers[request.method](...request.args);
|
|
81
|
+
}).then(value => {
|
|
82
|
+
respond({ ...reply, kind: 'result', value: checked(value === undefined ? null : value) });
|
|
83
|
+
}).catch(error => {
|
|
84
|
+
let detail = 'rpc_failed';
|
|
85
|
+
try {
|
|
86
|
+
detail = String(error instanceof Error ? error.message : error).slice(0, 1000);
|
|
87
|
+
}
|
|
88
|
+
catch { }
|
|
89
|
+
respond({ ...reply, kind: 'error', error: detail });
|
|
90
|
+
}).finally(() => active.delete(key));
|
|
91
|
+
});
|
|
92
|
+
return { dispose() { stopped = true; off(); active.clear(); } };
|
|
93
|
+
}
|
|
94
|
+
/** Create once per window, dispose on unmount. Timeout/disposal cancels waiting,
|
|
95
|
+
* not a worker side effect that already started. All arguments/results are JSON.
|
|
96
|
+
*/
|
|
97
|
+
export function useWorkerApi(options = {}) {
|
|
98
|
+
const transport = options.transport ?? channel;
|
|
99
|
+
const timeoutMs = options.timeoutMs ?? 30_000;
|
|
100
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 300_000)
|
|
101
|
+
throw new Error('rpc_invalid_timeout');
|
|
102
|
+
const client = globalThis.crypto.randomUUID();
|
|
103
|
+
let sequence = 0, stopped = false;
|
|
104
|
+
const pending = new Map();
|
|
105
|
+
const settle = (id, value, error) => {
|
|
106
|
+
const request = pending.get(id);
|
|
107
|
+
if (!request)
|
|
108
|
+
return;
|
|
109
|
+
pending.delete(id);
|
|
110
|
+
clearTimeout(request.timer);
|
|
111
|
+
if (error !== undefined)
|
|
112
|
+
request.reject(new Error(error));
|
|
113
|
+
else
|
|
114
|
+
request.resolve(value);
|
|
115
|
+
};
|
|
116
|
+
const off = transport.onMessage(message => {
|
|
117
|
+
if (!packet(message) || message.client !== client || message.kind === 'request')
|
|
118
|
+
return;
|
|
119
|
+
try {
|
|
120
|
+
const response = checked(message);
|
|
121
|
+
if (response.kind === 'error')
|
|
122
|
+
settle(response.id, null, typeof response.error === 'string' ? response.error : 'rpc_failed');
|
|
123
|
+
else
|
|
124
|
+
settle(response.id, checked(response.value));
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
settle(message.id, null, error instanceof Error ? error.message : 'rpc_invalid_json');
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
const dispose = () => {
|
|
131
|
+
if (stopped)
|
|
132
|
+
return;
|
|
133
|
+
stopped = true;
|
|
134
|
+
off();
|
|
135
|
+
options.signal?.removeEventListener('abort', dispose);
|
|
136
|
+
for (const id of pending.keys())
|
|
137
|
+
settle(id, null, 'rpc_disposed');
|
|
138
|
+
};
|
|
139
|
+
options.signal?.addEventListener('abort', dispose, { once: true });
|
|
140
|
+
if (options.signal?.aborted)
|
|
141
|
+
dispose();
|
|
142
|
+
return new Proxy(Object.create(null), {
|
|
143
|
+
get(_target, method) {
|
|
144
|
+
if (method === '$dispose')
|
|
145
|
+
return dispose;
|
|
146
|
+
if (method === 'then' || typeof method !== 'string')
|
|
147
|
+
return undefined;
|
|
148
|
+
return (...args) => {
|
|
149
|
+
if (stopped)
|
|
150
|
+
return Promise.reject(new Error('rpc_disposed'));
|
|
151
|
+
if (pending.size >= MAX_PENDING)
|
|
152
|
+
return Promise.reject(new Error('rpc_busy'));
|
|
153
|
+
const id = String(++sequence);
|
|
154
|
+
let message;
|
|
155
|
+
try {
|
|
156
|
+
message = checked({ protocol: PROTOCOL, kind: 'request', client, id, method, args });
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
return Promise.reject(error);
|
|
160
|
+
}
|
|
161
|
+
return new Promise((resolve, reject) => {
|
|
162
|
+
const timer = setTimeout(() => settle(id, null, 'rpc_timeout'), timeoutMs);
|
|
163
|
+
pending.set(id, { resolve, reject, timer });
|
|
164
|
+
void Promise.resolve().then(() => {
|
|
165
|
+
if (!stopped && pending.has(id))
|
|
166
|
+
return transport.post(message);
|
|
167
|
+
}).catch(error => settle(id, null, error instanceof Error ? error.message : 'rpc_send_failed'));
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PluginAPI, PluginContext, PlatformEvent, PluginLocaleState } from './index.ts';
|
|
2
|
+
export type FakeHandler = (args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
3
|
+
type CommandHandler = Parameters<PluginContext['commands']['register']>[1];
|
|
4
|
+
export interface FakeHostOptions {
|
|
5
|
+
storage?: Record<string, unknown>;
|
|
6
|
+
/** Declared configuration keys and their initial/default values. */
|
|
7
|
+
config?: Record<string, unknown>;
|
|
8
|
+
handlers?: Record<string, FakeHandler>;
|
|
9
|
+
commands?: Record<string, CommandHandler>;
|
|
10
|
+
}
|
|
11
|
+
/** Offline bridge for real SDK calls. No OS, permissions, persistence or manifest
|
|
12
|
+
* validation is simulated. Channel messages loop back to this host's listeners.
|
|
13
|
+
* Install only within a serial test scope (globals cannot isolate concurrent tests).
|
|
14
|
+
* Every unimplemented capability fails unless supplied in `handlers`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createFakeHost(options?: FakeHostOptions): {
|
|
17
|
+
api: PluginAPI;
|
|
18
|
+
context: PluginContext;
|
|
19
|
+
/** Explicit installation: importing this module never changes the global. */
|
|
20
|
+
install(): () => void;
|
|
21
|
+
emitEvent: (event: PlatformEvent) => void;
|
|
22
|
+
emitMessage: (message: unknown) => void;
|
|
23
|
+
emitLocaleChanged: (state: PluginLocaleState) => void;
|
|
24
|
+
dispose(): Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
/** Installs a bridge for a callback and restores it even when the callback throws. */
|
|
27
|
+
export declare function withFakeHost<T>(options: FakeHostOptions, run: (host: ReturnType<typeof createFakeHost>) => T | Promise<T>): Promise<T>;
|
|
28
|
+
export {};
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { METHODS } from "./registry.js";
|
|
2
|
+
const installations = [];
|
|
3
|
+
function installAPI(api) {
|
|
4
|
+
const entry = { api, previous: Object.getOwnPropertyDescriptor(globalThis, 'medeoPlugin'), removed: false };
|
|
5
|
+
Object.defineProperty(globalThis, 'medeoPlugin', { configurable: true, writable: true, value: api });
|
|
6
|
+
installations.push(entry);
|
|
7
|
+
return () => {
|
|
8
|
+
if (entry.removed)
|
|
9
|
+
return;
|
|
10
|
+
entry.removed = true;
|
|
11
|
+
while (installations.at(-1)?.removed) {
|
|
12
|
+
const top = installations.pop();
|
|
13
|
+
// Never overwrite a bridge installed by unrelated test code.
|
|
14
|
+
if (Object.getOwnPropertyDescriptor(globalThis, 'medeoPlugin')?.value !== top.api)
|
|
15
|
+
continue;
|
|
16
|
+
if (top.previous)
|
|
17
|
+
Object.defineProperty(globalThis, 'medeoPlugin', top.previous);
|
|
18
|
+
else
|
|
19
|
+
Reflect.deleteProperty(globalThis, 'medeoPlugin');
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Offline bridge for real SDK calls. No OS, permissions, persistence or manifest
|
|
24
|
+
* validation is simulated. Channel messages loop back to this host's listeners.
|
|
25
|
+
* Install only within a serial test scope (globals cannot isolate concurrent tests).
|
|
26
|
+
* Every unimplemented capability fails unless supplied in `handlers`.
|
|
27
|
+
*/
|
|
28
|
+
export function createFakeHost(options = {}) {
|
|
29
|
+
const clone = (value) => structuredClone(value);
|
|
30
|
+
const storage = new Map(Object.entries(clone(options.storage ?? {})));
|
|
31
|
+
const config = new Map(Object.entries(clone(options.config ?? {})));
|
|
32
|
+
const handlers = new Map(Object.entries(options.handlers ?? {}));
|
|
33
|
+
const commands = new Map();
|
|
34
|
+
const messages = new Set();
|
|
35
|
+
const events = new Set();
|
|
36
|
+
const locales = new Set();
|
|
37
|
+
const restores = new Set();
|
|
38
|
+
let disposed = false;
|
|
39
|
+
const assertActive = () => { if (disposed)
|
|
40
|
+
throw new Error('fake_host_disposed'); };
|
|
41
|
+
const subscribe = (listeners, callback) => {
|
|
42
|
+
assertActive();
|
|
43
|
+
listeners.add(callback);
|
|
44
|
+
return () => { listeners.delete(callback); };
|
|
45
|
+
};
|
|
46
|
+
const emit = (listeners, value) => {
|
|
47
|
+
assertActive();
|
|
48
|
+
for (const listener of [...listeners])
|
|
49
|
+
listener(clone(value));
|
|
50
|
+
};
|
|
51
|
+
const key = (args) => {
|
|
52
|
+
if (typeof args.key !== 'string')
|
|
53
|
+
throw new Error('invalid_key');
|
|
54
|
+
return args.key;
|
|
55
|
+
};
|
|
56
|
+
const builtins = {
|
|
57
|
+
'storage.get': args => clone(storage.get(key(args)) ?? null),
|
|
58
|
+
'storage.set': args => { storage.set(key(args), clone(args.value)); return null; },
|
|
59
|
+
'storage.remove': args => { storage.delete(key(args)); return null; },
|
|
60
|
+
'config.get': args => {
|
|
61
|
+
if (args.key === undefined)
|
|
62
|
+
return Object.fromEntries(config);
|
|
63
|
+
const name = key(args);
|
|
64
|
+
if (!config.has(name))
|
|
65
|
+
throw new Error(`unknown_configuration: ${name}`);
|
|
66
|
+
return config.get(name);
|
|
67
|
+
},
|
|
68
|
+
'config.set': args => {
|
|
69
|
+
const name = key(args);
|
|
70
|
+
if (!config.has(name))
|
|
71
|
+
throw new Error(`unknown_configuration: ${name}`);
|
|
72
|
+
config.set(name, clone(args.value));
|
|
73
|
+
emit(events, { type: 'signal', channel: 'config' });
|
|
74
|
+
return null;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
const api = {
|
|
78
|
+
async invoke(method, args = {}) {
|
|
79
|
+
assertActive();
|
|
80
|
+
const handler = handlers.get(method) ?? (Object.hasOwn(builtins, method) ? builtins[method] : undefined);
|
|
81
|
+
if (!handler)
|
|
82
|
+
throw new Error(`${Object.hasOwn(METHODS, method) ? 'unimplemented_method' : 'unknown_method'}: ${method}`);
|
|
83
|
+
return clone(await handler(clone(args)));
|
|
84
|
+
},
|
|
85
|
+
async run(command, args) {
|
|
86
|
+
assertActive();
|
|
87
|
+
const handler = commands.get(command);
|
|
88
|
+
if (!handler)
|
|
89
|
+
throw new Error(`unregistered_command: ${command}`);
|
|
90
|
+
return clone(await handler(clone(args)));
|
|
91
|
+
},
|
|
92
|
+
async post(message) { emit(messages, message); },
|
|
93
|
+
onMessage: callback => subscribe(messages, callback),
|
|
94
|
+
onEvent: callback => subscribe(events, callback),
|
|
95
|
+
onLocaleChanged: callback => subscribe(locales, callback),
|
|
96
|
+
};
|
|
97
|
+
const context = {
|
|
98
|
+
subscriptions: [],
|
|
99
|
+
commands: {
|
|
100
|
+
register(id, handler) {
|
|
101
|
+
assertActive();
|
|
102
|
+
if (commands.has(id))
|
|
103
|
+
throw new Error(`duplicate_command: ${id}`);
|
|
104
|
+
commands.set(id, handler);
|
|
105
|
+
const disposable = { dispose() { if (commands.get(id) === handler)
|
|
106
|
+
commands.delete(id); } };
|
|
107
|
+
context.subscriptions.push(disposable);
|
|
108
|
+
return disposable;
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
for (const [id, handler] of Object.entries(options.commands ?? {}))
|
|
113
|
+
context.commands.register(id, handler);
|
|
114
|
+
return {
|
|
115
|
+
api, context,
|
|
116
|
+
/** Explicit installation: importing this module never changes the global. */
|
|
117
|
+
install() {
|
|
118
|
+
assertActive();
|
|
119
|
+
const restore = installAPI(api);
|
|
120
|
+
const off = () => { restore(); restores.delete(off); };
|
|
121
|
+
restores.add(off);
|
|
122
|
+
return off;
|
|
123
|
+
},
|
|
124
|
+
emitEvent: (event) => emit(events, event),
|
|
125
|
+
emitMessage: (message) => emit(messages, message),
|
|
126
|
+
emitLocaleChanged: (state) => emit(locales, state),
|
|
127
|
+
async dispose() {
|
|
128
|
+
if (disposed)
|
|
129
|
+
return;
|
|
130
|
+
disposed = true;
|
|
131
|
+
for (const restore of [...restores].reverse())
|
|
132
|
+
restore();
|
|
133
|
+
messages.clear();
|
|
134
|
+
events.clear();
|
|
135
|
+
locales.clear();
|
|
136
|
+
commands.clear();
|
|
137
|
+
const errors = [];
|
|
138
|
+
for (const subscription of context.subscriptions.splice(0).reverse()) {
|
|
139
|
+
try {
|
|
140
|
+
await subscription.dispose();
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
errors.push(error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (errors.length)
|
|
147
|
+
throw new AggregateError(errors, 'fake_host_cleanup_failed');
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/** Installs a bridge for a callback and restores it even when the callback throws. */
|
|
152
|
+
export async function withFakeHost(options, run) {
|
|
153
|
+
const host = createFakeHost(options);
|
|
154
|
+
host.install();
|
|
155
|
+
try {
|
|
156
|
+
return await run(host);
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
await host.dispose();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Layout only: colors, type and dimensions remain owned by /_medeo/theme.css. */
|
|
2
|
+
export declare const uiStyles = "\n.medeo-ui{font-family:var(--font-family-plain);color:var(--color-surface-on-surface);font-size:var(--type-label-large-size);line-height:var(--type-label-large-leading)}\n.medeo-card{padding:var(--space-s5);border:1px solid var(--color-surface-outline-variant);border-radius:var(--shape-radius-16);background:var(--color-surface-surface-container-lowest);display:grid;gap:var(--space-s3)}\n.medeo-card h2,.medeo-empty h2{margin:0;font-size:var(--type-title-medium-size);line-height:var(--type-title-medium-leading);font-weight:var(--type-title-medium-weight);letter-spacing:var(--type-title-medium-tracking)}\n.medeo-description{margin:0;color:var(--color-surface-on-surface-variant)}\n.medeo-pill{display:inline-flex;align-items:center;justify-self:start;padding:var(--space-s1) var(--space-s3);border-radius:var(--shape-radius-full);background:var(--medeo-selected);font-size:var(--type-label-medium-size);line-height:var(--type-label-medium-leading);font-weight:var(--type-label-medium-prominent-weight)}\n.medeo-pill[data-tone=error]{color:var(--color-schemes-error);background:var(--medeo-error-wash)}\n.medeo-pill[data-tone=neutral]{background:var(--medeo-hover)}\n.medeo-toggle{display:inline-flex;align-items:center;gap:var(--space-s2);min-height:var(--space-s8);cursor:pointer}\n.medeo-toggle input{width:var(--space-s4);height:var(--space-s4);accent-color:var(--color-schemes-primary)}\n.medeo-toggle:has(input:disabled){opacity:.45;cursor:default}\n.medeo-segmented{display:flex;flex-wrap:wrap;gap:var(--space-s1);padding:var(--space-s1);border:1px solid var(--color-surface-outline-variant);border-radius:var(--shape-radius-12)}\n.medeo-segmented button{font:inherit;color:inherit;border:0;border-radius:var(--shape-radius-8);background:transparent;min-height:var(--space-s8);padding:var(--space-s2) var(--space-s3);cursor:pointer}\n.medeo-segmented button[aria-checked=true]{background:var(--color-schemes-primary);color:var(--color-schemes-on-primary)}\n.medeo-segmented button:hover:not(:disabled):not([aria-checked=true]){background:var(--medeo-hover)}\n.medeo-segmented button:disabled{opacity:.45;cursor:default}\n.medeo-ui :focus-visible{outline:2px solid var(--color-schemes-primary);outline-offset:2px}\n.medeo-list{list-style:none;margin:0;padding:0;display:grid;gap:var(--space-s2)}\n.medeo-list>li{padding:var(--space-s3);border-bottom:1px solid var(--color-surface-outline-variant)}\n.medeo-empty{display:grid;gap:var(--space-s2);padding:var(--space-s7);text-align:center;border:1px dashed var(--color-surface-outline);border-radius:var(--shape-radius-12)}\n";
|
|
3
|
+
/** Explicit and idempotent; importing this module has no DOM side effects. */
|
|
4
|
+
export declare function installStyles(doc?: Document): void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Layout only: colors, type and dimensions remain owned by /_medeo/theme.css. */
|
|
2
|
+
export const uiStyles = `
|
|
3
|
+
.medeo-ui{font-family:var(--font-family-plain);color:var(--color-surface-on-surface);font-size:var(--type-label-large-size);line-height:var(--type-label-large-leading)}
|
|
4
|
+
.medeo-card{padding:var(--space-s5);border:1px solid var(--color-surface-outline-variant);border-radius:var(--shape-radius-16);background:var(--color-surface-surface-container-lowest);display:grid;gap:var(--space-s3)}
|
|
5
|
+
.medeo-card h2,.medeo-empty h2{margin:0;font-size:var(--type-title-medium-size);line-height:var(--type-title-medium-leading);font-weight:var(--type-title-medium-weight);letter-spacing:var(--type-title-medium-tracking)}
|
|
6
|
+
.medeo-description{margin:0;color:var(--color-surface-on-surface-variant)}
|
|
7
|
+
.medeo-pill{display:inline-flex;align-items:center;justify-self:start;padding:var(--space-s1) var(--space-s3);border-radius:var(--shape-radius-full);background:var(--medeo-selected);font-size:var(--type-label-medium-size);line-height:var(--type-label-medium-leading);font-weight:var(--type-label-medium-prominent-weight)}
|
|
8
|
+
.medeo-pill[data-tone=error]{color:var(--color-schemes-error);background:var(--medeo-error-wash)}
|
|
9
|
+
.medeo-pill[data-tone=neutral]{background:var(--medeo-hover)}
|
|
10
|
+
.medeo-toggle{display:inline-flex;align-items:center;gap:var(--space-s2);min-height:var(--space-s8);cursor:pointer}
|
|
11
|
+
.medeo-toggle input{width:var(--space-s4);height:var(--space-s4);accent-color:var(--color-schemes-primary)}
|
|
12
|
+
.medeo-toggle:has(input:disabled){opacity:.45;cursor:default}
|
|
13
|
+
.medeo-segmented{display:flex;flex-wrap:wrap;gap:var(--space-s1);padding:var(--space-s1);border:1px solid var(--color-surface-outline-variant);border-radius:var(--shape-radius-12)}
|
|
14
|
+
.medeo-segmented button{font:inherit;color:inherit;border:0;border-radius:var(--shape-radius-8);background:transparent;min-height:var(--space-s8);padding:var(--space-s2) var(--space-s3);cursor:pointer}
|
|
15
|
+
.medeo-segmented button[aria-checked=true]{background:var(--color-schemes-primary);color:var(--color-schemes-on-primary)}
|
|
16
|
+
.medeo-segmented button:hover:not(:disabled):not([aria-checked=true]){background:var(--medeo-hover)}
|
|
17
|
+
.medeo-segmented button:disabled{opacity:.45;cursor:default}
|
|
18
|
+
.medeo-ui :focus-visible{outline:2px solid var(--color-schemes-primary);outline-offset:2px}
|
|
19
|
+
.medeo-list{list-style:none;margin:0;padding:0;display:grid;gap:var(--space-s2)}
|
|
20
|
+
.medeo-list>li{padding:var(--space-s3);border-bottom:1px solid var(--color-surface-outline-variant)}
|
|
21
|
+
.medeo-empty{display:grid;gap:var(--space-s2);padding:var(--space-s7);text-align:center;border:1px dashed var(--color-surface-outline);border-radius:var(--shape-radius-12)}
|
|
22
|
+
`;
|
|
23
|
+
/** Explicit and idempotent; importing this module has no DOM side effects. */
|
|
24
|
+
export function installStyles(doc = document) {
|
|
25
|
+
if (doc.querySelector('style[data-medeo-ui]'))
|
|
26
|
+
return;
|
|
27
|
+
const style = doc.createElement('style');
|
|
28
|
+
style.dataset.medeoUi = '';
|
|
29
|
+
style.textContent = uiStyles;
|
|
30
|
+
doc.head.append(style);
|
|
31
|
+
}
|