wire cross-sheet references into the editor and document lifecycle

This commit is contained in:
Horizontal42 2026-07-02 05:38:02 +03:00
parent 8fb9ee3a31
commit 9d8f977c46
2 changed files with 20 additions and 4 deletions

View file

@ -14,6 +14,7 @@ import type { LineResult } from "./engine";
import { setLang, detectLang, t } from "./i18n"; import { setLang, detectLang, t } from "./i18n";
import { runExtensions } from "./extensions"; import { runExtensions } from "./extensions";
import { checkForUpdate } from "./updater"; import { checkForUpdate } from "./updater";
import { Workspace } from "./workspace";
import { EN, RU } from "./engine/vocab-data"; import { EN, RU } from "./engine/vocab-data";
function welcomeText(lang: string): string { function welcomeText(lang: string): string {
@ -25,6 +26,7 @@ const $ = <T extends HTMLElement>(sel: string): T => document.querySelector(sel)
let engine: SumEngine; let engine: SumEngine;
let editor: SumEditor; let editor: SumEditor;
let workspace: Workspace;
let settings: SettingsData; let settings: SettingsData;
let data: AppData; let data: AppData;
let lastResults: LineResult[] = []; let lastResults: LineResult[] = [];
@ -204,7 +206,7 @@ function searchAllSheets(query: string): SearchHit[] {
const hits: SearchHit[] = []; const hits: SearchHit[] = [];
for (const doc of data.docs) { for (const doc of data.docs) {
const contents = data.contents[doc.id] ?? ""; const contents = data.contents[doc.id] ?? "";
const results = engine.evaluateDocument(contents); const results = workspace.evaluateSheet(doc.id, contents);
const lines = contents.split("\n"); const lines = contents.split("\n");
for (let i = 0; i < results.length; i++) { for (let i = 0; i < results.length; i++) {
const r = results[i]; const r = results[i];
@ -536,6 +538,7 @@ async function refreshRates(force = false): Promise<void> {
Object.entries(payload.rates).map(([k, v]) => [k, Number(v)]) Object.entries(payload.rates).map(([k, v]) => [k, Number(v)])
); );
applyAllRates(); applyAllRates();
workspace.invalidateAll();
editor.refresh(); editor.refresh();
ratesFetchedAt = payload.fetchedAt; ratesFetchedAt = payload.fetchedAt;
if (force) toast(t("ratesUpdated")); if (force) toast(t("ratesUpdated"));
@ -568,6 +571,7 @@ async function refreshMarket(force = false): Promise<void> {
marketPrices = prices; marketPrices = prices;
marketFetchedAt = Math.floor(Date.now() / 1000); marketFetchedAt = Math.floor(Date.now() / 1000);
applyAllRates(); applyAllRates();
workspace.invalidateAll();
editor.refresh(); editor.refresh();
if (force) toast(t("marketUpdated")); if (force) toast(t("marketUpdated"));
} }
@ -588,7 +592,7 @@ async function fetchNeededHistoricalRates(text: string): Promise<void> {
if (rates) { engine.setHistoricalRates(date, rates); fetched = true; } if (rates) { engine.setHistoricalRates(date, rates); fetched = true; }
} }
} }
if (fetched) editor.refresh(); if (fetched) { workspace.invalidateAll(); editor.refresh(); }
} }
function renderMarketInfo(): void { function renderMarketInfo(): void {
@ -657,7 +661,7 @@ async function renderSheetImage(): Promise<void> {
const lineH = Math.round(fontSize * 1.7); const lineH = Math.round(fontSize * 1.7);
const padX = 20, padTop = 16, padBot = 16; const padX = 20, padTop = 16, padBot = 16;
const doc = engine.evaluateDocument(editor.getText()); const doc = workspace.evaluateSheet(activeDoc().id, editor.getText());
const rawLines = editor.getText().split("\n"); const rawLines = editor.getText().split("\n");
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
@ -775,6 +779,10 @@ async function boot(): Promise<void> {
const scripts = await loadExtensionScripts(); const scripts = await loadExtensionScripts();
runExtensions(engine, scripts); runExtensions(engine, scripts);
workspace = new Workspace(engine, () =>
data.docs.map((d) => ({ id: d.id, title: d.title, text: data.contents[d.id] ?? "" })),
);
editor = new SumEditor( editor = new SumEditor(
$("#editor"), $("#editor"),
$("#results"), $("#results"),
@ -783,6 +791,7 @@ async function boot(): Promise<void> {
onChange(text) { onChange(text) {
const doc = activeDoc(); const doc = activeDoc();
data.contents[doc.id] = text; data.contents[doc.id] = text;
workspace.invalidate(doc.id);
if (!doc.customTitle) { if (!doc.customTitle) {
doc.title = titleFromContent(text); doc.title = titleFromContent(text);
syncTitleField(); syncTitleField();
@ -804,6 +813,7 @@ async function boot(): Promise<void> {
renderTotal(); renderTotal();
}, },
}, },
(text) => workspace.evaluateSheet(activeDoc().id, text),
data.contents[data.activeId] ?? "", data.contents[data.activeId] ?? "",
); );
@ -815,13 +825,18 @@ async function boot(): Promise<void> {
const titleField = $<HTMLInputElement>("#doc-title"); const titleField = $<HTMLInputElement>("#doc-title");
titleField.addEventListener("change", () => { titleField.addEventListener("change", () => {
const doc = activeDoc(); const doc = activeDoc();
const oldTitle = doc.title;
const v = titleField.value.trim(); const v = titleField.value.trim();
if (v) { if (v) {
doc.title = v.slice(0, 60); doc.title = v.slice(0, 60);
doc.customTitle = true; doc.customTitle = true;
const rewrites = workspace.renameSheet(doc.id, oldTitle, doc.title);
for (const r of rewrites) data.contents[r.id] = r.text;
workspace.invalidateAll();
} else { } else {
doc.customTitle = false; doc.customTitle = false;
doc.title = titleFromContent(data.contents[doc.id] ?? ""); doc.title = titleFromContent(data.contents[doc.id] ?? "");
workspace.invalidateAll();
} }
syncTitleField(); syncTitleField();
renderDocList(); renderDocList();

View file

@ -160,6 +160,7 @@ export class SumEditor {
resultsEl: HTMLElement, resultsEl: HTMLElement,
private engine: SumEngine, private engine: SumEngine,
private cb: EditorCallbacks, private cb: EditorCallbacks,
private evaluateDoc: (text: string) => LineResult[],
initialText: string, initialText: string,
) { ) {
this.resultsEl = resultsEl; this.resultsEl = resultsEl;
@ -310,7 +311,7 @@ export class SumEditor {
} }
private evaluate(text: string): void { private evaluate(text: string): void {
this.results = this.engine.evaluateDocument(text); this.results = this.evaluateDoc(text);
this.view.dispatch({ effects: setResults.of(this.results) }); this.view.dispatch({ effects: setResults.of(this.results) });
this.cb.onResults(this.results); this.cb.onResults(this.results);
} }