pipeup 0.5.4 → 0.7.0-beta.1

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.
Files changed (42) hide show
  1. package/README.md +3 -3
  2. package/cli/link.d.mts +17 -0
  3. package/cli/link.mjs +207 -0
  4. package/cli/pipeup.mjs +226 -0
  5. package/cli/skills.mjs +154 -0
  6. package/dist/pipeup.agents.js +2 -0
  7. package/dist/pipeup.core.js +1 -1
  8. package/dist/pipeup.esm.js +1 -1
  9. package/dist/pipeup.min.js +1 -1
  10. package/dist/types/core.d.ts +3 -1
  11. package/dist/types/crypto/portable.d.ts +15 -0
  12. package/dist/types/document.d.ts +20 -4
  13. package/dist/types/export/agent.d.ts +35 -0
  14. package/dist/types/export/format.d.ts +5 -1
  15. package/dist/types/index.d.ts +5 -2
  16. package/dist/types/model/animals.d.ts +7 -1
  17. package/dist/types/model/fold.d.ts +2 -0
  18. package/dist/types/model/ops.d.ts +5 -0
  19. package/dist/types/model/types.d.ts +35 -2
  20. package/dist/types/ui/addons.d.ts +22 -1
  21. package/dist/types/ui/animals.d.ts +1 -1
  22. package/dist/types/ui/app.d.ts +6 -0
  23. package/dist/types/ui/composer.d.ts +6 -1
  24. package/dist/types/ui/context.d.ts +16 -2
  25. package/dist/types/ui/draft-view.d.ts +5 -1
  26. package/dist/types/ui/highlights.d.ts +2 -0
  27. package/dist/types/ui/icons.d.ts +2 -0
  28. package/dist/types/ui/identity.d.ts +27 -0
  29. package/dist/types/ui/launcher.d.ts +0 -5
  30. package/dist/types/ui/legible.d.ts +27 -0
  31. package/dist/types/ui/mentions.d.ts +46 -0
  32. package/dist/types/ui/mount.d.ts +12 -0
  33. package/dist/types/ui/reactions.d.ts +31 -0
  34. package/dist/types/ui/styles.d.ts +1 -1
  35. package/dist/types/ui/theme.d.ts +31 -2
  36. package/dist/types/ui/thread-view.d.ts +14 -2
  37. package/package.json +7 -2
  38. package/skills/pipeup-add/SKILL.md +53 -0
  39. package/skills/pipeup-apply/SKILL.md +77 -0
  40. package/skills/pipeup-integrate/SKILL.md +240 -0
  41. package/skills/pipeup-send-to-git-setup/SKILL.md +107 -0
  42. package/skills/pipeup-summarise/SKILL.md +39 -0
@@ -1,4 +1,6 @@
1
- export declare const VERSION = "0.5.4";
1
+ export declare const VERSION = "0.7.0-beta.1";
2
+ /** The day this version was made (set with the version): Pipeup says so, once, in the console when it is over a year old. */
3
+ export declare const BUILT = "2026-10-13";
2
4
  export { PipeupDocument, UnsavedChangeError, type Listener, type OpenOptions } from "./document";
3
5
  export { DEFAULT_PROFILE_NAME, MemoryStore, loadOrCreateProfile, type OpStore, type Profile, } from "./storage/store";
4
6
  export { IndexedDbStore } from "./storage/indexeddb";
@@ -0,0 +1,15 @@
1
+ import type { Identity } from "./identity";
2
+ /** A new identity the reviewer can back up, and its recovery code. */
3
+ export declare function makePortable(): Promise<{
4
+ identity: Identity;
5
+ code: string;
6
+ }>;
7
+ /** The recovery code of an identity that was made portable; null for an ordinary one, which can't be exported. */
8
+ export declare function codeOf(identity: Identity): Promise<string | null>;
9
+ export type CodeProblem = "shape" | "mistake";
10
+ /** The identity a recovery code stands for, or what is wrong with the code, in a word the UI turns into a sentence. */
11
+ export declare function identityFromCode(text: string): Promise<{
12
+ identity: Identity;
13
+ } | {
14
+ problem: CodeProblem;
15
+ }>;
@@ -1,5 +1,5 @@
1
1
  import { type Identity } from "./crypto/identity";
2
- import type { Anchor, SignedOp, Thread } from "./model/types";
2
+ import type { Anchor, Mention, SignedOp, Thread } from "./model/types";
3
3
  import type { OpStore } from "./storage/store";
4
4
  export interface OpenOptions {
5
5
  doc: string;
@@ -42,10 +42,26 @@ export declare class PipeupDocument {
42
42
  /** Every verified op this browser holds, as {body, sig}. A new array each call; don't change the ops. */
43
43
  ops(): readonly SignedOp[];
44
44
  onChange(fn: Listener): () => void;
45
- comment(anchor: Anchor, text: string): Promise<string>;
46
- reply(parentId: string, text: string): Promise<string>;
47
- edit(commentId: string, text: string): Promise<void>;
45
+ comment(anchor: Anchor, text: string, mentions?: Mention[]): Promise<string>;
46
+ reply(parentId: string, text: string, mentions?: Mention[]): Promise<string>;
47
+ edit(commentId: string, text: string, mentions?: Mention[]): Promise<void>;
48
48
  remove(commentId: string): Promise<void>;
49
+ /** Shares a way to reach this reviewer (an email address, or `gh:<GitHub name>`) with everyone who has the comments. */
50
+ contact(value: string): Promise<void>;
51
+ /** Stops sharing it: people who learn of this later won't see it. */
52
+ uncontact(): Promise<void>;
53
+ /** The ways people have chosen to be reached, by writer: the latest each shared, unless they stopped sharing. */
54
+ contacts(): Map<string, string>;
55
+ /** This reviewer's identity was made portable, so it has a recovery code (an ordinary one can't leave the browser). */
56
+ get portable(): boolean;
57
+ /** The recovery code of a portable identity; null for an ordinary one. */
58
+ identityCode(): Promise<string | null>;
59
+ /** Marks comments read by this reviewer (one `seen` op each, for those not already marked), so every device knows. */
60
+ seen(commentIds: readonly string[]): Promise<void>;
61
+ /** Reacts to a comment with one emoji; reacting twice with the same emoji changes nothing. */
62
+ react(commentId: string, emoji: string): Promise<void>;
63
+ /** Takes back this reviewer's own reaction. */
64
+ unreact(commentId: string, emoji: string): Promise<void>;
49
65
  resolve(threadId: string): Promise<void>;
50
66
  reopen(threadId: string): Promise<void>;
51
67
  /**
@@ -0,0 +1,35 @@
1
+ import { type ExportItem, type ExportMeta } from "./format";
2
+ /** One thread as an agent reads it. */
3
+ export interface AgentThread {
4
+ /** The id to answer a thread by. */
5
+ id: string;
6
+ state: "open" | "addressed" | "resolved";
7
+ where: string;
8
+ /** The page's `data-pipeup-id` for the block, when it has one. */
9
+ element?: string;
10
+ quote?: string;
11
+ pin?: string;
12
+ addressed?: {
13
+ note: string;
14
+ by: string;
15
+ };
16
+ comments: {
17
+ id: string;
18
+ by: string;
19
+ agent: boolean;
20
+ at: string;
21
+ text: string;
22
+ /** Who reacted with what; absent when nobody did. */
23
+ reactions?: {
24
+ emoji: string;
25
+ by: string[];
26
+ }[];
27
+ }[];
28
+ }
29
+ export interface AgentComments {
30
+ /** The same Markdown as Copy as Markdown. */
31
+ markdown: string;
32
+ threads: AgentThread[];
33
+ }
34
+ /** The page's comments as an agent reads them: the Markdown, and the threads as data. */
35
+ export declare function agentComments(items: ExportItem[], meta: ExportMeta): AgentComments;
@@ -1,5 +1,5 @@
1
1
  import type { Location } from "../anchor/locate";
2
- import type { Thread } from "../model/types";
2
+ import type { Comment, Thread } from "../model/types";
3
3
  export type CopyAs = "ai" | "text";
4
4
  export interface ExportItem {
5
5
  thread: Thread;
@@ -9,9 +9,13 @@ export interface ExportMeta {
9
9
  title: string;
10
10
  url: string;
11
11
  exportedAt: Date;
12
+ /** Also the resolved threads (default: open ones only). */
13
+ all?: boolean;
12
14
  }
13
15
  export declare function formatAgo(at: number, now: number): string;
14
16
  /** Copy one thread — always the whole thread. */
15
17
  export declare function copyThread(item: ExportItem, as: CopyAs, page: string, now: number): string;
16
18
  /** Copy every open thread, in the order given (page or slide order). */
17
19
  export declare function copyAll(items: ExportItem[], as: CopyAs, meta: ExportMeta): string;
20
+ /** A comment's reactions as one line: "👍 2 (Sam, Red Fox) · ❤️ 1 (Lee)"; empty when nobody reacted. */
21
+ export declare function reactionsLine(c: Comment): string;
@@ -1,4 +1,7 @@
1
1
  export * from "./core";
2
- export { mount, type MountOptions, type PipeupInstance } from "./ui/mount";
3
- export { ADDON_API, addons, use, type AddonDocument, type AddonHost, type AddonInfo, type Capability, type ComposerHandle, type ComposerTool, type Dictation, type ItemHandle, type MenuItem, type NetworkUse, type Off, type PanelHandle, type PanelOptions, type PipeupAddon, type Status, type Teardown, type UiSnapshot, } from "./ui/addons";
2
+ export { comments, mount, type MountOptions, type PipeupInstance } from "./ui/mount";
3
+ export type { Appearance } from "./ui/legible";
4
+ export { setPeople, type Person } from "./ui/mentions";
5
+ export { ADDON_API, addons, use, type AddonDocument, type AddonHost, type AddonInfo, type Capability, type ComposerHandle, type ComposerTool, type Dictation, type ItemHandle, type ListNote, type ReactionPicker, type MenuItem, type NetworkUse, type Off, type PanelHandle, type PanelOptions, type PipeupAddon, type Status, type Teardown, type UiSnapshot, } from "./ui/addons";
6
+ export { type AgentComments, type AgentThread } from "./export/agent";
4
7
  export { onReveal, setViewState, type SlideHook, type ViewState } from "./ui/here";
@@ -13,10 +13,16 @@ export declare const animalOf: (author: string) => number;
13
13
  export declare const colourOf: (author: string) => number;
14
14
  /** A reviewer's animal in its colour, such as "Red Fox". */
15
15
  export declare const animalName: (author: string) => string;
16
- /** What to call a comment's writer: their name, or their animal until they add one. */
16
+ /** A short, stable tag from a reviewer's key ("4F2A"), shown beside a name only where two people share it. */
17
+ export declare function shortId(author: string): string;
18
+ /**
19
+ * What to call a comment's writer: their name, or their animal until they add one; with their short tag when another
20
+ * person in the document has the same name. An agent shows as "Claude", not "AI assistant · Claude".
21
+ */
17
22
  export declare const nameOf: (c: {
18
23
  author: string;
19
24
  name: string;
25
+ alike?: boolean;
20
26
  }) => string;
21
27
  /** The name an assistant's replies carry: a person can't choose it, and a reply under it is drawn as an AI's. */
22
28
  export declare const AI_NAME = "AI assistant";
@@ -1,4 +1,6 @@
1
1
  import type { SignedOp, Thread } from "./types";
2
+ /** At most this many different emoji on one comment, so a comment can't be flooded; later ones are ignored. */
3
+ export declare const MAX_REACTIONS = 12;
2
4
  /**
3
5
  * Turns ops into threads. Deterministic: peers holding the same ops see the same threads,
4
6
  * whatever order the ops arrived in. Assumes ops are already well formed and authentic.
@@ -18,7 +18,12 @@ export declare const LIMITS: {
18
18
  readonly viewEntries: 16;
19
19
  readonly viewKey: 64;
20
20
  readonly viewValue: 200;
21
+ readonly mentions: 20;
21
22
  };
23
+ /** Who a mention names: a writer's key, or a person the page or an add-on supplied (`host:<id>`, `gh:<name>`). */
24
+ /** A way to reach someone that they chose to share: an email address, or a GitHub name (`gh:octocat`). */
25
+ export declare const CONTACT: RegExp;
26
+ export declare const isEmoji: (s: unknown) => s is string;
22
27
  export declare function signOp(body: OpBody, identity: Identity): Promise<SignedOp>;
23
28
  export declare function isAnchor(a: unknown): boolean;
24
29
  /** Shape checks that need nothing but the op itself. */
@@ -23,7 +23,13 @@ export interface Anchor {
23
23
  /** A short plain-text excerpt of what the reviewer saw. */
24
24
  snapshot: string;
25
25
  }
26
- export type OpKind = "create" | "reply" | "edit" | "delete" | "resolve" | "reopen";
26
+ export type OpKind = "create" | "reply" | "edit" | "delete" | "resolve" | "reopen" | "addressed" | "react" | "unreact" | "seen" | "contact" | "uncontact";
27
+ /** A person named in a comment's words: `text.slice(start, end)` is the "@Name"; `ref` says who (a key, or a supplied person). */
28
+ export interface Mention {
29
+ start: number;
30
+ end: number;
31
+ ref: string;
32
+ }
27
33
  export interface OpBody {
28
34
  v: 1;
29
35
  id: string;
@@ -31,9 +37,12 @@ export interface OpBody {
31
37
  doc: string;
32
38
  /** Thread id: the id of the thread's create op. */
33
39
  thread: string;
34
- /** reply: the comment answered. edit/delete: the comment changed. */
40
+ /** reply: the comment answered. edit/delete: the comment changed. react/unreact: the comment reacted to. */
35
41
  target?: string;
42
+ /** The words; for react and unreact, the one emoji. */
36
43
  text?: string;
44
+ /** create, reply and edit: the people named in the words. Signed with them, so it can't be altered. */
45
+ mentions?: Mention[];
37
46
  /** create only. */
38
47
  anchor?: Anchor;
39
48
  /** Author's public key. */
@@ -61,10 +70,34 @@ export interface Comment {
61
70
  edited: boolean;
62
71
  deleted: boolean;
63
72
  replies: Comment[];
73
+ /** Emoji people reacted with, in the order they first appeared; none for a comment nobody reacted to. */
74
+ reactions: Reaction[];
75
+ /** Another person in the document has the same name: show this writer's short tag too. */
76
+ alike?: true;
77
+ /** The people named in the words; absent when none. */
78
+ mentions?: Mention[];
79
+ /** The writers who have marked this comment seen (their `seen` ops); absent when none. */
80
+ seenBy?: string[];
81
+ }
82
+ export interface Reaction {
83
+ emoji: string;
84
+ /** Who gave it, in the order they did; the name is "" for someone who hasn't added one. */
85
+ by: {
86
+ author: string;
87
+ name: string;
88
+ alike?: true;
89
+ }[];
64
90
  }
65
91
  export interface Thread {
66
92
  id: string;
67
93
  anchor: Anchor;
68
94
  root: Comment;
69
95
  resolved: boolean;
96
+ /** An agent has said it dealt with this thread. Not resolved: a person closes it, and reopening clears it. */
97
+ addressed?: {
98
+ text: string;
99
+ author: string;
100
+ name: string;
101
+ at: number;
102
+ };
70
103
  }
@@ -7,7 +7,7 @@ import type { ViewState } from "./here";
7
7
  export declare const ADDON_API = 1;
8
8
  export type Teardown = () => void;
9
9
  export type Off = () => void;
10
- export type Capability = "sync" | "menu" | "notify" | "note" | "composer" | "panel" | "styles" | "status" | "overlay" | "here" | "sign";
10
+ export type Capability = "sync" | "menu" | "notify" | "note" | "composer" | "panel" | "styles" | "status" | "overlay" | "here" | "sign" | "listnote" | "reactions";
11
11
  export interface NetworkUse {
12
12
  when: "never" | "after-consent" | "page-configured";
13
13
  /** Hosts; "page" is an address the page names. */
@@ -47,6 +47,8 @@ export interface AddonDocument {
47
47
  readonly name: string;
48
48
  threads(): readonly Thread[];
49
49
  ops(): readonly SignedOp[];
50
+ /** The ways people chose to be reached (an email address, or `gh:<name>`), by writer's key. */
51
+ contacts(): ReadonlyMap<string, string>;
50
52
  onChange(fn: Listener): Off;
51
53
  }
52
54
  export interface MenuItem {
@@ -83,6 +85,14 @@ export interface Status {
83
85
  view?: ViewState;
84
86
  }[];
85
87
  }
88
+ /** A quiet line under the All comments header: words, and optionally one text button ("Check for replies"). */
89
+ export interface ListNote {
90
+ text: string;
91
+ action?: {
92
+ label: string;
93
+ run(): Promise<void>;
94
+ };
95
+ }
86
96
  export interface UiSnapshot {
87
97
  /** Comments are showing. */
88
98
  shown: boolean;
@@ -94,6 +104,8 @@ export interface UiSnapshot {
94
104
  /** null: not writing; "": the new-comment box; else that thread's reply line. */
95
105
  writing: string | null;
96
106
  }
107
+ import type { ReactionPicker } from "./reactions";
108
+ export type { ReactionPicker } from "./reactions";
97
109
  export type { ComposerHandle, ComposerTool, Dictation } from "./composer";
98
110
  export interface AddonHost {
99
111
  readonly api: number;
@@ -126,6 +138,10 @@ export interface AddonHost {
126
138
  openPanel(content: Node, options: PanelOptions): PanelHandle;
127
139
  addStyles(css: string): Off;
128
140
  setStatus(status: Status | null): void;
141
+ /** Shows (or, with null, removes) this add-on's line under the All comments header. */
142
+ setListNote(note: ListNote | null): void;
143
+ /** Offers the full set of emoji for reactions: the quick row then ends with a button that opens it. */
144
+ setReactionPicker(picker: ReactionPicker | null): void;
129
145
  overlay(): HTMLElement;
130
146
  onFrame(fn: () => void): Off;
131
147
  /** What a comment on `at` would save. */
@@ -156,6 +172,10 @@ export declare class Surface {
156
172
  private readonly marked;
157
173
  private paint;
158
174
  readonly statuses: Map<string, Status>;
175
+ readonly listNotes: Map<string, ListNote>;
176
+ /** The emoji picker an add-on brought, if any (the latest to set one). */
177
+ picker: ReactionPicker | null;
178
+ private pickerOwner;
159
179
  private readonly toolList;
160
180
  private readonly frames;
161
181
  private readonly uis;
@@ -182,6 +202,7 @@ export declare class Surface {
182
202
  private repaint;
183
203
  /** Sets (or, with null, removes) one add-on's note or status. */
184
204
  setIn<T>(map: Map<string, T>, id: string, value: T | null): void;
205
+ setPicker(id: string, picker: ReactionPicker | null): void;
185
206
  addTool(id: string, tool: ComposerTool): Off;
186
207
  /** Comments are closed: only the control shows, so a notice waits for the reviewer to open Pipeup. */
187
208
  private closed;
@@ -11,7 +11,7 @@ export declare const faceOf: (author: string, name: string) => string;
11
11
  * A squircle avatar: the writer's animal drawn in its colour on a tint of it, or their initial in the same colours once they have a
12
12
  * name. Decorative: the name is always written beside it, so screen readers skip it.
13
13
  */
14
- export declare function avatar(author: string, name: string, busy?: boolean): HTMLElement;
14
+ export declare function avatar(author: string, name: string): HTMLElement;
15
15
  /** Removes an avatar that a new one is easing in over, once it is covered; never leaves its spot empty. */
16
16
  export declare function retire(el: HTMLElement, done?: () => void): void;
17
17
  /** Eases a new avatar in once it is on the page (it starts faded and slightly small; .in brings it to rest). */
@@ -1,12 +1,18 @@
1
1
  import { type PipeupDocument } from "../document";
2
+ import type { Identity } from "../crypto/identity";
2
3
  import type { Ctx, UiState, View } from "./context";
4
+ import { type Appearance } from "./legible";
3
5
  export interface AppOptions {
4
6
  doc: PipeupDocument;
5
7
  root: Element;
6
8
  onName(name: string): Promise<void>;
9
+ /** Keeps another identity for this browser (a new portable one, or one restored from a recovery code). */
10
+ onIdentity(identity: Identity): Promise<void>;
7
11
  }
8
12
  export interface App {
9
13
  readonly ctx: Ctx;
14
+ /** How Pipeup looks and why: its look, who chose it, and what the page's styles do to it. */
15
+ appearance(): Appearance;
10
16
  destroy(): void;
11
17
  }
12
18
  /** Documents with free space beside them get a column; everything else gets bubbles and popovers. */
@@ -1,3 +1,5 @@
1
+ import { type Person } from "./mentions";
2
+ import type { Mention } from "../model/types";
1
3
  export interface ComposerOptions {
2
4
  label: string;
3
5
  /** A new comment, or a reply in `thread()`. */
@@ -5,8 +7,11 @@ export interface ComposerOptions {
5
7
  thread?: () => string;
6
8
  /** Something to show before the line instead, such as the writer's avatar. */
7
9
  before?: Node;
8
- onSend(text: string): Promise<void> | void;
10
+ /** `mentions`: the people named in the words, found after trimming. */
11
+ onSend(text: string, mentions: Mention[]): Promise<void> | void;
9
12
  onCancel?(): void;
13
+ /** Who can be mentioned with `@`; without it, `@` is just a character. */
14
+ people?(): Person[];
10
15
  }
11
16
  export interface Composer {
12
17
  element: HTMLElement;
@@ -1,10 +1,11 @@
1
1
  import type { Resolved } from "../anchor/resolve";
2
2
  import type { PipeupDocument } from "../document";
3
3
  import type { CopyAs, ExportItem } from "../export/format";
4
- import type { Anchor, Thread } from "../model/types";
4
+ import type { Anchor, Mention, Thread } from "../model/types";
5
5
  import type { Surface } from "./addons";
6
6
  import type { DraftBox } from "./draft-view";
7
7
  import type { Here } from "./here";
8
+ import type { Person } from "./mentions";
8
9
  import type { ThreadActions } from "./thread-view";
9
10
  /** A comment being written: what it's on, and where that is now. */
10
11
  export interface Draft {
@@ -19,6 +20,8 @@ export interface UiState {
19
20
  active: string | null;
20
21
  hot: string | null;
21
22
  showResolved: boolean;
23
+ /** All comments shows only the threads that name this reviewer. */
24
+ mine: boolean;
22
25
  /** Comments are out of sight: comment mode is off and All comments is closed. */
23
26
  hidden: boolean;
24
27
  mode: "column" | "bubbles";
@@ -41,12 +44,19 @@ export interface View {
41
44
  export interface MenuActions {
42
45
  copyAll(as: CopyAs): Promise<void>;
43
46
  toggleResolved(): void;
47
+ toggleMine(): void;
48
+ /** Opens the dialog for making the identity portable, backing it up, or using one that was backed up. */
49
+ identity(): void;
44
50
  rename(name: string): Promise<void>;
45
51
  /** The reviewer's name; "" until they add one. */
46
52
  name(): string;
47
53
  /** Every thread in page order, with where each one is. */
48
54
  all(): ExportItem[];
55
+ /** The reviewer's choice of look, kept in their browser: Automatic follows the author, then the page. */
56
+ look(): LookChoice;
57
+ setLook(choice: LookChoice): void;
49
58
  }
59
+ export type LookChoice = "auto" | "light" | "dark";
50
60
  /** What every view shares: the document, state, and the few things views can ask the app to do. */
51
61
  export interface Ctx {
52
62
  readonly doc: PipeupDocument;
@@ -94,7 +104,11 @@ export interface Ctx {
94
104
  keepCaret(fn: () => void): void;
95
105
  /** Goes back to the open draft: to its slide or view if that has gone, then focus and the caret into it. */
96
106
  backToDraft(): void;
97
- postDraft(text: string): Promise<void>;
107
+ postDraft(text: string, mentions?: Mention[]): Promise<void>;
108
+ /** This reviewer has read the comments in `t` that name them: marks them seen, so every device knows. */
109
+ seen(t: Thread): void;
110
+ /** Who can be mentioned with `@`: people in this document, agents the page lists, and people supplied. */
111
+ people(): Person[];
98
112
  cancelDraft(): void;
99
113
  /** Turns comment mode on or off; comments show while it is on. `keys`: from the keyboard (the block cursor starts). */
100
114
  setCommenting(on: boolean, keys?: boolean): void;
@@ -1,4 +1,6 @@
1
+ import type { Mention } from "../model/types";
1
2
  import type { Ctx, Draft } from "./context";
3
+ import type { Person } from "./mentions";
2
4
  export interface DraftOptions {
3
5
  /** What the comment is on: a quote or an element label. */
4
6
  label: string;
@@ -8,8 +10,10 @@ export interface DraftOptions {
8
10
  name(): string;
9
11
  /** Saves a new name (the same path as the menu's Your name). */
10
12
  rename(name: string): Promise<void>;
11
- onSend(text: string): Promise<void>;
13
+ onSend(text: string, mentions: Mention[]): Promise<void>;
12
14
  onCancel(): void;
15
+ /** Who can be mentioned with `@`. */
16
+ people?(): Person[];
13
17
  }
14
18
  export interface DraftBox {
15
19
  element: HTMLElement;
@@ -6,6 +6,8 @@ export interface PageSheet {
6
6
  gutter(px: number | null): void;
7
7
  /** How strongly highlights are painted, 0 to 1. */
8
8
  fade(k: number): void;
9
+ /** Paints the highlights for a dark page (true) or a light one. */
10
+ dark(on: boolean): void;
9
11
  /**
10
12
  * Makes room for the All comments panel: a right margin of `px` on the page, easing in and out unless
11
13
  * `still`; 0 takes it away (the page eases back, then the rule goes). The width is also published as
@@ -11,7 +11,9 @@ declare const PATHS: {
11
11
  readonly shrink: readonly ["M20 10h-6V4M14 10l6-6M4 14h6v6M10 14l-6 6M14 20v-6h6M14 14l6 6M10 4v6H4M10 10L4 4"];
12
12
  readonly bubble: readonly ["M20.5 11.5a8 8 0 0 1-11.7 7.1L4 19.8l1.2-4.4A8 8 0 1 1 20.5 11.5z"];
13
13
  readonly close: readonly ["M6 6l12 12M18 6L6 18"];
14
+ readonly smile: readonly ["M12 3a9 9 0 1 0 0 18a9 9 0 0 0 0-18z", "M8.5 14a4.5 4.5 0 0 0 7 0", "M9 9.6h.01M15 9.6h.01"];
14
15
  readonly edit: readonly ["M4 20h4L19 9l-4-4L4 16z"];
16
+ readonly look: readonly ["M12 3a9 9 0 1 0 0 18a9 9 0 0 0 0-18z", "M12 3v18"];
15
17
  };
16
18
  export type IconName = keyof typeof PATHS;
17
19
  /** Outline icons drawn from constant path data; `stroke` thins or thickens the line (in 24-unit terms). */
@@ -0,0 +1,27 @@
1
+ import type { CodeProblem } from "../crypto/portable";
2
+ export interface IdentityDeps {
3
+ /** This identity was made portable: it has a recovery code. */
4
+ portable: boolean;
5
+ /** The recovery code of a portable identity. */
6
+ code(): Promise<string | null>;
7
+ /** Makes a new portable identity, keeps it, and returns its recovery code. */
8
+ make(): Promise<string>;
9
+ /** Keeps the identity a code stands for, or says what is wrong with the code. */
10
+ use(code: string): Promise<CodeProblem | null>;
11
+ copy(text: string): Promise<void>;
12
+ /** Starts over with the identity now kept (the page reloads). */
13
+ reload(): void;
14
+ /** Whether the comments on this page were written by the current identity (so a change is felt). */
15
+ hasComments: boolean;
16
+ /** The way to reach this reviewer they have shared ("" when none), and how to share or stop sharing one. */
17
+ contact: {
18
+ value: string;
19
+ share(value: string): Promise<boolean>;
20
+ stop(): Promise<void>;
21
+ };
22
+ }
23
+ /**
24
+ * A small dialog over the page for what a reviewer can do with their identity: make it portable or back it up (see the
25
+ * code, copy it, save it as a file), or use one they backed up. Plain words say what each does.
26
+ */
27
+ export declare function openIdentity(layer: HTMLElement, deps: IdentityDeps): void;
@@ -1,7 +1,2 @@
1
1
  import type { Ctx, View } from "./context";
2
- /**
3
- * The comment control: one round button in the corner (the comment icon, or the open count inside a comment
4
- * bubble) that opens a menu, Comment nearest it. All comments is a panel on the right that stays open while
5
- * choosing threads reveals them, so every counted thread can be reached.
6
- */
7
2
  export declare function createLauncher(ctx: Ctx): View;
@@ -0,0 +1,27 @@
1
+ import { type Look } from "./theme";
2
+ /** Pipeup's own words and surface in each look, as in the stylesheet (a test keeps the two equal). */
3
+ export declare const PAIRS: Record<Look, [string, string]>;
4
+ export interface Appearance {
5
+ /** The look Pipeup is showing. */
6
+ look: Look;
7
+ /** Who chose it: the reviewer (the Look row), the author (data-pipeup-theme) or what the page shows. */
8
+ source: "reviewer" | "author" | "page";
9
+ /** Contrast of Pipeup's words on its own surface, as the page's styles leave it; 3 is the least that reads. */
10
+ ratio: number;
11
+ /** The page's styles on <html> that change everything under it, as "property: value". */
12
+ effects: string[];
13
+ }
14
+ /**
15
+ * How well Pipeup's words read in `look` when the page fades <html> to `opacity` over its canvas: both the words
16
+ * and their surface are blended toward the canvas, which is what the eye gets.
17
+ */
18
+ export declare function reading(look: Look, opacity: number, canvas: number[]): number;
19
+ /**
20
+ * Looks at what the page's own styles on <html> do to Pipeup, which sits under it: how well Pipeup's words still read
21
+ * when the page fades <html> over its canvas, and every other effect (filters, blending, transforms, zoom). Pipeup
22
+ * can't undo any of them from inside, so it reports them; switching look wouldn't help, since fading costs both looks
23
+ * about the same.
24
+ */
25
+ export declare function inspect(doc: Document, look: Look, source: Appearance["source"]): Appearance;
26
+ /** The line for the console when the page's styles change how Pipeup looks, or null when they don't. */
27
+ export declare function warning(a: Appearance): string | null;
@@ -0,0 +1,46 @@
1
+ import type { Comment, Mention, Thread } from "../model/types";
2
+ /** Someone who can be mentioned: `ref` says who (a writer's key, or `host:<id>` / `gh:<name>` for a supplied person). */
3
+ export interface Person {
4
+ ref: string;
5
+ name: string;
6
+ /** An AI agent. */
7
+ agent?: boolean;
8
+ }
9
+ /** People the page or an add-on supplies (a company's directory, a repository's members), shown in the `@` list. */
10
+ export declare function setPeople(list: readonly Person[]): void;
11
+ export declare const heardPeople: (fn: () => void) => (() => void);
12
+ /**
13
+ * Everyone who can be mentioned here: the people who have commented or reacted in this document, the agents the page
14
+ * lists, and the people the page or an add-on supplied; not this reviewer. A person comes once.
15
+ */
16
+ export declare function peopleFor(threads: readonly Thread[], me: string): Person[];
17
+ /** The `@name` being typed at the end of `before`: where it starts and what has been typed after the `@`. */
18
+ export declare function mentionToken(before: string): {
19
+ start: number;
20
+ query: string;
21
+ } | null;
22
+ /** The people matching what was typed: names that start with it first, then names that contain it. */
23
+ export declare function filterPeople(list: readonly Person[], query: string): Person[];
24
+ /**
25
+ * Where each chosen person is still named in the final words. A person whose `@Name` was edited away is no longer
26
+ * mentioned; the same name twice takes its places in the order chosen.
27
+ */
28
+ export declare function locate(text: string, chosen: readonly {
29
+ label: string;
30
+ ref: string;
31
+ }[]): Mention[];
32
+ /** Whether a comment names any of `refs`. */
33
+ export declare const names: (c: Comment, refs: readonly string[]) => boolean;
34
+ /** Who the page says this reviewer is (`host:<id>`), so comments naming them there count as naming this reviewer. */
35
+ export declare function setIdentity(who: {
36
+ id: string;
37
+ } | null): void;
38
+ /** Every way a comment can name this reviewer: their key, and the page's own name for them. */
39
+ export declare const myRefs: (me: string) => string[];
40
+ /** Comments by others that name this reviewer and that they haven't marked seen, with their threads. */
41
+ export declare function unseenMentions(threads: readonly Thread[], me: string): {
42
+ thread: Thread;
43
+ comment: Comment;
44
+ }[];
45
+ /** Whether a thread has a comment by someone else naming this reviewer. */
46
+ export declare const mentionsMe: (t: Thread, me: string) => boolean;
@@ -1,6 +1,8 @@
1
1
  import { PipeupDocument } from "../document";
2
2
  import { type OpStore } from "../storage/store";
3
3
  import { type SlideHook } from "./here";
4
+ import { type AgentComments } from "../export/agent";
5
+ import type { Appearance } from "./legible";
4
6
  export interface MountOptions {
5
7
  /** The area people can comment on. Default: document.body. */
6
8
  root?: Element;
@@ -13,10 +15,20 @@ export interface MountOptions {
13
15
  }
14
16
  export interface PipeupInstance {
15
17
  readonly document: PipeupDocument;
18
+ /** The page's comments, for an agent: open threads, or every thread with `all`. */
19
+ comments(options?: {
20
+ all?: boolean;
21
+ }): AgentComments;
22
+ /** How Pipeup looks and why (for `pipeup check` and agents): its look, who chose it, and what the page's styles do to it. */
23
+ appearance(): Appearance;
16
24
  flush(): Promise<void>;
17
25
  unmount(): void;
18
26
  }
19
27
  /** Adds Pipeup to the page. Calling it again returns the same instance. */
20
28
  export declare function mount(options?: MountOptions): Promise<PipeupInstance>;
29
+ /** The page's comments for an agent that is looking at the page: waits for Pipeup to start, then reads them. */
30
+ export declare function comments(options?: {
31
+ all?: boolean;
32
+ }): Promise<AgentComments>;
21
33
  /** Pages that carry data-pipeup-doc mount themselves, unless data-pipeup-auto="off". */
22
34
  export declare function autoMount(): void;
@@ -0,0 +1,31 @@
1
+ import type { Comment } from "../model/types";
2
+ /** The quick row: what a reviewer can react with at a tap. (The full set of emoji is the emoji add-on's.) */
3
+ export declare const QUICK: readonly ["👍", "❤️", "😂", "🎉", "😮", "🙏"];
4
+ /** The full emoji picker, when an add-on provides one: Pipeup draws the popover, the add-on fills it. */
5
+ export interface ReactionPicker {
6
+ /** Fills the popover; `pick` hears the chosen emoji. May return a function that tears the picker down. */
7
+ fill(into: HTMLElement, pick: (emoji: string) => void): void | (() => void);
8
+ /** The emoji's name, for screen readers. */
9
+ name(emoji: string): string | undefined;
10
+ }
11
+ /** Opens the picker in a popover beside `anchor`; it closes on a choice, Escape, a click elsewhere or when asked. */
12
+ export declare function openPicker(anchor: HTMLElement, picker: ReactionPicker, pick: (emoji: string) => void): void;
13
+ export interface ReactionOptions {
14
+ /** The full picker, when an add-on has one: the quick row then ends with a button that opens it. */
15
+ picker?: ReactionPicker | null;
16
+ /** This reviewer's key, to mark their own reactions. */
17
+ me: string;
18
+ /** Reactions can't be added or taken back on a resolved thread; they still show. */
19
+ resolved: boolean;
20
+ /** The quick row is open under this comment. */
21
+ quick: boolean;
22
+ /** Gives or takes back a reaction. */
23
+ react(emoji: string, on: boolean): void;
24
+ /** Chips and quick buttons already on the page once, so only new ones ease in. */
25
+ seen: Set<string>;
26
+ }
27
+ /**
28
+ * What people reacted to a comment with, quietly beneath its words: each emoji with how many gave it (yours marked;
29
+ * pointing at one says who), and, when asked for, the quick row. Nothing for a comment nobody reacted to.
30
+ */
31
+ export declare function reactionRow(c: Comment, o: ReactionOptions): HTMLElement | null;