@tai42/studio-sdk 6.1.1 → 7.0.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 (84) hide show
  1. package/dist/components/completion-input.d.ts.map +1 -1
  2. package/dist/components/completion-input.js +19 -2
  3. package/dist/components/completion-input.js.map +1 -1
  4. package/dist/components/components.css +4 -3
  5. package/dist/components/explorer-view.d.ts.map +1 -1
  6. package/dist/components/explorer-view.js +11 -22
  7. package/dist/components/explorer-view.js.map +1 -1
  8. package/dist/components/field.d.ts +14 -1
  9. package/dist/components/field.d.ts.map +1 -1
  10. package/dist/components/field.js +5 -2
  11. package/dist/components/field.js.map +1 -1
  12. package/dist/components/open-target.d.ts +68 -0
  13. package/dist/components/open-target.d.ts.map +1 -0
  14. package/dist/components/open-target.js +48 -0
  15. package/dist/components/open-target.js.map +1 -0
  16. package/dist/expression/ExpressionField.d.ts +140 -0
  17. package/dist/expression/ExpressionField.d.ts.map +1 -0
  18. package/dist/expression/ExpressionField.js +88 -0
  19. package/dist/expression/ExpressionField.js.map +1 -0
  20. package/dist/expression/context.d.ts +50 -0
  21. package/dist/expression/context.d.ts.map +1 -0
  22. package/dist/expression/context.js +53 -0
  23. package/dist/expression/context.js.map +1 -0
  24. package/dist/expression/types.d.ts +157 -0
  25. package/dist/expression/types.d.ts.map +1 -0
  26. package/dist/expression/types.js +11 -0
  27. package/dist/expression/types.js.map +1 -0
  28. package/dist/hooks/useApi.d.ts +8 -0
  29. package/dist/hooks/useApi.d.ts.map +1 -1
  30. package/dist/index.d.ts +8 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +7 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/plugin/registry.d.ts.map +1 -1
  35. package/dist/plugin/registry.js +39 -1
  36. package/dist/plugin/registry.js.map +1 -1
  37. package/dist/plugin/types.d.ts +31 -0
  38. package/dist/plugin/types.d.ts.map +1 -1
  39. package/dist/schema-editor/SchemaEditor.d.ts.map +1 -1
  40. package/dist/schema-editor/SchemaEditor.js +8 -4
  41. package/dist/schema-editor/SchemaEditor.js.map +1 -1
  42. package/dist/schema-form/classify.d.ts +5 -3
  43. package/dist/schema-form/classify.d.ts.map +1 -1
  44. package/dist/schema-form/classify.js +42 -16
  45. package/dist/schema-form/classify.js.map +1 -1
  46. package/dist/schema-form/default-value.d.ts.map +1 -1
  47. package/dist/schema-form/default-value.js +4 -1
  48. package/dist/schema-form/default-value.js.map +1 -1
  49. package/dist/schema-form/field-node.d.ts.map +1 -1
  50. package/dist/schema-form/field-node.js +3 -3
  51. package/dist/schema-form/field-node.js.map +1 -1
  52. package/dist/schema-form/json-field.d.ts +14 -0
  53. package/dist/schema-form/json-field.d.ts.map +1 -0
  54. package/dist/schema-form/json-field.js +104 -0
  55. package/dist/schema-form/json-field.js.map +1 -0
  56. package/dist/schema-form/types.d.ts +2 -2
  57. package/dist/schema-form/types.js +2 -2
  58. package/dist/schema-form/validate.d.ts.map +1 -1
  59. package/dist/schema-form/validate.js +11 -4
  60. package/dist/schema-form/validate.js.map +1 -1
  61. package/package.json +2 -2
  62. package/src/components/completion-input.tsx +19 -2
  63. package/src/components/components.css +4 -3
  64. package/src/components/explorer-view.tsx +16 -27
  65. package/src/components/field.tsx +20 -2
  66. package/src/components/open-target.ts +121 -0
  67. package/src/expression/ExpressionField.tsx +330 -0
  68. package/src/expression/context.tsx +83 -0
  69. package/src/expression/types.ts +164 -0
  70. package/src/index.ts +26 -0
  71. package/src/plugin/registry.ts +50 -1
  72. package/src/plugin/types.ts +32 -0
  73. package/src/schema-editor/SchemaEditor.tsx +8 -4
  74. package/src/schema-form/classify.ts +103 -18
  75. package/src/schema-form/default-value.ts +4 -1
  76. package/src/schema-form/field-node.tsx +7 -4
  77. package/src/schema-form/json-field.tsx +143 -0
  78. package/src/schema-form/types.ts +2 -2
  79. package/src/schema-form/validate.ts +10 -4
  80. package/dist/schema-form/unsupported-notice.d.ts +0 -18
  81. package/dist/schema-form/unsupported-notice.d.ts.map +0 -1
  82. package/dist/schema-form/unsupported-notice.js +0 -8
  83. package/dist/schema-form/unsupported-notice.js.map +0 -1
  84. package/src/schema-form/unsupported-notice.tsx +0 -39
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * `validateAgainstSchema` — a loud, structured pre-submit check. It walks the
3
3
  * schema against a value and returns a path-keyed `SchemaFormErrors` bag:
