mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-20 11:55:34 +00:00
dev: apply review suggestions and make auto-preview toggleable
This commit is contained in:
parent
fcaf7a7e9a
commit
25b8a77ae8
9 changed files with 61 additions and 63 deletions
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.3.1/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.3.5/schema.json",
|
||||
"vcs": {
|
||||
"enabled": false,
|
||||
"clientKind": "git",
|
||||
|
|
|
|||
|
|
@ -282,7 +282,8 @@ export const messageHandlers: Record<string, MessageHandler> = {
|
|||
}
|
||||
}
|
||||
} catch (error) {
|
||||
await vscode.window.showErrorMessage(`Export failed: ${error}`);
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
await vscode.window.showErrorMessage(`Export failed: ${message}`);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -298,17 +299,14 @@ export const messageHandlers: Record<string, MessageHandler> = {
|
|||
});
|
||||
};
|
||||
|
||||
console.log(`Generating preview for format=${format}, extraArgs=`, extraArgs);
|
||||
try {
|
||||
const ops = exportOps(extraArgs);
|
||||
const formatProvider = provideFormats(extraArgs);
|
||||
|
||||
// await vscode.window.showInformationMessage(`Active `)
|
||||
|
||||
// Get the active document
|
||||
const uri = ops.resolveInputPath();
|
||||
if (!uri) {
|
||||
// sendError("No active document found");
|
||||
// Just ignore if no active document
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -332,7 +330,6 @@ export const messageHandlers: Record<string, MessageHandler> = {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log(`Generating preview for format=${format}, extraArgs=${extraArgs}, uri=${uri}`);
|
||||
// For visual formats, generate PNG/SVG previews
|
||||
if (format === "pdf" || format === "png" || format === "svg") {
|
||||
// Extract base64 data from the response
|
||||
|
|
@ -362,7 +359,6 @@ export const messageHandlers: Record<string, MessageHandler> = {
|
|||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.log("Preview generation failed:", error);
|
||||
sendError(`Preview generation failed: ${message}`);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,34 +12,6 @@ export const ActionButtons = ({ onExport }: ActionButtonsProps) => {
|
|||
|
||||
h3("Export Actions"),
|
||||
|
||||
// Task Label Input
|
||||
/* div(
|
||||
{ class: "card flex flex-col gap-xs" },
|
||||
label(
|
||||
{
|
||||
class: "text-sm font-medium",
|
||||
for: "task-label",
|
||||
style: "margin-bottom: 0.5rem; display: block;",
|
||||
},
|
||||
"Custom Task Label (Optional)",
|
||||
),
|
||||
input({
|
||||
class: "input",
|
||||
type: "text",
|
||||
id: "task-label",
|
||||
placeholder: `Export to ${exportConfig.val.format.label}`,
|
||||
value: customTaskLabel,
|
||||
oninput: (e: Event) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
customTaskLabel.val = target.value;
|
||||
},
|
||||
}),
|
||||
div(
|
||||
{ class: "text-xs text-desc" },
|
||||
"This will be used as the task name in tasks.json. Leave empty for default naming.",
|
||||
),
|
||||
), */
|
||||
|
||||
// Action Buttons
|
||||
div(
|
||||
{ class: "action-buttons flex items-center gap-md" },
|
||||
|
|
|
|||
|
|
@ -23,20 +23,8 @@ export const Header =
|
|||
{ class: "flex flex-col sm:flex-row sm:justify-between sm:items-center gap-sm" },
|
||||
div(
|
||||
{ class: "flex flex-col gap-xs" },
|
||||
h1(
|
||||
{
|
||||
class: "text-xl font-semibold text-base-content",
|
||||
style: "margin: 0; font-size: 1.25rem; font-weight: 600;",
|
||||
},
|
||||
title,
|
||||
),
|
||||
p(
|
||||
{
|
||||
class: "text-desc",
|
||||
style: "margin: 0; font-size: 0.875rem;",
|
||||
},
|
||||
description,
|
||||
),
|
||||
h1({ class: "text-xl font-semibold text-base-content" }, title),
|
||||
p({ class: "text-desc font-sm" }, description),
|
||||
),
|
||||
actions ? div({ class: "flex gap-xs" }, actions) : null,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ const OptionField = (schema: OptionSchema, valueState: State<Scalar>) => {
|
|||
|
||||
const renderInput = (
|
||||
schema: OptionSchema,
|
||||
valueState: State<Scalar>,
|
||||
valueState: State<Scalar | undefined>,
|
||||
validationError: State<string | undefined>,
|
||||
) => {
|
||||
const { type, key, options: selectOptions, min, max } = schema;
|
||||
|
|
@ -102,7 +102,7 @@ const renderInput = (
|
|||
const target = e.target as HTMLInputElement;
|
||||
// Call custom validation function if provided
|
||||
validationError.val = schema.validate?.(target.value);
|
||||
valueState.val = parseFloat(target.value);
|
||||
valueState.val = target.value === "" ? undefined : parseFloat(target.value);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import van, { type State } from "vanjs-core";
|
||||
import type { ExportFormat, PreviewData, PreviewPage } from "../types";
|
||||
|
||||
const { div, h3, img, span, button } = van.tags;
|
||||
const { div, h3, img, span, button, label, input } = van.tags;
|
||||
|
||||
interface PreviewGridProps {
|
||||
format: State<ExportFormat>;
|
||||
previewData: State<PreviewData>;
|
||||
previewGenerating: State<boolean>;
|
||||
autoPreview: State<boolean>;
|
||||
onPreview: () => void;
|
||||
}
|
||||
|
||||
export const PreviewGrid = (props: PreviewGridProps) => {
|
||||
const { format, previewData, previewGenerating, onPreview } = props;
|
||||
const { format, previewData, previewGenerating, autoPreview, onPreview } = props;
|
||||
|
||||
const thumbnailZoom = van.state<number>(100); // Percentage for thumbnail sizing
|
||||
|
||||
|
|
@ -19,14 +20,13 @@ export const PreviewGrid = (props: PreviewGridProps) => {
|
|||
// Preview Header
|
||||
div(
|
||||
{ class: "flex justify-between items-center mb-md" },
|
||||
h3({ class: "text-lg font-semibold" }, () => `Preview (${format.val.label})`),
|
||||
() =>
|
||||
div(
|
||||
{ class: "flex items-center gap-sm", style: "min-height: 2rem;" },
|
||||
// Only show zoom controls for image content (thumbnails)
|
||||
!previewGenerating.val && previewData.val.pages && previewData.val.pages.length > 0
|
||||
? ZoomControls(thumbnailZoom)
|
||||
: null,
|
||||
{ class: "flex items-center gap-sm" },
|
||||
|
||||
h3({ class: "text-lg font-semibold" }, () => `Preview (${format.val.label})`),
|
||||
|
||||
// Generate Preview Button
|
||||
() =>
|
||||
button(
|
||||
{
|
||||
class: "btn btn-secondary",
|
||||
|
|
@ -35,6 +35,31 @@ export const PreviewGrid = (props: PreviewGridProps) => {
|
|||
},
|
||||
previewGenerating.val ? "Generating..." : "Generate Preview",
|
||||
),
|
||||
|
||||
// Auto-preview toggle
|
||||
label(
|
||||
{ class: "flex items-center gap-xs cursor-pointer", style: "user-select: none;" },
|
||||
input({
|
||||
type: "checkbox",
|
||||
class: "toggle",
|
||||
checked: autoPreview,
|
||||
onchange: (e: Event) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
autoPreview.val = target.checked;
|
||||
},
|
||||
}),
|
||||
span({ class: "text-sm" }, "Auto"),
|
||||
),
|
||||
),
|
||||
|
||||
() =>
|
||||
div(
|
||||
{ class: "flex items-center gap-sm", style: "min-height: 2rem;" },
|
||||
|
||||
// Only show zoom controls for image content (thumbnails)
|
||||
!previewGenerating.val && previewData.val.pages && previewData.val.pages.length > 0
|
||||
? ZoomControls(thumbnailZoom)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export function useExporter() {
|
|||
let previewVersion = 0;
|
||||
const previewGenerating = van.state(false);
|
||||
const previewData = van.state<PreviewData>({});
|
||||
const autoPreview = van.state(true);
|
||||
|
||||
const buildOptions = () => {
|
||||
const extraOpts = Object.fromEntries(
|
||||
|
|
@ -52,6 +53,8 @@ export function useExporter() {
|
|||
|
||||
// Regenerate preview automatically when format or options change
|
||||
van.derive(() => {
|
||||
if (!autoPreview.val) return;
|
||||
|
||||
if (format.oldVal !== format.val) {
|
||||
// Clear previous preview data when format changes
|
||||
previewData.val = {};
|
||||
|
|
@ -71,6 +74,10 @@ export function useExporter() {
|
|||
};
|
||||
window?.addEventListener("message", handleMessage);
|
||||
|
||||
const cleanup = () => {
|
||||
window?.removeEventListener("message", handleMessage);
|
||||
};
|
||||
|
||||
return {
|
||||
inputPath,
|
||||
outputPath,
|
||||
|
|
@ -78,8 +85,10 @@ export function useExporter() {
|
|||
optionStates,
|
||||
previewGenerating,
|
||||
previewData,
|
||||
autoPreview,
|
||||
exportDocument,
|
||||
generatePreview,
|
||||
cleanup,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,9 +45,11 @@ export const EXPORT_FORMATS: ExportFormat[] = [
|
|||
if (value.trim() === "") {
|
||||
return; // Allow empty input
|
||||
}
|
||||
if (!/^\d+$/.test(value)) {
|
||||
return "Creation timestamp must be a valid non-negative integer UNIX timestamp";
|
||||
}
|
||||
const num = Number(value);
|
||||
if (Number.isNaN(num) || !Number.isInteger(num) || num < 0) {
|
||||
// fixme: it still accepts floating point numbers like "1e5"
|
||||
return "Creation timestamp must be a valid non-negative integer UNIX timestamp";
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -23,10 +23,15 @@ const ExportTool = () => {
|
|||
optionStates,
|
||||
previewGenerating,
|
||||
previewData,
|
||||
autoPreview,
|
||||
exportDocument,
|
||||
generatePreview,
|
||||
} = useExporter();
|
||||
|
||||
// Note: cleanup() should be called when the component is unmounted
|
||||
// In the current single-page architecture, this might not be needed,
|
||||
// but it's available for future use if the tool becomes part of a larger app
|
||||
|
||||
return div(
|
||||
{ class: "export-tool-container flex flex-col gap-lg text-base-content" },
|
||||
|
||||
|
|
@ -49,6 +54,7 @@ const ExportTool = () => {
|
|||
format: format,
|
||||
previewData,
|
||||
previewGenerating,
|
||||
autoPreview,
|
||||
onPreview: generatePreview,
|
||||
}),
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue