@appsoftwareltd/etherpk-extension-api 0.12.0 → 0.13.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.
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Images an extension hands to the open graph (ADR 0147): made smaller the way a pasted image is,
3
+ * and kept as one of the graph's assets, so a document can show it.
4
+ */
5
+ /**
6
+ * The open graph's assets, as far as an extension may add to them.
7
+ *
8
+ * @beta
9
+ */
10
+ export interface ExtensionAssets {
11
+ /**
12
+ * An image made ready to send or keep, as a pasted image is: a large photo or screenshot is
13
+ * scaled down and re-encoded, and an image that is small enough comes back as it was. Rejects
14
+ * when the bytes are not an image the browser can read.
15
+ */
16
+ prepareImage(image: Blob): Promise<Blob>;
17
+ /**
18
+ * Keep an image as one of the graph's assets, and answer the markdown that shows it in a
19
+ * document, such as `![photo](assets/photo.webp)`. Null where the graph keeps no assets here,
20
+ * or the image was refused.
21
+ */
22
+ saveImage(image: Blob, name: string): Promise<string | null>;
23
+ }
package/dist/assets.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Images an extension hands to the open graph (ADR 0147): made smaller the way a pasted image is,
3
+ * and kept as one of the graph's assets, so a document can show it.
4
+ */
5
+ export {};
package/dist/context.d.ts CHANGED
@@ -7,14 +7,18 @@
7
7
  * only through the index's answers and its own fences, and changes them only through its fences,
8
8
  * the blocks it puts at the caret and the Client's Commands.
9
9
  */
10
+ import type { ExtensionAssets } from './assets';
10
11
  import type { ExtensionAuth } from './auth';
11
12
  import type { ExtensionDialogs } from './dialogs';
13
+ import type { ExtensionDocuments } from './documents';
12
14
  import type { ExtensionEditor } from './editor';
13
15
  import type { ExtensionEmbeds } from './embeds';
14
16
  import type { ExtensionEvents } from './events';
15
17
  import type { ExtensionIndex } from './graph-index';
16
18
  import type { ExtensionJournalPanels } from './journal';
17
- import type { CommandMenuItem, ContextMenuItem, GraphSidebarButton } from './menus';
19
+ import type { CommandBarButton, CommandMenuItem, ContextMenuItem, GraphSidebarButton } from './menus';
20
+ import type { ExtensionGraphRecords } from './records';
21
+ import type { SettingsSection } from './settings-sections';
18
22
  import type { ViewContribution, ViewRef } from './views';
19
23
  /**
20
24
  * A Command's handler. What it returns, or resolves to, is what `execute` resolves to.
@@ -73,6 +77,16 @@ export interface ExtensionCommandMenu {
73
77
  /** Add a row, its id under the extension's id. */
74
78
  register(item: CommandMenuItem): () => void;
75
79
  }
80
+ /**
81
+ * Adding buttons to the Command Bar, the row of buttons across the bottom of a phone's screen:
82
+ * after the editing buttons that scroll, or in the pinned group at its right end.
83
+ *
84
+ * @beta
85
+ */
86
+ export interface ExtensionCommandBar {
87
+ /** Add a button, its id under the extension's id. */
88
+ register(button: CommandBarButton): () => void;
89
+ }
76
90
  /**
77
91
  * Adding buttons to the Graph Sidebar's group of action buttons, after Search and today's date.
78
92
  *
@@ -146,6 +160,26 @@ export interface ExtensionLayout {
146
160
  activeDocument(): string | null;
147
161
  /** True while the desktop presenter is showing, 1024 pixels wide and more. */
148
162
  isDesktop(): boolean;
163
+ /**
164
+ * Draw something over the whole EtherPK window, with a heading and a close button the Client
165
+ * draws, as a fence's widget opens over the window. One at a time: opening another closes the
166
+ * one before. The close button, Escape and `close` all close it, and `onClose` is told once.
167
+ *
168
+ * @beta
169
+ */
170
+ openWindow(options: {
171
+ /** The heading the Client draws beside the close button. */
172
+ title: string;
173
+ /** Draw into `host`, which fills the window under the heading. */
174
+ mount(host: HTMLElement, window: {
175
+ close(): void;
176
+ }): {
177
+ destroy(): void;
178
+ };
179
+ onClose?(): void;
180
+ }): {
181
+ close(): void;
182
+ };
149
183
  }