4
- * required-presence, type/format mismatches, enum/const violations, and any
5
- * `unsupported` construct (surfaced, never hidden). It never throws on a
4
+ * required-presence, type/format mismatches, enum/const violations, and the
5
+ * container constraint on a free-form `json` field. It never throws on a
6
6
  * malformed value — a malformed value IS the error it reports.
7
7
  */
8
8
  import { classifySchema } from './classify';
@@ -52,8 +52,15 @@ function walk(schema, value, path, root, errors, maxUploadBytes) {
52
52
  }
53
53
  const { model } = classified;
54
54
  switch (model.kind) {
55
- case 'unsupported':
56
- errors[path] = `unsupported schema (${model.reason})`;
55
+ case 'json':
56
+ // A free-form JSON field: any JSON is accepted, subject only to the
57
+ // container the schema commits to (mirrors the renderer's inline check).
58
+ if (model.jsonType === 'object' && !isPlainObject(value)) {
59
+ errors[path] = 'must be a JSON object';
60
+ }
61
+ else if (model.jsonType === 'array' && !Array.isArray(value)) {
62
+ errors[path] = 'must be a JSON array';
63
+ }
57
64
  return;
58
65
  case 'const':
59
66
  if (value !== model.value)
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/schema-form/validate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/schema-form/validate.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tai42/studio-sdk",
3
- "version": "6.1.1",
3
+ "version": "7.0.0",
4
4
  "description": "The Studio plugin contract, shared UI components, and hooks that every feature builds on.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "tai42 <oss@tai42.ai>",
@@ -90,7 +90,7 @@
90
90
  "@radix-ui/react-select": "^2.3.2",
91
91
  "@radix-ui/react-tabs": "^1.1.16",
92
92
  "@radix-ui/react-tooltip": "^1.2.11",
93
- "@tai42/api-client": "6.1.1"
93
+ "@tai42/api-client": "7.0.0"
94
94
  },
