@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.
Files changed (89) hide show
  1. package/dist/cache.d.ts +7 -11
  2. package/dist/cache.d.ts.map +1 -1
  3. package/dist/cache.js +32 -53
  4. package/dist/cache.js.map +1 -1
  5. package/dist/client.d.ts +4 -15
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +2 -29
  8. package/dist/client.js.map +1 -1
  9. package/dist/components/card.d.ts +2 -2
  10. package/dist/components/card.d.ts.map +1 -1
  11. package/dist/components/card.js +3 -3
  12. package/dist/components/card.js.map +1 -1
  13. package/dist/components/editor.d.ts +31 -18
  14. package/dist/components/editor.d.ts.map +1 -1
  15. package/dist/components/editor.js +81 -131
  16. package/dist/components/editor.js.map +1 -1
  17. package/dist/components/icons.d.ts +2 -2
  18. package/dist/components/icons.d.ts.map +1 -1
  19. package/dist/components/icons.js +2 -2
  20. package/dist/components/icons.js.map +1 -1
  21. package/dist/components/panel.d.ts +13 -16
  22. package/dist/components/panel.d.ts.map +1 -1
  23. package/dist/components/panel.js +13 -97
  24. package/dist/components/panel.js.map +1 -1
  25. package/dist/components/shared.d.ts +2 -6
  26. package/dist/components/shared.d.ts.map +1 -1
  27. package/dist/components/shared.js +2 -6
  28. package/dist/components/shared.js.map +1 -1
  29. package/dist/components/view.d.ts +7 -8
  30. package/dist/components/view.d.ts.map +1 -1
  31. package/dist/components/view.js +16 -15
  32. package/dist/components/view.js.map +1 -1
  33. package/dist/context.d.ts +14 -9
  34. package/dist/context.d.ts.map +1 -1
  35. package/dist/context.js.map +1 -1
  36. package/dist/editor-session.d.ts +77 -165
  37. package/dist/editor-session.d.ts.map +1 -1
  38. package/dist/editor-session.js +164 -586
  39. package/dist/editor-session.js.map +1 -1
  40. package/dist/hooks.d.ts +2 -23
  41. package/dist/hooks.d.ts.map +1 -1
  42. package/dist/hooks.js +24 -116
  43. package/dist/hooks.js.map +1 -1
  44. package/dist/i18n.d.ts +4 -66
  45. package/dist/i18n.d.ts.map +1 -1
  46. package/dist/i18n.js +6 -68
  47. package/dist/i18n.js.map +1 -1
  48. package/dist/index.d.ts +6 -9
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +4 -8
  51. package/dist/index.js.map +1 -1
  52. package/dist/provider.d.ts +1 -1
  53. package/dist/provider.d.ts.map +1 -1
  54. package/dist/provider.js +12 -12
  55. package/dist/provider.js.map +1 -1
  56. package/dist/styles.d.ts +1 -1
  57. package/dist/styles.d.ts.map +1 -1
  58. package/dist/styles.js +4 -4
  59. package/package.json +2 -2
  60. package/src/cache.ts +32 -55
  61. package/src/client.ts +7 -55
  62. package/src/components/card.tsx +6 -7
  63. package/src/components/editor.tsx +208 -308
  64. package/src/components/icons.tsx +2 -2
  65. package/src/components/panel.tsx +43 -300
  66. package/src/components/shared.tsx +3 -12
  67. package/src/components/view.tsx +26 -29
  68. package/src/context.ts +16 -11
  69. package/src/editor-session.ts +197 -675
  70. package/src/hooks.ts +25 -140
  71. package/src/i18n.ts +6 -68
  72. package/src/index.ts +7 -39
  73. package/src/provider.tsx +13 -13
  74. package/src/styles.ts +4 -4
  75. package/dist/components/approval.d.ts +0 -34
  76. package/dist/components/approval.d.ts.map +0 -1
  77. package/dist/components/approval.js +0 -80
  78. package/dist/components/approval.js.map +0 -1
  79. package/dist/components/version-diff.d.ts +0 -35
  80. package/dist/components/version-diff.d.ts.map +0 -1
  81. package/dist/components/version-diff.js +0 -85
  82. package/dist/components/version-diff.js.map +0 -1
  83. package/dist/line-diff.d.ts +0 -25
  84. package/dist/line-diff.d.ts.map +0 -1
  85. package/dist/line-diff.js +0 -96
  86. package/dist/line-diff.js.map +0 -1
  87. package/src/components/approval.tsx +0 -219
  88. package/src/components/version-diff.tsx +0 -266
  89. 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). Versions are immutable, so an
16
- * entry for a specific version never needs revalidation; the `latest` entry only moves forward and is
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, version?: number): CacheEntry {
27
- return this.entries.get(keyOf(id, version)) ?? EMPTY;
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(keyOf(id))?.artifact;
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 a known artifact version (commit result, prop, status change). `latest` only moves forward,
54
- * except that a status change on the same version replaces it.
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
- const sameVersionChanged =
59
- current !== undefined &&
60
- artifact.version === current.version &&
61
- (artifact.status !== current.status ||
62
- artifact.meta.updatedAt !== current.meta.updatedAt ||
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 unless a fresh value is cached. `force` revalidates `latest` (specific versions never change). */
74
- load(id: string, version?: number, force = false): Promise<Artifact> {
75
- const key = keyOf(id, version);
76
- const existing = this.entries.get(key);
77
- if (existing?.artifact && (version !== undefined || !force)) return Promise.resolve(existing.artifact);
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, version, { ...(existing ?? {}), loading: true });
67
+ this.entries.set(id, { ...(existing ?? {}), loading: true });
81
68
  this.notify(id);
82
- const promise = this.client.get(id, version === undefined ? undefined : { version }).then(
69
+ const promise = this.client.get(id).then(
83
70
  (artifact) => {
84
- this.inflight.delete(key);
85
- if (version === undefined) {
86
- // A commit may have landed while the request was in flight; never move `latest` backwards.
87
- const current = this.latest(id);
88
- if (current && current.version > artifact.version) {
89
- this.set(id, undefined, { artifact: current, loading: false });
90
- this.notify(id);
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(artifact.id, artifact.version, { artifact, loading: false });
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(key);
103
- const previous = this.entries.get(key);
104
- this.set(id, version, {
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(key, promise);
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 ProjectionMode,
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?: ProjectionMode;
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 & { version?: number }): Promise<Artifact>;
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', withQuery(path(id), { version: opts?.version }), undefined, opts?.signal),
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, opts) =>
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
  });
@@ -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, kindLabel, rootAttributes, type Slots, StatusBadge, slot } from './shared.js';
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' | 'kind' | 'title' | 'version' | 'status'>;
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, kind, version, status and an open action (spec §13.1). */
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-kind={artifact.kind}
38
+ data-format={artifact.format}
39
39
  >
40
40
  {props.icon ?? (
41
41
  <span className="ak-card-icon">
42
- <KindIcon kind={artifact.kind} />
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>{kindLabel(ctx, artifact.kind)}</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')}