v0.3.1 with Saxon fully working

This commit is contained in:
2026-06-05 03:19:04 +02:00
parent fc828363f0
commit 71f2f3d44b
51 changed files with 12683 additions and 1 deletions

View File

@@ -0,0 +1,76 @@
import type { WorkbenchState } from './workspaceTypes';
export const DEFAULT_XML = `<?xml version="1.0" encoding="UTF-8"?>
<library>
<book id="b1">
<title>Designing XML Workflows</title>
<author>Ada Example</author>
<year>2026</year>
</book>
<book id="b2">
<title>Practical XSLT</title>
<author>Lin Example</author>
<year>2025</year>
</book>
</library>
`;
export const DEFAULT_XSLT = `<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/library">
<books>
<xsl:apply-templates select="book"/>
</books>
</xsl:template>
<xsl:template match="book">
<book>
<xsl:attribute name="id">
<xsl:value-of select="@id"/>
</xsl:attribute>
<label>
<xsl:value-of select="upper-case(title)"/>
<xsl:text> — </xsl:text>
<xsl:value-of select="author"/>
</label>
</book>
</xsl:template>
</xsl:stylesheet>
`;
export function createDefaultWorkbenchState(): WorkbenchState {
return {
schemaVersion: 1,
options: {
prettifyOutputAfterTransform: true,
askBeforeOverwritingOutput: true,
},
selectedEngine: 'saxon-js-dynamic',
xmlInput: {
kind: 'xmlInput',
label: 'XML input',
text: DEFAULT_XML,
fileName: 'input.xml',
dirty: false,
},
xsltCode: {
kind: 'xsltCode',
label: 'XSL transformation code',
text: DEFAULT_XSLT,
fileName: 'transform.xsl',
dirty: false,
},
xmlOutput: {
kind: 'xmlOutput',
label: 'XML output',
text: '',
fileName: 'output.xml',
dirty: false,
},
};
}

View File

@@ -0,0 +1,52 @@
import { createDefaultWorkbenchState } from './defaultWorkspace';
import type { WorkbenchState } from './workspaceTypes';
const STORAGE_KEY = 'xslt-tools:workspace:v1';
function isWorkbenchState(value: unknown): value is WorkbenchState {
if (!value || typeof value !== 'object') return false;
const candidate = value as Partial<WorkbenchState>;
return (
candidate.schemaVersion === 1 &&
Boolean(candidate.xmlInput) &&
Boolean(candidate.xsltCode) &&
Boolean(candidate.xmlOutput)
);
}
export function loadWorkbenchState(): WorkbenchState {
const fallback = createDefaultWorkbenchState();
try {
const raw = window.localStorage.getItem(STORAGE_KEY);
if (!raw) return fallback;
const parsed = JSON.parse(raw) as unknown;
if (!isWorkbenchState(parsed)) return fallback;
return {
...fallback,
...parsed,
options: { ...fallback.options, ...parsed.options },
xmlInput: { ...fallback.xmlInput, ...parsed.xmlInput },
xsltCode: { ...fallback.xsltCode, ...parsed.xsltCode },
xmlOutput: { ...fallback.xmlOutput, ...parsed.xmlOutput },
};
} catch (error) {
console.warn('Failed to load workspace from LocalStorage.', error);
return fallback;
}
}
export function saveWorkbenchState(state: WorkbenchState): void {
try {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
} catch (error) {
console.warn('Failed to save workspace to LocalStorage.', error);
}
}
export function clearWorkbenchState(): void {
window.localStorage.removeItem(STORAGE_KEY);
}

View File

@@ -0,0 +1,91 @@
import { useEffect, useMemo, useState } from 'react';
import { createDefaultWorkbenchState } from './defaultWorkspace';
import {
clearWorkbenchState,
loadWorkbenchState,
saveWorkbenchState,
} from './localStorageStore';
import type { WorkbenchDocumentKind, WorkbenchState } from './workspaceTypes';
import type { TransformationRun } from '../transform/transformTypes';
export function useWorkbenchState() {
const [state, setState] = useState<WorkbenchState>(() =>
loadWorkbenchState()
);
useEffect(() => {
const timeout = window.setTimeout(() => {
saveWorkbenchState(state);
}, 250);
return () => window.clearTimeout(timeout);
}, [state]);
const documents = useMemo(
() => [state.xmlInput, state.xsltCode, state.xmlOutput],
[state.xmlInput, state.xsltCode, state.xmlOutput]
);
const updateDocumentText = (kind: WorkbenchDocumentKind, text: string) => {
setState((current) => ({
...current,
[kind]: {
...current[kind],
text,
dirty: true,
},
}));
};
const replaceDocument = (
kind: WorkbenchDocumentKind,
text: string,
fileName?: string,
dirty = true
) => {
setState((current) => ({
...current,
[kind]: {
...current[kind],
text,
fileName: fileName ?? current[kind].fileName,
dirty,
},
}));
};
const markDocumentSaved = (
kind: WorkbenchDocumentKind,
fileName?: string
) => {
setState((current) => ({
...current,
[kind]: {
...current[kind],
fileName: fileName ?? current[kind].fileName,
dirty: false,
lastSavedAt: new Date().toISOString(),
},
}));
};
const setLastTransformation = (lastTransformation?: TransformationRun) => {
setState((current) => ({ ...current, lastTransformation }));
};
const resetWorkspace = () => {
clearWorkbenchState();
setState(createDefaultWorkbenchState());
};
return {
state,
documents,
setState,
updateDocumentText,
replaceDocument,
markDocumentSaved,
setLastTransformation,
resetWorkspace,
};
}

View File

@@ -0,0 +1,20 @@
import type { WorkbenchDocumentKind } from './workspaceTypes';
export interface WorkspaceCommandRecord {
id: string;
label: string;
target: WorkbenchDocumentKind;
createdAt: string;
}
export function createWorkspaceCommandRecord(
label: string,
target: WorkbenchDocumentKind
): WorkspaceCommandRecord {
return {
id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
label,
target,
createdAt: new Date().toISOString(),
};
}

View File

@@ -0,0 +1,35 @@
import type { DiagnosticMessage } from '../validation/validationTypes';
import type {
TransformEngineId,
TransformationRun,
} from '../transform/transformTypes';
export type WorkbenchDocumentKind = 'xmlInput' | 'xsltCode' | 'xmlOutput';
export interface WorkbenchDocument {
kind: WorkbenchDocumentKind;
label: string;
text: string;
fileName?: string;
dirty: boolean;
lastSavedAt?: string;
}
export interface WorkbenchOptions {
prettifyOutputAfterTransform: boolean;
askBeforeOverwritingOutput: boolean;
}
export interface WorkbenchState {
schemaVersion: 1;
options: WorkbenchOptions;
xmlInput: WorkbenchDocument;
xsltCode: WorkbenchDocument;
xmlOutput: WorkbenchDocument;
selectedEngine: TransformEngineId;
lastTransformation?: TransformationRun;
}
export interface WorkbenchDocumentWithDiagnostics extends WorkbenchDocument {
diagnostics: DiagnosticMessage[];
}