150
184
  /**
151
185
  * The rules for naming a concept, which every index answer follows.
@@ -203,15 +237,15 @@ export interface ExtensionSettings {
203
237
  }
204
238
  /**
205
239
  * The extension's own records, kept with the person (ADR 0140): on this device, and where the
206
- * person's account syncs a graph, in that account's vault too, encrypted on the device so only the
207
- * person's own devices can read them. So a record follows the person to their other devices, as
240
+ * person's account syncs a graph, in that account's Settings Vault too, encrypted on the device so
241
+ * only the person's own devices can read them. So a record follows the person to their other devices, as
208
242
  * Extension Settings do, such as what signing in to another service gave the extension. The person
209
243
  * never sees these records, and uninstalling the extension clears them on every device.
210
244
  *
211
245
  * Each record is kept as JSON, at most 64 KB of it. Between two devices the later change to a
212
246
  * record wins, record by record. A record is readable on the device by any code in the page, as
213
- * localStorage is, and by a Headless Client given an agent token for the same account, which opens
214
- * the account's vault.
247
+ * localStorage is. A Headless Client signed in to the same account cannot read it: the Sync Server
248
+ * never gives an agent token the Settings Vault (ADR 0148).
215
249
  *
216
250
  * @public
217
251
  */
@@ -335,6 +369,12 @@ export interface ExtensionContext {
335
369
  readonly commandMenu: ExtensionCommandMenu;
336
370
  /** Adding buttons to the Graph Sidebar. */
337
371
  readonly graphSidebar: ExtensionGraphSidebar;
372
+ /**
373
+ * Adding buttons to the phone's Command Bar.
374
+ *
375
+ * @beta
376
+ */
377
+ readonly commandBar: ExtensionCommandBar;
338
378
  /** Adding panels beneath each Journal Entry. */
339
379
  readonly journalPanels: ExtensionJournalPanels;
340
380
  /**
@@ -357,6 +397,24 @@ export interface ExtensionContext {
357
397
  * @beta
358
398
  */
359
399
  readonly index: ExtensionIndex;
400
+ /**
401
+ * Reading the open graph's documents, with every Protected Document left out, name and all.
402
+ *
403
+ * @beta
404
+ */
405
+ readonly documents: ExtensionDocuments;
406
+ /**
407
+ * The extension's records for the open graph, kept on this device only.
408
+ *
409
+ * @beta
410
+ */
411
+ readonly records: ExtensionGraphRecords;
412
+ /**
413
+ * Images kept as the open graph's assets.
414
+ *
415
+ * @beta
416
+ */
417
+ readonly assets: ExtensionAssets;
360
418
  /** Opening Views and documents. */
361
419
  readonly layout: ExtensionLayout;
362
420
  /**
@@ -402,4 +460,11 @@ export interface ExtensionModule {
402
460
  * called after that, for anything else the extension holds.
403
461
  */
404
462
  activate(context: ExtensionContext): void | (() => void);
463
+ /**
464
+ * Sections the extension adds to the Client's settings tabs, which the Client mounts while the
465
+ * extension is switched on, whether or not a graph is open.
466
+ *
467
+ * @beta
468
+ */
469
+ settingsSections?: readonly SettingsSection[];
405
470
  }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Reading the open graph's documents, as a language model may read them for a person (ADR 0147).
