v0.3.1 with Saxon fully working
This commit is contained in:
76
src/workspace/defaultWorkspace.ts
Normal file
76
src/workspace/defaultWorkspace.ts
Normal 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,
|
||||
},
|
||||
};
|
||||
}
|
||||
52
src/workspace/localStorageStore.ts
Normal file
52
src/workspace/localStorageStore.ts
Normal 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);
|
||||
}
|
||||
91
src/workspace/useWorkbenchState.ts
Normal file
91
src/workspace/useWorkbenchState.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
20
src/workspace/workspaceCommands.ts
Normal file
20
src/workspace/workspaceCommands.ts
Normal 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(),
|
||||
};
|
||||
}
|
||||
35
src/workspace/workspaceTypes.ts
Normal file
35
src/workspace/workspaceTypes.ts
Normal 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[];
|
||||
}
|
||||
Reference in New Issue
Block a user