plugins: consolidate under third/, run the clock fully sandboxed
This commit is contained in:
parent
cd8b7a9c70
commit
7bf51bdff8
10 changed files with 55 additions and 43 deletions
|
|
@ -9,12 +9,28 @@ add UI in predefined slots.
|
|||
> between releases. Plugins are deployment-controlled (same trust level as the
|
||||
> app itself) — there is no user-uploaded plugin marketplace, by design.
|
||||
|
||||
## Where plugins live
|
||||
|
||||
Plugin files live under **`public/plugins/third/`** (served at `/app/plugins/third/`).
|
||||
Third-party plugins **should be sandboxed** — run in an opaque-origin iframe reachable
|
||||
only through the capability bridge (see [SANDBOX.md](./SANDBOX.md)). In-page loading
|
||||
(full page access, no isolation) is reserved for trusted first-party code and is
|
||||
being phased out.
|
||||
|
||||
## Enabling plugins
|
||||
|
||||
Add script URLs to `plugins` in [`config.json`](../CONFIG.md):
|
||||
Add script URLs to `plugins` in [`config.json`](../CONFIG.md). Prefer the sandboxed
|
||||
object form with an explicit permission list:
|
||||
|
||||
```json
|
||||
{ "plugins": ["/app/plugins/orbit-demo.js"] }
|
||||
{ "plugins": [{ "url": "/app/plugins/third/orbit-clock.js", "sandbox": true, "permissions": [] }] }
|
||||
```
|
||||
|
||||
A bare URL (or an object without `"sandbox": true`) still loads **in-page** — trusted,
|
||||
full access — for first-party plugins that need the React API:
|
||||
|
||||
```json
|
||||
{ "plugins": ["/app/plugins/third/orbit-demo.js"] }
|
||||
```
|
||||
|
||||
They load in order, after the app boots. Host them anywhere the page can reach
|
||||
|
|
@ -143,6 +159,6 @@ a hook that isn't here.
|
|||
|
||||
| File | Shows |
|
||||
|---|---|
|
||||
| [`orbit-demo.js`](../public/plugins/orbit-demo.js) | events, a `composer_button`, an IRC action |
|
||||
| [`orbit-clock.js`](../public/plugins/orbit-clock.js) | a `topbar_item` with live React-hook state |
|
||||
| [`orbit-copy.js`](../public/plugins/orbit-copy.js) | a `message_action` (toolbar copy button with copied-confirmation) |
|
||||
| [`orbit-demo.js`](../public/plugins/third/orbit-demo.js) | events, a `composer_button`, an IRC action |
|
||||
| [`orbit-clock.js`](../public/plugins/third/orbit-clock.js) | a `topbar_item` with live React-hook state |
|
||||
| [`orbit-copy.js`](../public/plugins/third/orbit-copy.js) | a `message_action` (toolbar copy button with copied-confirmation) |
|
||||
|
|
|
|||
|
|
@ -18,10 +18,13 @@ Two things use it:
|
|||
fetched and mounted the same way. Use for community / third-party / less-trusted
|
||||
plugins. (Plain string entries still load in-page as a trusted `<script>`.)
|
||||
|
||||
Plugin files live under `public/plugins/third/` (served at `/app/plugins/third/`):
|
||||
|
||||
```jsonc
|
||||
"plugins": [
|
||||
"/app/plugins/orbit-clock.js", // in-page (trusted)
|
||||
{ "url": "/app/plugins/community-thing.js", // sandboxed
|
||||
{ "url": "/app/plugins/third/orbit-clock.js", // sandboxed, no permissions
|
||||
"sandbox": true, "permissions": [] },
|
||||
{ "url": "/app/plugins/third/community-thing.js", // sandboxed, community
|
||||
"sandbox": true, "permissions": ["irc", "storage"] }
|
||||
]
|
||||
```
|
||||
|
|
@ -56,7 +59,7 @@ The app's theme CSS vars (`--bg`, `--ink`, `--accent`, `--muted`, `--border`,
|
|||
change, so plugin UI can use `var(--accent)` and look native in light + dark.
|
||||
|
||||
Examples: `src/modules/sandbox/features/dice.js` (a built-in, bundled + mounted by core) and
|
||||
`public/plugins/orbit-sandbox-demo.js` (the minimal config-loaded form).
|
||||
`public/plugins/third/orbit-sandbox-demo.js` (the minimal config-loaded form).
|
||||
|
||||
## Required CSP (APPLIED 2026-07-04)
|
||||
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@
|
|||
"saslScram": true
|
||||
},
|
||||
"plugins": [
|
||||
"/app/plugins/orbit-clock.js",
|
||||
"/app/plugins/orbit-copy.js",
|
||||
"/app/plugins/orbit-games.js",
|
||||
"/app/plugins/orbit-invite.js"
|
||||
{ "url": "/app/plugins/third/orbit-clock.js", "sandbox": true, "permissions": [] },
|
||||
"/app/plugins/third/orbit-copy.js",
|
||||
"/app/plugins/third/orbit-games.js",
|
||||
"/app/plugins/third/orbit-invite.js"
|
||||
],
|
||||
"builtins": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Orbit clock plugin — adds a live HH:MM clock to the topbar.
|
||||
*
|
||||
* Demonstrates the `topbar_item` UI slot and using React hooks through
|
||||
* orbit.React. Note the pattern: addUi returns an ELEMENT (orbit.h(Clock)), so
|
||||
* Clock is a real component that owns its own hook state across re-renders.
|
||||
*
|
||||
* Load via config.json: "plugins": ["/app/plugins/orbit-clock.js"]
|
||||
*/
|
||||
Orbit.plugin('orbit-clock', (orbit, log) => {
|
||||
const { useState, useEffect } = orbit.React;
|
||||
|
||||
function Clock() {
|
||||
const [now, setNow] = useState(() => new Date());
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setNow(new Date()), 15000);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
const hh = String(now.getHours()).padStart(2, '0');
|
||||
const mm = String(now.getMinutes()).padStart(2, '0');
|
||||
return orbit.html`
|
||||
<span
|
||||
title=${orbit.i18n.t('plugins.clock.title')}
|
||||
style=${{ fontWeight: 600, fontSize: '.82rem', lineHeight: '1', opacity: 0.7, padding: '0 .45rem', alignSelf: 'center' }}
|
||||
>${hh}:${mm}</span>
|
||||
`;
|
||||
}
|
||||
|
||||
orbit.addUi('topbar_item', () => orbit.h(Clock));
|
||||
log('clock ready');
|
||||
});
|
||||
24
public/plugins/third/orbit-clock.js
Normal file
24
public/plugins/third/orbit-clock.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Orbit clock — a live HH:MM clock in the topbar. FULLY SANDBOXED.
|
||||
*
|
||||
* Runs in an opaque-origin iframe reachable only through the capability bridge, so
|
||||
* it can touch NOTHING outside itself: no page, no store, no IRC, no storage. It
|
||||
* renders its own DOM (no React) and styles itself with the app's theme vars, which
|
||||
* are mirrored into the sandbox — so it still matches light + dark. Needs zero
|
||||
* permissions.
|
||||
*
|
||||
* { "url": "/app/plugins/third/orbit-clock.js", "sandbox": true, "permissions": [] }
|
||||
*/
|
||||
Orbit.plugin('orbit-clock', function (orbit, log) {
|
||||
orbit.ui('topbar_item', function (root) {
|
||||
root.style.cssText = 'font:600 13px/1 system-ui,sans-serif;color:var(--ink,inherit);opacity:.7;white-space:nowrap';
|
||||
root.title = 'Local time';
|
||||
function tick() {
|
||||
var d = new Date();
|
||||
root.textContent = String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
|
||||
}
|
||||
tick();
|
||||
setInterval(tick, 15000);
|
||||
});
|
||||
log('clock ready');
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue