Updated desktop context menu
This commit is contained in:
parent
00b82c6548
commit
9cad04edcd
9 changed files with 124 additions and 31 deletions
|
|
@ -14,7 +14,7 @@
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png?v=4">
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png?v=4">
|
||||||
<link rel="manifest" href="/site.webmanifest?v=4">
|
<link rel="manifest" href="/site.webmanifest?v=4">
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg?v=4" color="#4cdfff">
|
<link rel="mask-icon" href="/safari-pinned-tab.svg?v=4" color="#4cdfff">
|
||||||
<link rel="shortcut icon" href="/favicon.ico?v=4">
|
<link rel="shortcut icon" type="image/x-icon" href="https://os.prozilla.dev/favicon.ico?">
|
||||||
<meta name="apple-mobile-web-app-title" content="ProzillaOS">
|
<meta name="apple-mobile-web-app-title" content="ProzillaOS">
|
||||||
<meta name="application-name" content="ProzillaOS">
|
<meta name="application-name" content="ProzillaOS">
|
||||||
<meta name="msapplication-TileColor" content="#0d1114">
|
<meta name="msapplication-TileColor" content="#0d1114">
|
||||||
|
|
@ -70,8 +70,6 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Inserted tags -->
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run ProzillaOS.</noscript>
|
<noscript>You need to enable JavaScript to run ProzillaOS.</noscript>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export interface ActionProps {
|
||||||
label?: string;
|
label?: string;
|
||||||
icon?: string | object;
|
icon?: string | object;
|
||||||
shortcut?: string[];
|
shortcut?: string[];
|
||||||
onTrigger?: (event: Event, triggerParams: unknown, ...args: unknown[]) => void;
|
onTrigger?: (event?: Event, triggerParams?: unknown, ...args: unknown[]) => void;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,8 +40,8 @@ export function Actions({ children, className, onAnyTrigger, triggerParams, avoi
|
||||||
const options = {};
|
const options = {};
|
||||||
const shortcuts = {};
|
const shortcuts = {};
|
||||||
|
|
||||||
|
let actionId = 0;
|
||||||
const iterateOverChildren = (children: ReactNode): ReactNode => {
|
const iterateOverChildren = (children: ReactNode): ReactNode => {
|
||||||
let actionId = 0;
|
|
||||||
const newChildren = Children.map(children, (child) => {
|
const newChildren = Children.map(children, (child) => {
|
||||||
if (!isValidElement(child))
|
if (!isValidElement(child))
|
||||||
return child;
|
return child;
|
||||||
|
|
@ -49,25 +49,30 @@ export function Actions({ children, className, onAnyTrigger, triggerParams, avoi
|
||||||
actionId++;
|
actionId++;
|
||||||
|
|
||||||
const { label, shortcut, onTrigger } = child.props as ActionProps;
|
const { label, shortcut, onTrigger } = child.props as ActionProps;
|
||||||
|
|
||||||
|
const onTriggerOverride = (event: Event, ...args: unknown[]) => {
|
||||||
|
onAnyTrigger?.(event, triggerParams, ...args);
|
||||||
|
onTrigger?.(event, triggerParams, ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register shortcut
|
||||||
if (label != null && onTrigger != null) {
|
if (label != null && onTrigger != null) {
|
||||||
options[actionId] = onTrigger;
|
options[actionId] = onTriggerOverride;
|
||||||
|
|
||||||
if (shortcut != null)
|
if (shortcut != null)
|
||||||
shortcuts[actionId] = shortcut;
|
shortcuts[actionId] = shortcut;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isListener) {
|
// Prevent listener from rendering
|
||||||
|
if (isListener)
|
||||||
return iterateOverChildren((child.props as ActionProps).children);
|
return iterateOverChildren((child.props as ActionProps).children);
|
||||||
}
|
|
||||||
|
|
||||||
return cloneElement(child, {
|
return cloneElement(child, {
|
||||||
...child.props,
|
...child.props,
|
||||||
actionId,
|
actionId,
|
||||||
children: iterateOverChildren((child.props as ActionProps).children),
|
children: iterateOverChildren((child.props as ActionProps).children),
|
||||||
onTrigger: (event, ...args) => {
|
onTrigger: onTriggerOverride
|
||||||
onAnyTrigger?.(event, triggerParams, ...args);
|
|
||||||
onTrigger?.(event, triggerParams, ...args);
|
|
||||||
}
|
|
||||||
} as ActionProps);
|
} as ActionProps);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.Content {
|
.Content {
|
||||||
|
--direction: column;
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: var(--direction);
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-content: flex-start;
|
align-content: flex-start;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import styles from "./Desktop.module.css";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext";
|
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext";
|
||||||
import { useContextMenu } from "../../hooks/modals/contextMenu";
|
import { useContextMenu } from "../../hooks/modals/contextMenu";
|
||||||
import { FALLBACK_ICON_SIZE, FALLBACK_WALLPAPER } from "../../config/desktop.config";
|
import { FALLBACK_ICON_DIRECTION, FALLBACK_ICON_SIZE, FALLBACK_WALLPAPER } from "../../config/desktop.config";
|
||||||
import { reloadViewport } from "../../features/_utils/browser.utils";
|
import { reloadViewport } from "../../features/_utils/browser.utils";
|
||||||
import { useVirtualRoot } from "../../hooks/virtual-drive/virtualRootContext";
|
import { useVirtualRoot } from "../../hooks/virtual-drive/virtualRootContext";
|
||||||
import { DirectoryList } from "../apps/file-explorer/directory-list/DirectoryList";
|
import { DirectoryList } from "../apps/file-explorer/directory-list/DirectoryList";
|
||||||
|
|
@ -13,7 +13,7 @@ import { APPS, APP_ICONS, APP_NAMES } from "../../config/apps.config";
|
||||||
import Vector2 from "../../features/math/vector2";
|
import Vector2 from "../../features/math/vector2";
|
||||||
import { Actions } from "../actions/Actions";
|
import { Actions } from "../actions/Actions";
|
||||||
import { ClickAction } from "../actions/actions/ClickAction";
|
import { ClickAction } from "../actions/actions/ClickAction";
|
||||||
import { faArrowsRotate, faEye, faFolder, faPaintBrush, faTerminal, faTrash } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowsRotate, faCompress, faExpand, faEye, faFolder, faPaintBrush, faTerminal, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { ToggleAction } from "../actions/actions/ToggleAction";
|
import { ToggleAction } from "../actions/actions/ToggleAction";
|
||||||
import { DropdownAction } from "../actions/actions/DropdownAction";
|
import { DropdownAction } from "../actions/actions/DropdownAction";
|
||||||
import { RadioAction } from "../actions/actions/RadioAction";
|
import { RadioAction } from "../actions/actions/RadioAction";
|
||||||
|
|
@ -34,6 +34,7 @@ export const Desktop = memo(() => {
|
||||||
const virtualRoot = useVirtualRoot();
|
const virtualRoot = useVirtualRoot();
|
||||||
const [showIcons, setShowIcons] = useState(false);
|
const [showIcons, setShowIcons] = useState(false);
|
||||||
const [iconSize, setIconSize] = useState(FALLBACK_ICON_SIZE);
|
const [iconSize, setIconSize] = useState(FALLBACK_ICON_SIZE);
|
||||||
|
const [iconDirection, setIconDirection] = useState(FALLBACK_ICON_DIRECTION);
|
||||||
const { openWindowedModal } = useWindowedModal();
|
const { openWindowedModal } = useWindowedModal();
|
||||||
|
|
||||||
const directory = virtualRoot.navigate("~/Desktop");
|
const directory = virtualRoot.navigate("~/Desktop");
|
||||||
|
|
@ -47,7 +48,15 @@ export const Desktop = memo(() => {
|
||||||
}} options={[
|
}} options={[
|
||||||
{ label: "Small icons" },
|
{ label: "Small icons" },
|
||||||
{ label: "Medium icons" },
|
{ label: "Medium icons" },
|
||||||
{ label: "Large icons" }
|
{ label: "Large icons" },
|
||||||
|
]}/>
|
||||||
|
<Divider/>
|
||||||
|
<RadioAction initialIndex={iconDirection} onTrigger={(event, params, value: string) => {
|
||||||
|
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.desktop);
|
||||||
|
void settings.set("icon-direction", value);
|
||||||
|
}} options={[
|
||||||
|
{ label: "Align vertically" },
|
||||||
|
{ label: "Align horizontally" },
|
||||||
]}/>
|
]}/>
|
||||||
<Divider/>
|
<Divider/>
|
||||||
<ToggleAction label="Show dekstop icons" initialValue={showIcons} onTrigger={() => {
|
<ToggleAction label="Show dekstop icons" initialValue={showIcons} onTrigger={() => {
|
||||||
|
|
@ -58,6 +67,25 @@ export const Desktop = memo(() => {
|
||||||
<ClickAction label="Reload" shortcut={["Control", "r"]} icon={faArrowsRotate} onTrigger={() => {
|
<ClickAction label="Reload" shortcut={["Control", "r"]} icon={faArrowsRotate} onTrigger={() => {
|
||||||
reloadViewport();
|
reloadViewport();
|
||||||
}}/>
|
}}/>
|
||||||
|
<ClickAction
|
||||||
|
label={!document.fullscreenElement ? "Enter fullscreen" : "Exit fullscreen"}
|
||||||
|
shortcut={["F11"]}
|
||||||
|
icon={!document.fullscreenElement ? faExpand : faCompress}
|
||||||
|
onTrigger={() => {
|
||||||
|
if (windowsManager.isAnyFocused())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!document.fullscreenElement) {
|
||||||
|
void document.body.requestFullscreen().catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
void document.exitFullscreen().catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<ClickAction label="Change appearance" icon={faPaintBrush} onTrigger={() => {
|
<ClickAction label="Change appearance" icon={faPaintBrush} onTrigger={() => {
|
||||||
windowsManager.open("settings", { tab: TABS.APPEARANCE });
|
windowsManager.open("settings", { tab: TABS.APPEARANCE });
|
||||||
}}/>
|
}}/>
|
||||||
|
|
@ -122,6 +150,10 @@ export const Desktop = memo(() => {
|
||||||
if (isValidInteger(value))
|
if (isValidInteger(value))
|
||||||
setIconSize(parseInt(value));
|
setIconSize(parseInt(value));
|
||||||
});
|
});
|
||||||
|
void settings.get("icon-direction", (value) => {
|
||||||
|
if (isValidInteger(value))
|
||||||
|
setIconDirection(parseInt(value));
|
||||||
|
});
|
||||||
}, [settingsManager]);
|
}, [settingsManager]);
|
||||||
|
|
||||||
const onError = () => {
|
const onError = () => {
|
||||||
|
|
@ -129,7 +161,7 @@ export const Desktop = memo(() => {
|
||||||
void settings.set("wallpaper", FALLBACK_WALLPAPER);
|
void settings.set("wallpaper", FALLBACK_WALLPAPER);
|
||||||
};
|
};
|
||||||
|
|
||||||
const iconScale = 1 + ((isValidInteger(iconSize) ? iconSize : FALLBACK_ICON_SIZE) - 1) / 4;
|
const iconScale = 1 + ((isValidInteger(iconSize) ? iconSize : FALLBACK_ICON_SIZE) - 1) / 5;
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
<ShortcutsListener/>
|
<ShortcutsListener/>
|
||||||
|
|
@ -141,7 +173,8 @@ export const Desktop = memo(() => {
|
||||||
directory={directory as VirtualFolder}
|
directory={directory as VirtualFolder}
|
||||||
className={styles.Content}
|
className={styles.Content}
|
||||||
style={{
|
style={{
|
||||||
"--scale": `${iconScale}rem`
|
"--scale": `${iconScale}rem`,
|
||||||
|
"--direction": iconDirection == 1 ? "row" : "column"
|
||||||
}}
|
}}
|
||||||
fileClassName={styles["Item"]}
|
fileClassName={styles["Item"]}
|
||||||
folderClassName={styles["Item"]}
|
folderClassName={styles["Item"]}
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,4 @@ export const WALLPAPERS = [
|
||||||
|
|
||||||
export const FALLBACK_WALLPAPER = WALLPAPERS[6];
|
export const FALLBACK_WALLPAPER = WALLPAPERS[6];
|
||||||
export const FALLBACK_ICON_SIZE = 1;
|
export const FALLBACK_ICON_SIZE = 1;
|
||||||
|
export const FALLBACK_ICON_DIRECTION = 0; // 0: vertical, 1: horizontal
|
||||||
|
|
@ -79,11 +79,18 @@ export function loadDefaultData(virtualRoot: VirtualRoot) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create files and folders based on repository tree
|
loadTree(virtualRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create files and folders based on repository tree
|
||||||
|
function loadTree(virtualRoot: VirtualRoot) {
|
||||||
|
const excludedFiles = [
|
||||||
|
"/public/config/tree.json"
|
||||||
|
];
|
||||||
|
|
||||||
void fetch("/config/tree.json").then((response) =>
|
void fetch("/config/tree.json").then((response) =>
|
||||||
response.json()
|
response.json()
|
||||||
).then(({ files, folders }: { files: string[], folders: string[] }) => {
|
).then(({ files, folders }: { files: string[], folders: string[] }) => {
|
||||||
|
|
||||||
folders.forEach((folderPath) => {
|
folders.forEach((folderPath) => {
|
||||||
const lastSlashIndex = folderPath.lastIndexOf("/");
|
const lastSlashIndex = folderPath.lastIndexOf("/");
|
||||||
|
|
||||||
|
|
@ -100,11 +107,12 @@ export function loadDefaultData(virtualRoot: VirtualRoot) {
|
||||||
});
|
});
|
||||||
|
|
||||||
files.forEach((filePath) => {
|
files.forEach((filePath) => {
|
||||||
|
if (excludedFiles.includes(filePath))
|
||||||
|
return;
|
||||||
|
|
||||||
const lastSlashIndex = filePath.lastIndexOf("/");
|
const lastSlashIndex = filePath.lastIndexOf("/");
|
||||||
|
|
||||||
const callback = (virtualFile: VirtualFile) => {
|
const callback = (virtualFile: VirtualFile) => {
|
||||||
console.log(virtualFile.absolutePath);
|
|
||||||
|
|
||||||
const virtualPath = virtualFile.absolutePath;
|
const virtualPath = virtualFile.absolutePath;
|
||||||
if (virtualPath.startsWith("/public/")) {
|
if (virtualPath.startsWith("/public/")) {
|
||||||
virtualFile.setSource(virtualPath.replace(/^\/public\//, "/"));
|
virtualFile.setSource(virtualPath.replace(/^\/public\//, "/"));
|
||||||
|
|
@ -126,6 +134,6 @@ export function loadDefaultData(virtualRoot: VirtualRoot) {
|
||||||
parentFolder.createFile(name, extension, callback);
|
parentFolder.createFile(name, extension, callback);
|
||||||
});
|
});
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
console.warn("Failed to fetch repository tree. Make sure the tree data is valid and up-to-date using 'npm run fetch'.");
|
console.warn("Failed to load repository tree. Make sure the tree data is valid and up-to-date using 'npm run fetch'.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +136,17 @@ export default class WindowsManager {
|
||||||
return this.windows[windowId].isFocused;
|
return this.windows[windowId].isFocused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isAnyFocused() {
|
||||||
|
let anyFocused = false;
|
||||||
|
|
||||||
|
Object.values(this.windows).forEach((window) => {
|
||||||
|
if (window.isFocused)
|
||||||
|
return anyFocused = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return anyFocused;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param minimized - Leave as undefined to toggle the window's minimization state
|
* @param minimized - Leave as undefined to toggle the window's minimization state
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ interface UseShortcutsParams {
|
||||||
useCategories?: boolean,
|
useCategories?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TO DO: rewrite to use a global shortcuts manager instead, to allow certain shortcuts to be prioritized and prevent conflicts
|
||||||
|
*/
|
||||||
export function useShortcuts({ options, shortcuts, useCategories = true }: UseShortcutsParams) {
|
export function useShortcuts({ options, shortcuts, useCategories = true }: UseShortcutsParams) {
|
||||||
const [activeKeys, setActiveKeys] = useState<string[]>([]);
|
const [activeKeys, setActiveKeys] = useState<string[]>([]);
|
||||||
|
|
||||||
|
|
@ -37,10 +40,6 @@ export function useShortcuts({ options, shortcuts, useCategories = true }: UseSh
|
||||||
|
|
||||||
const keys = [...activeKeys];
|
const keys = [...activeKeys];
|
||||||
|
|
||||||
// Prevent alt-tabbing from messing with alt key registration
|
|
||||||
if (keys.includes("Alt") && !event.altKey)
|
|
||||||
removeFromArray("Alt", keys);
|
|
||||||
|
|
||||||
const checkGroup = (group: Record<string, string[]>, category?: string) => {
|
const checkGroup = (group: Record<string, string[]>, category?: string) => {
|
||||||
for (const [name, shortcut] of Object.entries(group)) {
|
for (const [name, shortcut] of Object.entries(group)) {
|
||||||
let active = true;
|
let active = true;
|
||||||
|
|
@ -60,9 +59,9 @@ export function useShortcuts({ options, shortcuts, useCategories = true }: UseSh
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
(options?.[category]?.[name] as Function)?.();
|
(options?.[category]?.[name] as Function)?.(event);
|
||||||
} else {
|
} else {
|
||||||
(options?.[name] as Function)?.();
|
(options?.[name] as Function)?.(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -78,6 +77,18 @@ export function useShortcuts({ options, shortcuts, useCategories = true }: UseSh
|
||||||
setActiveKeys(keys);
|
setActiveKeys(keys);
|
||||||
}, [activeKeys, options, shortcuts, useCategories]);
|
}, [activeKeys, options, shortcuts, useCategories]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onBlur = () => {
|
||||||
|
setActiveKeys([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("blur", onBlur);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("blur", onBlur);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const onKeyDown = (event: KeyboardEvent) => {
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
const isRepeated = activeKeys.includes(event.key);
|
const isRepeated = activeKeys.includes(event.key);
|
||||||
checkShortcuts(event, isRepeated);
|
checkShortcuts(event, isRepeated);
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,23 @@ function generateCname() {
|
||||||
return DOMAIN;
|
return DOMAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateTemplate(html: string) {
|
||||||
|
const baseUrlRegex = /(?<=")https?:\/\/[a-z0-9-]+(\.)([a-zA-Z0-9-]+(\.))*[a-z]{2,3}\/(?=.*")/gi;
|
||||||
|
html = html.replaceAll(baseUrlRegex, BASE_URL);
|
||||||
|
|
||||||
|
const commentsRegex = /<!--.*?-->/g;
|
||||||
|
html = html.replaceAll(commentsRegex, "");
|
||||||
|
|
||||||
|
const emptyLinesRegex = /^\s*$\n/gm;
|
||||||
|
html = html.replaceAll(emptyLinesRegex, "");
|
||||||
|
|
||||||
|
const path = `${BUILD_DIR}/index.html`;
|
||||||
|
fs.writeFileSync(path, html, { flag: "w+" });
|
||||||
|
console.log(`- ${ANSI.fg.cyan}${path}${ANSI.reset}`);
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To avoid GitHub pages rendering certain pages that are only defined by React router as a 404 page,
|
* To avoid GitHub pages rendering certain pages that are only defined by React router as a 404 page,
|
||||||
* we copy the content of our index file to the 404 page, letting React router properly handle routing on every page
|
* we copy the content of our index file to the 404 page, letting React router properly handle routing on every page
|
||||||
|
|
@ -69,6 +86,9 @@ function generate404Page(template: string) {
|
||||||
console.log(`- ${ANSI.fg.cyan}${path}${ANSI.reset}`);
|
console.log(`- ${ANSI.fg.cyan}${path}${ANSI.reset}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an HTML file for every app page so they can be properly crawled and indexed
|
||||||
|
*/
|
||||||
function generateAppPages(template: string) {
|
function generateAppPages(template: string) {
|
||||||
for (const [key, value] of Object.entries(APPS)) {
|
for (const [key, value] of Object.entries(APPS)) {
|
||||||
const appId = value;
|
const appId = value;
|
||||||
|
|
@ -120,9 +140,13 @@ try {
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`\n${ANSI.fg.yellow}Generating pages...${ANSI.reset}`);
|
console.log(`\n${ANSI.fg.yellow}Generating pages...${ANSI.reset}`);
|
||||||
const template = fs.readFileSync(PATHS.indexHtml, "utf-8");
|
|
||||||
|
const html = fs.readFileSync(PATHS.indexHtml, "utf-8");
|
||||||
|
const template = generateTemplate(html);
|
||||||
|
|
||||||
generate404Page(template);
|
generate404Page(template);
|
||||||
generateAppPages(template);
|
generateAppPages(template);
|
||||||
|
|
||||||
console.log(`\n${ANSI.fg.green}✓ Staging complete${ANSI.reset}`);
|
console.log(`\n${ANSI.fg.green}✓ Staging complete${ANSI.reset}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue