@lloyal-labs/ui 0.1.0-alpha.10

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/dist/prose.js ADDED
@@ -0,0 +1,176 @@
1
+ /**
2
+ * The headings, links and heading ids of a markdown body, read with the grammar
3
+ * that renders it. `Markdown` draws prose through remark; what a view derives
4
+ * from the same prose — an outline, citation chips, the id a heading is given —
5
+ * comes from the same parser, so what the outline names is what the page shows,
6
+ * by construction: a heading's `text` here is the string its rendered element
7
+ * contains, inline markup and all resolved.
8
+ *
9
+ * Two grammars are used on purpose, split by concern. Headings and links
10
+ * (`headingsOf`, `linksOf`) come from remark, memoized, and parsed in the two
11
+ * pieces the streaming view renders — the finished blocks and the block under
12
+ * the caret — so a fact read per token costs one block's parse. The
13
+ * streaming boundary (`splitStreaming`) comes from marked's block lexer, which
14
+ * runs on every token over the whole buffer and is fifteen times cheaper — and
15
+ * a boundary it misjudges costs one extra parse of one block, never a wrong
16
+ * document, so it need not agree with the renderer to the byte.
17
+ *
18
+ * Framework-free: a node script, the Ink view and the browser page read the
19
+ * same headings and links.
20
+ *
21
+ * @packageDocumentation
22
+ * @category UI
23
+ */
24
+ import { lexer } from 'marked';
25
+ import { unified } from 'unified';
26
+ import remarkParse from 'remark-parse';
27
+ import remarkGfm from 'remark-gfm';
28
+ import remarkMath from 'remark-math';
29
+ import { toString } from 'mdast-util-to-string';
30
+ /** The url schemes a link may carry: react-markdown's own rule (micromark's `sanitizeUri` without the encode
31
+ * step — https://github.com/remarkjs/react-markdown, `defaultUrlTransform`), restated here because that module
32
+ * imports React at scope and this one is framework-free. */
33
+ const SAFE_PROTOCOL = /^(https?|ircs?|mailto|xmpp)$/i;
34
+ /** The ONE url policy of a harness view: the content plane's scheme (`attachment://…`) is admitted, a relative
35
+ * url or a safe scheme passes, anything else (`javascript:`, `data:`) is stripped to `''`. `Markdown` renders
36
+ * through it and `linksOf` reports through it, so a citation built from a link carries the href the page
37
+ * carries. */
38
+ export function admitUrl(url) {
39
+ if (url.startsWith('attachment://'))
40
+ return url;
41
+ const colon = url.indexOf(':');
42
+ const questionMark = url.indexOf('?');
43
+ const numberSign = url.indexOf('#');
44
+ const slash = url.indexOf('/');
45
+ if (colon === -1 || // no protocol: relative
46
+ (slash !== -1 && colon > slash) || // the first colon is not a protocol's
47
+ (questionMark !== -1 && colon > questionMark) ||
48
+ (numberSign !== -1 && colon > numberSign) ||
49
+ SAFE_PROTOCOL.test(url.slice(0, colon)))
50
+ return url;
51
+ return '';
52
+ }
53
+ /** The renderer's grammar, exactly: `Markdown` parses with these two plugins, so a `$…$` span is math here as
54
+ * it is there, never emphasis. */
55
+ const parser = unified().use(remarkParse).use(remarkGfm).use(remarkMath);
56
+ /** Parsed texts, most recent last. A body is parsed in two pieces (see {@link nodesOf}), and a canvas reads the
57
+ * settled body, its exchanges and the sections in one pass, so a small memo would thrash; an unbounded one
58
+ * would pin every body a session ever showed. */
59
+ const parsed = new Map();
60
+ const KEEP = 32;
61
+ function rootOf(markdown) {
62
+ const hit = parsed.get(markdown);
63
+ if (hit) {
64
+ parsed.delete(markdown);
65
+ parsed.set(markdown, hit);
66
+ return hit;
67
+ }
68
+ const root = parser.parse(markdown);
69
+ parsed.set(markdown, root);
70
+ if (parsed.size > KEEP)
71
+ parsed.delete(parsed.keys().next().value);
72
+ return root;
73
+ }
74
+ function* walk(nodes) {
75
+ for (const node of nodes) {
76
+ yield node;
77
+ if ('children' in node && Array.isArray(node.children))
78
+ yield* walk(node.children);
79
+ }
80
+ }
81
+ /** Every node of a body with where it starts, parsed the way the streaming view renders it: the finished
82
+ * blocks as one piece, kept while they stand, and the block under the caret as another, parsed fresh. A fact
83
+ * read per token — the outline of a section as it streams — then costs one block's parse, not the body's;
84
+ * and a settled body costs its parse once. The pieces are {@link splitStreaming}'s, so a body it refuses to
85
+ * split (a reference definition, display math) is one piece, and no block is ever read across the seam. The
86
+ * offsets count into the body with its line endings normalised to `\n`, as the split normalises them. */
87
+ function* nodesOf(markdown) {
88
+ const { head, tail } = splitStreaming(markdown);
89
+ for (const node of walk(rootOf(head).children))
90
+ yield { node, offset: node.position?.start.offset ?? 0 };
91
+ for (const node of walk(rootOf(tail).children))
92
+ yield { node, offset: head.length + (node.position?.start.offset ?? 0) };
93
+ }
94
+ /** Every heading in document order. */
95
+ export function headingsOf(markdown) {
96
+ const out = [];
97
+ for (const { node, offset } of nodesOf(markdown)) {
98
+ if (node.type === 'heading')
99
+ out.push({ depth: node.depth, text: toString(node), offset });
100
+ }
101
+ return out;
102
+ }
103
+ /** Every link in document order, as the renderer draws them: inline links, the bare urls GFM links, and
104
+ * reference-style links resolved through their definitions — the FIRST definition of an identifier, as
105
+ * CommonMark resolves it (an unresolved reference renders as text and is not a link). Each href has passed
106
+ * {@link admitUrl}, so a link whose scheme the renderer strips is reported with the same empty href. */
107
+ export function linksOf(markdown) {
108
+ const definitions = new Map();
109
+ for (const { node } of nodesOf(markdown)) {
110
+ if (node.type === 'definition' && !definitions.has(node.identifier))
111
+ definitions.set(node.identifier, node.url);
112
+ }
113
+ const out = [];
114
+ for (const { node, offset } of nodesOf(markdown)) {
115
+ if (node.type === 'link')
116
+ out.push({ href: admitUrl(node.url), text: toString(node), offset });
117
+ else if (node.type === 'linkReference') {
118
+ const href = definitions.get(node.identifier);
119
+ if (href !== undefined)
120
+ out.push({ href: admitUrl(href), text: toString(node), offset });
121
+ }
122
+ }
123
+ return out;
124
+ }
125
+ const slugify = (text) => text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 60) || 'h';
126
+ /** The id each heading is given under `prefix`, in document order — the ONE derivation, so an outline that
127
+ * names an anchor and the prose that carries it cannot disagree. A repeated heading is numbered from its
128
+ * second appearance, and the number is taken against the ids already given out, not the slug's count: `A`,
129
+ * `A`, `A-2` become `a`, `a-2`, `a-2-2`, never two `a-2`s. A renderer assigns these to its heading elements
130
+ * in the same order. */
131
+ export function anchorsOf(headings, prefix) {
132
+ const given = new Set();
133
+ return headings.map((h) => {
134
+ const base = `${prefix}-${slugify(h.text)}`;
135
+ let anchor = base;
136
+ for (let n = 2; given.has(anchor); n++)
137
+ anchor = `${base}-${n}`;
138
+ given.add(anchor);
139
+ return { anchor, text: h.text, depth: h.depth };
140
+ });
141
+ }
142
+ /** Where streaming prose stops being finished. Everything up to the last block CommonMark recognises is
143
+ * complete, parsed once and kept while it stands; the last block is the one still being written, parsed per
144
+ * token. The boundaries are marked's — a spec-tested block tokenizer, used here only to find where the last
145
+ * block starts: a loose list is one block, a fence owns its blank lines. A block boundary this misjudges
146
+ * would cost one extra parse of one block, never a wrong document — except for two constructs marked does not
147
+ * see the way the renderer does, where the split is refused and the whole buffer is the tail:
148
+ * - a reference definition (anywhere, a blockquote included) resolves links in EARLIER blocks, so a head
149
+ * rendered without it would show those references as text;
150
+ * - a display equation (`$$…$$`) may span a blank line, which marked reads as a block boundary; cut there,
151
+ * the renderer would draw an equation, a paragraph and an empty equation — and nothing short of the
152
+ * renderer's grammar can say where one opens (a `$$` in inline code is not a delimiter; `$$$$` is).
153
+ * A document that carries either is rendered whole: the split is refused for the document's whole life, and
154
+ * the cost of that is paid only by documents with display math or reference definitions. */
155
+ export function splitStreaming(markdown) {
156
+ // marked reports raw text with line endings normalised; normalise first so head + tail is the text parsed.
157
+ const text = markdown.replace(/\r\n?/g, '\n');
158
+ const tokens = lexer(text);
159
+ if (tokens.length < 2 || text.includes('$$') || hasDefinition(tokens))
160
+ return { head: '', tail: text };
161
+ const cut = text.length - tokens[tokens.length - 1].raw.length;
162
+ return { head: text.slice(0, cut), tail: text.slice(cut) };
163
+ }
164
+ /** A reference definition at any depth of marked's token tree — a blockquote or list nests its own. */
165
+ function hasDefinition(tokens) {
166
+ for (const t of tokens) {
167
+ if (t.type === 'def')
168
+ return true;
169
+ if (t.tokens && hasDefinition(t.tokens))
170
+ return true;
171
+ if (t.items && hasDefinition(t.items))
172
+ return true;
173
+ }
174
+ return false;
175
+ }
176
+ //# sourceMappingURL=prose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prose.js","sourceRoot":"","sources":["../src/prose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { ReactElement, ReactNode } from 'react';
2
+ import type { Availability, Bridge, Projection, WireStatus } from '@lloyal-labs/binding';
3
+ export interface Harness<E, C, S extends object> {
4
+ bridge: Bridge<E, C, S>;
5
+ projection: Projection<S>;
6
+ }
7
+ /** The one projection over a bridge — the provider's, shared with a consumer outside React (a history adapter). */
8
+ export declare function projectionFor<E, C, S extends object>(bridge: Bridge<E, C, S>, initialState: S, reduce: (state: S, ev: E) => S): Projection<S>;
9
+ export declare function HarnessProvider<E, C, S extends object>({ bridge, initialState, reduce, children }: {
10
+ bridge: Bridge<E, C, S>;
11
+ initialState: S;
12
+ reduce: (state: S, ev: E) => S;
13
+ children: ReactNode;
14
+ }): ReactElement;
15
+ /** The provider's bridge and projection, for a consumer outside the hooks (a history adapter). */
16
+ export declare function useHarness<E = unknown, C = unknown, S extends object = object>(): Harness<E, C, S>;
17
+ /** Read a derivation of the folded state, memoized per fold. */
18
+ export declare function useProjection<S extends object, T>(select: (state: S) => T): T;
19
+ /** Dispatch a command to the harness. */
20
+ export declare function useSend<C>(): (command: C) => void;
21
+ /** The wire's status. A bridge without `onStatus` (in-process) reads 'connected' forever. */
22
+ export declare function useConnection(): WireStatus;
23
+ /**
24
+ * Whether this harness can take work, and if not, why — the question a view actually asks.
25
+ *
26
+ * Derived from the transport's status and the host's session phase, because neither answers it
27
+ * alone: a queued reader's socket is healthy, and an ended session is followed by a close that
28
+ * reads as a network failure. Prefer this to {@link useConnection}, which is the transport fact
29
+ * on its own.
30
+ */
31
+ export declare function useAvailability(): Availability;
32
+ /**
33
+ * Ask the placement for a working harness — or null where this bridge cannot provide one.
34
+ *
35
+ * A view knows WHEN recovery is wanted and never what it costs: a browser opens a new connection
36
+ * because a served host cannot re-admit an existing one; a desktop shell starts a new engine while
37
+ * its renderer's IPC link stays up throughout. Same button, different price, and the view pays
38
+ * neither.
39
+ */
40
+ export declare function useRecover(): (() => void) | null;
41
+ /** The content plane's origin, or null on a bridge without one. */
42
+ export declare function useContentOrigin(): string | null;
43
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAgB,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvG,MAAM,WAAW,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,MAAM;IAC7C,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACxB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CAC3B;AASD,mHAAmH;AACnH,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAO7I;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;IAClG,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACxB,YAAY,EAAE,CAAC,CAAC;IAChB,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC/B,QAAQ,EAAE,SAAS,CAAC;CACrB,GAAG,YAAY,CAGf;AAED,kGAAkG;AAClG,wBAAgB,UAAU,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAIlG;AAID,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAc7E;AAED,yCAAyC;AACzC,wBAAgB,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAGjD;AAqCD,6FAA6F;AAC7F,wBAAgB,aAAa,IAAI,UAAU,CAI1C;AA4CD;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAI9C;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAGhD;AAED,mEAAmE;AACnE,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAGhD"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * The projection, in React: a provider that connects a bridge once, and hooks
3
+ * that read the fold, the wire's status, the command sink and the content
4
+ * plane's origin. One projection per provider; a remount under the same bridge
5
+ * reattaches to the running fold, it never re-subscribes or resets.
6
+ *
7
+ * `useProjection(select)` memoizes per fold: the state is immutable between
8
+ * events, so a named selector returns the SAME reference until the next fold —
9
+ * what `useSyncExternalStore` needs. The contract: a selector with a stable
10
+ * identity may build objects; an inline selector must return a primitive.
11
+ *
12
+ * The folded state must be an OBJECT, and a new one on every event. Both halves
13
+ * are load-bearing and neither is a style preference: the memo is a `WeakMap`
14
+ * keyed by the snapshot, so a scalar or `null` state is not a legal key at all,
15
+ * and a state mutated in place keeps its identity and so keeps returning the
16
+ * selection cached against the previous fold. `S extends object` states the
17
+ * first half to the compiler. The second is a discipline the type system cannot
18
+ * carry, which is why it is written here.
19
+ *
20
+ * @category UI
21
+ */
22
+ import { createContext, createElement, useContext, useMemo, useSyncExternalStore } from 'react';
23
+ import { connectProjection, availabilityOf } from '@lloyal-labs/binding';
24
+ // `object`, not `unknown`, is what the erased context can say now that `S` is
25
+ // constrained — the hooks below cast back out of it exactly as before.
26
+ const HarnessContext = createContext(null);
27
+ /** One projection per bridge, for the life of the page: a React remount under the same bridge reattaches. */
28
+ const projections = new WeakMap();
29
+ /** The one projection over a bridge — the provider's, shared with a consumer outside React (a history adapter). */
30
+ export function projectionFor(bridge, initialState, reduce) {
31
+ let projection = projections.get(bridge);
32
+ if (!projection) {
33
+ projection = connectProjection(bridge, initialState, reduce);
34
+ projections.set(bridge, projection);
35
+ }
36
+ return projection;
37
+ }
38
+ export function HarnessProvider({ bridge, initialState, reduce, children }) {
39
+ const value = useMemo(() => ({ bridge, projection: projectionFor(bridge, initialState, reduce) }), [bridge]);
40
+ return createElement(HarnessContext.Provider, { value: value }, children);
41
+ }
42
+ /** The provider's bridge and projection, for a consumer outside the hooks (a history adapter). */
43
+ export function useHarness() {
44
+ const h = useContext(HarnessContext);
45
+ if (!h)
46
+ throw new Error('useHarness: no HarnessProvider above this component');
47
+ return h;
48
+ }
49
+ const derived = new WeakMap();
50
+ /** Read a derivation of the folded state, memoized per fold. */
51
+ export function useProjection(select) {
52
+ const { projection } = useHarness();
53
+ const read = () => {
54
+ const state = projection.getSnapshot();
55
+ let memo = derived.get(state);
56
+ if (!memo) {
57
+ memo = new Map();
58
+ derived.set(state, memo);
59
+ }
60
+ const key = select;
61
+ if (!memo.has(key))
62
+ memo.set(key, select(state));
63
+ return memo.get(key);
64
+ };
65
+ return useSyncExternalStore(projection.subscribe, read, read);
66
+ }
67
+ /** Dispatch a command to the harness. */
68
+ export function useSend() {
69
+ const { bridge } = useHarness();
70
+ return bridge.send;
71
+ }
72
+ /** One status per bridge, for the life of the page. React reads `getSnapshot`
73
+ * during render, so the status it reads must outlive the render that reads it:
74
+ * the bridge is told once, here, and every renderer of it shares that one
75
+ * answer. A bridge without `onStatus` (in-process) never leaves 'connected'. */
76
+ const statuses = new WeakMap();
77
+ function statusFor(bridge) {
78
+ let store = statuses.get(bridge);
79
+ if (!store) {
80
+ let status = 'connected';
81
+ const listeners = new Set();
82
+ bridge.onStatus?.((next) => {
83
+ status = next;
84
+ for (const notify of listeners)
85
+ notify();
86
+ });
87
+ store = {
88
+ subscribe(notify) {
89
+ listeners.add(notify);
90
+ return () => { listeners.delete(notify); };
91
+ },
92
+ getSnapshot: () => status,
93
+ };
94
+ statuses.set(bridge, store);
95
+ }
96
+ return store;
97
+ }
98
+ /** The status a server render reads: no wire has been asked yet. */
99
+ const connected = () => 'connected';
100
+ /** The wire's status. A bridge without `onStatus` (in-process) reads 'connected' forever. */
101
+ export function useConnection() {
102
+ const { bridge } = useHarness();
103
+ const wire = statusFor(bridge);
104
+ return useSyncExternalStore(wire.subscribe, wire.getSnapshot, connected);
105
+ }
106
+ /** One derivation per bridge, for the life of the page — the same reason the status has one. */
107
+ const availabilities = new WeakMap();
108
+ function availabilityFor(bridge) {
109
+ let store = availabilities.get(bridge);
110
+ if (!store) {
111
+ // A bridge with no droppable link is up, and one with no session plane has a single implicit
112
+ // session that is live: nothing queues it and nothing reaps it, so "waiting" and "ended" are not
113
+ // states it can be in. This is the ONE place a placement difference is stated; the derivation
114
+ // itself knows no placements.
115
+ let wire = 'connected';
116
+ let session = bridge.onSession ? null : { phase: 'live' };
117
+ let value = availabilityOf(session, wire);
118
+ const listeners = new Set();
119
+ const settle = () => {
120
+ const next = availabilityOf(session, wire);
121
+ if (next === value)
122
+ return;
123
+ value = next;
124
+ for (const notify of listeners)
125
+ notify();
126
+ };
127
+ bridge.onStatus?.((next) => { wire = next; settle(); });
128
+ bridge.onSession?.((next) => { session = next; settle(); });
129
+ store = {
130
+ subscribe(notify) {
131
+ listeners.add(notify);
132
+ return () => { listeners.delete(notify); };
133
+ },
134
+ getSnapshot: () => value,
135
+ };
136
+ availabilities.set(bridge, store);
137
+ }
138
+ return store;
139
+ }
140
+ /** What a server render reads: nothing has been asked of either plane yet. */
141
+ const starting = () => 'connecting';
142
+ /**
143
+ * Whether this harness can take work, and if not, why — the question a view actually asks.
144
+ *
145
+ * Derived from the transport's status and the host's session phase, because neither answers it
146
+ * alone: a queued reader's socket is healthy, and an ended session is followed by a close that
147
+ * reads as a network failure. Prefer this to {@link useConnection}, which is the transport fact
148
+ * on its own.
149
+ */
150
+ export function useAvailability() {
151
+ const { bridge } = useHarness();
152
+ const store = availabilityFor(bridge);
153
+ return useSyncExternalStore(store.subscribe, store.getSnapshot, starting);
154
+ }
155
+ /**
156
+ * Ask the placement for a working harness — or null where this bridge cannot provide one.
157
+ *
158
+ * A view knows WHEN recovery is wanted and never what it costs: a browser opens a new connection
159
+ * because a served host cannot re-admit an existing one; a desktop shell starts a new engine while
160
+ * its renderer's IPC link stays up throughout. Same button, different price, and the view pays
161
+ * neither.
162
+ */
163
+ export function useRecover() {
164
+ const { bridge } = useHarness();
165
+ return useMemo(() => (bridge.recover ? () => bridge.recover() : null), [bridge]);
166
+ }
167
+ /** The content plane's origin, or null on a bridge without one. */
168
+ export function useContentOrigin() {
169
+ const { bridge } = useHarness();
170
+ return bridge.contentOrigin?.() ?? null;
171
+ }
172
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAEhG,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQzE,8EAA8E;AAC9E,uEAAuE;AACvE,MAAM,cAAc,GAAG,aAAa,CAA2C,IAAI,CAAC,CAAC;AAErF,6GAA6G;AAC7G,MAAM,WAAW,GAAG,IAAI,OAAO,EAA+B,CAAC;AAE/D,mHAAmH;AACnH,MAAM,UAAU,aAAa,CAAyB,MAAuB,EAAE,YAAe,EAAE,MAA8B;IAC5H,IAAI,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAA8B,CAAC;IACtE,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,UAAU,GAAG,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;QAC7D,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,eAAe,CAAyB,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAK/F;IACC,MAAM,KAAK,GAAG,OAAO,CAAmB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/H,OAAO,aAAa,CAAC,cAAc,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,KAA0C,EAAE,EAAE,QAAQ,CAAC,CAAC;AACjH,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,UAAU;IACxB,MAAM,CAAC,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC;IACrC,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAC/E,OAAO,CAAqB,CAAC;AAC/B,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAA+C,CAAC;AAE3E,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAAsB,MAAuB;IACxE,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,EAAuB,CAAC;IACzD,MAAM,IAAI,GAAG,GAAM,EAAE;QACnB,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;QACvC,IAAI,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,MAAM,GAAG,GAAG,MAA0C,CAAC;QACvD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAM,CAAC;IAC5B,CAAC,CAAC;IACF,OAAO,oBAAoB,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAChE,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,OAAO;IACrB,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,EAAsB,CAAC;IACpD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAOD;;;iFAGiF;AACjF,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAqB,CAAC;AAElD,SAAS,SAAS,CAAC,MAAyC;IAC1D,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,MAAM,GAAe,WAAW,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;QACxC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE;YACzB,MAAM,GAAG,IAAI,CAAC;YACd,KAAK,MAAM,MAAM,IAAI,SAAS;gBAAE,MAAM,EAAE,CAAC;QAC3C,CAAC,CAAC,CAAC;QACH,KAAK,GAAG;YACN,SAAS,CAAC,MAAM;gBACd,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACtB,OAAO,GAAG,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,CAAC;YACD,WAAW,EAAE,GAAG,EAAE,CAAC,MAAM;SAC1B,CAAC;QACF,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,oEAAoE;AACpE,MAAM,SAAS,GAAG,GAAe,EAAE,CAAC,WAAW,CAAC;AAEhD,6FAA6F;AAC7F,MAAM,UAAU,aAAa;IAC3B,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/B,OAAO,oBAAoB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AAC3E,CAAC;AAOD,gGAAgG;AAChG,MAAM,cAAc,GAAG,IAAI,OAAO,EAA6B,CAAC;AAEhE,SAAS,eAAe,CAAC,MAAyC;IAChE,IAAI,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,6FAA6F;QAC7F,iGAAiG;QACjG,8FAA8F;QAC9F,8BAA8B;QAC9B,IAAI,IAAI,GAAe,WAAW,CAAC;QACnC,IAAI,OAAO,GAAwB,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC/E,IAAI,KAAK,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;QACxC,MAAM,MAAM,GAAG,GAAS,EAAE;YACxB,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC3C,IAAI,IAAI,KAAK,KAAK;gBAAE,OAAO;YAC3B,KAAK,GAAG,IAAI,CAAC;YACb,KAAK,MAAM,MAAM,IAAI,SAAS;gBAAE,MAAM,EAAE,CAAC;QAC3C,CAAC,CAAC;QACF,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,KAAK,GAAG;YACN,SAAS,CAAC,MAAM;gBACd,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACtB,OAAO,GAAG,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,CAAC;YACD,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK;SACzB,CAAC;QACF,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,MAAM,QAAQ,GAAG,GAAiB,EAAE,CAAC,YAAY,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACtC,OAAO,oBAAoB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAC5E,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IAChC,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAS,EAAE,CAAC,MAAM,CAAC,OAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,gBAAgB;IAC9B,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IAChC,OAAO,MAAM,CAAC,aAAa,EAAE,EAAE,IAAI,IAAI,CAAC;AAC1C,CAAC"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@lloyal-labs/ui",
3
+ "version": "0.1.0-alpha.10",
4
+ "description": "The view side of a harness: the projection provider and hooks over a bridge, the generic agent fold, and the prose and figure primitives",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./fold": {
14
+ "types": "./dist/fold.d.ts",
15
+ "default": "./dist/fold.js"
16
+ },
17
+ "./prose": {
18
+ "types": "./dist/prose.d.ts",
19
+ "default": "./dist/prose.js"
20
+ }
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/lloyal-ai/hdk.git",
28
+ "directory": "packages/ui"
29
+ },
30
+ "homepage": "https://github.com/lloyal-ai/hdk/tree/main/packages/ui#readme",
31
+ "bugs": {
32
+ "url": "https://github.com/lloyal-ai/hdk/issues"
33
+ },
34
+ "keywords": [
35
+ "ui",
36
+ "react",
37
+ "harness",
38
+ "projection",
39
+ "markdown"
40
+ ],
41
+ "license": "SEE LICENSE IN LICENSE",
42
+ "files": [
43
+ "dist/",
44
+ "LICENSE"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsc -p tsconfig.json"
48
+ },
49
+ "dependencies": {
50
+ "@lloyal-labs/binding": "0.2.0-alpha.6",
51
+ "@lloyal-labs/media": "0.2.0-alpha.6",
52
+ "@types/mdast": "^4.0.4",
53
+ "katex": "^0.16.22",
54
+ "marked": "^18.0.13",
55
+ "mdast-util-to-string": "^4.0.0",
56
+ "react-markdown": "^10.1.0",
57
+ "rehype-katex": "^7.0.1",
58
+ "remark-gfm": "^4.0.1",
59
+ "remark-math": "^6.0.0",
60
+ "remark-parse": "^11.0.0",
61
+ "unified": "^11.0.5"
62
+ },
63
+ "peerDependencies": {
64
+ "react": "^18.3.0 || ^19.0.0"
65
+ }
66
+ }