Something went wrong. Try again.
[PRE-ALPHA] an open source web-based 2D/3D graphing calculator cartesium.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116import type { Expression2d, Expression3d } from '#lib/engine/types.js';
type Expression = Expression2d | Expression3d;
export function formatBatchText(expressions: Expression[]): string { if (expressions.some((expression) => expression.visual_draft)) { throw new Error('Finish or clear unfinished visual drafts before editing all expressions in TXT.'); }
const legacyLatexIndex = expressions.findIndex( (expression) => expression.input_mode === 'latex' && expression.source.trim() );
if (legacyLatexIndex >= 0) { throw new Error( `Expression ${legacyLatexIndex + 1} is still stored as legacy LaTeX and could not be migrated to ASCII. Its original source is preserved; finish editing it visually or remove it before opening TXT.` ); }
return expressions .map(({ source }) => source.trim()) .filter(Boolean) .join(';\n\n');}
export function parseBatchText(text: string): { sources: string[]; error?: string } { const sources: string[] = []; const stack: { closing: string; line: number }[] = []; const closing: Record<string, string> = { '(': ')', '[': ']', '{': '}' }; let start = 0; let line = 1; let definitionLine = 1;
for (let index = 0; index < text.length; index += 1) { const char = text[index];
if (char === '\n') line += 1;
if (char in closing) stack.push({ closing: closing[char], line }); else if (char === ')' || char === ']' || char === '}') { if (stack.at(-1)?.closing !== char) { return { sources, error: `Unexpected “${char}” on line ${line}.` }; }
stack.pop(); } else if (char === ';' && stack.length === 0) { const source = text.slice(start, index).trim();
if (!source) return { sources, error: `Empty definition near line ${definitionLine}.` }; sources.push(source); start = index + 1; definitionLine = line; } }
if (stack.length) { const opening = stack.at(-1)!;
return { sources, error: `Missing “${opening.closing}” for group opened on line ${opening.line}.` }; }
const tail = text.slice(start).trim();
if (tail) sources.push(tail);
return { sources };}
export function expressionsFromBatchText<T extends Expression>( sources: string[], previous: T[]): T[] { const matchedIds = new Set<string>();
const matches = sources.map((source) => { const existing = previous.find( (expression) => !matchedIds.has(expression.id) && expression.source.trim() === source );
if (existing) matchedIds.add(existing.id);
return existing; });
const allocated = new Set(matchedIds);
const nextText = sources.map((source, index) => { let existing = matches[index]; const available = previous.find((candidate) => !allocated.has(candidate.id));
if (!existing && available) { existing = available; allocated.add(existing.id); }
const unchanged = existing?.source.trim() === source;
return { ...(existing ?? { id: `expr-${crypto.randomUUID()}`, input_mode: 'text', enabled: true, color: null }), input_mode: unchanged ? existing!.input_mode : 'text', source: unchanged ? existing!.source : source, editor_mode: unchanged ? (existing?.editor_mode ?? 'visual') : 'visual', visual_source: unchanged ? existing?.visual_source : undefined, visual_source_for: unchanged ? existing?.visual_source_for : undefined, visual_draft: undefined } as T; });
return nextText;}