95
95
  "scripts": {
96
96
  "build": "tsc -b tsconfig.build.json && node scripts/copy-assets.mjs",
@@ -30,10 +30,27 @@ export interface CompletionInputProps {
30
30
  const wrapStyle: CSSProperties = { position: 'relative' };
31
31
 
32
32
  /**
33
- * The popup sits directly under the field rather than against its top edge. It
34
- * is a `<ul>`, so it also drops the marker a host without a CSS reset would draw.
33
+ * The popup FLOATS below the field: absolutely positioned within the
34
+ * `position: relative` wrapper (`top: 100%` = the field's bottom edge, stretched
35
+ * `left`/`right` to the field's width) so it is taken OUT OF FLOW — it neither
36
+ * displaces the content below the field nor gets laid out inside it. Its stacking
37
+ * comes from `tai-select-content`'s `--tai-z-popover` z-index, which was inert
38
+ * while the list was in-flow (z-index needs a positioned box) and is exactly what
39
+ * this `position` now activates, so the list clears a sibling control it overlaps.
40
+ *
41
+ * Absolute-in-wrapper rather than a portal on purpose: this is an inline form
42
+ * control (a `SchemaForm` string field). A portal would need anchor-rect popper
43
+ * plumbing, and it buys nothing here — the wrapper is a plain flow box, so the
44
+ * only container that could clip the list is a scrollable ancestor (e.g. the
45
+ * `Dialog` body), where an absolute list simply extends that ancestor's scroll
46
+ * area instead of being cut off. It is still a `<ul>`, so it also drops the marker
47
+ * a host without a CSS reset would draw.
35
48
  */
36
49
  const listStyle: CSSProperties = {
50
+ position: 'absolute',
51
+ top: '100%',
52
+ left: 0,
53
+ right: 0,
37
54
  marginTop: 'var(--tai-space-1)',
38
55
  marginBottom: 0,
39
56
  listStyle: 'none',
@@ -533,8 +533,9 @@
533
533
  clears the dialog scrim that would otherwise cover and swallow it. The
534
534
  height cap yields to the space the popper actually has, so a long list in a
535
535
  short viewport still scrolls inside the screen; the fallback covers the
536
- in-flow suggestion list, which wears these classes without a popper to
537
- publish that measurement. */
536
+ completion suggestion list, which wears these classes without a Radix popper
537
+ to publish that measurement (it floats via its own absolute position, not a
538
+ popper — see `completion-input.tsx`). */
538
539
  .tai-select-content {
539
540
  z-index: var(--tai-z-popover);
540
541
  /* Capped against the VIEWPORT as well as its own content, like every other
@@ -545,7 +546,7 @@
545
546
  overflow-y: auto;
546
547
  /* The cap is on the BORDER box, or it is not a cap at all: with the default
547
548
  `content-box` the side padding and the 1 px boundary sit outside it. Radix
548
- inline-styles `box-sizing: border-box` on its own popper, but the in-flow
549
+ inline-styles `box-sizing: border-box` on its own popper, but the completion
549
550
  suggestion list wears this class as a plain `<ul>` with no Radix near it. */
550
551
  box-sizing: border-box;
551
552
  background: var(--tai-color-surface-raised);
@@ -12,12 +12,13 @@
12
12
  * {@link matchesSelectedTags}, {@link UNTAGGED_TOKEN}) so every consuming screen
13
13
  * shares one vocabulary/untagged-sentinel/OR-match rule rather than copying it.
14
14
  */
15
- import { useCallback, useEffect, useState, type MouseEvent, type ReactNode } from 'react';
15
+ import { useCallback, useEffect, useState, type ReactNode } from 'react';
16
16
 
17
17
  import { EntityCardGrid } from './entity-card-grid';
18
18
  import { childFolders, FolderBreadcrumb, FolderRow, type Folder } from './folder-nav';
19
19
  import { SearchIcon } from './icons';
20
20
  import { TextInput } from './inputs';
21
+ import { openTargetProps, type OpenTargetProps } from './open-target';
21
22
  import { Button, Card, EmptyState } from './primitives';
22
23
  import { ScrollRegion } from './scroll-region';
23
24
  import { Select } from './select';
@@ -373,32 +374,20 @@ export function ExplorerView<T>({
373
374
  tags.onChange(next);
374
375
  };
375
376
 
376
- // The pointer-open props for one item's row/card, present only when `onOpenItem`
377
- // is set. The handler yields to any nested interactive element the click landed
378
- // on (a name link, an actions kebab) so it never hijacks its activation; a
379
- // portalled menu (Radix) never bubbles here, so the role selectors are defensive
380
- // for an inline menu only.
381
- const openProps = (
382
- item: T,
383
- ): { onClick?: (event: MouseEvent<HTMLElement>) => void; style?: { cursor: 'pointer' } } => {
384
- if (onOpenItem === undefined) return {};
385
- return {
386
- onClick: (event) => {
387
- // A press-drag that selects the name text also fires a click; that is a
388
- // selection, not an open intent.
389
- if (window.getSelection()?.isCollapsed === false) return;
390
- const target = event.target;
391
- if (
392
- target instanceof Element &&
393
- target.closest('button, a, [role="menu"], [role="menuitem"]') !== null
394
- ) {
395
- return;
396
- }
397
- onOpenItem(item);
398
- },
399
- style: { cursor: 'pointer' },
400
- };
401
- };
377
+ // The pointer-open props for one item's row/card, via the shared house pattern
378
+ // ({@link openTargetProps}): present only when `onOpenItem` is set, it yields to
379
+ // the item's own nested interactive elements (its name link, an actions kebab)
380
+ // and to a text-selection drag. The accessible activation path stays that nested
381
+ // name link, so no `keyboard` here — the row is never itself a focus stop.
382
+ const openProps = (item: T): OpenTargetProps =>
383
+ openTargetProps({
384
+ onOpen:
385
+ onOpenItem === undefined
386
+ ? undefined
387
+ : () => {
388
+ onOpenItem(item);
389
+ },
390
+ });
402
391
 
403
392
  const hasEntries = subfolders.length > 0 || filtered.length > 0;
404
393
 
@@ -91,6 +91,19 @@ export interface FieldProps {
91
91
  * `group`.
92
92
  */
93
93
  readonly group?: boolean;
94
+ /**
95
+ * Render the label for ASSISTIVE TECH ONLY — visually hidden, but still the
96
+ * control's accessible name through the very same `<label for>` (or, in a group,
97
+ * the same `aria-labelledby`). The label element stays in the DOM behind
98
+ * `tai-visually-hidden`, so nothing about the accessible name changes; only its
99
+ * paint is dropped. It is the standard idiom for a dense grid of otherwise
100
+ * identical fields, where each row needs a UNIQUE, addressable name (a
101
+ * voice-control user can only say a name that exists) but there is no room for a
102
+ * visible label on every row — the same trade `RadioGroup`'s `visuallyHiddenLabel`
103
+ * and `ViewToggle` make. The label text still governs WCAG 2.5.3 (Label in Name),
104
+ * so a caller passes the real per-row name as `label`, not a decoration.
105
+ */
106
+ readonly hideLabel?: boolean;
94
107
  }
95
108
 
96
109
  export function Field({
@@ -101,6 +114,7 @@ export function Field({
101
114
  style,
102
115
  className,
103
116
  group = false,
117
+ hideLabel = false,
104
118
  }: FieldProps) {
105
119
  const controlId = useId();
106
120
  const labelId = `${controlId}-label`;
@@ -122,6 +136,10 @@ export function Field({
122
136
  ? { controlId: undefined, describedBy: undefined, invalid: error !== undefined }
123
137
  : { controlId, describedBy, invalid: error !== undefined };
124
138
 
139
+ // The label keeps its `htmlFor`/`id` wiring either way — `hideLabel` only drops
140
+ // its PAINT, so the accessible name it gives the control is unchanged.
141
+ const labelClassName = hideLabel ? 'tai-field-label tai-visually-hidden' : 'tai-field-label';
142
+
125
143
  return (
126
144
  <FieldContext.Provider value={value}>
127
145
  <div
@@ -129,11 +147,11 @@ export function Field({
129
147
  style={style}
130
148
  >
131
149
  {group ? (
132
- <span id={labelId} className="tai-field-label">
150
+ <span id={labelId} className={labelClassName}>
133
151
  {label}
134
152
  </span>
135
153
  ) : (
136
- <label id={labelId} htmlFor={controlId} className="tai-field-label">
154
+ <label id={labelId} htmlFor={controlId} className={labelClassName}>
137
155
  {label}
138
156
  </label>
139
157
  )}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * `openTargetProps` — the ONE house pattern for a whole-row / whole-card "open"
3
+ * affordance.
4
+ *
5
+ * A navigable list entry (a table `<TR>`, a card `<div role="listitem">`) has a
6
+ * single natural destination: opening it. The industry-standard affordance is to
7
+ * make the WHOLE entry the click target — not only the name link buried in its
8
+ * first cell — while never HIJACKING the interactive elements nested inside it
9
+ * (the name link itself, an actions kebab, a Run button, an expandable preview).
10
+ * This helper is that affordance, extracted once so every surface spreads the
11
+ * same object rather than hand-rolling — and subtly diverging on — the yield
12
+ * rules.
13
+ *
14
+ * The contract, spread onto the entry element:
15
+ * - `cursor: pointer`, so the whole entry reads as activatable.
16
+ * - a click handler that opens the entry, EXCEPT when the click
17
+ * • landed on (or inside) a nested interactive element — a `button`, an
18
+ * `a`, or an inline `[role="menu"]`/`[role="menuitem"]` — which owns its
19
+ * own activation, or
20
+ * • is the tail of a press-drag that SELECTED text (a non-collapsed
21
+ * selection), which is a read gesture, not an open intent.
22
+ * - with `keyboard`, `tabIndex: 0` and an Enter/Space handler under the same
23
+ * yield rules, so the entry is operable without a pointer.
24
+ *
25
+ * Keyboard note: PREFER giving the entry a real nested interactive element (the
26
+ * name `AppLink`) as its accessible activation path and leaving `keyboard` off —
27
+ * the helper yields to that element, so it stays the one keyboard/AT target and
28
+ * the whole-entry click is a pure pointer convenience layered over it. Reach for
29
+ * `keyboard` only when the entry genuinely has no such nested control (a run row
30
+ * of timestamps and badges), where the entry itself must become the focus stop.
31
+ *
32
+ * Dependency-free: it reads only the DOM `event.target`, `window.getSelection()`,
33
+ * and returns plain React attributes — nothing to bundle, nothing to mock.
34
+ */
35
+ import type { KeyboardEvent, MouseEvent } from 'react';
36
+
37
+ /** The nested interactive elements a whole-target open always yields to: a click
38
+ * or key that originates on one of these (or a descendant, e.g. an icon `<svg>`
39
+ * inside a button) is that element's own activation, never an open of the entry.
40
+ * The menu roles cover an INLINE menu; a portalled menu (Radix) never bubbles to
41
+ * the entry, so it needs no selector here. */
42
+ const NESTED_INTERACTIVE = 'button, a, [role="menu"], [role="menuitem"]';
43
+
44
+ export interface OpenTargetOptions {
45
+ /** Open the entry. `undefined` disables the affordance entirely — the helper
46
+ * returns empty props so the element stays inert (no cursor, no handlers),
47
+ * which lets a caller wire the whole thing behind an optional prop. */
48
+ readonly onOpen: (() => void) | undefined;
49
+ /** Extra selector(s) that also count as "nested interactive" for THIS surface,
50
+ * merged with the built-in set. Use it for a marked non-`button`/`a` subtree
51
+ * that owns its own clicks (e.g. an expandable preview keyed by a data
52
+ * attribute). Standard CSS selector syntax, comma-separated. */
53
+ readonly ignoreWithin?: string;
54
+ /** Make the entry itself keyboard-operable: a `tabIndex: 0` focus stop with
55
+ * Enter/Space activation. Leave off when a nested interactive element already
56
+ * carries the accessible path (see the keyboard note above) — adding it there
57
+ * would create a duplicate tab stop. */
58
+ readonly keyboard?: boolean;
59
+ }
60
+
61
+ /** The React attributes {@link openTargetProps} yields — spread onto the entry
62
+ * element (`<TR>`, card `<div>`). Every field is optional so the disabled case
63
+ * is the empty object. */
64
+ export interface OpenTargetProps {
65
+ readonly onClick?: (event: MouseEvent<HTMLElement>) => void;
66
+ readonly onKeyDown?: (event: KeyboardEvent<HTMLElement>) => void;
67
+ readonly tabIndex?: number;
68
+ readonly style?: { readonly cursor: 'pointer' };
69
+ }
70
+
71
+ /** True when the event originated on, or inside, an element the entry must yield
72
+ * to — a built-in nested-interactive element or a caller `ignoreWithin` match. */
73
+ function landedOnNestedInteractive(target: EventTarget | null, selector: string): boolean {
74
+ return target instanceof Element && target.closest(selector) !== null;
75
+ }
76
+
77
+ /**
78
+ * Build the whole-target open props for one entry. See the module doc for the
79
+ * contract; pass `onOpen: undefined` to get an inert entry (empty props).
80
+ */
81
+ export function openTargetProps({
82
+ onOpen,
83
+ ignoreWithin,
84
+ keyboard = false,
85
+ }: OpenTargetOptions): OpenTargetProps {
86
+ if (onOpen === undefined) return {};
87
+
88
+ const selector =
89
+ ignoreWithin === undefined ? NESTED_INTERACTIVE : `${NESTED_INTERACTIVE}, ${ignoreWithin}`;
90
+
91
+ const props: {
92
+ onClick: (event: MouseEvent<HTMLElement>) => void;
93
+ onKeyDown?: (event: KeyboardEvent<HTMLElement>) => void;
94
+ tabIndex?: number;
95
+ style: { readonly cursor: 'pointer' };
96
+ } = {
97
+ onClick: (event) => {
98
+ // A press-drag that selects the entry's text also fires a click; that is a
99
+ // read gesture, not an open intent, so an active (non-collapsed) selection
100
+ // suppresses the open. A keyboard Enter cannot leave a selection, so its
101
+ // handler skips this check.
102
+ if (window.getSelection()?.isCollapsed === false) return;
103
+ if (landedOnNestedInteractive(event.target, selector)) return;
104
+ onOpen();
105
+ },
106
+ style: { cursor: 'pointer' },
107
+ };
108
+
109
+ if (keyboard) {
110
+ props.tabIndex = 0;
111
+ props.onKeyDown = (event) => {
112
+ if (event.key !== 'Enter' && event.key !== ' ') return;
113
+ if (landedOnNestedInteractive(event.target, selector)) return;
114
+ // Space would otherwise scroll the region; Enter/Space both activate.
115
+ event.preventDefault();
116
+ onOpen();
117
+ };
118
+ }
119
+
120
+ return props;
121
+ }