3
+ *
4
+ * Every answer leaves out a [[Protected Document]] entirely: not its content, and not its name
5
+ * either, even while the person holds the Protection Key. That is stricter than the index's
6
+ * answers (`context.index`), which list a Protected Document by name, because what an extension
7
+ * reads here may be sent to a model provider. A document whose text holds an encrypted fence
8
+ * anywhere counts as protected here too.
9
+ *
10
+ * Every answer is bounded: a list is paged, and a document's text is cut at a cap and says so.
11
+ */
12
+ /**
13
+ * A document as a listing names it.
14
+ *
15
+ * @beta
16
+ */
17
+ export interface DocumentSummary {
18
+ /** The document's name: a page's title, or a journal entry's day, `YYYY-MM-DD`. */
19
+ name: string;
20
+ /** A page or a journal entry. */
21
+ kind: 'page' | 'journal';
22
+ /** The page's other names. */
23
+ aliases: string[];
24
+ }
25
+ /**
26
+ * A document's text, as {@link ExtensionDocuments.read} answers.
27
+ *
28
+ * @beta
29
+ */
30
+ export interface DocumentText extends DocumentSummary {
31
+ /** The body: the text after the frontmatter block, cut at the cap. */
32
+ text: string;
33
+ /** True when the text was cut at the cap. */
34
+ truncated: boolean;
35
+ }
36
+ /**
37
+ * One document a search matched, with the lines that matched.
38
+ *
39
+ * @beta
40
+ */
41
+ export interface DocumentSearchResult extends Omit<DocumentSummary, 'aliases'> {
42
+ /** How many lines matched. */
43
+ matches: number;
44
+ /** The matching lines, best first. */
45
+ hits: {
46
+ /** The line's number in the document's text, from 0. */
47
+ line: number;
48
+ /** The labels of the blocks the line sits under, outermost first. */
49
+ breadcrumb: string[];
50
+ /** The line's text around the match. */
51
+ snippet: string;
52
+ }[];
53
+ }
54
+ /**
55
+ * One document whose text links to the concept asked about, with each place it does.
56
+ *
57
+ * @beta
58
+ */
59
+ export interface DocumentBacklink extends Omit<DocumentSummary, 'aliases'> {
60
+ references: {
61
+ /** The line's number in the source's text, from 0. */
62
+ line: number;
63
+ /** The labels of the blocks the reference sits under, outermost first. */
64
+ breadcrumb: string[];
65
+ /** The block holding the link with its children, or the prose around it. */
66
+ text: string;
67
+ }[];
68
+ }
69
+ /**
70
+ * One task, as the Tasks View lists it.
71
+ *
72
+ * @beta
73
+ */
74
+ export interface DocumentTask extends Omit<DocumentSummary, 'aliases'> {
75
+ /** The task's line in the document's text, from 0. */
76
+ line: number;
77
+ /** The task's words, without the checkbox. */
78
+ text: string;
79
+ status: 'open' | 'doing' | 'waiting' | 'done' | 'cancelled';
80
+ /** 1 is the highest; null when the task has none. */
81
+ priority: 1 | 2 | 3 | null;
82
+ /** `YYYY-MM-DD`, or null. */
83
+ due: string | null;
84
+ /** `YYYY-MM-DD`, or null. */
85
+ scheduled: string | null;
86
+ /** The labels of the blocks the task sits under, outermost first. */
87
+ breadcrumb: string[];
88
+ }
89
+ /**
90
+ * Reading the open graph's documents.
91
+ *
92
+ * @beta
93
+ */
94
+ export interface ExtensionDocuments {
95
+ /** Pages and journal entries, journals newest first and then pages by name, a page at a time. */
96
+ list(options?: {
97
+ kind?: 'page' | 'journal';
98
+ offset?: number;
99
+ limit?: number;
100
+ }): Promise<{
101
+ documents: DocumentSummary[];
102
+ total: number;
103
+ hasMore: boolean;
104
+ }>;
105
+ /**
106
+ * A document's text by its name or an alias, or null when there is no such document or it is
107
+ * protected. The text is the document as it is now, edits not yet saved included.
108
+ */
109
+ read(name: string): Promise<DocumentText | null>;
110
+ /**
111
+ * The names of the documents a document's wikilinks name directly, in the order they first
112
+ * appear, each once. Names with no document, and protected documents, are left out. Null when
113
+ * the document cannot be read.
114
+ */
115
+ links(name: string): Promise<string[] | null>;
116
+ /** The documents whose text matches the words, best first, a page at a time. */
117
+ search(query: string, options?: {
118
+ offset?: number;
119
+ limit?: number;
120
+ }): Promise<{
121
+ results: DocumentSearchResult[];
122
+ total: number;
123
+ hasMore: boolean;
124
+ }>;
125
+ /** The documents that link to a concept, or null when the concept is a protected document. */
126
+ backlinks(name: string): Promise<DocumentBacklink[] | null>;
127
+ /**
128
+ * How the graph's documents are written, as the Client describes it to agents: names and
129
+ * wikilinks, the outline, tasks and the rest of the syntax, in markdown. The same words the
130
+ * `AGENTS.md` of a graph folder carries, less what is about files on a disk, so a model reading
131
+ * the graph's text understands it and names documents as `[[Name]]`.
132
+ */
133
+ formatGuide(): string;
134
+ /** Tasks, open ones unless asked otherwise, of one concept or of the whole graph, a page at a time. */
135
+ tasks(options?: {
136
+ /** Only the tasks that answer to this concept. */
137
+ concept?: string;
138
+ statuses?: DocumentTask['status'][];
139
+ offset?: number;
140
+ limit?: number;
141
+ }): Promise<{
142
+ tasks: DocumentTask[];
143
+ total: number;
144
+ hasMore: boolean;
145
+ }>;
146
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Reading the open graph's documents, as a language model may read them for a person (ADR 0147).
3
+ *
4
+ * Every answer leaves out a [[Protected Document]] entirely: not its content, and not its name
5
+ * either, even while the person holds the Protection Key. That is stricter than the index's
6
+ * answers (`context.index`), which list a Protected Document by name, because what an extension
7
+ * reads here may be sent to a model provider. A document whose text holds an encrypted fence
8
+ * anywhere counts as protected here too.
9
+ *
10
+ * Every answer is bounded: a list is paged, and a document's text is cut at a cap and says so.
11
+ */
12
+ export {};
package/dist/editor.d.ts CHANGED
@@ -236,6 +236,72 @@ export interface FenceOptions {
236
236
  */
237
237
  keepsHistory?: boolean;
238
238
  }
239
+ /**
240
+ * A panel an extension draws in a document, beneath one of its lines, which is not part of the
241
+ * document's text: nothing of it is saved, synced or seen on another device (ADR 0147).
242
+ *
243
+ * The panel follows its line as the document changes, here or on another device, and moves with
244
+ * the editor when the editor is rebuilt (a Draft becoming a document, say). It closes when the
245
+ * person closes it, or when the document's editor closes for good: the panel lasts for the visit.
246
+ *
247
+ * @beta
248
+ */
249
+ export interface InlinePanel {
250
+ /** The document the panel is in, by its concept. */
251
+ readonly document: string | null;
252
+ /** False once the panel has closed. */
253
+ readonly isOpen: boolean;
254
+ /**
255
+ * Put a block at the panel's line, by the rule {@link ExtensionEditor.insertBlock} follows at the
256
+ * caret: an empty line is replaced, an empty bullet takes an outline's place at its own
257
+ * indentation (or any other block as its content), and after any other line the block goes on new
258
+ * lines beneath at the line's content column. The panel then sits beneath the block, and the next
259
+ * block goes after it at the indentation of its first line. One undoable change. False where
260
+ * nothing was put in: the panel is closed, its editor has closed, or its line is now in the
261
+ * frontmatter, a fenced block or a table.
262
+ */
263
+ insertBlock(text: string): boolean;
264
+ /** Bring the panel into view. */
265
+ focus(): void;
266
+ /**
267
+ * Give the panel a new height in CSS pixels, which its content scrolls inside: the lines below
268
+ * it move once. Grow it when there is more to show, rather than on every change.
269
+ */
270
+ resize(height: number): void;
271
+ /**
272
+ * The outline around the panel's line, as the document writes it: the line's ancestors, each its
273
+ * own lines without its other children, and its siblings with their branches, the line's own
274
+ * branch among them while the line has text. A block the panel put in is left out. Null when
275
+ * the line is not a bullet, or the panel is closed or under the document's last line.
276
+ */
277
+ outline(): string | null;
278
+ /** Close the panel. Its `onClose` is told. */
279
+ close(): void;
280
+ }
281
+ /**
282
+ * What an inline panel is drawn with.
283
+ *
284
+ * @beta
285
+ */
286
+ export interface InlinePanelOptions {
287
+ /**
288
+ * The panel's height in CSS pixels, which its content scrolls inside, so the lines below never
289
+ * move as its content grows. {@link InlinePanel.resize} changes it.
290
+ */
291
+ height?: number;
292
+ /**
293
+ * Draw the panel into `host`, which the Client owns and places. Called once, when the panel is
294
+ * first drawn: the host keeps its content while the panel is scrolled out of view and when the
295
+ * editor is rebuilt, and `destroy` is called when the panel closes. Events inside the host are
296
+ * the panel's own: the editor neither types nor moves its caret for them, but Mod+Z and Mod+Y
297
+ * outside a text field undo and redo in the document.
298
+ */
299
+ mount(host: HTMLElement, panel: InlinePanel): {
300
+ destroy(): void;
301
+ };
302
+ /** Told once, when the panel closes for any reason. */
303
+ onClose?(): void;
304
+ }
239
305
  /**
240
306
  * The document editor, as far as an extension may reach it.
241
307
  *
@@ -263,4 +329,18 @@ export interface ExtensionEditor {
263
329
  insertBlock(text: string, options?: {
264
330
  focus?: boolean;
265
331
  }): boolean;
332
+ /**
333
+ * Draw a panel beneath the caret's line in the editor the person is working in, from where the
334
+ * line's text starts, or beside the dot of an empty bullet, as a code block opened there is. With
335
+ * `at` `end`, it is drawn beneath the document's last line, at the editor's left edge, a line's
336
+ * height clear of it. With `caret-or-end`, at the caret while the person is typing in the
337
+ * document (the editor has the keyboard, as a Command Bar button leaves it), else at the end: for
338
+ * a button pressed from outside the text. Null where no panel can go: no editor, a Protected
339
+ * Document, or a caret in the frontmatter, a fenced block or a table.
340
+ *
341
+ * @beta
342
+ */
343
+ openPanel(options: InlinePanelOptions & {
344
+ at?: 'caret' | 'end' | 'caret-or-end';
345
+ }): InlinePanel | null;
266
346
  }
