@ai-matrx/records-ui 0.93.72 → 0.93.74

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/CHANGELOG.md CHANGED
@@ -1,8 +1,21 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+ ## 0.93.74
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/records-ui/v0.93.73..npm/records-ui/v0.93.74 -- apps/shared/records-ui`).
7
+ No source changes intended and no consumer action required.
8
+
9
+ ## 0.93.73
10
+
11
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
12
+ `git diff npm/records-ui/v0.93.72..npm/records-ui/v0.93.73 -- apps/shared/records-ui`).
13
+ No source changes intended and no consumer action required.
14
+
3
15
  ## 0.93.72
4
16
  ### Unreleased
5
17
 
18
+ - **Rows that are not a file read as rows after the write** (lane SAVE-AS-TABLE-EVERYWHERE, walked 2026-09-29): after rows from a chat answer landed, the wizard said "the rows above has been sent to this table … To import another file, choose it above" under a line still promising "Nothing is written until you press Import". With a `source`, it now says "These rows have been sent to this table" and the pre-write line is gone.
6
19
  - **An import no longer calls every row of a choice column refused** (lane SAVE-AS-TABLE-EVERYWHERE, found walking Cedar Ridge 2026-09-29): the pre-write rule check judged a choice column without its options and said "3 of 3 rows would be refused … its options have not been read yet" over words that WERE choices, and held Import behind "Import the rest anyway". `checkImportRules` now takes the options the wizard reads for the enum ask; a choice column is judged only once they are in hand, and a word it does not know is the enum ask's to raise. The wizard's source line for rows with no title reads "From the rows above".
7
20
  - **Changing a booking page never sends the store's own columns back** (lane HANDOVER, 2026-09-29): with the page's stored questions loaded, "Change it" also sent the store's hidden `slot`, `status` and `booked_with`, and the save was refused ("A booking page never asks for \"slot\""). Hidden questions and `STORE_ANSWERS_THESE` are left out on load. Proof: `a-booking-page-that-exists-can-be-changed.test.tsx` (the stored list as the store keeps it), red on the old builder.
8
21
 
@@ -39,6 +52,13 @@ No source changes intended and no consumer action required.
39
52
  beside `field`/`then`), so Save keeps the exploration as a Saved view; the drill trail also
40
53
  narrows the page the grid reads ("See these records"). Proof:
41
54
  `a-grid-drills-through-the-store.test.tsx`, red 4/5 on the HEAD grid.
55
+ - **The drill reads the one read door** (lane DRILL-TABLE-PRIMITIVE, after D1/D2): Dimensions, Measures and
56
+ drill paths come from `platform.drill_describe` (a custom Table's from `custom.table_dimensions`, with its
57
+ overrides) and every grouping from `platform.drill_ask` — nothing is inferred in the browser any more.
58
+ `useDrill({ source })` is the port for ANY source: a custom Table `{kind: "table", id}` or a standard table
59
+ `{kind: "entity", token}`, drawn by the same table; relation groups read by the names the door read as the
60
+ person. A view whose question is nested (a Rule filter, which the contract's flat `where` does not carry
61
+ yet) is answered by the store's aggregate door `drill_ask` itself delegates to, with the same rule.
42
62
 
43
63
  ## 0.93.67
44
64
 
package/dist/index.cjs CHANGED
@@ -269,6 +269,10 @@ __export(src_exports, {
269
269
  declareView: () => declareView,
270
270
  defaultViewOf: () => defaultViewOf,
271
271
  designatedLayoutOf: () => designatedLayoutOf,
272
+ drillDimensionsOf: () => drillDimensionsOf,
273
+ drillMeasuresOf: () => drillMeasuresOf,
274
+ drillPathsOf: () => drillPathsOf,
275
+ drillWhere: () => drillWhere,
272
276
  editorKindFor: () => editorKindFor,
273
277
  ensureSystemTable: () => ensureSystemTable,
274
278
  entityReferenceSpec: () => entityReferenceSpec,
@@ -378,6 +382,9 @@ __export(src_exports, {
378
382
  unknownViewLine: () => unknownViewLine,
379
383
  useCanShare: () => useCanShare,
380
384
  useChoiceNudge: () => useChoiceNudge,
385
+ useDrill: () => useDrill,
386
+ useDrillAnswers: () => useDrillAnswers,
387
+ useDrillDefinition: () => useDrillDefinition,
381
388
  useEmbedHandshake: () => useEmbedHandshake,
382
389
  useEnrichCells: () => useEnrichCells,
383
390
  useGridEditing: () => useGridEditing,
@@ -9596,55 +9603,206 @@ var import_react51 = require("react");
9596
9603
  var import_react52 = require("@ai-matrx/records/react");
9597
9604
  var import_records17 = require("@ai-matrx/records");
9598
9605
  var import_data_table8 = require("@ai-matrx/design-system/data-table");
9599
- var MEASURE_OPS = [
9600
- { op: "sum", word: "Sum of", additive: true },
9601
- { op: "avg", word: "Average", additive: false },
9602
- { op: "min", word: "Lowest", additive: false },
9603
- { op: "max", word: "Highest", additive: false },
9604
- { op: "median", word: "Median", additive: false }
9605
- ];
9606
- var DIMENSION_KINDS = {
9607
- select: "choice",
9608
- checkbox: "boolean",
9609
- text: "text",
9610
- datetime: "time"
9611
- };
9612
- function drillDimensionsFor(fields, options) {
9613
- const out = [];
9614
- for (const field2 of fields) {
9615
- const kind = DIMENSION_KINDS[String(editorKindFor(field2))];
9616
- if (!kind) continue;
9617
- if (field2.multi) continue;
9618
- const dim = { key: field2.key, label: fieldName(field2), kind };
9619
- if (kind === "text") dim.cardinality = "high";
9620
- if (kind === "choice" && isChoiceField(field2)) {
9606
+ var ADDITIVE_OPS = /* @__PURE__ */ new Set(["count", "sum", "filled", "empty"]);
9607
+ function drillDimensionsOf(def, labels) {
9608
+ if (!def) return [];
9609
+ return def.dimensions.map((d) => {
9610
+ const dim = { key: d.key, label: d.label, kind: d.kind };
9611
+ if (d.cardinality) dim.cardinality = d.cardinality;
9612
+ if (d.grains) dim.grains = d.grains.filter((g) => g !== "hour");
9613
+ const choices = new Map((d.choices ?? []).map((c) => [c.value, c.label]));
9614
+ if (d.kind === "choice" || d.kind === "relation") {
9621
9615
  dim.labelFor = (value) => {
9622
9616
  if (value === null || value === "") return "No value";
9623
- return optionFor(options.get(field2.key), value)?.label ?? value;
9617
+ return choices.get(value) ?? labels.get(d.key)?.get(value) ?? value;
9624
9618
  };
9625
9619
  }
9626
- out.push(dim);
9627
- }
9628
- return out;
9620
+ return dim;
9621
+ });
9629
9622
  }
9630
- function drillMeasuresFor(fields) {
9631
- const out = [{ key: "count", label: "Count", additive: true }];
9632
- for (const field2 of fields) {
9633
- const kind = String(editorKindFor(field2));
9634
- if (kind !== "number" && kind !== "currency" && kind !== "percent") continue;
9635
- for (const m of MEASURE_OPS) {
9636
- out.push({ key: `${m.op}_${field2.key}`, label: `${m.word} ${fieldName(field2)}`, additive: m.additive });
9637
- }
9623
+ function drillMeasuresOf(def) {
9624
+ if (!def) return [];
9625
+ return def.measures.map((m) => ({ key: m.key, label: m.label, additive: m.additive ?? ADDITIVE_OPS.has(m.op) }));
9626
+ }
9627
+ function drillPathsOf(def) {
9628
+ return (def?.paths ?? []).map((p) => p.levels);
9629
+ }
9630
+ function useDrillDefinition(source) {
9631
+ const client = (0, import_react52.useRecordsClient)();
9632
+ const key = source ? JSON.stringify(source) : null;
9633
+ const [state, setState] = (0, import_react51.useState)({ key: null, def: null, error: null });
9634
+ (0, import_react51.useEffect)(() => {
9635
+ if (!key) return;
9636
+ let cancelled = false;
9637
+ void client.drillDescribe({ source: JSON.parse(key) }).then((got) => {
9638
+ if (cancelled) return;
9639
+ setState(got.ok ? { key, def: got.data ?? null, error: null } : { key, def: null, error: got.error.message || "This table's groupings could not be read." });
9640
+ });
9641
+ return () => {
9642
+ cancelled = true;
9643
+ };
9644
+ }, [client, key]);
9645
+ return state.key === key ? { def: state.def, error: state.error } : { def: null, error: null };
9646
+ }
9647
+ function drillWhere(base, trail, booleans) {
9648
+ const where = { ...base ?? {} };
9649
+ for (const w of trail) {
9650
+ const { key, grain } = (0, import_data_table8.parseDimensionRef)(w.dim);
9651
+ if (grain) where[w.dim] = w.value;
9652
+ else if (booleans.has(key) && (w.value === "true" || w.value === "false")) where[key] = w.value === "true";
9653
+ else where[key] = w.value;
9638
9654
  }
9639
- return out;
9655
+ return where;
9640
9656
  }
9641
- function measureFor(key) {
9657
+ function rowOf(row) {
9658
+ const groups = {};
9659
+ for (const [k, v] of Object.entries(row.groups ?? {})) groups[k] = v === null || v === void 0 ? null : String(v);
9660
+ const measures = {};
9661
+ for (const [k, v] of Object.entries(row.measures ?? {})) measures[k] = v === null || v === void 0 ? null : Number(v);
9662
+ return { groups, measures, row_count: Number(row.row_count ?? 0) };
9663
+ }
9664
+ function storeMeasure(key) {
9642
9665
  if (key === "count") return { op: "count" };
9643
9666
  const at = key.indexOf("_");
9644
9667
  if (at <= 0) return null;
9645
- const op2 = key.slice(0, at);
9646
- if (!MEASURE_OPS.some((m) => m.op === op2)) return null;
9647
- return { op: op2, key: key.slice(at + 1) };
9668
+ return { op: key.slice(0, at), key: key.slice(at + 1) };
9669
+ }
9670
+ function useDrillAnswers(args) {
9671
+ const client = (0, import_react52.useRecordsClient)();
9672
+ const { source, question, enabled, version } = args;
9673
+ const [answers, setAnswers] = (0, import_react51.useState)({});
9674
+ const [labels, setLabels] = (0, import_react51.useState)(() => /* @__PURE__ */ new Map());
9675
+ const [error, setError] = (0, import_react51.useState)(null);
9676
+ const [says, setSays] = (0, import_react51.useState)([]);
9677
+ const askKey2 = JSON.stringify({
9678
+ source,
9679
+ by: question.by,
9680
+ across: question.across ?? null,
9681
+ show: question.show,
9682
+ where: args.where,
9683
+ rule: args.rule && (0, import_records17.isRuleFilter)(args.rule) ? args.rule : null
9684
+ });
9685
+ (0, import_react51.useEffect)(() => {
9686
+ if (!enabled || !source || question.by.length === 0) return;
9687
+ const asked2 = JSON.parse(askKey2);
9688
+ let cancelled = false;
9689
+ setAnswers({});
9690
+ setError(null);
9691
+ const requests = (0, import_data_table8.drillRequests)({ by: asked2.by, across: asked2.across, show: asked2.show, where: [] });
9692
+ void Promise.all(
9693
+ requests.map(async (request) => {
9694
+ if (asked2.rule && asked2.source.kind === "table") {
9695
+ const time = request.by.find((ref) => (0, import_data_table8.parseDimensionRef)(ref).grain !== null) ?? null;
9696
+ const t = time ? (0, import_data_table8.parseDimensionRef)(time) : null;
9697
+ const measures = asked2.show.map(storeMeasure).filter((m) => m !== null);
9698
+ const got2 = await client.recordAggregate({
9699
+ table_id: asked2.source.id,
9700
+ groupBy: request.by.filter((ref) => ref !== time),
9701
+ measures,
9702
+ bucket: t && t.grain ? { key: t.key, by: t.grain } : null,
9703
+ filter: asked2.rule,
9704
+ limit: 200
9705
+ });
9706
+ if (!got2.ok) return { request, error: got2.error.message };
9707
+ return {
9708
+ request,
9709
+ rows: (got2.data ?? []).map((row) => {
9710
+ const groups = {};
9711
+ for (const ref of request.by) {
9712
+ const k = ref === time && t ? t.key : ref;
9713
+ groups[ref] = row.groups[k] ?? null;
9714
+ }
9715
+ return { groups, measures: row.measures, row_count: row.row_count };
9716
+ }),
9717
+ labels: {},
9718
+ says: []
9719
+ };
9720
+ }
9721
+ const got = await client.drillAsk({
9722
+ source: asked2.source,
9723
+ question: { by: request.by, show: asked2.show, where: asked2.where }
9724
+ });
9725
+ if (!got.ok) return { request, error: got.error.message || "The groups could not be read." };
9726
+ const answer = got.data;
9727
+ const kind = request.by.length === 0 ? "total" : "group";
9728
+ const rows = answer.rows.filter((r) => r.kind === kind && (kind === "group" || !r.groups || Object.keys(r.groups).length === 0)).map(rowOf);
9729
+ const found = {};
9730
+ for (const r of answer.rows) {
9731
+ for (const [dim, text] of Object.entries(r.labels ?? {})) {
9732
+ const value = r.groups?.[dim];
9733
+ if (value === null || value === void 0) continue;
9734
+ (found[dim] ??= {})[String(value)] = text;
9735
+ }
9736
+ }
9737
+ return { request, rows, labels: found, says: answer.says };
9738
+ })
9739
+ ).then((results) => {
9740
+ if (cancelled) return;
9741
+ const failed = results.find((r) => "error" in r);
9742
+ if (failed) {
9743
+ setError(failed.error);
9744
+ return;
9745
+ }
9746
+ const out = {};
9747
+ const nextLabels = /* @__PURE__ */ new Map();
9748
+ const sentences = [];
9749
+ for (const r of results) {
9750
+ if (!("rows" in r)) continue;
9751
+ out[r.request.key] = r.rows;
9752
+ for (const [dim, map] of Object.entries(r.labels ?? {})) {
9753
+ const held = nextLabels.get(dim) ?? /* @__PURE__ */ new Map();
9754
+ for (const [v, t] of Object.entries(map)) held.set(v, t);
9755
+ nextLabels.set(dim, held);
9756
+ }
9757
+ for (const s of r.says ?? []) if (!sentences.includes(s)) sentences.push(s);
9758
+ }
9759
+ setLabels(nextLabels);
9760
+ setSays(sentences);
9761
+ setAnswers(out);
9762
+ });
9763
+ return () => {
9764
+ cancelled = true;
9765
+ };
9766
+ }, [client, askKey2, enabled, version, question.by.length]);
9767
+ return { answers, labels, error, says };
9768
+ }
9769
+ function drillQuestionFor(question, def) {
9770
+ if (!def) return question;
9771
+ const known = (ref) => def.dimensions.some((d) => d.key === (0, import_data_table8.parseDimensionRef)(ref).key);
9772
+ const show = question.show.filter((key) => def.measures.some((m) => m.key === key));
9773
+ return {
9774
+ ...question,
9775
+ by: question.by.filter(known),
9776
+ across: question.across && known(question.across) ? question.across : null,
9777
+ show: show.length > 0 ? show : [def.measures[0]?.key ?? "count"],
9778
+ where: question.where.filter((w) => known(w.dim))
9779
+ };
9780
+ }
9781
+ function useDrill(args) {
9782
+ const enabled = args.enabled ?? true;
9783
+ const { def, error: describeError } = useDrillDefinition(enabled ? args.source : null);
9784
+ const question = (0, import_react51.useMemo)(() => drillQuestionFor(args.question, def), [args.question, def]);
9785
+ const booleans = (0, import_react51.useMemo)(() => new Set((def?.dimensions ?? []).filter((d) => d.kind === "boolean").map((d) => d.key)), [def]);
9786
+ const where = (0, import_react51.useMemo)(() => drillWhere(args.filter, question.where, booleans), [args.filter, question.where, booleans]);
9787
+ const { answers, labels, error } = useDrillAnswers({
9788
+ source: args.source,
9789
+ question,
9790
+ where,
9791
+ rule: args.rule,
9792
+ version: args.version,
9793
+ enabled: enabled && def !== null
9794
+ });
9795
+ const dimensions = (0, import_react51.useMemo)(() => drillDimensionsOf(def, labels), [def, labels]);
9796
+ const measures = (0, import_react51.useMemo)(() => drillMeasuresOf(def), [def]);
9797
+ const paths = (0, import_react51.useMemo)(() => drillPathsOf(def), [def]);
9798
+ const { onQuestionChange } = args;
9799
+ const rowNoun = args.rowNoun ?? "record";
9800
+ const rootLabel = args.rootLabel ?? "All records";
9801
+ const config = (0, import_react51.useMemo)(() => {
9802
+ if (!enabled || !def || dimensions.length === 0) return null;
9803
+ return { dimensions, measures, paths, question, answers, error: describeError ?? error, rowNoun, rootLabel, onQuestionChange };
9804
+ }, [enabled, def, dimensions, measures, paths, question, answers, describeError, error, rowNoun, rootLabel, onQuestionChange]);
9805
+ return { config, def };
9648
9806
  }
9649
9807
  function questionFromGrouping(grouping) {
9650
9808
  if (!grouping) return { by: [], across: null, show: ["count"], where: [] };
@@ -9705,110 +9863,49 @@ function filterWithTrail(fields, base, trail) {
9705
9863
  }
9706
9864
  return { op: "and", args };
9707
9865
  }
9708
- function useDrillAnswers(args) {
9709
- const client = (0, import_react52.useRecordsClient)();
9710
- const { tableId, question, enabled, version } = args;
9711
- const [answers, setAnswers] = (0, import_react51.useState)({});
9712
- const [error, setError] = (0, import_react51.useState)(null);
9713
- const askKey2 = JSON.stringify({ by: question.by, across: question.across ?? null, show: question.show, filter: args.filter ?? null });
9714
- (0, import_react51.useEffect)(() => {
9715
- if (!enabled || question.by.length === 0) return;
9716
- const asked2 = JSON.parse(askKey2);
9717
- const measures = asked2.show.map(measureFor).filter((m) => m !== null);
9718
- if (!measures.some((m) => m.op === "count")) measures.unshift({ op: "count" });
9719
- let cancelled = false;
9720
- setAnswers({});
9721
- setError(null);
9722
- const requests = (0, import_data_table8.drillRequests)({ by: asked2.by, across: asked2.across, show: asked2.show, where: [] });
9723
- void Promise.all(
9724
- requests.map(async (request) => {
9725
- const time = request.by.find((ref) => (0, import_data_table8.parseDimensionRef)(ref).grain !== null) ?? null;
9726
- const plain = request.by.filter((ref) => ref !== time);
9727
- const grain = time ? (0, import_data_table8.parseDimensionRef)(time) : null;
9728
- const got = await client.recordAggregate({
9729
- table_id: tableId,
9730
- groupBy: plain,
9731
- measures,
9732
- bucket: grain && grain.grain ? { key: grain.key, by: grain.grain } : null,
9733
- ...asked2.filter ? { filter: asked2.filter } : {},
9734
- limit: 200
9735
- });
9736
- if (!got.ok) return { request, error: got.error.message || "The groups could not be read." };
9737
- const rows = (got.data ?? []).map((row) => {
9738
- const groups = {};
9739
- for (const ref of plain) groups[ref] = row.groups[ref] ?? null;
9740
- if (time && grain) groups[time] = row.groups[grain.key] ?? null;
9741
- return { groups, measures: row.measures, row_count: row.row_count };
9742
- });
9743
- return { request, rows };
9744
- })
9745
- ).then((results) => {
9746
- if (cancelled) return;
9747
- const failed = results.find((r) => "error" in r);
9748
- if (failed) {
9749
- setError(failed.error);
9750
- return;
9751
- }
9752
- const out = {};
9753
- for (const r of results) if ("rows" in r) out[r.request.key] = r.rows;
9754
- setAnswers(out);
9755
- });
9756
- return () => {
9757
- cancelled = true;
9758
- };
9759
- }, [client, tableId, askKey2, enabled, version, question.by.length]);
9760
- return { answers, error };
9761
- }
9762
9866
  function useGridDrillQuestion(args) {
9763
- const { fields, presentation, filter } = args;
9764
- const choiceKeys = (0, import_react51.useMemo)(() => fields.filter((f) => isChoiceField(f) && !f.multi).map((f) => f.key), [fields]);
9765
- const options = useChoiceOptions(fields, args.enabled ? choiceKeys : []);
9766
- const dimensions = (0, import_react51.useMemo)(() => drillDimensionsFor(fields, options), [fields, options]);
9767
- const measures = (0, import_react51.useMemo)(() => drillMeasuresFor(fields), [fields]);
9867
+ const { tableId, fields, presentation, filter } = args;
9868
+ const source = (0, import_react51.useMemo)(() => ({ kind: "table", id: tableId }), [tableId]);
9869
+ const { def } = useDrillDefinition(args.enabled ? source : null);
9768
9870
  const saved = (0, import_react51.useMemo)(() => questionFromGrouping(presentation.grouping), [presentation.grouping]);
9769
9871
  const url = (0, import_data_table8.useDrillUrlState)({ fallback: saved });
9770
- const question = (0, import_react51.useMemo)(() => {
9771
- const known = (ref) => dimensions.some((d) => d.key === (0, import_data_table8.parseDimensionRef)(ref).key);
9772
- const show = url.question.show.filter((key) => measures.some((m) => m.key === key));
9773
- return {
9774
- ...url.question,
9775
- by: url.question.by.filter(known),
9776
- across: url.question.across && known(url.question.across) ? url.question.across : null,
9777
- show: show.length > 0 ? show : ["count"],
9778
- where: url.question.where.filter((w) => known(w.dim))
9779
- };
9780
- }, [url.question, dimensions, measures]);
9781
- const enabled = args.enabled && dimensions.length > 0;
9872
+ const question = (0, import_react51.useMemo)(() => drillQuestionFor(url.question, def), [url.question, def]);
9873
+ const enabled = args.enabled && def !== null && def.dimensions.length > 0;
9782
9874
  const trailFilter = (0, import_react51.useMemo)(
9783
9875
  () => enabled ? filterWithTrail(fields, filter, question.where) : filter,
9784
9876
  [enabled, fields, filter, question.where]
9785
9877
  );
9786
- return { enabled, dimensions, measures, question, setQuestion: url.setQuestion, trailFilter };
9878
+ return { enabled, source, def, question, setQuestion: url.setQuestion, filter, trailFilter };
9787
9879
  }
9788
9880
  function useGridDrillConfig(args) {
9789
- const { tableId, state, presentation, onPresentationChange, version } = args;
9790
- const { enabled, dimensions, measures, question, setQuestion, trailFilter } = state;
9791
- const { answers, error } = useDrillAnswers({ tableId, question, filter: trailFilter, version, enabled });
9792
- return (0, import_react51.useMemo)(() => {
9793
- if (!enabled) return null;
9794
- return {
9795
- dimensions,
9796
- measures,
9797
- question,
9798
- answers,
9799
- error,
9800
- rootLabel: "All records",
9801
- rowNoun: "record",
9802
- onQuestionChange: (next) => {
9803
- setQuestion(next);
9804
- if (onPresentationChange) {
9805
- const grouping = groupingFromQuestion(next, presentation.grouping);
9806
- const { grouping: _dropped, ...rest } = presentation;
9807
- onPresentationChange(grouping ? { ...rest, grouping } : rest);
9808
- }
9881
+ const { state, presentation, onPresentationChange, version } = args;
9882
+ const { setQuestion } = state;
9883
+ const onQuestionChange = (0, import_react51.useMemo)(
9884
+ () => (next) => {
9885
+ setQuestion(next);
9886
+ if (onPresentationChange) {
9887
+ const grouping = groupingFromQuestion(next, presentation.grouping);
9888
+ const { grouping: _dropped, ...rest } = presentation;
9889
+ onPresentationChange(grouping ? { ...rest, grouping } : rest);
9809
9890
  }
9810
- };
9811
- }, [enabled, dimensions, measures, question, answers, error, setQuestion, onPresentationChange, presentation]);
9891
+ },
9892
+ [setQuestion, onPresentationChange, presentation]
9893
+ );
9894
+ const flat = state.filter && !(0, import_records17.isRuleFilter)(state.filter) ? state.filter : void 0;
9895
+ const rule = state.filter && (0, import_records17.isRuleFilter)(state.filter) ? filterWithTrailRuleOnly(state) : void 0;
9896
+ const { config } = useDrill({
9897
+ source: state.source,
9898
+ question: state.question,
9899
+ onQuestionChange,
9900
+ filter: flat,
9901
+ rule,
9902
+ version,
9903
+ enabled: state.enabled
9904
+ });
9905
+ return config;
9906
+ }
9907
+ function filterWithTrailRuleOnly(state) {
9908
+ return state.trailFilter;
9812
9909
  }
9813
9910
 
9814
9911
  // src/Enrich.tsx
@@ -11359,7 +11456,7 @@ function ImportWizard({ tableId, source, onDone, onWrote, className }) {
11359
11456
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "text-muted-foreground", children: table.data?.name }),
11360
11457
  fields.data && table.data ? null : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "ml-auto text-muted-foreground", children: fields.data ? "Reading this table\u2026" : "Reading this table's fields\u2026" })
11361
11458
  ] }),
11362
- source ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { className: "text-muted-foreground", "data-import-source": "", children: [
11459
+ source && !afterWrite ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { className: "text-muted-foreground", "data-import-source": "", children: [
11363
11460
  "From ",
11364
11461
  source.name,
11365
11462
  " \u2014 ",
@@ -11393,8 +11490,8 @@ function ImportWizard({ tableId, source, onDone, onWrote, className }) {
11393
11490
  phase === "reading" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
11394
11491
  phase === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
11395
11492
  plan && parsed && afterWrite ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { className: "text-muted-foreground", "data-import-after-write": "", children: [
11396
- fileName ? `${fileName} has been sent to this table` : "This file has been sent to this table",
11397
- ledger && !ledger.final ? ", but the run stopped part-way. Importing it again would write the rows that already landed a second time, so fix the file (or keep only the rows below the ones that landed) and choose it again." : ". What landed and what was refused is below. To import another file, choose it above."
11493
+ source ? `These rows have been sent to this table${ledger && !ledger.final ? ", but the run stopped part-way." : ". What landed and what was refused is below."}` : fileName ? `${fileName} has been sent to this table` : "This file has been sent to this table",
11494
+ source ? null : ledger && !ledger.final ? ", but the run stopped part-way. Importing it again would write the rows that already landed a second time, so fix the file (or keep only the rows below the ones that landed) and choose it again." : ". What landed and what was refused is below. To import another file, choose it above."
11398
11495
  ] }) : null,
11399
11496
  plan && parsed && !afterWrite ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
11400
11497
  titleKey ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex flex-wrap items-center gap-2 rounded border bg-muted/40 px-2 py-1.5", children: [
@@ -13554,7 +13651,7 @@ function useGridAssist(args) {
13554
13651
  (ok, message) => ok ? host.notify?.success(message) : host.notify?.error(message),
13555
13652
  [host.notify]
13556
13653
  );
13557
- const rowOf = (0, import_react71.useCallback)((rowId) => rows.find((r) => r.id === rowId), [rows]);
13654
+ const rowOf2 = (0, import_react71.useCallback)((rowId) => rows.find((r) => r.id === rowId), [rows]);
13558
13655
  const titleOf = (0, import_react71.useCallback)(
13559
13656
  (row) => row ? recordNameIn(table, row.document ?? {}) : "",
13560
13657
  [table]
@@ -13644,18 +13741,18 @@ function useGridAssist(args) {
13644
13741
  );
13645
13742
  const openWhole = (0, import_react71.useCallback)(
13646
13743
  (rowId, key) => {
13647
- const row = rowOf(rowId);
13744
+ const row = rowOf2(rowId);
13648
13745
  if (!row) return;
13649
13746
  const document2 = row.document ?? {};
13650
13747
  const value = editing.valueFor(row, fieldOf2(key));
13651
13748
  const pointer = typeof document2[key] === "string" ? (0, import_core9.wholeValueOf)(document2, key) : null;
13652
13749
  setWhole({ rowId, key, rowTitle: titleOf(row), value, pointer });
13653
13750
  },
13654
- [rowOf, fieldOf2, titleOf, editing]
13751
+ [rowOf2, fieldOf2, titleOf, editing]
13655
13752
  );
13656
13753
  const cellItems = (0, import_react71.useCallback)(
13657
13754
  (rowId, columnId) => {
13658
- const row = rowOf(rowId);
13755
+ const row = rowOf2(rowId);
13659
13756
  const field2 = fieldOf2(columnId);
13660
13757
  if (!row || !field2) return [];
13661
13758
  const items = [];
@@ -13680,11 +13777,11 @@ function useGridAssist(args) {
13680
13777
  items.push(...refItem("cell-reference", "Copy cell reference", refFor.cell(row, field2), "The cell"));
13681
13778
  return items;
13682
13779
  },
13683
- [rowOf, fieldOf2, openWhole, host.cleanText, editing, args, refItem, refFor]
13780
+ [rowOf2, fieldOf2, openWhole, host.cleanText, editing, args, refItem, refFor]
13684
13781
  );
13685
13782
  const rowItems = (0, import_react71.useCallback)(
13686
13783
  (rowId) => {
13687
- const row = rowOf(rowId);
13784
+ const row = rowOf2(rowId);
13688
13785
  const askAgent = args.askAgent;
13689
13786
  return [
13690
13787
  ...rowActionMenuItems({
@@ -13699,7 +13796,7 @@ function useGridAssist(args) {
13699
13796
  ...row ? refItem("row-reference", "Copy row reference", refFor.row(row), "The row") : []
13700
13797
  ];
13701
13798
  },
13702
- [rowOf, args, fields, refItem, refFor]
13799
+ [rowOf2, args, fields, refItem, refFor]
13703
13800
  );
13704
13801
  const tableItems = (0, import_react71.useCallback)(
13705
13802
  () => refItem("table-reference", "Copy table reference", refFor.table(), "The table"),
@@ -14630,6 +14727,7 @@ function Grid({
14630
14727
  [merged, fields.data, filter, query.columnFilters]
14631
14728
  );
14632
14729
  const drillState = useGridDrillQuestion({
14730
+ tableId,
14633
14731
  fields: fields.data ?? NO_DRILL_FIELDS,
14634
14732
  presentation,
14635
14733
  filter: question.filter,