import { ExportIcon, questionCircle, saveAs } from "../components/icons"; import { ProjectName } from "../components/ProjectName"; import { ToolButton } from "../components/ToolButton"; import { Tooltip } from "../components/Tooltip"; import { DarkModeToggle } from "../components/DarkModeToggle"; import { loadFromJSON, saveAsJSON } from "../data"; import { resaveAsImageWithScene } from "../data/resave"; import { t } from "../i18n"; import { useDevice } from "../components/App"; import { KEYS } from "../keys"; import { register } from "./register"; import { CheckboxItem } from "../components/CheckboxItem"; import { getExportSize } from "../scene/export"; import { DEFAULT_EXPORT_PADDING, EXPORT_SCALES, THEME } from "../constants"; import { getSelectedElements, isSomeElementSelected } from "../scene"; import { getNonDeletedElements } from "../element"; import { isImageFileHandle } from "../data/blob"; import { nativeFileSystemSupported } from "../data/filesystem"; import type { Theme } from "../element/types"; import "../components/ToolIcon.scss"; import { StoreAction } from "../store"; export const actionChangeProjectName = register({ name: "changeProjectName", label: "labels.fileTitle", trackEvent: false, perform: (_elements, appState, value) => { return { appState: { ...appState, name: value }, storeAction: StoreAction.NONE, }; }, PanelComponent: ({ appState, updateData, appProps, data, app }) => ( updateData(name)} ignoreFocus={data?.ignoreFocus ?? false} /> ), }); export const actionChangeExportScale = register({ name: "changeExportScale", label: "imageExportDialog.scale", trackEvent: { category: "export", action: "scale" }, perform: (_elements, appState, value) => { return { appState: { ...appState, exportScale: value }, storeAction: StoreAction.NONE, }; }, PanelComponent: ({ elements: allElements, appState, updateData }) => { const elements = getNonDeletedElements(allElements); const exportSelected = isSomeElementSelected(elements, appState); const exportedElements = exportSelected ? getSelectedElements(elements, appState) : elements; return ( <> {EXPORT_SCALES.map((s) => { const [width, height] = getExportSize( exportedElements, DEFAULT_EXPORT_PADDING, s, ); const scaleButtonTitle = `${t( "imageExportDialog.label.scale", )} ${s}x (${width}x${height})`; return ( updateData(s)} /> ); })} ); }, }); export const actionChangeExportBackground = register({ name: "changeExportBackground", label: "imageExportDialog.label.withBackground", trackEvent: { category: "export", action: "toggleBackground" }, perform: (_elements, appState, value) => { return { appState: { ...appState, exportBackground: value }, storeAction: StoreAction.NONE, }; }, PanelComponent: ({ appState, updateData }) => ( updateData(checked)} > {t("imageExportDialog.label.withBackground")} ), }); export const actionChangeExportEmbedScene = register({ name: "changeExportEmbedScene", label: "imageExportDialog.tooltip.embedScene", trackEvent: { category: "export", action: "embedScene" }, perform: (_elements, appState, value) => { return { appState: { ...appState, exportEmbedScene: value }, storeAction: StoreAction.NONE, }; }, PanelComponent: ({ appState, updateData }) => ( updateData(checked)} > {t("imageExportDialog.label.embedScene")}
{questionCircle}
), }); export const actionSaveToActiveFile = register({ name: "saveToActiveFile", label: "buttons.save", icon: ExportIcon, trackEvent: { category: "export" }, predicate: (elements, appState, props, app) => { return ( !!app.props.UIOptions.canvasActions.saveToActiveFile && !!appState.fileHandle && !appState.viewModeEnabled ); }, perform: async (elements, appState, value, app) => { const fileHandleExists = !!appState.fileHandle; try { const { fileHandle } = isImageFileHandle(appState.fileHandle) ? await resaveAsImageWithScene( elements, appState, app.files, app.getName(), ) : await saveAsJSON(elements, appState, app.files, app.getName()); return { storeAction: StoreAction.NONE, appState: { ...appState, fileHandle, toast: fileHandleExists ? { message: fileHandle?.name ? t("toast.fileSavedToFilename").replace( "{filename}", `"${fileHandle.name}"`, ) : t("toast.fileSaved"), } : null, }, }; } catch (error: any) { if (error?.name !== "AbortError") { console.error(error); } else { console.warn(error); } return { storeAction: StoreAction.NONE }; } }, keyTest: (event) => event.key === KEYS.S && event[KEYS.CTRL_OR_CMD] && !event.shiftKey, }); export const actionSaveFileToDisk = register({ name: "saveFileToDisk", label: "exportDialog.disk_title", icon: ExportIcon, viewMode: true, trackEvent: { category: "export" }, perform: async (elements, appState, value, app) => { try { const { fileHandle } = await saveAsJSON( elements, { ...appState, fileHandle: null, }, app.files, app.getName(), ); return { storeAction: StoreAction.NONE, appState: { ...appState, openDialog: null, fileHandle, toast: { message: t("toast.fileSaved") }, }, }; } catch (error: any) { if (error?.name !== "AbortError") { console.error(error); } else { console.warn(error); } return { storeAction: StoreAction.NONE }; } }, keyTest: (event) => event.key === KEYS.S && event.shiftKey && event[KEYS.CTRL_OR_CMD], PanelComponent: ({ updateData }) => (