@wildwinter/expr-editor 0.15.3 → 0.16.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/index.d.cts CHANGED
@@ -212,6 +212,16 @@ declare const displayName: (e: {
212
212
  declare function filterCatalogue(entries: readonly CatalogueEntry[], filter?: Filter): CatalogueEntry[];
213
213
  /** Filter by a case-insensitive query against the display name AND the purpose text. */
214
214
  declare function searchCatalogue(entries: readonly CatalogueEntry[], query: string, defaultScope: string): CatalogueEntry[];
215
+ /** The tip on a pill naming another engine's property (see `otherEngineScopes`). */
216
+ declare function otherEngineTip(label: string): string;
217
+ /** What a property pill says about itself: an issue when the catalogue does not know it, unless its
218
+ * scope is another engine's (that engine checks it), and the tip on hover. */
219
+ declare function propertyPillNote(entry: CatalogueEntry | null | undefined, scope: string, label: string, otherEngineScopes?: readonly string[]): {
220
+ issue?: string;
221
+ title?: string;
222
+ };
223
+ /** The scope of a written reference (`@patter.gold` -> "patter"), or undefined for a bare `@name`. */
224
+ declare function scopeOfRef(ref: string): string | undefined;
215
225
  /** Group entries by scope, scopes in `scopeOrder` first (then alphabetical), names sorted within. */
216
226
  declare function groupByScope(entries: readonly CatalogueEntry[], scopeOrder?: string[]): Array<{
217
227
  scope: string;
@@ -286,6 +296,8 @@ interface EditCtx {
286
296
  defaultScope: string;
287
297
  catalogue: CatalogueEntry[];
288
298
  scopeOrder: string[];
299
+ /** See ExpressionEditorOptions.otherEngineScopes. */
300
+ otherEngineScopes?: readonly string[];
289
301
  functions: FunctionTemplateSpec[];
290
302
  byPath: Map<string, ExpressionValidationIssue[]>;
291
303
  /** The current root AST (never null here; the empty/always state is handled by mount). */
@@ -347,6 +359,10 @@ interface ExpressionEditorOptions {
347
359
  catalogue: CatalogueEntry[];
348
360
  /** Scope display order for the picker groups. */
349
361
  scopeOrder?: string[];
362
+ /** Scopes another engine owns (a product family's shared vocabulary, such as `patter` in a
363
+ * Storylets card): the catalogue never lists their names, and the other engine checks them, so a
364
+ * reference into one renders as an ordinary property pill instead of an unknown one. */
365
+ otherEngineScopes?: readonly string[];
350
366
  /** Dialect-specific clause templates (beyond the generic property comparisons). */
351
367
  functions?: FunctionTemplateSpec[];
352
368
  /** "tree" (default) for conditions, "flat" for a single inline expression. */
@@ -439,6 +455,10 @@ interface EffectsEditorOptions {
439
455
  /** Properties the target picker + value editors offer. */
440
456
  catalogue: CatalogueEntry[];
441
457
  scopeOrder?: string[];
458
+ /** Scopes another engine owns (a product family's shared vocabulary, such as `patter` in a
459
+ * Storylets card): the catalogue never lists their names, and the other engine checks them, so a
460
+ * reference into one renders as an ordinary property pill instead of an unknown one. */
461
+ otherEngineScopes?: readonly string[];
442
462
  /** Dialect clause templates passed through to each value editor. */
443
463
  functions?: FunctionTemplateSpec[];
444
464
  /** Known host event names to suggest when adding an `emit` (optional). */
@@ -511,6 +531,10 @@ interface PreviewOptions {
511
531
  dialect: Dialect;
512
532
  catalogue: CatalogueEntry[];
513
533
  scopeOrder?: string[];
534
+ /** Scopes another engine owns (a product family's shared vocabulary, such as `patter` in a
535
+ * Storylets card): the catalogue never lists their names, and the other engine checks them, so a
536
+ * reference into one renders as an ordinary property pill instead of an unknown one. */
537
+ otherEngineScopes?: readonly string[];
514
538
  /** Resolve a node id to a readable label for seen()/visits() node pills. */
515
539
  nodeLabel?: (id: string) => string;
516
540
  /** Host actions for a property pill (e.g. "Go to definition"). When set, the
@@ -560,4 +584,4 @@ interface ValueWizardOptions {
560
584
  /** Build the wizard UI into a fresh element (drive it via the host's popover). */
561
585
  declare function valueWizard(opts: ValueWizardOptions): HTMLElement;
562
586
 
563
- export { ARITHMETIC_OPS, BINARY_LABEL, COMPARABLE_TYPES, COMPARISON_OPS, type CatalogueEntry, EQUALITY_OPS, type EditCtx, type EditorEffect, type EffectsEditorHandle, type EffectsEditorOptions, type ExpressionEditorHandle, type ExpressionEditorOptions, type Filter, type FunctionTemplateSpec, OP_WORD, type PreviewOptions, type TreeRow, UNARY_LABEL, type Validation, type ValueWizardOptions, type WizardSpec, type WizardStepSpec, type WizardValue, addArg, addChildToContainer, addEmit, addSet, advancedRef, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagChangeSrc, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, normaliseRef, notNode, numLit, opSwapGroup, pathKey, placeholderForOp, propertyTip, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, targetRefOf, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
587
+ export { ARITHMETIC_OPS, BINARY_LABEL, COMPARABLE_TYPES, COMPARISON_OPS, type CatalogueEntry, EQUALITY_OPS, type EditCtx, type EditorEffect, type EffectsEditorHandle, type EffectsEditorOptions, type ExpressionEditorHandle, type ExpressionEditorOptions, type Filter, type FunctionTemplateSpec, OP_WORD, type PreviewOptions, type TreeRow, UNARY_LABEL, type Validation, type ValueWizardOptions, type WizardSpec, type WizardStepSpec, type WizardValue, addArg, addChildToContainer, addEmit, addSet, advancedRef, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagChangeSrc, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, normaliseRef, notNode, numLit, opSwapGroup, otherEngineTip, pathKey, placeholderForOp, propertyPillNote, propertyTip, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopeOfRef, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, targetRefOf, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
package/dist/index.d.ts CHANGED
@@ -212,6 +212,16 @@ declare const displayName: (e: {
212
212
  declare function filterCatalogue(entries: readonly CatalogueEntry[], filter?: Filter): CatalogueEntry[];
213
213
  /** Filter by a case-insensitive query against the display name AND the purpose text. */
214
214
  declare function searchCatalogue(entries: readonly CatalogueEntry[], query: string, defaultScope: string): CatalogueEntry[];
215
+ /** The tip on a pill naming another engine's property (see `otherEngineScopes`). */
216
+ declare function otherEngineTip(label: string): string;
217
+ /** What a property pill says about itself: an issue when the catalogue does not know it, unless its
218
+ * scope is another engine's (that engine checks it), and the tip on hover. */
219
+ declare function propertyPillNote(entry: CatalogueEntry | null | undefined, scope: string, label: string, otherEngineScopes?: readonly string[]): {
220
+ issue?: string;
221
+ title?: string;
222
+ };
223
+ /** The scope of a written reference (`@patter.gold` -> "patter"), or undefined for a bare `@name`. */
224
+ declare function scopeOfRef(ref: string): string | undefined;
215
225
  /** Group entries by scope, scopes in `scopeOrder` first (then alphabetical), names sorted within. */
216
226
  declare function groupByScope(entries: readonly CatalogueEntry[], scopeOrder?: string[]): Array<{
217
227
  scope: string;
@@ -286,6 +296,8 @@ interface EditCtx {
286
296
  defaultScope: string;
287
297
  catalogue: CatalogueEntry[];
288
298
  scopeOrder: string[];
299
+ /** See ExpressionEditorOptions.otherEngineScopes. */
300
+ otherEngineScopes?: readonly string[];
289
301
  functions: FunctionTemplateSpec[];
290
302
  byPath: Map<string, ExpressionValidationIssue[]>;
291
303
  /** The current root AST (never null here; the empty/always state is handled by mount). */
@@ -347,6 +359,10 @@ interface ExpressionEditorOptions {
347
359
  catalogue: CatalogueEntry[];
348
360
  /** Scope display order for the picker groups. */
349
361
  scopeOrder?: string[];
362
+ /** Scopes another engine owns (a product family's shared vocabulary, such as `patter` in a
363
+ * Storylets card): the catalogue never lists their names, and the other engine checks them, so a
364
+ * reference into one renders as an ordinary property pill instead of an unknown one. */
365
+ otherEngineScopes?: readonly string[];
350
366
  /** Dialect-specific clause templates (beyond the generic property comparisons). */
351
367
  functions?: FunctionTemplateSpec[];
352
368
  /** "tree" (default) for conditions, "flat" for a single inline expression. */
@@ -439,6 +455,10 @@ interface EffectsEditorOptions {
439
455
  /** Properties the target picker + value editors offer. */
440
456
  catalogue: CatalogueEntry[];
441
457
  scopeOrder?: string[];
458
+ /** Scopes another engine owns (a product family's shared vocabulary, such as `patter` in a
459
+ * Storylets card): the catalogue never lists their names, and the other engine checks them, so a
460
+ * reference into one renders as an ordinary property pill instead of an unknown one. */
461
+ otherEngineScopes?: readonly string[];
442
462
  /** Dialect clause templates passed through to each value editor. */
443
463
  functions?: FunctionTemplateSpec[];
444
464
  /** Known host event names to suggest when adding an `emit` (optional). */
@@ -511,6 +531,10 @@ interface PreviewOptions {
511
531
  dialect: Dialect;
512
532
  catalogue: CatalogueEntry[];
513
533
  scopeOrder?: string[];
534
+ /** Scopes another engine owns (a product family's shared vocabulary, such as `patter` in a
535
+ * Storylets card): the catalogue never lists their names, and the other engine checks them, so a
536
+ * reference into one renders as an ordinary property pill instead of an unknown one. */
537
+ otherEngineScopes?: readonly string[];
514
538
  /** Resolve a node id to a readable label for seen()/visits() node pills. */
515
539
  nodeLabel?: (id: string) => string;
516
540
  /** Host actions for a property pill (e.g. "Go to definition"). When set, the
@@ -560,4 +584,4 @@ interface ValueWizardOptions {
560
584
  /** Build the wizard UI into a fresh element (drive it via the host's popover). */
561
585
  declare function valueWizard(opts: ValueWizardOptions): HTMLElement;
562
586
 
563
- export { ARITHMETIC_OPS, BINARY_LABEL, COMPARABLE_TYPES, COMPARISON_OPS, type CatalogueEntry, EQUALITY_OPS, type EditCtx, type EditorEffect, type EffectsEditorHandle, type EffectsEditorOptions, type ExpressionEditorHandle, type ExpressionEditorOptions, type Filter, type FunctionTemplateSpec, OP_WORD, type PreviewOptions, type TreeRow, UNARY_LABEL, type Validation, type ValueWizardOptions, type WizardSpec, type WizardStepSpec, type WizardValue, addArg, addChildToContainer, addEmit, addSet, advancedRef, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagChangeSrc, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, normaliseRef, notNode, numLit, opSwapGroup, pathKey, placeholderForOp, propertyTip, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, targetRefOf, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
587
+ export { ARITHMETIC_OPS, BINARY_LABEL, COMPARABLE_TYPES, COMPARISON_OPS, type CatalogueEntry, EQUALITY_OPS, type EditCtx, type EditorEffect, type EffectsEditorHandle, type EffectsEditorOptions, type ExpressionEditorHandle, type ExpressionEditorOptions, type Filter, type FunctionTemplateSpec, OP_WORD, type PreviewOptions, type TreeRow, UNARY_LABEL, type Validation, type ValueWizardOptions, type WizardSpec, type WizardStepSpec, type WizardValue, addArg, addChildToContainer, addEmit, addSet, advancedRef, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagChangeSrc, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, normaliseRef, notNode, numLit, opSwapGroup, otherEngineTip, pathKey, placeholderForOp, propertyPillNote, propertyTip, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopeOfRef, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, targetRefOf, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
package/dist/index.js CHANGED
@@ -307,6 +307,20 @@ function searchCatalogue(entries, query, defaultScope) {
307
307
  if (!q) return [...entries];
308
308
  return entries.filter((e) => displayName(e, defaultScope).toLowerCase().includes(q) || (e.purpose ?? "").toLowerCase().includes(q));
309
309
  }
310
+ function otherEngineTip(label) {
311
+ return `${label} belongs to another engine, which checks it`;
312
+ }
313
+ function propertyPillNote(entry, scope, label, otherEngineScopes) {
314
+ if (entry) {
315
+ const title = propertyTip(entry);
316
+ return title ? { title } : {};
317
+ }
318
+ if (otherEngineScopes?.includes(scope)) return { title: otherEngineTip(label) };
319
+ return { issue: `Unknown property ${label}` };
320
+ }
321
+ function scopeOfRef(ref) {
322
+ return /^@([^.\s]+)\./.exec(ref)?.[1];
323
+ }
310
324
  function groupByScope(entries, scopeOrder = []) {
311
325
  const byScope = /* @__PURE__ */ new Map();
312
326
  for (const e of entries) {
@@ -480,11 +494,12 @@ function renderNode(node, path, ctx, parentOp, side) {
480
494
  case "scopedvar": {
481
495
  const entry = lookup(ctx.catalogue, node.scope, node.name);
482
496
  const label = displayName({ scope: node.scope, name: node.name }, ctx.defaultScope);
497
+ const note = propertyPillNote(entry, node.scope, label, ctx.otherEngineScopes);
483
498
  const varPill = pill(
484
499
  "var",
485
500
  label,
486
501
  (b) => variableEditor(ctx, path, node, b),
487
- { issue: issue ?? (entry ? void 0 : `Unknown property ${label}`), title: propertyTip(entry) }
502
+ { issue: issue ?? note.issue, title: note.title }
488
503
  );
489
504
  attachPropertyMenu(ctx, node, varPill);
490
505
  return varPill;
@@ -1165,6 +1180,7 @@ function mountExpressionEditor(host, opts) {
1165
1180
  catalogue: opts.catalogue,
1166
1181
  scopeOrder: opts.scopeOrder ?? [],
1167
1182
  functions: opts.functions ?? [],
1183
+ ...opts.otherEngineScopes ? { otherEngineScopes: opts.otherEngineScopes } : {},
1168
1184
  byPath: v.byPath,
1169
1185
  getAst: () => ast,
1170
1186
  apply: (next) => emit(toSrc(next)),
@@ -1606,6 +1622,7 @@ function mountEffectsEditor(host, opts) {
1606
1622
  scopeOrder: opts.scopeOrder,
1607
1623
  functions: opts.functions,
1608
1624
  mode: "flat",
1625
+ ...opts.otherEngineScopes ? { otherEngineScopes: opts.otherEngineScopes } : {},
1609
1626
  // Every editor here holds a VALUE (a set's value, an emit's argument), never a condition. Say
1610
1627
  // so, or emptying one falls back to the condition empty state and an outcome row reads
1611
1628
  // "always / + Add your first condition" - condition vocabulary, offering clauses where a value
@@ -1665,7 +1682,8 @@ function mountEffectsEditor(host, opts) {
1665
1682
  });
1666
1683
  }, "change the target property");
1667
1684
  const targetEntry = entryForTarget(opts.catalogue, eff.target, defaultScope);
1668
- const tip = propertyTip(targetEntry);
1685
+ const targetScope = targetEntry ? void 0 : scopeOfRef(eff.target);
1686
+ const tip = targetScope !== void 0 && opts.otherEngineScopes?.includes(targetScope) ? otherEngineTip(eff.target) : propertyTip(targetEntry);
1669
1687
  if (tip) targetBtn.title = tip;
1670
1688
  if (targetEntry && opts.propertyActions) {
1671
1689
  const actions = opts.propertyActions({ scope: targetEntry.scope, name: targetEntry.name });
@@ -1809,6 +1827,7 @@ function frozenCtx(src, o, selfAdvanceRef) {
1809
1827
  catalogue: o.catalogue,
1810
1828
  scopeOrder: o.scopeOrder ?? [],
1811
1829
  functions: [],
1830
+ ...o.otherEngineScopes ? { otherEngineScopes: o.otherEngineScopes } : {},
1812
1831
  compact: true,
1813
1832
  byPath: v.byPath,
1814
1833
  getAst: () => v.ast,
@@ -1841,7 +1860,11 @@ function renderConditionPreview(src, o) {
1841
1860
  function targetPill(ref, o) {
1842
1861
  const pill2 = el("span", "exed-pill exed-pill-prop", [ref || "(property)"]);
1843
1862
  const entry = o.catalogue.find((e) => refOf(e, o.dialect.defaultScope) === ref);
1844
- if (!entry) return pill2;
1863
+ if (!entry) {
1864
+ const scope = scopeOfRef(ref);
1865
+ if (scope !== void 0 && o.otherEngineScopes?.includes(scope)) pill2.title = otherEngineTip(ref);
1866
+ return pill2;
1867
+ }
1845
1868
  const tip = propertyTip(entry);
1846
1869
  if (tip) pill2.title = tip;
1847
1870
  const actions = o.propertyActions?.({ scope: entry.scope, name: entry.name }) ?? [];
@@ -1875,6 +1898,6 @@ function renderEffectsPreview(effects, o) {
1875
1898
  return wrap;
1876
1899
  }
1877
1900
 
1878
- export { ARITHMETIC_OPS, BINARY_LABEL, COMPARABLE_TYPES, COMPARISON_OPS, EQUALITY_OPS, OP_WORD, UNARY_LABEL, addArg, addChildToContainer, addEmit, addSet, advancedRef, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagChangeSrc, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, normaliseRef, notNode, numLit, opSwapGroup, pathKey, placeholderForOp, propertyTip, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, targetRefOf, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
1901
+ export { ARITHMETIC_OPS, BINARY_LABEL, COMPARABLE_TYPES, COMPARISON_OPS, EQUALITY_OPS, OP_WORD, UNARY_LABEL, addArg, addChildToContainer, addEmit, addSet, advancedRef, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagChangeSrc, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, normaliseRef, notNode, numLit, opSwapGroup, otherEngineTip, pathKey, placeholderForOp, propertyPillNote, propertyTip, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopeOfRef, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, targetRefOf, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
1879
1902
  //# sourceMappingURL=index.js.map
1880
1903
  //# sourceMappingURL=index.js.map