package/dist/index.d.ts CHANGED
@@ -14,15 +14,19 @@
14
14
  *
15
15
  * @packageDocumentation
16
16
  */
17
- export type { CommandHandler, ExtensionCommandMenu, ExtensionCommands, ExtensionConcepts, ExtensionContext, ExtensionContextMenu, ExtensionContributions, ExtensionGraphSidebar, ExtensionIcons, ExtensionKeybindings, ExtensionLayout, ExtensionModule, ExtensionPersonStorage, ExtensionSettings, ExtensionStorage, ExtensionTheme, ExtensionViews, IconMarkup, Keybinding, } from './context';
17
+ export type { CommandHandler, ExtensionCommandBar, ExtensionCommandMenu, ExtensionCommands, ExtensionConcepts, ExtensionContext, ExtensionContextMenu, ExtensionContributions, ExtensionGraphSidebar, ExtensionIcons, ExtensionKeybindings, ExtensionLayout, ExtensionModule, ExtensionPersonStorage, ExtensionSettings, ExtensionStorage, ExtensionTheme, ExtensionViews, IconMarkup, Keybinding, } from './context';
18
+ export type { ExtensionAssets } from './assets';
18
19
  export type { AuthorizeOptions, ExtensionAuth } from './auth';
19
20
  export type { ExtensionDialogs, PromptOptions } from './dialogs';
