@artkit-ai/react 0.1.0 → 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/dist/cache.d.ts +7 -11
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +32 -53
- package/dist/cache.js.map +1 -1
- package/dist/client.d.ts +4 -15
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -29
- package/dist/client.js.map +1 -1
- package/dist/components/card.d.ts +2 -2
- package/dist/components/card.d.ts.map +1 -1
- package/dist/components/card.js +3 -3
- package/dist/components/card.js.map +1 -1
- package/dist/components/editor.d.ts +31 -18
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +81 -131
- package/dist/components/editor.js.map +1 -1
- package/dist/components/icons.d.ts +2 -2
- package/dist/components/icons.d.ts.map +1 -1
- package/dist/components/icons.js +2 -2
- package/dist/components/icons.js.map +1 -1
- package/dist/components/panel.d.ts +13 -16
- package/dist/components/panel.d.ts.map +1 -1
- package/dist/components/panel.js +13 -97
- package/dist/components/panel.js.map +1 -1
- package/dist/components/shared.d.ts +2 -6
- package/dist/components/shared.d.ts.map +1 -1
- package/dist/components/shared.js +2 -6
- package/dist/components/shared.js.map +1 -1
- package/dist/components/view.d.ts +7 -8
- package/dist/components/view.d.ts.map +1 -1
- package/dist/components/view.js +16 -15
- package/dist/components/view.js.map +1 -1
- package/dist/context.d.ts +14 -9
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/editor-session.d.ts +77 -165
- package/dist/editor-session.d.ts.map +1 -1
- package/dist/editor-session.js +164 -586
- package/dist/editor-session.js.map +1 -1
- package/dist/hooks.d.ts +2 -23
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +24 -116
- package/dist/hooks.js.map +1 -1
- package/dist/i18n.d.ts +4 -66
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +6 -68
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts +6 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -8
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +12 -12
- package/dist/provider.js.map +1 -1
- package/dist/styles.d.ts +1 -1
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +4 -4
- package/package.json +2 -2
- package/src/cache.ts +32 -55
- package/src/client.ts +7 -55
- package/src/components/card.tsx +6 -7
- package/src/components/editor.tsx +208 -308
- package/src/components/icons.tsx +2 -2
- package/src/components/panel.tsx +43 -300
- package/src/components/shared.tsx +3 -12
- package/src/components/view.tsx +26 -29
- package/src/context.ts +16 -11
- package/src/editor-session.ts +197 -675
- package/src/hooks.ts +25 -140
- package/src/i18n.ts +6 -68
- package/src/index.ts +7 -39
- package/src/provider.tsx +13 -13
- package/src/styles.ts +4 -4
- package/dist/components/approval.d.ts +0 -34
- package/dist/components/approval.d.ts.map +0 -1
- package/dist/components/approval.js +0 -80
- package/dist/components/approval.js.map +0 -1
- package/dist/components/version-diff.d.ts +0 -35
- package/dist/components/version-diff.d.ts.map +0 -1
- package/dist/components/version-diff.js +0 -85
- package/dist/components/version-diff.js.map +0 -1
- package/dist/line-diff.d.ts +0 -25
- package/dist/line-diff.d.ts.map +0 -1
- package/dist/line-diff.js +0 -96
- package/dist/line-diff.js.map +0 -1
- package/src/components/approval.tsx +0 -219
- package/src/components/version-diff.tsx +0 -266
- package/src/line-diff.ts +0 -111
package/src/cache.ts
CHANGED
|
@@ -9,12 +9,9 @@ export interface CacheEntry {
|
|
|
9
9
|
|
|
10
10
|
const EMPTY: CacheEntry = Object.freeze({ loading: false }) as CacheEntry;
|
|
11
11
|
|
|
12
|
-
const keyOf = (id: string, version?: number) => `${id}@${version ?? 'latest'}`;
|
|
13
|
-
|
|
14
12
|
/**
|
|
15
|
-
* Artifact cache shared by the hooks under one provider (spec §13.2).
|
|
16
|
-
*
|
|
17
|
-
* revalidated on demand. Listeners are per artifact id so history and editors hear about new commits.
|
|
13
|
+
* Artifact cache shared by the hooks under one provider (spec §13.2). One entry per artifact: the current content the
|
|
14
|
+
* application last loaded or saved. The library keeps no older versions, so there is nothing else to cache.
|
|
18
15
|
*/
|
|
19
16
|
export class ArtifactCache {
|
|
20
17
|
private readonly entries = new Map<string, CacheEntry>();
|
|
@@ -23,12 +20,12 @@ export class ArtifactCache {
|
|
|
23
20
|
|
|
24
21
|
constructor(private readonly client: ArtifactClient) {}
|
|
25
22
|
|
|
26
|
-
entry(id: string
|
|
27
|
-
return this.entries.get(
|
|
23
|
+
entry(id: string): CacheEntry {
|
|
24
|
+
return this.entries.get(id) ?? EMPTY;
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
latest(id: string): Artifact | undefined {
|
|
31
|
-
return this.entries.get(
|
|
28
|
+
return this.entries.get(id)?.artifact;
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
subscribe(id: string, listener: () => void): () => void {
|
|
@@ -45,63 +42,48 @@ export class ArtifactCache {
|
|
|
45
42
|
for (const listener of [...(this.listeners.get(id) ?? [])]) listener();
|
|
46
43
|
}
|
|
47
44
|
|
|
48
|
-
private set(id: string, version: number | undefined, entry: CacheEntry): void {
|
|
49
|
-
this.entries.set(keyOf(id, version), entry);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
45
|
/**
|
|
53
|
-
* Records
|
|
54
|
-
*
|
|
46
|
+
* Records an artifact the application loaded or saved. An older version never replaces a newer one; a newer
|
|
47
|
+
* `updatedAt` of the same version replaces it.
|
|
55
48
|
*/
|
|
56
49
|
put(artifact: Artifact): void {
|
|
57
50
|
const current = this.latest(artifact.id);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
artifact.meta.publishedVersion !== current.meta.publishedVersion);
|
|
64
|
-
const known = this.entries.get(keyOf(artifact.id, artifact.version))?.artifact;
|
|
65
|
-
if (current && !sameVersionChanged && artifact.version <= current.version && known) return;
|
|
66
|
-
this.set(artifact.id, artifact.version, { artifact, loading: false });
|
|
67
|
-
if (!current || artifact.version > current.version || sameVersionChanged) {
|
|
68
|
-
this.set(artifact.id, undefined, { artifact, loading: false });
|
|
51
|
+
if (current) {
|
|
52
|
+
const older = artifact.version < current.version;
|
|
53
|
+
const sameVersionOlder =
|
|
54
|
+
artifact.version === current.version && artifact.meta.updatedAt <= current.meta.updatedAt;
|
|
55
|
+
if (older || sameVersionOlder) return;
|
|
69
56
|
}
|
|
57
|
+
this.entries.set(artifact.id, { artifact, loading: false });
|
|
70
58
|
this.notify(artifact.id);
|
|
71
59
|
}
|
|
72
60
|
|
|
73
|
-
/** Loads
|
|
74
|
-
load(id: string,
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const pending = this.inflight.get(key);
|
|
61
|
+
/** Loads the artifact unless it is cached. `force` fetches it again. */
|
|
62
|
+
load(id: string, force = false): Promise<Artifact> {
|
|
63
|
+
const existing = this.entries.get(id);
|
|
64
|
+
if (existing?.artifact && !force) return Promise.resolve(existing.artifact);
|
|
65
|
+
const pending = this.inflight.get(id);
|
|
79
66
|
if (pending) return pending;
|
|
80
|
-
this.set(id,
|
|
67
|
+
this.entries.set(id, { ...(existing ?? {}), loading: true });
|
|
81
68
|
this.notify(id);
|
|
82
|
-
const promise = this.client.get(id
|
|
69
|
+
const promise = this.client.get(id).then(
|
|
83
70
|
(artifact) => {
|
|
84
|
-
this.inflight.delete(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return current;
|
|
92
|
-
}
|
|
71
|
+
this.inflight.delete(id);
|
|
72
|
+
// A save may have landed while the request was in flight; never move the cache backwards.
|
|
73
|
+
const current = this.latest(id);
|
|
74
|
+
if (current && current.version > artifact.version) {
|
|
75
|
+
this.entries.set(id, { artifact: current, loading: false });
|
|
76
|
+
this.notify(id);
|
|
77
|
+
return current;
|
|
93
78
|
}
|
|
94
|
-
this.set(
|
|
95
|
-
if (version === undefined) this.set(id, undefined, { artifact, loading: false });
|
|
96
|
-
else if (!this.latest(id) || this.latest(id)!.version < artifact.version)
|
|
97
|
-
this.set(id, undefined, { artifact, loading: false });
|
|
79
|
+
this.entries.set(id, { artifact, loading: false });
|
|
98
80
|
this.notify(id);
|
|
99
81
|
return artifact;
|
|
100
82
|
},
|
|
101
83
|
(error: unknown) => {
|
|
102
|
-
this.inflight.delete(
|
|
103
|
-
const previous = this.entries.get(
|
|
104
|
-
this.set(id,
|
|
84
|
+
this.inflight.delete(id);
|
|
85
|
+
const previous = this.entries.get(id);
|
|
86
|
+
this.entries.set(id, {
|
|
105
87
|
...(previous?.artifact ? { artifact: previous.artifact } : {}),
|
|
106
88
|
error,
|
|
107
89
|
loading: false,
|
|
@@ -110,14 +92,9 @@ export class ArtifactCache {
|
|
|
110
92
|
throw error;
|
|
111
93
|
},
|
|
112
94
|
);
|
|
113
|
-
this.inflight.set(
|
|
95
|
+
this.inflight.set(id, promise);
|
|
114
96
|
return promise;
|
|
115
97
|
}
|
|
116
|
-
|
|
117
|
-
/** Signals listeners of an id without changing entries (e.g. history must refetch after a restore). */
|
|
118
|
-
touch(id: string): void {
|
|
119
|
-
this.notify(id);
|
|
120
|
-
}
|
|
121
98
|
}
|
|
122
99
|
|
|
123
100
|
const caches = new WeakMap<ArtifactClient, ArtifactCache>();
|
package/src/client.ts
CHANGED
|
@@ -14,39 +14,30 @@ import {
|
|
|
14
14
|
type Lock,
|
|
15
15
|
type Page,
|
|
16
16
|
type PageRequest,
|
|
17
|
-
type
|
|
18
|
-
type ReadResult,
|
|
17
|
+
type ReadMode,
|
|
18
|
+
type ArtifactReadResult as ReadResult,
|
|
19
19
|
type Scope,
|
|
20
|
-
type VersionEntry,
|
|
21
20
|
} from '@artkit-ai/core';
|
|
22
21
|
|
|
23
|
-
/** Lifecycle actions accepted by `POST {base}/{id}/status` (spec §15.3). */
|
|
24
|
-
export type StatusAction = 'submit' | 'approve' | 'reject' | 'publish' | 'archive';
|
|
25
|
-
|
|
26
|
-
export const STATUS_ACTIONS: readonly StatusAction[] = ['submit', 'approve', 'reject', 'publish', 'archive'];
|
|
27
|
-
|
|
28
22
|
export interface RequestOptions {
|
|
29
23
|
signal?: AbortSignal;
|
|
30
24
|
}
|
|
31
25
|
|
|
32
26
|
export interface ReadOptions extends RequestOptions {
|
|
33
|
-
mode?:
|
|
27
|
+
mode?: ReadMode;
|
|
34
28
|
range?: string;
|
|
35
29
|
maxTokens?: number;
|
|
36
30
|
cursor?: string;
|
|
37
|
-
version?: number;
|
|
38
31
|
}
|
|
39
32
|
|
|
33
|
+
/** Operations computed against `baseVersion`; a different current version answers VERSION_CONFLICT. */
|
|
40
34
|
export interface ApplyInput {
|
|
41
35
|
baseVersion: number;
|
|
42
36
|
operations: unknown[];
|
|
43
|
-
idempotencyKey?: string;
|
|
44
|
-
message?: string;
|
|
45
37
|
}
|
|
46
38
|
|
|
47
39
|
export interface ExportInput extends RequestOptions {
|
|
48
40
|
format: string;
|
|
49
|
-
version?: number;
|
|
50
41
|
options?: Record<string, unknown>;
|
|
51
42
|
}
|
|
52
43
|
|
|
@@ -64,14 +55,11 @@ export interface ClientExportResult {
|
|
|
64
55
|
export interface ArtifactClient {
|
|
65
56
|
list(filter?: ArtifactFilter, page?: PageRequest, options?: RequestOptions): Promise<Page<ArtifactSummary>>;
|
|
66
57
|
create(request: CreateRequest, options?: RequestOptions): Promise<CommitResult>;
|
|
67
|
-
get(id: string, options?: RequestOptions
|
|
58
|
+
get(id: string, options?: RequestOptions): Promise<Artifact>;
|
|
68
59
|
read(id: string, options?: ReadOptions): Promise<ReadResult>;
|
|
69
60
|
apply(id: string, input: ApplyInput, options?: RequestOptions): Promise<CommitResult>;
|
|
70
61
|
/** Dry run on the current version: diff and lint without committing. */
|
|
71
62
|
preview(id: string, input: ApplyInput, options?: RequestOptions): Promise<BatchResult>;
|
|
72
|
-
history(id: string, page?: PageRequest, options?: RequestOptions): Promise<Page<VersionEntry>>;
|
|
73
|
-
restore(id: string, version: number, message?: string, options?: RequestOptions): Promise<CommitResult>;
|
|
74
|
-
setStatus(id: string, action: StatusAction, message?: string, options?: RequestOptions): Promise<Artifact>;
|
|
75
63
|
lock(id: string, options?: RequestOptions & { ttlMs?: number }): Promise<Lock>;
|
|
76
64
|
unlock(id: string, lock: Lock, options?: RequestOptions): Promise<void>;
|
|
77
65
|
/** Current lock, or null when nobody holds one. */
|
|
@@ -251,7 +239,7 @@ export function createHttpClient(options: HttpClientOptions = {}): ArtifactClien
|
|
|
251
239
|
opts?.signal,
|
|
252
240
|
),
|
|
253
241
|
create: (request, opts) => json('POST', path(), request, opts?.signal),
|
|
254
|
-
get: (id, opts) => json('GET',
|
|
242
|
+
get: (id, opts) => json('GET', path(id), undefined, opts?.signal),
|
|
255
243
|
read: (id, opts = {}) =>
|
|
256
244
|
json(
|
|
257
245
|
'GET',
|
|
@@ -260,24 +248,12 @@ export function createHttpClient(options: HttpClientOptions = {}): ArtifactClien
|
|
|
260
248
|
range: opts.range,
|
|
261
249
|
maxTokens: opts.maxTokens,
|
|
262
250
|
cursor: opts.cursor,
|
|
263
|
-
version: opts.version,
|
|
264
251
|
}),
|
|
265
252
|
undefined,
|
|
266
253
|
opts.signal,
|
|
267
254
|
),
|
|
268
255
|
apply: (id, input, opts) => json('POST', path(id, 'apply'), input, opts?.signal),
|
|
269
256
|
preview: (id, input, opts) => json('POST', path(id, 'preview'), input, opts?.signal),
|
|
270
|
-
history: (id, page = {}, opts) =>
|
|
271
|
-
json(
|
|
272
|
-
'GET',
|
|
273
|
-
withQuery(path(id, 'history'), { limit: page.limit, cursor: page.cursor }),
|
|
274
|
-
undefined,
|
|
275
|
-
opts?.signal,
|
|
276
|
-
),
|
|
277
|
-
restore: (id, version, message, opts) =>
|
|
278
|
-
json('POST', path(id, 'restore'), { version, ...(message ? { message } : {}) }, opts?.signal),
|
|
279
|
-
setStatus: (id, action, message, opts) =>
|
|
280
|
-
json('POST', path(id, 'status'), { action, ...(message ? { message } : {}) }, opts?.signal),
|
|
281
257
|
lock: (id, opts) =>
|
|
282
258
|
json('POST', path(id, 'lock'), opts?.ttlMs === undefined ? {} : { ttlMs: opts.ttlMs }, opts?.signal),
|
|
283
259
|
async unlock(id, lock, opts) {
|
|
@@ -290,7 +266,6 @@ export function createHttpClient(options: HttpClientOptions = {}): ArtifactClien
|
|
|
290
266
|
path(id, 'export'),
|
|
291
267
|
{
|
|
292
268
|
format: input.format,
|
|
293
|
-
...(input.version === undefined ? {} : { version: input.version }),
|
|
294
269
|
...(input.options ? { options: input.options } : {}),
|
|
295
270
|
},
|
|
296
271
|
input.signal,
|
|
@@ -337,8 +312,7 @@ export function createServiceClient(service: ArtifactService, scope: Scope | (()
|
|
|
337
312
|
return {
|
|
338
313
|
list: (filter, page) => run((s) => service.list(s, filter, page)),
|
|
339
314
|
create: (request) => run((s) => service.create(s, request)),
|
|
340
|
-
get: (id
|
|
341
|
-
run((s) => service.get(s, id, opts?.version === undefined ? undefined : { version: opts.version })),
|
|
315
|
+
get: (id) => run((s) => service.get(s, id)),
|
|
342
316
|
read: (id, opts = {}) =>
|
|
343
317
|
run((s) => {
|
|
344
318
|
const { signal: _signal, ...rest } = opts;
|
|
@@ -346,27 +320,6 @@ export function createServiceClient(service: ArtifactService, scope: Scope | (()
|
|
|
346
320
|
}),
|
|
347
321
|
apply: (id, input) => run((s) => service.apply(s, { artifactId: id, ...input })),
|
|
348
322
|
preview: (id, input) => run((s) => service.preview(s, { artifactId: id, ...input })),
|
|
349
|
-
history: (id, page) => run((s) => service.history(s, id, page)),
|
|
350
|
-
restore: (id, version, message) => run((s) => service.restore(s, id, version, message)),
|
|
351
|
-
setStatus: (id, action, message) =>
|
|
352
|
-
run((s) => {
|
|
353
|
-
switch (action) {
|
|
354
|
-
case 'submit':
|
|
355
|
-
return service.submit(s, id, message);
|
|
356
|
-
case 'approve':
|
|
357
|
-
return service.approve(s, id, message);
|
|
358
|
-
case 'reject':
|
|
359
|
-
return service.reject(s, id, message);
|
|
360
|
-
case 'publish':
|
|
361
|
-
return service.publish(s, id);
|
|
362
|
-
case 'archive':
|
|
363
|
-
return service.archive(s, id);
|
|
364
|
-
default:
|
|
365
|
-
throw new ArtifactError('INVALID_OPERATION', `Unknown status action "${String(action)}".`, {
|
|
366
|
-
issues: [{ path: 'action', message: `Use one of: ${STATUS_ACTIONS.join(', ')}.` }],
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
}),
|
|
370
323
|
lock: (id, opts) => run((s) => service.lock(s, id, opts?.ttlMs)),
|
|
371
324
|
unlock: (_id, lock) => run((s) => service.unlock(s, lock)),
|
|
372
325
|
getLock: (id) => run((s) => service.getLock(s, id)),
|
|
@@ -375,7 +328,6 @@ export function createServiceClient(service: ArtifactService, scope: Scope | (()
|
|
|
375
328
|
const result = await service.export(s, {
|
|
376
329
|
artifactId: id,
|
|
377
330
|
format: input.format,
|
|
378
|
-
...(input.version === undefined ? {} : { version: input.version }),
|
|
379
331
|
...(input.options ? { options: input.options } : {}),
|
|
380
332
|
...(input.signal ? { signal: input.signal } : {}),
|
|
381
333
|
});
|
package/src/components/card.tsx
CHANGED
|
@@ -3,13 +3,13 @@ import { ArrowUpRight } from 'lucide-react';
|
|
|
3
3
|
import { type ReactNode, useId } from 'react';
|
|
4
4
|
import { useArtkit } from '../context.js';
|
|
5
5
|
import { KindIcon } from './icons.js';
|
|
6
|
-
import { cx,
|
|
6
|
+
import { cx, formatLabel, rootAttributes, type Slots, slot } from './shared.js';
|
|
7
7
|
|
|
8
8
|
export type ArtifactCardPart = 'body' | 'title' | 'meta' | 'open';
|
|
9
9
|
|
|
10
10
|
export interface ArtifactCardProps {
|
|
11
11
|
/** Summary or full artifact (only the summary fields are used). */
|
|
12
|
-
artifact: Pick<ArtifactSummary, 'id' | '
|
|
12
|
+
artifact: Pick<ArtifactSummary, 'id' | 'format' | 'title' | 'version'>;
|
|
13
13
|
onOpen?(artifact: ArtifactCardProps['artifact']): void;
|
|
14
14
|
/** Render the open action as a link instead of a button. */
|
|
15
15
|
href?: string;
|
|
@@ -23,7 +23,7 @@ export interface ArtifactCardProps {
|
|
|
23
23
|
slots?: Slots<ArtifactCardPart>;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/** Compact card for chat messages: title,
|
|
26
|
+
/** Compact card for chat messages: title, format, version and an open action (spec §13.1). */
|
|
27
27
|
export function ArtifactCard(props: ArtifactCardProps) {
|
|
28
28
|
const ctx = useArtkit();
|
|
29
29
|
const { t } = ctx;
|
|
@@ -35,11 +35,11 @@ export function ArtifactCard(props: ArtifactCardProps) {
|
|
|
35
35
|
className={cx('ak-root ak-card', props.className)}
|
|
36
36
|
{...rootAttributes(ctx)}
|
|
37
37
|
aria-labelledby={titleId}
|
|
38
|
-
data-
|
|
38
|
+
data-format={artifact.format}
|
|
39
39
|
>
|
|
40
40
|
{props.icon ?? (
|
|
41
41
|
<span className="ak-card-icon">
|
|
42
|
-
<KindIcon
|
|
42
|
+
<KindIcon format={artifact.format} />
|
|
43
43
|
</span>
|
|
44
44
|
)}
|
|
45
45
|
<div {...slot(props.slots, 'body', 'ak-card-body')}>
|
|
@@ -47,12 +47,11 @@ export function ArtifactCard(props: ArtifactCardProps) {
|
|
|
47
47
|
{artifact.title}
|
|
48
48
|
</h3>
|
|
49
49
|
<div {...slot(props.slots, 'meta', 'ak-card-meta')}>
|
|
50
|
-
<span>{
|
|
50
|
+
<span>{formatLabel(ctx, artifact.format)}</span>
|
|
51
51
|
<span>
|
|
52
52
|
<span className="ak-sr-only">{`${t('version')} ${artifact.version}`}</span>
|
|
53
53
|
<span aria-hidden="true">{t('versionShort', { version: artifact.version })}</span>
|
|
54
54
|
</span>
|
|
55
|
-
<StatusBadge status={artifact.status} />
|
|
56
55
|
{props.streaming ? (
|
|
57
56
|
<span className="ak-badge" data-variant="provisional" role="status">
|
|
58
57
|
{t('generating')}
|