@rigee/plugin-api 0.0.0-stage → 0.2.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 +137 -2
- package/dist/context.d.ts +130 -0
- package/dist/context.js +23 -0
- package/dist/data.d.ts +62 -0
- package/dist/data.js +14 -0
- package/dist/host.d.ts +460 -0
- package/dist/host.js +234 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +21 -0
- package/dist/manifest.d.ts +331 -0
- package/dist/manifest.js +70 -0
- package/dist/ui.d.ts +106 -0
- package/dist/ui.js +12 -0
- package/package.json +45 -4
package/README.md
CHANGED
|
@@ -1,3 +1,138 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @rigee/plugin-api
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The contract a [Rigee](https://github.com/Qyuan-Tech/rigee) plugin is written against.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install --save-dev @rigee/plugin-api
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
It is a development dependency on purpose: a renderer half leaves it out of its bundle and gets
|
|
10
|
+
Rigee's own copy at runtime (see Building), so the version you build against is for types and for
|
|
11
|
+
`minAppVersion`, not something that ships.
|
|
12
|
+
|
|
13
|
+
A plugin is up to two halves, because Electron leaves no choice: a PDF renders in the sandboxed
|
|
14
|
+
renderer, and the bytes it renders can only be read in the main process. Each half default-exports
|
|
15
|
+
a [cordis](https://github.com/cordiverse/cordis) plugin, and the two share an id and talk over one
|
|
16
|
+
IPC channel.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
my-plugin/
|
|
20
|
+
package.json the manifest, under a "rigee" key
|
|
21
|
+
dist/main.js ESM. Node. Optional.
|
|
22
|
+
dist/renderer.js ESM. DOM. Optional.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```jsonc
|
|
26
|
+
{
|
|
27
|
+
"name": "rigee-plugin-hello",
|
|
28
|
+
"version": "1.0.0",
|
|
29
|
+
"rigee": {
|
|
30
|
+
"id": "hello",
|
|
31
|
+
"version": "1.0.0",
|
|
32
|
+
"displayName": "Hello",
|
|
33
|
+
"apiVersion": 1,
|
|
34
|
+
"minAppVersion": "1.1.0",
|
|
35
|
+
"permissions": ["files:read"],
|
|
36
|
+
"main": "dist/main.js",
|
|
37
|
+
"renderer": "dist/renderer.js",
|
|
38
|
+
"contributes": {
|
|
39
|
+
"views": [{ "id": "hello.panel", "title": "Hello", "slot": "left", "icon": "plugin" }]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { icons, openFile, type PluginViewProps } from '@rigee/plugin-api'
|
|
47
|
+
import type { Context, Plugin } from 'cordis'
|
|
48
|
+
|
|
49
|
+
function Panel({ onCollapse }: PluginViewProps) {
|
|
50
|
+
return <button onClick={onCollapse}><icons.CollapseLeft size={14} /></button>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const plugin: Plugin = {
|
|
54
|
+
name: 'hello',
|
|
55
|
+
inject: ['contributions'],
|
|
56
|
+
apply(ctx: Context) {
|
|
57
|
+
ctx.contributions.view('hello.panel', Panel)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export default plugin
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Four entry points
|
|
64
|
+
|
|
65
|
+
| import | contains | realm |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| `@rigee/plugin-api` | everything | renderer half |
|
|
68
|
+
| `@rigee/plugin-api/manifest` | the declarative contract only — no React, no DOM | either |
|
|
69
|
+
| `@rigee/plugin-api/data` | the shared data types only | either |
|
|
70
|
+
| `@rigee/plugin-api/context` | types only: what `ctx.contributions`, `ctx.rpc`, `ctx.commands` and `ctx.shell` are | either |
|
|
71
|
+
|
|
72
|
+
The main half must not import the root entry: it would pull React into a process that cannot
|
|
73
|
+
render. It gets the `ctx` typings with `import type {} from '@rigee/plugin-api/context'`; a plugin
|
|
74
|
+
with a renderer half has them already, through the root entry.
|
|
75
|
+
|
|
76
|
+
## Building
|
|
77
|
+
|
|
78
|
+
**The renderer half** leaves exactly `react`, `react-dom`, `react/jsx-runtime` and
|
|
79
|
+
`@rigee/plugin-api` external, as ESM. The shell resolves those through an import map to its own
|
|
80
|
+
copies — two Reacts in one window means broken hooks, and a second copy of this package means
|
|
81
|
+
talking to a host nobody installed. Bundle everything else, including anything you import from
|
|
82
|
+
`@rigee/plugin-api/manifest`: the import map names the package, not its subpaths.
|
|
83
|
+
|
|
84
|
+
**The main half** is one bundled ESM file for Node, with only `electron` and Node's built-ins left
|
|
85
|
+
external. One file because Rigee re-imports it after an update under a new URL, and a second file
|
|
86
|
+
it imported relatively would come back from Node's module cache as the old one.
|
|
87
|
+
|
|
88
|
+
**Styles** cannot be a `<link>`: the window's policy does not load stylesheets from a plugin.
|
|
89
|
+
Inline your CSS into the bundle and add it as a `<style>` element when the module loads.
|
|
90
|
+
|
|
91
|
+
**Workers** cannot be started from a plugin's own files — a window only starts workers from its
|
|
92
|
+
own origin. Start one through the shell instead, from a self-contained script (a worker has no
|
|
93
|
+
import map):
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
new Worker(workerUrl(new URL('./parse.worker.js', import.meta.url)), { type: 'module' })
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Code highlighting** comes from the text editor's own tokenizers: `tokenizeCode(text, 'ts')`
|
|
100
|
+
returns the tokens of each line, so a plugin need not ship a highlighter of its own.
|
|
101
|
+
|
|
102
|
+
## Publishing, so that Rigee can find it
|
|
103
|
+
|
|
104
|
+
Rigee's plugins view searches public GitHub. A repository is found and installable when:
|
|
105
|
+
|
|
106
|
+
| | the rule |
|
|
107
|
+
|---|---|
|
|
108
|
+
| found by | the topic `rigee-plugin` |
|
|
109
|
+
| named | `rigee-plugin-<id>` — the id in lower-case letters, digits and hyphens |
|
|
110
|
+
| released | its latest release (not a draft or pre-release) is tagged `v<version>` |
|
|
111
|
+
| packaged | that release carries `rigee-plugin-<id>-<version>.tgz`, exactly as `npm pack` writes it |
|
|
112
|
+
| agreeing | `name` is the repository's name; the tag, `version` and `rigee.version` are one version |
|
|
113
|
+
|
|
114
|
+
Rigee downloads the tarball, checks it against the SHA-256 GitHub reports for it, unpacks it
|
|
115
|
+
(regular files and directories under `package/`, nothing else), checks the manifest, and only then
|
|
116
|
+
asks the user — saying where the plugin came from, that it was found by searching, and that Rigee
|
|
117
|
+
has not reviewed it. Turn on immutable releases if you can: a release that cannot be changed after
|
|
118
|
+
it is published is one a user can trust to stay what they approved.
|
|
119
|
+
|
|
120
|
+
## What `apiVersion` and `minAppVersion` are for
|
|
121
|
+
|
|
122
|
+
`apiVersion` is the generation of this contract the plugin was built against. Rigee refuses a
|
|
123
|
+
plugin whose `apiVersion` it does not implement, at discovery, with that as the stated reason —
|
|
124
|
+
rather than letting it fail later as a missing export in a stack trace nobody can act on. Adding a
|
|
125
|
+
function does not bump it; removing or reshaping one does.
|
|
126
|
+
|
|
127
|
+
`minAppVersion` is the oldest Rigee the plugin runs in. A function added to this package exists
|
|
128
|
+
only in Rigee releases from then on, so a plugin that calls one should say so — `tokenizeCode` and
|
|
129
|
+
`workerUrl` arrived in Rigee 1.1.0. An older Rigee then refuses the plugin by name instead of
|
|
130
|
+
failing to link it.
|
|
131
|
+
|
|
132
|
+
## Permissions, and what they are not
|
|
133
|
+
|
|
134
|
+
`permissions` is an integrity boundary: the grant is recorded per plugin, and a new version that
|
|
135
|
+
asks for more has to ask again. It is **not** a sandbox. A renderer half runs in the shell's own
|
|
136
|
+
JavaScript context, so a hostile plugin can reach whatever the shell can reach regardless of what
|
|
137
|
+
it declared. Installing one is a decision to run its code with the application's privileges, and
|
|
138
|
+
Rigee's install dialog says so in those words.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The services a plugin's halves are given on `ctx`, as a plugin sees them.
|
|
3
|
+
*
|
|
4
|
+
* The shell implements each one as a cordis Service, and used to declare them on `Context` from its
|
|
5
|
+
* own source — which only a plugin compiled INSIDE this repository could see. A plugin from another
|
|
6
|
+
* repository wrote `ctx.contributions.editor(…)` against a Context that, as far as its compiler
|
|
7
|
+
* knew, had no such property. So the shapes live here, with the augmentation, and the shell's
|
|
8
|
+
* classes implement them: one declaration, and a plugin anywhere types against it.
|
|
9
|
+
*
|
|
10
|
+
* Types only — this module emits nothing. It is reached through the root entry, which a renderer
|
|
11
|
+
* half imports anyway, and on its own as `@rigee/plugin-api/context`: a plugin with only a main
|
|
12
|
+
* half gets the typings with `import type {} from '@rigee/plugin-api/context'`, which costs
|
|
13
|
+
* nothing at runtime and keeps React out of a process that cannot render.
|
|
14
|
+
*
|
|
15
|
+
* Each service has to be named in a half's `inject` before it can be read — `inject: ['commands']`
|
|
16
|
+
* — or cordis throws, and a half whose injection cannot be met never runs at all.
|
|
17
|
+
*
|
|
18
|
+
* BIND A METHOD BEFORE PASSING IT AROUND: `const shell = ctx.shell; const emit = shell.emit.bind(shell)`.
|
|
19
|
+
* cordis tells the shell WHICH plugin is calling by swapping `this` when a method is called on the
|
|
20
|
+
* service itself, so a bare `const emit = ctx.shell.emit` arrives with no `this` and throws. Bound,
|
|
21
|
+
* it is also some eighty times cheaper per call than going through `ctx` each time.
|
|
22
|
+
*/
|
|
23
|
+
import type { Context } from 'cordis';
|
|
24
|
+
import type { BrowserWindow, WebContents } from 'electron';
|
|
25
|
+
import type { PluginId, ViewType } from './manifest';
|
|
26
|
+
import type { PluginEditorComponent, PluginOverlayComponent, PluginStatusItemComponent, PluginTabComponent, PluginTabFactory, PluginViewComponent } from './ui';
|
|
27
|
+
/**
|
|
28
|
+
* `ctx.contributions`: the components a manifest promised.
|
|
29
|
+
*
|
|
30
|
+
* Every id must be one the manifest declares, under the plugin's own prefix — `pdf.preview` for
|
|
31
|
+
* the plugin `pdf` — or the bind throws. Each bind is revoked with the plugin, so a tab showing a
|
|
32
|
+
* component of a plugin that was switched off falls back to the shell's notice.
|
|
33
|
+
*/
|
|
34
|
+
export interface PluginContributionsService {
|
|
35
|
+
/** A viewer for the files an `editors` contribution claims. */
|
|
36
|
+
editor(viewType: ViewType, component: PluginEditorComponent): void;
|
|
37
|
+
/** A tab type the plugin owns outright. */
|
|
38
|
+
tab(viewType: ViewType, component: PluginTabComponent): void;
|
|
39
|
+
/** What a fresh tab of a `creatable` tab type starts as. */
|
|
40
|
+
tabFactory(viewType: ViewType, factory: PluginTabFactory): void;
|
|
41
|
+
/** A side-bar panel. */
|
|
42
|
+
view(id: ViewType, component: PluginViewComponent): void;
|
|
43
|
+
statusItem(id: ViewType, component: PluginStatusItemComponent): void;
|
|
44
|
+
/** Mounted at the shell's root while the plugin is active. */
|
|
45
|
+
overlay(id: ViewType, component: PluginOverlayComponent): void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* `ctx.rpc`: the renderer half's line to its own main half, and nobody else's. A command or event
|
|
49
|
+
* name without the plugin's own prefix — `pdf:read` for `pdf` — throws locally.
|
|
50
|
+
*/
|
|
51
|
+
export interface PluginRpcService {
|
|
52
|
+
/** Call one of this plugin's main-half commands. Rejects with whatever the handler threw. */
|
|
53
|
+
invoke<T = unknown>(command: string, payload?: unknown): Promise<T>;
|
|
54
|
+
/** Listen for one of this plugin's own events — `status`, not `scm:status`. Revoked with the plugin. */
|
|
55
|
+
on<T = unknown>(event: string, listener: (payload: T) => void): void;
|
|
56
|
+
}
|
|
57
|
+
/** `ctx.commands`: what the renderer half can call. */
|
|
58
|
+
export interface PluginCommandsService {
|
|
59
|
+
/**
|
|
60
|
+
* Answer `name`, which must carry the plugin's own prefix: `pdf:read`. What the handler returns
|
|
61
|
+
* — or the promise it returns resolves to — is what the renderer's `invoke` resolves to, so it
|
|
62
|
+
* must survive structured cloning: no functions, no class instances. Revoked with the plugin.
|
|
63
|
+
*/
|
|
64
|
+
register(name: string, handler: (payload: unknown) => unknown): void;
|
|
65
|
+
}
|
|
66
|
+
/** A question for `ctx.shell.ask`: Electron's message box, cut down to what a question needs. */
|
|
67
|
+
export interface AskOptions {
|
|
68
|
+
type?: 'none' | 'info' | 'error' | 'question' | 'warning';
|
|
69
|
+
message: string;
|
|
70
|
+
detail?: string;
|
|
71
|
+
buttons?: string[];
|
|
72
|
+
/** The button Return presses. */
|
|
73
|
+
defaultId?: number;
|
|
74
|
+
/** The button Escape presses, and the answer when the dialog is dismissed. */
|
|
75
|
+
cancelId?: number;
|
|
76
|
+
}
|
|
77
|
+
/** `ctx.shell`: what a main half is given of the shell — the window, the renderer, and the user. */
|
|
78
|
+
export interface PluginShellService {
|
|
79
|
+
/** The app window, or null before it exists and after it is gone. */
|
|
80
|
+
window(): BrowserWindow | null;
|
|
81
|
+
/** The shell's own WebContents: the React renderer, not a browser tab. */
|
|
82
|
+
webContents(): WebContents | null;
|
|
83
|
+
/** Push an event to this plugin's renderer half. Plugin-local name: `status`, not `scm:status`. */
|
|
84
|
+
emit(event: string, payload: unknown): void;
|
|
85
|
+
/**
|
|
86
|
+
* A URL the renderer can stream this file from, Range requests and all — what a <video> needs.
|
|
87
|
+
* It names a token, not the path, and works until `releaseFile` or until the plugin is disposed.
|
|
88
|
+
*/
|
|
89
|
+
serveFile(path: string, mime: string): string;
|
|
90
|
+
/** Stop serving a URL `serveFile` returned. Unknown or already released is not an error. */
|
|
91
|
+
releaseFile(url: string): void;
|
|
92
|
+
/**
|
|
93
|
+
* Ask the user something in a native dialog over the window: the index of the button pressed.
|
|
94
|
+
* Through the shell rather than Electron's `dialog`, so that Rigee's own tests can answer it.
|
|
95
|
+
*/
|
|
96
|
+
ask(options: AskOptions): Promise<number>;
|
|
97
|
+
/**
|
|
98
|
+
* Offer to take keyboard focus instead of the shell — return true if you took it. For a plugin
|
|
99
|
+
* that composites a native view over the window, which the window never focuses by itself.
|
|
100
|
+
*/
|
|
101
|
+
claimFocus(handler: () => boolean): void;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The Context both halves receive, with the services above declared on it.
|
|
105
|
+
*
|
|
106
|
+
* Also what keeps `cordis` imported in the published declarations: the augmentation below merges
|
|
107
|
+
* into cordis's own `Context` only when this file imports the module, and a type import that
|
|
108
|
+
* nothing used would be dropped from the emitted .d.ts — leaving a declaration that shadows cordis's
|
|
109
|
+
* Context instead of extending it.
|
|
110
|
+
*/
|
|
111
|
+
export type PluginContext = Context;
|
|
112
|
+
declare module 'cordis' {
|
|
113
|
+
interface Context {
|
|
114
|
+
/** Renderer half. */
|
|
115
|
+
contributions: PluginContributionsService;
|
|
116
|
+
/** Renderer half. */
|
|
117
|
+
rpc: PluginRpcService;
|
|
118
|
+
/** Main half. */
|
|
119
|
+
commands: PluginCommandsService;
|
|
120
|
+
/** Main half. */
|
|
121
|
+
shell: PluginShellService;
|
|
122
|
+
/**
|
|
123
|
+
* Which plugin this Context belongs to, set by the host on each plugin's own Context.
|
|
124
|
+
*
|
|
125
|
+
* The `?` is a TypeScript fiction over a Proxy that THROWS on an unknown property read; both
|
|
126
|
+
* hosts install an accessor answering undefined on the root, so reading it is always safe.
|
|
127
|
+
*/
|
|
128
|
+
pluginId?: PluginId;
|
|
129
|
+
}
|
|
130
|
+
}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The services a plugin's halves are given on `ctx`, as a plugin sees them.
|
|
3
|
+
*
|
|
4
|
+
* The shell implements each one as a cordis Service, and used to declare them on `Context` from its
|
|
5
|
+
* own source — which only a plugin compiled INSIDE this repository could see. A plugin from another
|
|
6
|
+
* repository wrote `ctx.contributions.editor(…)` against a Context that, as far as its compiler
|
|
7
|
+
* knew, had no such property. So the shapes live here, with the augmentation, and the shell's
|
|
8
|
+
* classes implement them: one declaration, and a plugin anywhere types against it.
|
|
9
|
+
*
|
|
10
|
+
* Types only — this module emits nothing. It is reached through the root entry, which a renderer
|
|
11
|
+
* half imports anyway, and on its own as `@rigee/plugin-api/context`: a plugin with only a main
|
|
12
|
+
* half gets the typings with `import type {} from '@rigee/plugin-api/context'`, which costs
|
|
13
|
+
* nothing at runtime and keeps React out of a process that cannot render.
|
|
14
|
+
*
|
|
15
|
+
* Each service has to be named in a half's `inject` before it can be read — `inject: ['commands']`
|
|
16
|
+
* — or cordis throws, and a half whose injection cannot be met never runs at all.
|
|
17
|
+
*
|
|
18
|
+
* BIND A METHOD BEFORE PASSING IT AROUND: `const shell = ctx.shell; const emit = shell.emit.bind(shell)`.
|
|
19
|
+
* cordis tells the shell WHICH plugin is calling by swapping `this` when a method is called on the
|
|
20
|
+
* service itself, so a bare `const emit = ctx.shell.emit` arrives with no `this` and throws. Bound,
|
|
21
|
+
* it is also some eighty times cheaper per call than going through `ctx` each time.
|
|
22
|
+
*/
|
|
23
|
+
export {};
|
package/dist/data.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell's data types that appear on the plugin boundary.
|
|
3
|
+
*
|
|
4
|
+
* Moved here from the shell's own `types.ts` rather than copied, and the direction is the point: a
|
|
5
|
+
* plugin compiled against this package must be looking at the SAME `TabId` and `ViewBounds` the
|
|
6
|
+
* shell hands it, or the two agree only by coincidence and drift the first time either side edits
|
|
7
|
+
* a field. `src/shared/types.ts` re-exports these, so the shell still reads as if they were local.
|
|
8
|
+
*
|
|
9
|
+
* Only what actually crosses the boundary lives here. The tab union, the group row and the
|
|
10
|
+
* persisted document stay in the shell: a plugin receives its own state and its own tab id, never
|
|
11
|
+
* the shape the shell keeps them in, and publishing those would freeze the shell's internals into
|
|
12
|
+
* a contract nobody asked for.
|
|
13
|
+
*/
|
|
14
|
+
export type WorkspaceId = string;
|
|
15
|
+
export type RepoId = string;
|
|
16
|
+
export type TabId = string;
|
|
17
|
+
/** One root folder inside a workspace. A workspace may hold several, VSCode-style. */
|
|
18
|
+
export interface WorkspaceFolder {
|
|
19
|
+
/** Absolute path. */
|
|
20
|
+
path: string;
|
|
21
|
+
/** Display name; defaults to basename(path) but the user may rename it. */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Whether `path` is (or is inside) a git work tree. Resolved at add time and on refresh. */
|
|
24
|
+
isGitRepo: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Absolute path to the repository root, when `isGitRepo`. May differ from `path` if the user
|
|
27
|
+
* added a subdirectory of a repo. This, not `path`, is the identity of a repo.
|
|
28
|
+
*/
|
|
29
|
+
repoRoot: string | null;
|
|
30
|
+
}
|
|
31
|
+
export interface Workspace {
|
|
32
|
+
id: WorkspaceId;
|
|
33
|
+
name: string;
|
|
34
|
+
folders: WorkspaceFolder[];
|
|
35
|
+
createdAt: number;
|
|
36
|
+
lastOpenedAt: number;
|
|
37
|
+
/** Where terminals start when the workspace has no folders. Only set for Scratch. */
|
|
38
|
+
defaultCwd?: string;
|
|
39
|
+
/**
|
|
40
|
+
* The `.code-workspace` file this workspace was opened from, when it came from one.
|
|
41
|
+
*
|
|
42
|
+
* Absent for a workspace that is just a folder — which is the majority, and is exactly VS Code's
|
|
43
|
+
* distinction between a folder and a saved multi-root workspace. Its presence is what makes
|
|
44
|
+
* "Save Workspace As" an overwrite rather than a new file.
|
|
45
|
+
*/
|
|
46
|
+
filePath?: string | null;
|
|
47
|
+
}
|
|
48
|
+
/** One entry in a directory listing. */
|
|
49
|
+
export interface FileStat {
|
|
50
|
+
path: string;
|
|
51
|
+
name: string;
|
|
52
|
+
isDir: boolean;
|
|
53
|
+
/** True when a .gitignore in scope excludes this entry; the tree dims it. */
|
|
54
|
+
ignored: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** Bounds of a DOM placeholder, in CSS pixels relative to the window's content area. */
|
|
57
|
+
export interface ViewBounds {
|
|
58
|
+
x: number;
|
|
59
|
+
y: number;
|
|
60
|
+
width: number;
|
|
61
|
+
height: number;
|
|
62
|
+
}
|
package/dist/data.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell's data types that appear on the plugin boundary.
|
|
3
|
+
*
|
|
4
|
+
* Moved here from the shell's own `types.ts` rather than copied, and the direction is the point: a
|
|
5
|
+
* plugin compiled against this package must be looking at the SAME `TabId` and `ViewBounds` the
|
|
6
|
+
* shell hands it, or the two agree only by coincidence and drift the first time either side edits
|
|
7
|
+
* a field. `src/shared/types.ts` re-exports these, so the shell still reads as if they were local.
|
|
8
|
+
*
|
|
9
|
+
* Only what actually crosses the boundary lives here. The tab union, the group row and the
|
|
10
|
+
* persisted document stay in the shell: a plugin receives its own state and its own tab id, never
|
|
11
|
+
* the shape the shell keeps them in, and publishing those would freeze the shell's internals into
|
|
12
|
+
* a contract nobody asked for.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|