mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Track datasetRefs against the live draft so manual edits and extract link immediately
This commit is contained in:
@@ -24,7 +24,7 @@ import {
|
||||
type CreateSnippetOptions,
|
||||
type Snippet,
|
||||
} from '@core/snippet';
|
||||
import { recomputeDatasetRefs, renameDatasetInSpec } from '@core/spec-refs';
|
||||
import { extractDatasetRefs, recomputeDatasetRefs, renameDatasetInSpec } from '@core/spec-refs';
|
||||
import {
|
||||
DEFAULT_SORT_BY,
|
||||
DEFAULT_SORT_ORDER,
|
||||
@@ -204,10 +204,13 @@ export const useSnippetStore = create<SnippetState>((set, get) => ({
|
||||
createSnippet: (options) => {
|
||||
get().commitDraft(); // flush the outgoing snippet's valid edits before switching away
|
||||
const created = createSnippet(options);
|
||||
// Mirror datasetRefs from the spec at creation, like publish does, so a snippet
|
||||
// built with a named-data reference (Chart Builder, §06) is linked to its dataset
|
||||
// immediately. Inline-data specs (the sample template) resolve to no refs.
|
||||
const snippet = { ...created, datasetRefs: recomputeDatasetRefs(created.spec) };
|
||||
// Mirror datasetRefs from the draft at creation, so a snippet built with a
|
||||
// named-data reference (Chart Builder, §06) is linked to its dataset immediately.
|
||||
// datasetRefs tracks the draft — the version being edited — at every mutation
|
||||
// (create/auto-save/extract/revert/publish), so the link reflects what the user
|
||||
// sees, not only the last publish (docs/architecture/07 §3). Inline-data specs
|
||||
// (the sample template) resolve to no refs. spec === draftSpec at creation.
|
||||
const snippet = { ...created, datasetRefs: recomputeDatasetRefs(created.draftSpec) };
|
||||
set((s) => ({
|
||||
snippets: [snippet, ...s.snippets],
|
||||
activeSnippetId: snippet.id,
|
||||
@@ -314,9 +317,13 @@ export const useSnippetStore = create<SnippetState>((set, get) => ({
|
||||
const { activeSnippetId } = get();
|
||||
if (!activeSnippetId) return;
|
||||
const modified = (now ?? new Date()).toISOString();
|
||||
const datasetRefs = recomputeDatasetRefs(text);
|
||||
set((s) => ({
|
||||
snippets: s.snippets.map((x) =>
|
||||
x.id === activeSnippetId ? { ...x, draftSpec: text, modified } : x,
|
||||
// Recompute datasetRefs from the new draft so a programmatic rewrite that
|
||||
// introduces a by-name reference — Extract-to-Dataset (spec §03F) — links
|
||||
// the dataset to the snippet immediately, not only on the next publish.
|
||||
x.id === activeSnippetId ? { ...x, draftSpec: text, datasetRefs, modified } : x,
|
||||
),
|
||||
draftText: text,
|
||||
editorView: 'draft',
|
||||
@@ -341,9 +348,15 @@ export const useSnippetStore = create<SnippetState>((set, get) => ({
|
||||
}
|
||||
|
||||
const modified = (now ?? new Date()).toISOString();
|
||||
// Recompute datasetRefs from the just-committed draft so a hand-typed by-name
|
||||
// reference links to its dataset as soon as auto-save fires. This runs on the
|
||||
// debounced auto-save and only on valid JSON (the parse guard above), so it is
|
||||
// not "every keystroke" and never sees a transiently-invalid draft — it keeps
|
||||
// datasetRefs mirroring the draft the user is editing (docs/architecture/07 §3).
|
||||
const datasetRefs = recomputeDatasetRefs(draftText);
|
||||
set({
|
||||
snippets: snippets.map((s) =>
|
||||
s.id === activeSnippetId ? { ...s, draftSpec: draftText, modified } : s,
|
||||
s.id === activeSnippetId ? { ...s, draftSpec: draftText, datasetRefs, modified } : s,
|
||||
),
|
||||
});
|
||||
return true;
|
||||
@@ -358,8 +371,18 @@ export const useSnippetStore = create<SnippetState>((set, get) => ({
|
||||
let updated = 0;
|
||||
let activeDraftAfter: string | null = null;
|
||||
|
||||
// Whether a spec text references the old name (case-insensitive). Reads the
|
||||
// content rather than reserializing, so a non-referencing snippet's text is
|
||||
// left byte-for-byte intact (no spurious reformat/modified bump).
|
||||
const referencesOld = (text: string): boolean =>
|
||||
extractDatasetRefs(text).some((r) => r.toLowerCase() === lower);
|
||||
|
||||
const next = snippets.map((s) => {
|
||||
if (!s.datasetRefs.some((r) => r.toLowerCase() === lower)) return s;
|
||||
// Consult both spec and draft, not only datasetRefs: datasetRefs now mirrors
|
||||
// the DRAFT, so a name still referenced only by the published spec (the user
|
||||
// removed it from the draft but hasn't published) would otherwise be missed
|
||||
// and the published spec would rot to a now-renamed dataset (arch 07 §6).
|
||||
if (!referencesOld(s.spec) && !referencesOld(s.draftSpec)) return s;
|
||||
updated++;
|
||||
const spec = renameDatasetInSpec(s.spec, oldName, newName);
|
||||
const draftSpec = renameDatasetInSpec(s.draftSpec, oldName, newName);
|
||||
@@ -368,7 +391,7 @@ export const useSnippetStore = create<SnippetState>((set, get) => ({
|
||||
...s,
|
||||
spec,
|
||||
draftSpec,
|
||||
datasetRefs: recomputeDatasetRefs(spec),
|
||||
datasetRefs: recomputeDatasetRefs(draftSpec),
|
||||
modified: (now ?? new Date()).toISOString(),
|
||||
};
|
||||
});
|
||||
@@ -422,9 +445,13 @@ export const useSnippetStore = create<SnippetState>((set, get) => ({
|
||||
if (!active) return false;
|
||||
|
||||
const modified = (now ?? new Date()).toISOString();
|
||||
// The draft is restored to the published spec, so datasetRefs must mirror it
|
||||
// again — drop any link that existed only in the discarded draft (e.g. an
|
||||
// Extract-to-Dataset rewrite the user reverted before publishing).
|
||||
const datasetRefs = recomputeDatasetRefs(active.spec);
|
||||
set((s) => ({
|
||||
snippets: s.snippets.map((x) =>
|
||||
x.id === activeSnippetId ? { ...x, draftSpec: x.spec, modified } : x,
|
||||
x.id === activeSnippetId ? { ...x, draftSpec: x.spec, datasetRefs, modified } : x,
|
||||
),
|
||||
// Reload the editor with the restored draft, on the editable view (spec §03D).
|
||||
draftText: active.spec,
|
||||
|
||||
Reference in New Issue
Block a user