20
- export type { ExtensionEditor, FenceContext, FenceEdit, FenceInfo, FenceOptions, FencePresentation, FenceWidget, MountedFence, PublishedFence, PublishFenceRequest, QuoteFenceRequest, } from './editor';
21
+ export type { DocumentBacklink, DocumentSearchResult, DocumentSummary, DocumentTask, DocumentText, ExtensionDocuments } from './documents';
22
+ export type { ExtensionEditor, FenceContext, FenceEdit, FenceInfo, FenceOptions, FencePresentation, FenceWidget, InlinePanel, InlinePanelOptions, MountedFence, PublishedFence, PublishFenceRequest, QuoteFenceRequest, } from './editor';
21
23
  export type { ExtensionEmbeds, FrameOptions } from './embeds';
22
24
  export type { ExtensionEventName, ExtensionEventPayloads, ExtensionEvents } from './events';
23
25
  export type { ConceptCandidate, ConceptKind, ExtensionIndex, IndexUpdate, LinkGraph, LinkGraphConcept, LinkGraphLink } from './graph-index';
24
26
  export type { ExtensionJournalPanels, JournalPanel, JournalPanelProps, MountedJournalPanel } from './journal';
25
27
  export type { ExtensionIsolation, ExtensionManifest, ExtensionPackage, ReadExtensionPackageResult, SettingDeclaration, ViewDeclaration, ViewRegion, } from './manifest';
