├── locales ├── game │ ├── en.json │ ├── zh_CN.json │ ├── zh_HK.json │ └── index.ts ├── tools │ ├── en.json │ ├── zh_CN.json │ ├── zh_HK.json │ └── index.ts ├── tip │ ├── zh_CN.json │ ├── zh_HK.json │ ├── en.json │ └── index.ts ├── rules │ ├── zh_CN.json │ ├── zh_HK.json │ ├── en.json │ └── index.ts ├── common │ ├── zh_CN.json │ ├── zh_HK.json │ ├── en.json │ └── index.ts ├── dashboard │ ├── index.ts │ ├── zh_HK.json │ ├── zh_CN.json │ └── en.json └── config.ts ├── src ├── views │ ├── game_of_life │ │ ├── index.scss │ │ └── index.tsx │ ├── mdhub │ │ ├── index.scss │ │ └── index.tsx │ ├── canvas │ │ ├── components │ │ │ ├── AddDirButton │ │ │ │ ├── index.scss │ │ │ │ └── index.tsx │ │ │ └── DeleteButton │ │ │ │ └── index.tsx │ │ ├── paper │ │ │ ├── icons │ │ │ │ └── cursor.svg │ │ │ ├── index.scss │ │ │ ├── components │ │ │ │ ├── SaveIcon │ │ │ │ │ └── index.tsx │ │ │ │ ├── BrushIcon │ │ │ │ │ └── index.tsx │ │ │ │ ├── EraserIcon │ │ │ │ │ └── index.tsx │ │ │ │ ├── ColorPicker │ │ │ │ │ ├── index.scss │ │ │ │ │ └── index.tsx │ │ │ │ └── ToolPalette │ │ │ │ │ ├── index.scss │ │ │ │ │ └── index.tsx │ │ │ ├── index.tsx │ │ │ └── canvas.ts │ │ ├── index.scss │ │ └── index.tsx │ └── dashboard │ │ └── index.tsx ├── utils │ ├── constant.ts │ ├── mdParser.ts │ ├── tools.ts │ └── color.ts ├── vite-env.d.ts ├── styles │ ├── index.scss │ ├── variable.scss │ ├── main.scss │ └── common.scss ├── components │ ├── Popover │ │ ├── index.scss │ │ └── index.tsx │ ├── Tooltip │ │ ├── index.scss │ │ └── index.tsx │ ├── Tabs │ │ ├── index.scss │ │ └── index.tsx │ ├── InputText │ │ ├── index.scss │ │ └── index.tsx │ ├── Editor │ │ ├── index.scss │ │ └── index.tsx │ ├── GoBack │ │ ├── index.scss │ │ └── index.tsx │ ├── SwitchLang │ │ └── index.tsx │ ├── OmbCard │ │ ├── OmbIcon.tsx │ │ ├── index.tsx │ │ ├── index.scss │ │ └── OmbItem.tsx │ └── Slider │ │ ├── index.scss │ │ └── index.tsx ├── hooks │ ├── useI18n.ts │ ├── useEvent.ts │ ├── useLang.ts │ ├── useFullCanvas.ts │ └── useLocalColor.ts ├── shims.d.ts ├── main.tsx ├── layouts │ ├── fullscreen.scss │ ├── index.scss │ ├── index.tsx │ └── FullSreen.tsx ├── system │ ├── os.tsx │ └── fs.ts ├── routes.tsx ├── favicon.svg ├── logo.svg └── oh-my-box.svg ├── src-tauri ├── build.rs ├── src │ ├── omb │ │ ├── mod.rs │ │ ├── menu.rs │ │ ├── setup.rs │ │ ├── tray.rs │ │ └── fs.rs │ └── main.rs ├── .gitignore ├── icons │ ├── 32x32.png │ ├── icon.icns │ ├── icon.ico │ ├── icon.png │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── StoreLogo.png │ ├── Square30x30Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ └── Square310x310Logo.png ├── Cargo.toml └── tauri.conf.json ├── UPDATE_LOG.md ├── .prettierrc ├── tsconfig.node.json ├── index.html ├── .gitignore ├── @omb └── game-of-life │ ├── Cargo.toml │ └── src │ └── lib.rs ├── .vscode └── settings.json ├── unocss.ts ├── tsconfig.json ├── scripts ├── updatelog.mjs ├── release.mjs └── updater.mjs ├── vite.config.ts ├── rsw.toml ├── package.json ├── README.md ├── .github └── workflows │ └── release.yml ├── plugins └── fsExtra.ts └── LICENSE /locales/game/en.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /locales/tools/en.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /locales/game/zh_CN.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /locales/game/zh_HK.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /locales/tools/zh_CN.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /locales/tools/zh_HK.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /src/views/game_of_life/index.scss: -------------------------------------------------------------------------------- 1 | // .game-of-life-view {} -------------------------------------------------------------------------------- /src/utils/constant.ts: -------------------------------------------------------------------------------- 1 | export const CANVAS_ROOT = `.omb/canvas`; -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './main'; 2 | @import './variable'; 3 | @import './common'; -------------------------------------------------------------------------------- /src-tauri/src/omb/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod fs; 2 | pub mod menu; 3 | pub mod setup; 4 | pub mod tray; 5 | -------------------------------------------------------------------------------- /src/components/Popover/index.scss: -------------------------------------------------------------------------------- 1 | .omb-popover { 2 | background: #fff; 3 | border-radius: 6px; 4 | } -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /locales/tip/zh_CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "goback": "点击返回", 3 | "mdPreview": "Markdown 预览", 4 | "save": "保存" 5 | } 6 | -------------------------------------------------------------------------------- /locales/tip/zh_HK.json: -------------------------------------------------------------------------------- 1 | { 2 | "goback": "點擊返回", 3 | "mdPreview": "Markdown 預覽", 4 | "save": "保存" 5 | } 6 | -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /src/views/mdhub/index.scss: -------------------------------------------------------------------------------- 1 | .markdown-body { 2 | .omb-code-wrap { 3 | background-color: #2a2a2a; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /locales/tip/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "goback": "Click to go back", 3 | "mdPreview": "Markdown Preivew", 4 | "save": "Save" 5 | } 6 | -------------------------------------------------------------------------------- /src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /locales/rules/zh_CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "check-name": "名称不能包含特殊字符", 3 | "check-file-exist": "此位置已存在文件或文件夹 <{{name}}>,请选择其他名称。" 4 | } 5 | -------------------------------------------------------------------------------- /locales/rules/zh_HK.json: -------------------------------------------------------------------------------- 1 | { 2 | "check-name": "名稱不能包含特殊字符", 3 | "check-file-exist": "此位置已存在文件或文件夹 <{{name}}>,请选择其他名称。" 4 | } 5 | -------------------------------------------------------------------------------- /src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/tauri-tutorial/HEAD/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /UPDATE_LOG.md: -------------------------------------------------------------------------------- 1 | # Updater Log 2 | 3 | ## v0.1.7 4 | 5 | feat: updater 6 | 7 | ## v0.1.6 8 | 9 | Development toolbox, and more... 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": true, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "es5", 7 | "printWidth": 80 8 | } 9 | -------------------------------------------------------------------------------- /src/styles/variable.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --theme: #000; 3 | 4 | --red: #EC4C4C; 5 | --yellow: #EDE576; 6 | --blue: #33B5CC; 7 | --brown: #B27C66; 8 | --gray: #DADADA; 9 | } -------------------------------------------------------------------------------- /src/hooks/useI18n.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next'; 2 | 3 | export default function useI18n(ns: string[]) { 4 | const { t } = useTranslation(ns); 5 | return t; 6 | } -------------------------------------------------------------------------------- /src/shims.d.ts: -------------------------------------------------------------------------------- 1 | import type { AttributifyAttributes } from '@unocss/preset-attributify' 2 | 3 | declare module 'react' { 4 | interface HTMLAttributes extends AttributifyAttributes { } 5 | } -------------------------------------------------------------------------------- /locales/common/zh_CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "btn": "按钮", 3 | "menu": "菜单", 4 | "palette": "调色板", 5 | "custom": "自定义", 6 | "recent": "最近的", 7 | "size": "尺寸", 8 | "opacity": "透明度" 9 | } 10 | -------------------------------------------------------------------------------- /locales/common/zh_HK.json: -------------------------------------------------------------------------------- 1 | { 2 | "btn": "按鈕", 3 | "menu": "菜單", 4 | "palette": "調色板", 5 | "custom": "自定義", 6 | "recent": "最近的", 7 | "size": "尺寸", 8 | "opacity": "透明度" 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts", "unocss.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /locales/common/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "btn": "button", 3 | "menu": "menu", 4 | "palette": "Palette", 5 | "custom": "Custom", 6 | "recent": "Recent", 7 | "size": "Size", 8 | "opacity": "Opacity" 9 | } 10 | -------------------------------------------------------------------------------- /locales/rules/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "check-name": "Name cannot contain special characters", 3 | "check-file-exist": "A file or folder <{{name}}> already exists at this location. Please choose a different name." 4 | } 5 | -------------------------------------------------------------------------------- /locales/game/index.ts: -------------------------------------------------------------------------------- 1 | import en from './en.json'; 2 | import zh_CN from './zh_CN.json'; 3 | import zh_HK from './zh_HK.json'; 4 | 5 | export default { 6 | __ns: 'game', 7 | en, 8 | zh_CN, 9 | zh_HK, 10 | }; -------------------------------------------------------------------------------- /locales/tip/index.ts: -------------------------------------------------------------------------------- 1 | import en from './en.json'; 2 | import zh_CN from './zh_CN.json'; 3 | import zh_HK from './zh_HK.json'; 4 | 5 | export default { 6 | __ns: 'tip', 7 | en, 8 | zh_CN, 9 | zh_HK, 10 | }; -------------------------------------------------------------------------------- /src/views/canvas/components/AddDirButton/index.scss: -------------------------------------------------------------------------------- 1 | .omb-add-dir { 2 | display: flex; 3 | padding: 0 8px; 4 | height: 30px; 5 | align-items: center; 6 | 7 | input { 8 | // all: unset; 9 | } 10 | } -------------------------------------------------------------------------------- /locales/common/index.ts: -------------------------------------------------------------------------------- 1 | import en from './en.json'; 2 | import zh_CN from './zh_CN.json'; 3 | import zh_HK from './zh_HK.json'; 4 | 5 | export default { 6 | __ns: 'common', 7 | en, 8 | zh_CN, 9 | zh_HK, 10 | }; -------------------------------------------------------------------------------- /locales/rules/index.ts: -------------------------------------------------------------------------------- 1 | import en from './en.json'; 2 | import zh_CN from './zh_CN.json'; 3 | import zh_HK from './zh_HK.json'; 4 | 5 | export default { 6 | __ns: 'rules', 7 | en, 8 | zh_CN, 9 | zh_HK, 10 | }; -------------------------------------------------------------------------------- /locales/tools/index.ts: -------------------------------------------------------------------------------- 1 | import en from './en.json'; 2 | import zh_CN from './zh_CN.json'; 3 | import zh_HK from './zh_HK.json'; 4 | 5 | export default { 6 | __ns: 'tools', 7 | en, 8 | zh_CN, 9 | zh_HK, 10 | }; -------------------------------------------------------------------------------- /src/views/canvas/paper/icons/cursor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /locales/dashboard/index.ts: -------------------------------------------------------------------------------- 1 | import en from './en.json'; 2 | import zh_CN from './zh_CN.json'; 3 | import zh_HK from './zh_HK.json'; 4 | 5 | export default { 6 | __ns: 'dashboard', 7 | en, 8 | zh_CN, 9 | zh_HK, 10 | }; -------------------------------------------------------------------------------- /locales/dashboard/zh_HK.json: -------------------------------------------------------------------------------- 1 | { 2 | "tools": "工具", 3 | "video": "視頻", 4 | "game": "遊戲", 5 | "other": "其他", 6 | "editor": "編輯器", 7 | 8 | "canvas": "畫板", 9 | "mdhub": "MdHub", 10 | "game-of-life": "康威生命遊戲" 11 | } 12 | -------------------------------------------------------------------------------- /locales/dashboard/zh_CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "tools": "工具", 3 | "video": "视频", 4 | "game": "游戏", 5 | "other": "其他", 6 | "editor": "编辑器", 7 | 8 | "canvas": "画板", 9 | "mdhub": "MdHub", 10 | "game-of-life": "Game of Life" 11 | } 12 | -------------------------------------------------------------------------------- /locales/dashboard/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "tools": "Tools", 3 | "video": "Video", 4 | "game": "Game", 5 | "other": "Other", 6 | "editor": "Editor", 7 | 8 | "canvas": "Canvas", 9 | "mdhub": "MdHub", 10 | "game-of-life": "Game of Life" 11 | } 12 | -------------------------------------------------------------------------------- /src/views/canvas/paper/index.scss: -------------------------------------------------------------------------------- 1 | .canvas_paper_screen { 2 | height: 100vh; 3 | overflow: hidden; 4 | user-select: none; 5 | -webkit-user-select: none; 6 | 7 | .omb-canvas-paper { 8 | cursor: url('./icons/cursor.svg') 3 3, auto; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src-tauri/src/omb/menu.rs: -------------------------------------------------------------------------------- 1 | use tauri::utils::assets::EmbeddedAssets; 2 | use tauri::{Context, Menu, WindowMenuEvent}; 3 | 4 | #[allow(dead_code)] 5 | pub fn init(context: &Context) -> Menu { 6 | let name = &context.package_info().name; 7 | tauri::Menu::os_default(name) 8 | } 9 | 10 | #[allow(dead_code)] 11 | pub fn handler(_event: WindowMenuEvent) {} 12 | -------------------------------------------------------------------------------- /src/components/Tooltip/index.scss: -------------------------------------------------------------------------------- 1 | .omb-tooltip { 2 | background: #2a2a2a; 3 | color: #fff; 4 | pointer-events: none; 5 | border-radius: 6px; 6 | padding: 4px 6px; 7 | font-size: 14px; 8 | z-index: 99; 9 | 10 | &.sys { 11 | font-size: 0.5rem; 12 | background-color: #eee; 13 | color: #3a3a3a; 14 | border-radius: 2px; 15 | padding: 2px 4px; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OhMyBox 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/Tabs/index.scss: -------------------------------------------------------------------------------- 1 | .omb-tabs { 2 | display: inline-block; 3 | width: auto; 4 | overflow: hidden; 5 | 6 | &-head { 7 | > div { 8 | display: inline-block; 9 | padding: 5px 10px; 10 | cursor: pointer; 11 | transition: 300ms ease; 12 | 13 | &.active { 14 | background-color: #eee; 15 | } 16 | } 17 | } 18 | 19 | &-body { 20 | background-color: #fff; 21 | } 22 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | # .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | /updater 27 | 28 | # rsw 29 | .rsw/ 30 | target/ 31 | Cargo.lock 32 | .woap -------------------------------------------------------------------------------- /src/hooks/useEvent.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | // https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md 3 | import { useLayoutEffect, useRef, useCallback } from 'react'; 4 | 5 | export default function useEvent(handler) { 6 | const handlerRef = useRef(null); 7 | 8 | useLayoutEffect(() => { 9 | handlerRef.current = handler; 10 | }); 11 | 12 | return useCallback((...args) => { 13 | return handlerRef.current(...args); 14 | }, []); 15 | } -------------------------------------------------------------------------------- /src/components/InputText/index.scss: -------------------------------------------------------------------------------- 1 | .omb-input-text { 2 | height: 20px; 3 | border: dashed 1px transparent; 4 | // cursor: text; 5 | 6 | // &:hover { 7 | // border: dashed 1px #000; 8 | // } 9 | 10 | .text { 11 | display: inline-block; 12 | width: 100%; 13 | } 14 | 15 | input { 16 | outline: none; 17 | border: none; 18 | background-color: transparent; 19 | margin: 0; 20 | padding: 0; 21 | font-size: 16px; 22 | } 23 | } -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | import 'uno.css'; 5 | 6 | import '@locales/config'; 7 | import '@/styles/index.scss'; 8 | 9 | import Routes from './routes'; 10 | 11 | ReactDOM.createRoot(document.getElementById('root')!).render( 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /@omb/game-of-life/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "game-of-life" 3 | version = "0.1.0" 4 | authors = ["lencx "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [lib] 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [package.metadata.wasm-pack.profile.release] 12 | wasm-opt = true 13 | 14 | [dependencies] 15 | wasm-bindgen = "0.2.71" 16 | fixedbitset = "0.4.0" 17 | js-sys = "0.3.48" 18 | -------------------------------------------------------------------------------- /src/hooks/useLang.ts: -------------------------------------------------------------------------------- 1 | import i18n from 'i18next'; 2 | import { useState, useEffect } from 'react'; 3 | 4 | export const useGetLang = () => { 5 | const [lang, setLanguage] = useState(i18n.language); 6 | 7 | useEffect(() => { 8 | const handleLang = () => setLanguage(i18n.language); 9 | i18n.on('languageChanged', handleLang); 10 | 11 | return () => i18n.on('removed', handleLang); 12 | }, []); 13 | 14 | return lang; 15 | }; 16 | 17 | export const useSetLang = () => (value: string) => 18 | i18n.changeLanguage(value); 19 | -------------------------------------------------------------------------------- /src/components/Editor/index.scss: -------------------------------------------------------------------------------- 1 | .omb-editor-container { 2 | width: 100%; 3 | height: 100vh; 4 | overflow: scroll; 5 | resize: both; 6 | 7 | // .overflow-guard { 8 | // .margin { 9 | // background-color: transparent; 10 | // } 11 | // } 12 | // .minimap-decorations-layer { 13 | // background-color: transparent; 14 | // } 15 | // .monaco-editor-background { 16 | // background-color: transparent; 17 | // } 18 | 19 | .preview { 20 | background: #fff; 21 | overflow: auto; 22 | height: auto; 23 | padding: 20px; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/hooks/useFullCanvas.ts: -------------------------------------------------------------------------------- 1 | import { RefObject, useEffect } from "react"; 2 | 3 | export default function useFullCanvas(el: RefObject, callback?: () => void) { 4 | const getSize = () => { 5 | if (el.current) { 6 | el.current.width = window.innerWidth; 7 | el.current.height = window.innerHeight; 8 | callback && callback(); 9 | } 10 | }; 11 | 12 | useEffect(() => { 13 | getSize(); 14 | window.addEventListener('resize', getSize); 15 | return () => window.removeEventListener('resize', getSize); 16 | }, []); 17 | }; -------------------------------------------------------------------------------- /src/components/GoBack/index.scss: -------------------------------------------------------------------------------- 1 | .omb-goback { 2 | &.float { 3 | position: fixed; 4 | top: 5px; 5 | left: 5px; 6 | font-size: 1.2rem; 7 | margin: 0 5px; 8 | z-index: 99; 9 | width: 30px; 10 | height: 30px; 11 | background-color: rgba(46, 41, 51, 0.2); 12 | box-shadow: 0 0 4px rgba(46, 41, 51, 0.2); 13 | } 14 | 15 | &.inline { 16 | width: 24px; 17 | height: 20px; 18 | display: flex; 19 | align-items: center; 20 | justify-content: center; 21 | } 22 | 23 | &:hover { 24 | color: var(--blue); 25 | transition: 300ms ease; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | ".git/": true, 4 | "node_modules/": false, 5 | "public/": false, 6 | "**/target": false 7 | }, 8 | "search.exclude": { 9 | "**/.cache": true, 10 | "**/node_modules": true, 11 | "**/public": true, 12 | "**/target": true 13 | }, 14 | "editor.rulers": [80], 15 | "editor.tabSize": 2, 16 | "editor.formatOnSave": true, 17 | // https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint 18 | "editor.codeActionsOnSave": { 19 | "source.fixAll.eslint": true 20 | }, 21 | "typescript.tsdk": "./node_modules/typescript/lib" 22 | } 23 | -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | body, 2 | #root { 3 | margin: 0; 4 | padding: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 6 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 7 | sans-serif; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | height: 100%; 11 | } 12 | 13 | code { 14 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 15 | monospace; 16 | } 17 | 18 | body { 19 | height: 100vh; 20 | overflow: hidden; 21 | } 22 | 23 | #root { 24 | overflow-y: auto; 25 | } 26 | 27 | * { 28 | box-sizing: border-box; 29 | } 30 | -------------------------------------------------------------------------------- /src-tauri/src/omb/setup.rs: -------------------------------------------------------------------------------- 1 | use tauri::{App, Manager}; 2 | use window_vibrancy::{self, NSVisualEffectMaterial}; 3 | 4 | /// omb setup 5 | pub fn init(app: &mut App) -> std::result::Result<(), Box> { 6 | let win = app.get_window("main").unwrap(); 7 | 8 | #[cfg(target_os = "macos")] 9 | window_vibrancy::apply_vibrancy(&win, NSVisualEffectMaterial::FullScreenUI) 10 | .expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS"); 11 | 12 | #[cfg(target_os = "windows")] 13 | window_vibrancy::apply_blur(&win, Some((18, 18, 18, 125))) 14 | .expect("Unsupported platform! 'apply_blur' is only supported on Windows"); 15 | 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /src/layouts/fullscreen.scss: -------------------------------------------------------------------------------- 1 | .omb-fullscreen { 2 | height: 100%; 3 | overflow: hidden; 4 | 5 | .omb-taskbar { 6 | position: fixed; 7 | width: 100%; 8 | width: 100%; 9 | z-index: 2; 10 | left: 0; 11 | top: 0; 12 | height: 28px; 13 | background: #fea; 14 | display: flex; 15 | align-items: center; 16 | box-shadow: 0 2px 2px rgba(255, 255, 255, 0.9); 17 | justify-content: space-between; 18 | padding-right: 10px; 19 | user-select: none; 20 | -webkit-user-select: none; 21 | } 22 | 23 | .fullscreen-body { 24 | padding-top: 30px; 25 | height: calc(100vh - 30px); 26 | } 27 | 28 | .omb-editor-container { 29 | .preview { 30 | height: 100%; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/layouts/index.scss: -------------------------------------------------------------------------------- 1 | .omb-layout { 2 | height: 100vh; 3 | 4 | &-sider { 5 | position: absolute; 6 | height: 100vh; 7 | width: 240px; 8 | top: 0; 9 | box-shadow: 0 0 3px rgba(46, 41, 51, 0.2); 10 | 11 | &-head { 12 | display: flex; 13 | width: 240px; 14 | height: 50px; 15 | align-items: center; 16 | font-weight: bold; 17 | background-color: rgba(46, 41, 51, 0.2); 18 | } 19 | 20 | &-body { 21 | height: calc(100vh - 50px); 22 | } 23 | } 24 | 25 | &-body { 26 | height: 100vh; 27 | margin-left: 240px; 28 | display: flex; 29 | overflow: hidden; 30 | 31 | &-scroll { 32 | padding: 20px; 33 | width: 100%; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/views/canvas/components/DeleteButton/index.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import { Icon } from '@iconify/react/offline'; 3 | import deleteIcon from '@iconify-icons/mdi/delete-outline'; 4 | 5 | interface DeleteButtonProps { 6 | className?: string; 7 | onClick?: () => void; 8 | } 9 | 10 | const DeleteButton: React.FC = ({ className, onClick }) => { 11 | const handleRemove = (e: React.FormEvent) => { 12 | e.stopPropagation(); 13 | onClick && onClick(); 14 | }; 15 | 16 | return ( 17 | 18 | 19 | 20 | ); 21 | }; 22 | 23 | export default DeleteButton; 24 | -------------------------------------------------------------------------------- /src/utils/mdParser.ts: -------------------------------------------------------------------------------- 1 | import MarkdownIt from 'markdown-it'; 2 | import hljs from 'highlight.js'; 3 | 4 | export const mdParser = MarkdownIt({ 5 | html: true, 6 | highlight: function (str: string, lang: string) { 7 | // https://github.com/MinecraftForge/Documentation/pull/433 8 | const _lang = lang === 'json5' ? 'js' : lang; 9 | if (_lang && hljs.getLanguage(_lang)) { 10 | try { 11 | const code = hljs 12 | .highlight(str, { language: _lang, ignoreIllegals: true }) 13 | .value; 14 | 15 | return `
${code}
`; 16 | } catch (__) { } 17 | } 18 | return ''; // use external default escaping 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /unocss.ts: -------------------------------------------------------------------------------- 1 | import { VitePluginConfig } from '@unocss/vite'; 2 | import presetUno from '@unocss/preset-uno' 3 | import presetAttributify from '@unocss/preset-attributify'; 4 | 5 | export default { 6 | rules: [ 7 | // ### font ### 8 | [/^fs-?(\d+)(\w+)?$/, ([, d, w]) => ({ 'font-size': w ? `${d}${w}` : `${+d / 16}em` })], 9 | ], 10 | shortcuts: [ 11 | { 12 | 'hv-center': 'flex items-center justify-center', 13 | 'omb-hover': 'cursor-pointer select-none', 14 | } 15 | ], 16 | presets: [ 17 | presetUno(), 18 | presetAttributify(), 19 | ], 20 | } as VitePluginConfig; 21 | 22 | // const fmtRules = (list: Array<[string, string]>) => 23 | // list.map((i) => [new RegExp(`^${i[0]}-?(\d+)(\w+)?$`), ([, d, w]) => ({ [i[1]]: w ? `${d}${w}` : `${+d / 4}rem` })]); 24 | -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr( 2 | all(not(debug_assertions), target_os = "windows"), 3 | windows_subsystem = "windows" 4 | )] 5 | 6 | mod omb; 7 | 8 | fn main() { 9 | let context = tauri::generate_context!(); 10 | let app = tauri::Builder::default() 11 | .setup(omb::setup::init) 12 | .plugin(omb::fs::FsExtra::default()) 13 | .menu(tauri::Menu::os_default(&context.package_info().name)) 14 | .system_tray(omb::tray::menu()) 15 | .on_system_tray_event(omb::tray::handler) 16 | .build(context) 17 | .expect("error while running OhMyBox application"); 18 | 19 | app.run(|_app_handle, event| match event { 20 | tauri::RunEvent::Updater(updater_event) => { 21 | dbg!(updater_event); 22 | } 23 | _ => {} 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/components/SwitchLang/index.tsx: -------------------------------------------------------------------------------- 1 | import { Icon } from '@iconify/react/dist/offline'; 2 | import langIcon from '@iconify-icons/fa/language'; 3 | 4 | import { useGetLang, useSetLang } from '@/hooks/useLang'; 5 | 6 | export default function DashboardView() { 7 | const lang = useGetLang(); 8 | const setLang = useSetLang(); 9 | 10 | const handleChange: React.ChangeEventHandler = (e) => { 11 | setLang(e.target.value); 12 | }; 13 | 14 | return ( 15 |
16 | 17 | 22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/components/OmbCard/OmbIcon.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from 'react-router-dom'; 2 | import { Icon } from '@iconify/react'; 3 | 4 | import useI18n from '@/hooks/useI18n'; 5 | 6 | interface OmbIcon { 7 | title: string; 8 | icon: string; 9 | to: string; 10 | onClick?: () => void; 11 | } 12 | 13 | const OmbIcon: React.FC = ({ title, icon, to, onClick }) => { 14 | const t = useI18n(['dashboard']); 15 | const navigate = useNavigate(); 16 | 17 | const handleClick = () => { 18 | if (to) { 19 | navigate(to); 20 | return; 21 | } 22 | onClick && onClick(); 23 | }; 24 | 25 | return ( 26 | 27 | 28 | {t(title)} 29 | 30 | ); 31 | }; 32 | 33 | export default OmbIcon; 34 | -------------------------------------------------------------------------------- /src/hooks/useLocalColor.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export default function useLocalColor(maxLength: number) { 4 | const [localColors, setColors] = useState([]); 5 | 6 | const getLocalColors = () => { 7 | let data = localStorage.getItem('ombColor') || '[]'; 8 | return JSON.parse(data); 9 | }; 10 | 11 | const setLocalColors = (color: string) => { 12 | const data = getLocalColors() 13 | .filter((i: string) => i !== color); 14 | data.unshift(color); 15 | const _data = [...new Set(data.slice(0, maxLength))] as string[]; 16 | localStorage.setItem('ombColor', JSON.stringify(_data)); 17 | setColors(_data); 18 | }; 19 | 20 | useEffect(() => { 21 | setColors(getLocalColors()); 22 | }, []) 23 | 24 | return { localColors, getLocalColors, setLocalColors }; 25 | }; 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": ".", 19 | "paths": { 20 | "@/*": ["./src/*"], 21 | "@locales/*": ["./locales/*"], 22 | "@plugins/*": ["./plugins/*"] 23 | } 24 | }, 25 | "include": ["src", "locales", "plugins"], 26 | "references": [{ "path": "./tsconfig.node.json" }] 27 | } 28 | -------------------------------------------------------------------------------- /src/components/OmbCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as OmbItem } from './OmbItem'; 2 | export { default as OmbIcon } from './OmbIcon'; 3 | 4 | import { Icon } from '@iconify/react'; 5 | import useI18n from '@/hooks/useI18n'; 6 | 7 | import './index.scss'; 8 | 9 | interface OmbCardProps { 10 | title: string; 11 | children: React.ReactNode; 12 | icon?: string; 13 | } 14 | 15 | const OmbCard: React.FC = ({ icon, title, children }) => { 16 | const t = useI18n(['dashboard']); 17 | 18 | return ( 19 |
20 |

21 | 22 | {icon && } 23 | {t(title)} 24 | 25 |

26 |
{children}
27 |
28 | ); 29 | }; 30 | 31 | export default OmbCard; 32 | -------------------------------------------------------------------------------- /src/layouts/index.tsx: -------------------------------------------------------------------------------- 1 | import GoBack from '@/components/GoBack'; 2 | 3 | import './index.scss'; 4 | 5 | interface LayoutProps { 6 | title: React.ReactNode; 7 | sider: React.ReactNode; 8 | children: React.ReactNode; 9 | } 10 | 11 | const Layout: React.FC = ({ title, sider, children }) => { 12 | return ( 13 |
14 |
15 |
16 | 17 | {title} 18 |
19 |
20 |
{sider}
21 |
22 |
23 | 24 |
25 |
{children}
26 |
27 |
28 | ); 29 | }; 30 | 31 | export default Layout; 32 | -------------------------------------------------------------------------------- /src/views/canvas/paper/components/SaveIcon/index.tsx: -------------------------------------------------------------------------------- 1 | export default function SaveIcon() { 2 | return ( 3 | 4 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/layouts/FullSreen.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import { isValidElement } from 'react'; 3 | 4 | import GoBack from '@/components/GoBack'; 5 | 6 | import './fullscreen.scss'; 7 | 8 | interface FullScreenProps { 9 | className?: string; 10 | hasGoBack?: boolean; 11 | taskbar?: React.ReactNode; 12 | children: React.ReactNode; 13 | } 14 | 15 | const FullScreen: React.FC = ({ 16 | hasGoBack = true, 17 | taskbar, 18 | children, 19 | className, 20 | }) => { 21 | return ( 22 |
23 | {hasGoBack && !taskbar && } 24 | {isValidElement(taskbar) && ( 25 |
26 | 27 | {taskbar} 28 |
29 | )} 30 |
{children}
31 |
32 | ); 33 | }; 34 | 35 | export default FullScreen; 36 | -------------------------------------------------------------------------------- /src/system/os.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react' 2 | import { os } from '@tauri-apps/api' 3 | 4 | function OsInfo() { 5 | const [arch, setArch] = useState('') 6 | const [platform, setPlatform] = useState('') 7 | const [tempdir, setTempdir] = useState('') 8 | const [type, setType] = useState('') 9 | const [version, setVersion] = useState('') 10 | 11 | useEffect(() => { 12 | os.arch().then(setArch) 13 | os.platform().then(setPlatform) 14 | os.tempdir().then(setTempdir) 15 | os.type().then(setType) 16 | os.version().then(setVersion) 17 | }, []) 18 | 19 | return ( 20 |
21 |
    22 |
  • CPU 架构: {arch}
  • 23 |
  • 平台名称: {platform}
  • 24 |
  • 临时文件目录: {tempdir}
  • 25 |
  • 系统类型: {type}
  • 26 |
  • 系统版本: {version}
  • 27 |
28 |
29 | ) 30 | } 31 | 32 | export default OsInfo; 33 | -------------------------------------------------------------------------------- /src/components/OmbCard/index.scss: -------------------------------------------------------------------------------- 1 | .omb-card { 2 | width: 100%; 3 | 4 | h2 { 5 | > span { 6 | display: inline-flex; 7 | align-items: center; 8 | font-size: 14px; 9 | background-color: #2a2a2a; 10 | padding: 4px 10px; 11 | color: #fff; 12 | border-radius: 5px; 13 | } 14 | 15 | .icon { 16 | margin-right: 5px; 17 | } 18 | } 19 | 20 | &-group { 21 | &-item { 22 | transition: 300ms ease; 23 | > div { 24 | cursor: pointer; 25 | user-select: none; 26 | -webkit-user-select: none; 27 | } 28 | } 29 | } 30 | 31 | &-icon { 32 | display: flex; 33 | flex-direction: column; 34 | align-items: center; 35 | width: 80px; 36 | cursor: pointer; 37 | transition: all 1s ease; 38 | 39 | * { 40 | color: #666; 41 | } 42 | 43 | &:hover * { 44 | color: #121212; 45 | } 46 | 47 | b { 48 | font-size: 12px; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/views/canvas/paper/components/BrushIcon/index.tsx: -------------------------------------------------------------------------------- 1 | export default function BrushIcon() { 2 | return ( 3 | 4 | 9 | 14 | 19 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/tools.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs'; 2 | 3 | export const fmtI18n = (...args: any[]) => { 4 | const i18nMap: Record = {}; 5 | const ns: string[] = []; 6 | args.forEach((i) => { 7 | Object.keys(i).forEach((j) => { 8 | if (j === '__ns') ns.push(i.__ns); 9 | if (j !== '__ns' && !i18nMap[j]) i18nMap[j] = {}; 10 | if (i18nMap[j]) i18nMap[j] = { ...i18nMap[j], [i.__ns]: i[j] }; 11 | }); 12 | }); 13 | return [i18nMap, ns] as const; 14 | } 15 | 16 | export const getScrollPosition = (el: any = window) => ({ 17 | x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft, 18 | y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop, 19 | }); 20 | 21 | export const getScreenSize = () => ({ 22 | width: document.body.clientWidth, 23 | height: document.body.clientHeight, 24 | }); 25 | 26 | export const fmtDate = (date: Date) => dayjs(date).format('YYYY/MM/DD'); 27 | 28 | export const ignoreFile = (name: string) => /[\.\\/@\*%#!]/ig.test(name); -------------------------------------------------------------------------------- /src/utils/color.ts: -------------------------------------------------------------------------------- 1 | export type RGBAObj = { r: number; g: number; b: number; a: number }; 2 | 3 | export const hex2rgba = (hex: any, a = 1) => { 4 | const [r, g, b] = hex.match(/\w\w/g)?.map((x: any) => parseInt(x, 16)); 5 | return { r, g, b, a }; 6 | }; 7 | 8 | export const rgb2hex = (r: number, g: number, b: number) => { 9 | let _r = r.toString(16), 10 | _g = g.toString(16), 11 | _b = b.toString(16); 12 | 13 | if (_r.length == 1) _r = `0${_r}`; 14 | if (_g.length == 1) _g = `0${_g}`; 15 | if (_b.length == 1) _b = `0${_b}`; 16 | return `#${_r}${_g}${_b}` 17 | } 18 | 19 | export const fmtRgba = (c: RGBAObj) => 20 | `rgba(${c.r},${c.g},${c.b},${c.a || 1})`; 21 | 22 | export const rgba2obj = (c: string) => { 23 | const a = c.match(/rgba\((.*)\)/)?.[1]?.split(','); 24 | return { r: Number(a?.[0]), g: Number(a?.[1]), b: Number(a?.[2]), a: Number(a?.[3]) }; 25 | } 26 | 27 | export const setCSS = (key: string, val: any) => { 28 | document.body.style.setProperty(`--omb-${key}`, val); 29 | } -------------------------------------------------------------------------------- /src/views/dashboard/index.tsx: -------------------------------------------------------------------------------- 1 | import OmbCard, { OmbIcon } from '@/components/OmbCard'; 2 | import SwitchLang from '@/components/SwitchLang'; 3 | 4 | export default function DashboardView() { 5 | // const t = useI18n(['dashboard', 'tools', 'game']); 6 | 7 | return ( 8 |
9 | 10 | 11 | 16 | 21 | 22 | 26 | 31 | 32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/components/OmbCard/OmbItem.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from 'react-router-dom'; 2 | import clsx from 'clsx'; 3 | 4 | interface OmbItemProps { 5 | to?: string; 6 | children: React.ReactNode; 7 | className?: string; 8 | cardClass?: string; 9 | onClick?: () => void; 10 | } 11 | 12 | const OmbItem: React.FC = ({ 13 | className, 14 | cardClass, 15 | children, 16 | onClick, 17 | to, 18 | }) => { 19 | const navigate = useNavigate(); 20 | 21 | const handleClick = () => { 22 | if (to) { 23 | navigate(to); 24 | } 25 | onClick && onClick(); 26 | }; 27 | 28 | return ( 29 |
35 |
42 | {children} 43 |
44 |
45 | ); 46 | }; 47 | 48 | export default OmbItem; 49 | -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "omb" 3 | version = "0.0.0" 4 | description = "OhMyBox" 5 | authors = ["lencx "] 6 | license = "MIT" 7 | repository = "https://github.com/lencx/OhMyBox" 8 | default-run = "omb" 9 | edition = "2021" 10 | rust-version = "1.57" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [build-dependencies] 15 | tauri-build = { version = "1.1.1", features = [] } 16 | 17 | [dependencies] 18 | serde_json = "1.0" 19 | serde = { version = "1.0", features = ["derive"] } 20 | tauri = { version = "1.1.1", features = ["api-all", "icon-png", "macos-private-api", "system-tray", "updater"] } 21 | thiserror = "1.0" 22 | window-vibrancy = "0.2.0" 23 | 24 | [features] 25 | # by default Tauri runs in production mode 26 | # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL 27 | default = ["custom-protocol"] 28 | # this feature is used used for production builds where `devPath` points to the filesystem 29 | # DO NOT remove this 30 | custom-protocol = ["tauri/custom-protocol"] 31 | -------------------------------------------------------------------------------- /src/components/GoBack/index.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate, useLocation } from 'react-router-dom'; 2 | import { Icon } from '@iconify/react/offline'; 3 | import backIcon from '@iconify-icons/mdi/arrow-back'; 4 | import clsx from 'clsx'; 5 | 6 | import useI18n from '@/hooks/useI18n'; 7 | import Tooltip from '@/components/Tooltip'; 8 | 9 | import './index.scss'; 10 | 11 | interface GoBackProps { 12 | type?: 'float' | 'inline'; 13 | to?: string | number; 14 | } 15 | 16 | const GoBack: React.FC = ({ type, to }) => { 17 | const location = useLocation(); 18 | const go = useNavigate(); 19 | const isRoot = location.pathname === '/'; 20 | 21 | const t = useI18n(['tip']); 22 | 23 | if (isRoot) return null; 24 | 25 | return ( 26 | 27 | 30 | go((to || -1) as string)} /> 31 | 32 | 33 | ); 34 | }; 35 | 36 | GoBack.defaultProps = { 37 | type: 'float', 38 | }; 39 | 40 | export default GoBack; 41 | -------------------------------------------------------------------------------- /scripts/updatelog.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | const UPDATE_LOG = 'UPDATE_LOG.md'; 5 | 6 | export default function updatelog(tag, type = 'updater') { 7 | const reTag = /## v[\d\.]+/; 8 | 9 | const file = path.join(process.cwd(), UPDATE_LOG); 10 | 11 | if (!fs.existsSync(file)) { 12 | console.log('Could not found UPDATE_LOG.md'); 13 | process.exit(1); 14 | } 15 | 16 | let _tag; 17 | const tagMap = {}; 18 | const content = fs.readFileSync(file, { encoding: 'utf8' }).split('\n'); 19 | 20 | content.forEach((line, index) => { 21 | if (reTag.test(line)) { 22 | _tag = line.slice(3).trim(); 23 | if (!tagMap[_tag]) { 24 | tagMap[_tag] = []; 25 | return; 26 | } 27 | } 28 | if (_tag) { 29 | tagMap[_tag].push(line); 30 | } 31 | if (reTag.test(content[index + 1])) { 32 | _tag = null; 33 | } 34 | }); 35 | 36 | if (!tagMap?.[tag]) { 37 | console.log( 38 | `${type === 'release' ? '[UPDATE_LOG.md] ' : ''}Tag ${tag} does not exist` 39 | ); 40 | process.exit(1); 41 | } 42 | 43 | return tagMap[tag].join('\n').trim() || ''; 44 | } 45 | -------------------------------------------------------------------------------- /src/styles/common.scss: -------------------------------------------------------------------------------- 1 | .shadow-raised { 2 | box-shadow: 0 1px 2px rgba(46, 41, 51, 0.08), 0 2px 4px rgba(71, 63, 79, 0.08); 3 | } 4 | 5 | .shadow-floating { 6 | box-shadow: 0 2px 4px rgba(46, 41, 51, 0.08), 0 4px 8px rgba(71, 63, 79, 0.16); 7 | } 8 | 9 | .shadow-overlay { 10 | box-shadow: 0 4px 8px rgba(46, 41, 51, 0.08), 0 8px 16px rgba(71, 63, 79, 0.16); 11 | } 12 | 13 | .shadow-dialog { 14 | box-shadow: 0 4px 16px rgba(46, 41, 51, 0.08), 0 8px 24px rgba(71, 63, 79, 0.16); 15 | } 16 | 17 | .omb-ico { 18 | display: inline-block; 19 | color: inherit; 20 | font-style: normal; 21 | line-height: 0; 22 | text-align: center; 23 | text-transform: none; 24 | vertical-align: -0.125em; 25 | text-rendering: optimizelegibility; 26 | -webkit-font-smoothing: antialiased; 27 | transition: transform .3s ease-in-out; 28 | will-change: transform; 29 | } 30 | 31 | .omb-scrollbar { 32 | overflow-y: auto; 33 | &::-webkit-scrollbar { 34 | width: 5px; 35 | background-color: #F5F5F5; 36 | } 37 | &::-webkit-scrollbar-track { 38 | background-color: #F5F5F5; 39 | } 40 | &::-webkit-scrollbar-thumb { 41 | background-color: var(--blue); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /locales/config.ts: -------------------------------------------------------------------------------- 1 | import i18n from 'i18next'; 2 | import { initReactI18next } from 'react-i18next'; 3 | // https://github.com/i18next/i18next-browser-languageDetector 4 | import LanguageDetector from 'i18next-browser-languagedetector'; 5 | 6 | import { fmtI18n } from '@/utils/tools'; 7 | 8 | import common from './common'; 9 | import tip from './tip'; 10 | // views 11 | import dashboard from './dashboard'; 12 | import tools from './tools'; 13 | import game from './game'; 14 | import rules from './rules'; 15 | 16 | const i18nData = fmtI18n(common, tip, dashboard, tools, game, rules); 17 | 18 | export const resources = i18nData[0]; 19 | 20 | i18n 21 | .use(LanguageDetector) 22 | .use(initReactI18next) 23 | .init({ 24 | // lng: 'en', 25 | fallbackLng: 'en', 26 | resources, 27 | ns: i18nData[1], 28 | detection: { 29 | caches: ['localStorage', 'sessionStorage', 'cookie'], 30 | lookupQuerystring: 'ombLng', 31 | lookupCookie: 'ombLng', 32 | lookupLocalStorage: 'ombLng', 33 | lookupSessionStorage: 'ombLng', 34 | }, 35 | interpolation: { 36 | escapeValue: false, // not needed for react as it escapes by default 37 | }, 38 | }); -------------------------------------------------------------------------------- /src/routes.tsx: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect } from 'react'; 2 | import { useLocation, useRoutes } from 'react-router-dom'; 3 | import type { RouteObject } from 'react-router-dom'; 4 | 5 | import DashboardView from '@/views/dashboard'; 6 | import CanvasView from '@/views/canvas'; 7 | import CanvasPaperView from '@/views/canvas/paper'; 8 | import GameOfLifeView from '@/views/game_of_life'; 9 | import MdHub from '@/views/mdhub'; 10 | 11 | const routes: RouteObject[] = [ 12 | { 13 | path: '/', 14 | element: , 15 | }, 16 | { 17 | path: 'tools/canvas', 18 | element: , 19 | }, 20 | { 21 | path: 'tools/canvas/paper/:path/:file', 22 | element: , 23 | }, 24 | { 25 | path: '/tools/mdhub', 26 | element: , 27 | }, 28 | { 29 | path: 'game/game-of-life', 30 | element: , 31 | }, 32 | ]; 33 | 34 | export default () => { 35 | const location = useLocation(); 36 | const pathname = location.pathname; 37 | useLayoutEffect(() => { 38 | document.body.className = 39 | pathname.substring(1).replace(/\//gi, '_') + '_screen'; 40 | }, [pathname]); 41 | return useRoutes(routes); 42 | }; 43 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | import tsconfigPaths from 'vite-tsconfig-paths'; 4 | import Unocss from 'unocss/vite'; 5 | import ViteRsw from 'vite-plugin-rsw'; 6 | 7 | import unoOptions from './unocss'; 8 | 9 | // https://vitejs.dev/config/ 10 | export default defineConfig({ 11 | plugins: [ 12 | ViteRsw(), 13 | Unocss(unoOptions), 14 | tsconfigPaths(), 15 | react(), 16 | ], 17 | build: { 18 | rollupOptions: { 19 | output: { 20 | manualChunks: { 21 | highlight: ['highlight.js'], 22 | utils: ['lodash', 'uuid', 'dayjs', 'clsx'], 23 | comps: ['react-colorful', '@floating-ui/react-dom-interactions', 'allotment', 'framer-motion', 'rc-slider'], 24 | react: ['react', 'react-dom', 'react-router-dom', '@iconify/react', '@monaco-editor/react', 'jotai'], 25 | i18next: ['i18next', 'react-i18next', 'i18next-browser-languagedetector'], 26 | md: ['markdown-it', 'github-markdown-css'], 27 | } 28 | } 29 | } 30 | }, 31 | esbuild: { 32 | // [vite] warning: Top-level "this" will be replaced with undefined since this file is an ECMAScript module 33 | // https://github.com/vitejs/vite/issues/8644 34 | logOverride: { 'this-is-undefined-in-esm': 'silent' } 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /scripts/release.mjs: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'module'; 2 | import { execSync } from 'child_process'; 3 | import fs from 'fs'; 4 | 5 | import updatelog from './updatelog.mjs'; 6 | 7 | const require = createRequire(import.meta.url); 8 | 9 | async function release() { 10 | const flag = process.argv[2] ?? 'patch'; 11 | const packageJson = require('../package.json'); 12 | let [a, b, c] = packageJson.version.split('.').map(Number); 13 | 14 | if (flag === 'major') { 15 | a += 1; 16 | b = 0; 17 | c = 0; 18 | } else if (flag === 'minor') { 19 | b += 1; 20 | c = 0; 21 | } else if (flag === 'patch') { 22 | c += 1; 23 | } else { 24 | console.log(`Invalid flag "${flag}"`); 25 | process.exit(1); 26 | } 27 | 28 | const nextVersion = `${a}.${b}.${c}`; 29 | packageJson.version = nextVersion; 30 | 31 | const nextTag = `v${nextVersion}`; 32 | await updatelog(nextTag, 'release'); 33 | 34 | fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2)); 35 | 36 | execSync('git add ./package.json ./UPDATE_LOG.md'); 37 | execSync(`git commit -m "v${nextVersion}"`); 38 | execSync(`git tag -a v${nextVersion} -m "v${nextVersion}"`); 39 | execSync(`git push`); 40 | execSync(`git push origin v${nextVersion}`); 41 | console.log(`Publish Successfully...`); 42 | } 43 | 44 | release().catch(console.error); 45 | -------------------------------------------------------------------------------- /rsw.toml: -------------------------------------------------------------------------------- 1 | name = "omb" 2 | version = "0.1.0" 3 | 4 | #! time interval for file changes to trigger wasm-pack build, default `50` milliseconds 5 | interval = 50 6 | 7 | #! link 8 | #! npm link @see https://docs.npmjs.com/cli/v8/commands/npm-link 9 | #! yarn link @see https://classic.yarnpkg.com/en/docs/cli/link 10 | #! pnpm link @see https://pnpm.io/cli/link 11 | #! The link command will only be executed if `[[crates]] link = true` 12 | #! cli: `npm` | `yarn` | `pnpm`, default is `npm` 13 | cli = "yarn" 14 | 15 | #! --------------------------- 16 | 17 | #! rsw new 18 | [new] 19 | #! @see https://rustwasm.github.io/docs/wasm-pack/commands/new.html 20 | #! using: `wasm-pack` | `rsw` | `user`, default is `wasm-pack` 21 | #! 1. wasm-pack: `rsw new --template