26
28
  export { readExtensionPackage } from './manifest';
27
- export type { CommandMenuContext, CommandMenuItem, ContextMenuItem, DocumentMenuTarget, ExtensionMenuTarget, GraphSidebarButton, MenuTarget, TabMenuTarget, WikilinkMenuTarget, } from './menus';
29
+ export type { CommandBarButton, CommandMenuContext, CommandMenuItem, ContextMenuItem, DocumentMenuTarget, ExtensionMenuTarget, GraphSidebarButton, MenuTarget, TabMenuTarget, WikilinkMenuTarget, } from './menus';
30
+ export type { ExtensionGraphRecords } from './records';
31
+ export type { MountedSettingsSection, SettingsSection, SettingsSectionContext, SettingsSectionTab } from './settings-sections';
28
32
  export type { MountedView, ViewContribution, ViewMountProps, ViewRef, ViewVisibility } from './views';
package/dist/menus.d.ts CHANGED
@@ -149,6 +149,30 @@ export interface CommandMenuItem {
149
149
  */
150
150
  args?: unknown;
151
151
  }
152
+ /**
153
+ * A button on the Command Bar, the row of buttons across the bottom of a phone's screen. Pressing
154
+ * it runs `command` and leaves the keyboard where it was, so a Command can tell whether the caret
155
+ * is in the document.
156
+ *
157
+ * @beta
158
+ */
159
+ export interface CommandBarButton {
160
+ /** Under the extension's id. */
161
+ id: string;
162
+ /** The button's name, said to a screen reader and shown as its tooltip: the bar shows icons only. */
163
+ title: string;
164
+ /** Its icon: the extension's own or one of the Client's. */
165
+ icon: string;
166
+ /**
167
+ * In the bar's pinned group at its right end, which never scrolls away, rather than after the
168
+ * editing buttons that scroll. For an action a person reaches for often.
169
+ */
170
+ pinned?: boolean;
171
+ /** The Command the button runs. */
172
+ command: string;
173
+ /** What the Command is given. */
174
+ args?: unknown;
175
+ }
152
176
  /**
153
177
  * A button in the Graph Sidebar's group of action buttons, after Search and today's date: a way in
154
178
  * to what the extension shows for the whole graph, such as the Graph Map. Pressing it runs
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Records an extension keeps on this device for the open graph alone (ADR 0147): never synced,
3
+ * never seen by another device or another member, and deleted when the graph's data is removed
4
+ * from the device. For what is the person's work on this device, such as a chat they have not
5
+ * saved, rather than a preference that should follow them (`personStorage`).
6
+ */
7
+ /**
8
+ * The extension's records for the open graph on this device.
9
+ *
10
+ * Each record is stored as the browser's structured clone of the value, so it may hold a `Blob`,
11
+ * an `ArrayBuffer`, a `Date` or plain data, but no function. Records live in the browser's
12
+ * IndexedDB, which a private window or a full disk may refuse: a refused write rejects, and a
13
+ * refused read answers as if there were nothing.
14
+ *
15
+ * @beta
16
+ */
17
+ export interface ExtensionGraphRecords {
18
+ /** The record under `key`, or undefined when there is none. */
19
+ get<T>(key: string): Promise<T | undefined>;
20
+ /**
21
+ * Keep a record under `key`: letters, digits, `.`, `_`, `:` and `-`, at most 200 of them.
22
+ * Rejects when the key is not one of those or the browser refuses the write.
23
+ */
24
+ set(key: string, value: unknown): Promise<void>;
25
+ /** Remove the record under `key`. */
26
+ remove(key: string): Promise<void>;
27
+ /** The keys of every record the extension keeps for the open graph, sorted. */
28
+ keys(): Promise<string[]>;
29
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Records an extension keeps on this device for the open graph alone (ADR 0147): never synced,
3
+ * never seen by another device or another member, and deleted when the graph's data is removed
4
+ * from the device. For what is the person's work on this device, such as a chat they have not
5
+ * saved, rather than a preference that should follow them (`personStorage`).
6
+ */
7
+ export {};
@@ -0,0 +1,56 @@
1
+ /**
2
+ * An extension's own section in one of the Client's settings tabs (ADR 0134, amended 2026-10-15),
3
+ * which the Client mounts with or without a graph open: on the Graphs page, and in a graph's
4
+ * settings. For settings that belong to the person and need more than the fields a manifest can
5
+ * declare, such as a list of accounts with a button that connects one.
6
+ */
7
+ import type { ExtensionAuth } from './auth';
8
+ import type { ExtensionPersonStorage, ExtensionSettings, ExtensionTheme } from './context';
9
+ /**
10
+ * The tabs an extension's settings section can go in.
11
+ *
12
+ * @beta
13
+ */
14
+ export type SettingsSectionTab = 'agents';
15
+ /**
16
+ * What a settings section is mounted with: what belongs to the person, and nothing from a graph,
17
+ * since there may be no graph open.
18
+ *
19
+ * @beta
20
+ */
21
+ export interface SettingsSectionContext {
22
+ /** The extension's id, from its manifest. */
23
+ readonly extensionId: string;
24
+ /** The person's answers to the settings the manifest declares. */
25
+ readonly settings: ExtensionSettings;
26
+ /** The extension's records kept with the person. */
27
+ readonly personStorage: ExtensionPersonStorage;
28
+ /** Signing in to another service. */
29
+ readonly auth: ExtensionAuth;
30
+ /** The Client's light or dark theme. */
31
+ readonly theme: ExtensionTheme;
32
+ /** Say something in passing, as the Client's own notices do. */
33
+ notify(text: string): void;
34
+ }
35
+ /**
36
+ * A section the section's host element shows, as the Client drives it.
37
+ *
38
+ * @beta
39
+ */
40
+ export interface MountedSettingsSection {
41
+ /** The tab closed, or the extension was switched off. */
42
+ destroy(): void;
43
+ }
44
+ /**
45
+ * One settings section an extension adds.
46
+ *
47
+ * @beta
48
+ */
49
+ export interface SettingsSection {
50
+ /** Unique within the extension. */
51
+ id: string;
52
+ /** The tab it goes in. */
53
+ tab: SettingsSectionTab;
54
+ /** Draw the section into `element`, which is empty and is the extension's alone. */
55
+ mount(element: HTMLElement, context: SettingsSectionContext): MountedSettingsSection;
56
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appsoftwareltd/etherpk-extension-api",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "license": "MIT",
5
5
  "description": "The API an EtherPK extension is written against: the context it is started with, its manifest, and the contract its Views follow.",
6
6
  "type": "module",