From f874b58dd98076b89971b16211adbb093e514037 Mon Sep 17 00:00:00 2001 From: Roman Timashev Date: Sat, 25 Apr 2026 01:33:06 +0300 Subject: [PATCH] Migrate to npm-workspaces monorepo (#294) * spec update * update md files * add plan: monorepo * feat: add empty apps and packages directories * Add shared tsconfig.base.json * feat add workspaces * feat: move hudozka-writer into apps/ * feat: move migrate-to-pocketbase into apps/ * feat: split package.json for apps/hudozka workspace * feat: extend tsconfig.base.json in apps/hudozka * add dockerfile path * feat: verify Phase 3 monorepo migration of Next app * feat: scaffold @hudozka/utils package * feat: add utils barrel export * feat: extract utilities into hudozka-utils package * feat: extract hooks into hudozka-hooks package * Extract Box/Button/Panel/Title/Overlay into @hudozka/ui * Scope Next ESLint rules to apps/hudozka in root flat config * Update README and CLAUDE.md for monorepo layout * disable image optimisation --- .github/workflows/build-app.yaml | 1 + .github/workflows/test-app.yml | 2 +- CLAUDE.md | 52 +- README.md | 26 +- apps/.gitkeep | 0 {modules => apps}/hudozka-writer/.gitignore | 0 {modules => apps}/hudozka-writer/README.md | 0 {modules => apps}/hudozka-writer/index.html | 0 .../hudozka-writer/package-lock.json | 0 {modules => apps}/hudozka-writer/package.json | 0 {modules => apps}/hudozka-writer/src/App.css | 0 {modules => apps}/hudozka-writer/src/App.tsx | 0 .../src/components/BlockActions.css | 0 .../src/components/BlockActions.tsx | 0 .../src/components/BlockInsert.css | 0 .../src/components/BlockInsert.tsx | 0 .../src/components/BlockMenu.css | 0 .../src/components/BlockMenu.tsx | 0 .../src/components/DraftBanner.css | 0 .../src/components/DraftBanner.tsx | 0 .../hudozka-writer/src/components/Editor.css | 0 .../hudozka-writer/src/components/Editor.tsx | 0 .../src/components/FilePicker.css | 0 .../src/components/FilePicker.tsx | 0 .../src/components/ImagePicker.css | 0 .../src/components/ImagePicker.tsx | 0 .../hudozka-writer/src/components/Login.css | 0 .../hudozka-writer/src/components/Login.tsx | 0 .../hudozka-writer/src/components/Toolbar.css | 0 .../hudozka-writer/src/components/Toolbar.tsx | 0 .../src/extensions/SlashCommands.css | 0 .../src/extensions/SlashCommands.tsx | 0 .../hudozka-writer/src/index.css | 0 .../hudozka-writer/src/lib/id.ts | 0 .../hudozka-writer/src/lib/markdown.ts | 0 .../hudozka-writer/src/lib/serialize.ts | 0 {modules => apps}/hudozka-writer/src/main.tsx | 0 .../src/nodes/CardGridBlock.tsx | 0 .../src/nodes/DocumentBlock.tsx | 0 .../hudozka-writer/src/nodes/EmbedBlock.tsx | 0 .../hudozka-writer/src/nodes/ImageBlock.tsx | 0 .../hudozka-writer/src/nodes/TextBlock.tsx | 0 .../src/nodes/WriterDocument.ts | 0 .../hudozka-writer/src/nodes/index.ts | 0 .../hudozka-writer/src/nodes/nodes.css | 0 {modules => apps}/hudozka-writer/src/pb.ts | 0 {modules => apps}/hudozka-writer/src/types.ts | 0 .../hudozka-writer/src/vite-env.d.ts | 0 .../hudozka-writer/tsconfig.json | 10 +- .../hudozka-writer/vite.config.ts | 0 .dockerignore => apps/hudozka/.dockerignore | 0 .env.example => apps/hudozka/.env.example | 0 .env.local => apps/hudozka/.env.local | 0 Dockerfile => apps/hudozka/Dockerfile | 27 +- next-env.d.ts => apps/hudozka/next-env.d.ts | 0 next.config.ts => apps/hudozka/next.config.ts | 3 + apps/hudozka/package.json | 43 + .../hudozka/public}/static/animate.min.css | 0 .../public}/static/graphics/favicon-114.png | Bin .../public}/static/graphics/favicon-144.png | Bin .../public}/static/graphics/favicon-32.png | Bin .../public}/static/graphics/favicon-57.png | Bin .../public}/static/graphics/favicon-72.png | Bin .../public}/static/img/collective2017.jpg | Bin .../public}/static/img/collective2019.jpg | Bin .../hudozka/public}/static/img/main.jpg | Bin .../hudozka/public}/static/likely.css | 0 .../hudozka/public}/static/likely.js | 0 .../static/shadow-font/Shadow-Regular.eot | Bin .../static/shadow-font/Shadow-Regular.svg | 0 .../static/shadow-font/Shadow-Regular.ttf | Bin .../static/shadow-font/Shadow-Regular.woff | Bin .../hudozka/src}/app/[...slug]/loading.tsx | 0 .../hudozka/src}/app/[...slug]/page.tsx | 2 +- .../hudozka/src}/app/feed.json/route.ts | 0 {src => apps/hudozka/src}/app/layout.tsx | 0 {src => apps/hudozka/src}/app/not-found.tsx | 0 {src => apps/hudozka/src}/app/page.tsx | 0 {src => apps/hudozka/src}/app/robots.ts | 0 {src => apps/hudozka/src}/app/sitemap.ts | 0 .../src}/assets/graphics/lesson-canceled.svg | 0 .../src}/assets/graphics/m-tape-bl.png | Bin .../src}/assets/graphics/m-tape-br.png | Bin .../src}/assets/graphics/m-tape-tl.png | Bin .../src}/assets/graphics/m-tape-tr.png | Bin .../hudozka/src}/assets/graphics/skull.jpg | Bin .../hudozka/src}/assets/graphics/tape-lb.png | Bin .../hudozka/src}/assets/graphics/tape-rb.png | Bin .../hudozka/src}/assets/graphics/tape-tl.png | Bin .../hudozka/src}/assets/graphics/tape-tr.png | Bin .../hudozka/src}/assets/hudozka-logo.png | Bin .../components/AccessibilityButton/index.tsx | 2 +- .../components/AccessibilityPanel/index.tsx | 0 .../AccessibilityPanel/panel.module.css | 0 .../components/AccessibilityPanel/voice.tsx | 0 .../src}/components/App/app.module.css | 0 .../hudozka/src}/components/App/index.tsx | 6 +- .../hudozka/src}/components/Author/index.tsx | 0 .../Breadcrumbs/Breadcrumbs.test.tsx | 0 .../src}/components/Breadcrumbs/index.tsx | 4 +- .../components/Breadcrumbs/styles.module.css | 0 .../hudozka/src}/components/Card/index.tsx | 0 .../src}/components/Card/styles.module.css | 0 .../src}/components/CardGrid/index.tsx | 0 .../components/CardGrid/styles.module.css | 0 .../components/Contacts/contacts.module.css | 0 .../src}/components/Contacts/index.tsx | 2 +- .../hudozka/src}/components/Content/index.tsx | 2 +- .../src}/components/Copyright/index.tsx | 0 .../hudozka/src}/components/File/index.tsx | 2 +- .../src}/components/File/styles.module.css | 0 .../src}/components/FileCard/Signature.tsx | 0 .../components/FileCard/filecard.module.css | 0 .../src}/components/FileCard/index.tsx | 5 +- .../components/FileCard/signature.module.css | 0 .../src}/components/Footer/footer.module.css | 0 .../hudozka/src}/components/Footer/index.tsx | 0 .../src}/components/Gosuslugi/index.tsx | 0 .../hudozka/src}/components/Gosuslugi/lib.ts | 0 .../src}/components/HomeContent/index.tsx | 2 +- .../hudozka/src}/components/Html/index.tsx | 0 .../src}/components/HudozkaLogo/index.tsx | 0 .../components/HudozkaLogo/logo.module.css | 0 .../src}/components/HudozkaTitle/index.tsx | 0 .../components/HudozkaTitle/title.module.css | 0 .../src}/components/Menu/ActiveLink.tsx | 0 .../hudozka/src}/components/Menu/index.tsx | 0 .../src}/components/Menu/styles.module.css | 0 .../Navigation/DesktopNavigation.tsx | 2 +- .../Navigation/MobileNavigation.tsx | 2 +- .../src}/components/Navigation/index.tsx | 0 .../src}/components/Navigation/nav.module.css | 0 .../src}/components/NotFound/error.module.css | 0 .../src}/components/NotFound/index.tsx | 0 .../hudozka/src}/components/Page/index.tsx | 0 .../src}/components/Page/styles.module.css | 0 .../src}/components/PageContent/index.tsx | 0 .../hudozka/src}/components/PageGrid/Date.tsx | 2 +- .../src}/components/PageGrid/date.module.css | 0 .../src}/components/PageGrid/index.tsx | 4 +- .../src}/components/PageMeta/index.tsx | 4 +- .../components/PageMeta/styles.module.css | 0 .../hudozka/src}/components/Share/index.tsx | 0 .../hudozka/src}/components/Spacer/index.tsx | 0 .../hudozka/src}/components/TagList/index.tsx | 2 +- .../src}/components/TagList/styles.module.css | 0 .../hudozka/src}/components/Wrapper/index.tsx | 0 .../components/Wrapper/wrapper.module.css | 0 .../hudozka/src}/components/YMetrika.test.ts | 0 .../hudozka/src}/components/YMetrika.tsx | 0 .../hudozka/src}/components/Youtube/index.tsx | 0 {src => apps/hudozka/src}/const.ts | 0 {src => apps/hudozka/src}/global.d.ts | 0 .../hudozka/src}/hooks/useAccessibility.ts | 0 .../hudozka/src}/hooks/useDarkTheme.ts | 2 +- {src => apps/hudozka/src}/lib/meta.test.ts | 0 {src => apps/hudozka/src}/lib/meta.ts | 0 {src => apps/hudozka/src}/play.ts | 0 {src => apps/hudozka/src}/remote/api.test.ts | 0 {src => apps/hudozka/src}/remote/api.ts | 0 {src => apps/hudozka/src}/remote/doc.ts | 0 .../hudozka/src}/remote/factory.test.ts | 0 {src => apps/hudozka/src}/remote/factory.ts | 2 +- {src => apps/hudozka/src}/remote/pb.ts | 0 {src => apps/hudozka/src}/remote/types.ts | 0 {src => apps/hudozka/src}/store/play.ts | 0 {src => apps/hudozka/src}/store/theme.ts | 2 +- {src => apps/hudozka/src}/style/article.css | 0 {src => apps/hudozka/src}/style/content.css | 0 .../hudozka/src}/style/kazimir/video.css | 0 .../src}/style/marker/marker-table.css | 0 {src => apps/hudozka/src}/style/style.css | 0 .../hudozka/src}/style/tape-viewer.scss | 0 .../hudozka/src}/style/vertical-image.scss | 0 {src => apps/hudozka/src}/types.ts | 0 .../hudozka/src}/ui/Picture/index.tsx | 0 {src => apps/hudozka/src}/ui/Picture/lib.tsx | 0 .../src}/ui/Picture/picture.module.css | 0 .../hudozka/src}/ui/ThemeColor/index.tsx | 0 apps/hudozka/tsconfig.json | 25 + .../hudozka/vitest.config.ts | 0 .../hudozka/vitest.setup.tsx | 0 .../migrate-to-pocketbase/.env.example | 0 .../migrate-to-pocketbase/.gitignore | 0 .../migrate-to-pocketbase/README.md | 0 .../migrate-to-pocketbase/package-lock.json | 0 .../migrate-to-pocketbase/package.json | 3 + .../migrate-to-pocketbase/src/blurhash.ts | 0 .../migrate-to-pocketbase/src/download.ts | 0 .../migrate-to-pocketbase/src/hash.ts | 0 .../migrate-to-pocketbase/src/index.ts | 0 .../migrate-to-pocketbase/src/strapi.ts | 0 .../migrate-to-pocketbase/src/wipe.ts | 0 apps/migrate-to-pocketbase/tsconfig.json | 8 + docs/plans/20260424-monorepo.md | 1727 +++++++++++++++++ .../20260407-writer-draft-publish.md} | 0 .../20260407-writer-editor-improvements.md} | 0 docs/specs/20260424-monorepo-design.md | 289 +++ eslint.config.js | 28 +- package-lock.json | 1479 +++++++++++++- package.json | 33 +- packages/.gitkeep | 0 packages/hooks/package.json | 15 + packages/hooks/src/index.ts | 5 + .../hooks/src}/useLockBodyScroll.test.ts | 0 .../hooks/src}/useLockBodyScroll.ts | 0 .../hooks/src}/useMediaQuery.test.ts | 0 .../hooks/src}/useMediaQuery.ts | 0 .../hooks => packages/hooks/src}/useMobile.ts | 2 +- .../hooks/src}/useReducedMotion.ts | 2 +- .../hooks/src}/useToggle.test.ts | 0 .../hooks => packages/hooks/src}/useToggle.ts | 0 packages/hooks/tsconfig.json | 4 + packages/hooks/vitest.config.ts | 9 + packages/hooks/vitest.setup.ts | 1 + packages/text/package.json | 19 + packages/text/src/index.ts | 1 + {src/lib => packages/text/src}/text.test.ts | 0 {src/lib => packages/text/src}/text.ts | 0 packages/text/tsconfig.json | 4 + packages/ui/package.json | 20 + {src/ui => packages/ui/src}/Box/Box.test.tsx | 0 .../ui => packages/ui/src}/Box/box.module.css | 0 {src/ui => packages/ui/src}/Box/index.tsx | 0 .../ui/src}/Button/Button.test.tsx | 0 .../ui/src}/Button/button.module.css | 0 {src/ui => packages/ui/src}/Button/index.tsx | 0 .../ui/src}/Overlay/Transition.tsx | 0 {src/ui => packages/ui/src}/Overlay/index.tsx | 3 +- .../ui/src}/Overlay/styles.module.css | 0 .../ui/src}/Panel/Panel.test.tsx | 0 {src/ui => packages/ui/src}/Panel/index.tsx | 0 .../ui/src}/Panel/panel.module.css | 0 {src/ui => packages/ui/src}/Title/index.tsx | 0 packages/ui/src/index.ts | 5 + packages/ui/tsconfig.json | 4 + packages/ui/vitest.config.ts | 10 + packages/ui/vitest.setup.tsx | 1 + packages/utils/package.json | 15 + {src/lib => packages/utils/src}/array.test.ts | 0 {src/lib => packages/utils/src}/array.ts | 0 {src/lib => packages/utils/src}/date.test.ts | 0 {src/lib => packages/utils/src}/date.ts | 0 {src/lib => packages/utils/src}/file.test.ts | 0 {src/lib => packages/utils/src}/file.ts | 0 {src/lib => packages/utils/src}/image.test.ts | 0 {src/lib => packages/utils/src}/image.ts | 0 packages/utils/src/index.ts | 7 + {src/lib => packages/utils/src}/math.test.ts | 0 {src/lib => packages/utils/src}/math.ts | 0 .../lib => packages/utils/src}/string.test.ts | 0 {src/lib => packages/utils/src}/string.ts | 0 {src/lib => packages/utils/src}/url.test.ts | 0 {src/lib => packages/utils/src}/url.ts | 0 packages/utils/tsconfig.json | 4 + packages/utils/vitest.config.ts | 7 + tsconfig.base.json | 19 + tsconfig.json | 45 - 258 files changed, 3793 insertions(+), 214 deletions(-) create mode 100644 apps/.gitkeep rename {modules => apps}/hudozka-writer/.gitignore (100%) rename {modules => apps}/hudozka-writer/README.md (100%) rename {modules => apps}/hudozka-writer/index.html (100%) rename {modules => apps}/hudozka-writer/package-lock.json (100%) rename {modules => apps}/hudozka-writer/package.json (100%) rename {modules => apps}/hudozka-writer/src/App.css (100%) rename {modules => apps}/hudozka-writer/src/App.tsx (100%) rename {modules => apps}/hudozka-writer/src/components/BlockActions.css (100%) rename {modules => apps}/hudozka-writer/src/components/BlockActions.tsx (100%) rename {modules => apps}/hudozka-writer/src/components/BlockInsert.css (100%) rename {modules => apps}/hudozka-writer/src/components/BlockInsert.tsx (100%) rename {modules => apps}/hudozka-writer/src/components/BlockMenu.css (100%) rename {modules => apps}/hudozka-writer/src/components/BlockMenu.tsx (100%) rename {modules => apps}/hudozka-writer/src/components/DraftBanner.css (100%) rename {modules => apps}/hudozka-writer/src/components/DraftBanner.tsx (100%) rename {modules => apps}/hudozka-writer/src/components/Editor.css (100%) rename {modules => apps}/hudozka-writer/src/components/Editor.tsx (100%) rename {modules => apps}/hudozka-writer/src/components/FilePicker.css (100%) rename {modules => apps}/hudozka-writer/src/components/FilePicker.tsx (100%) rename {modules => apps}/hudozka-writer/src/components/ImagePicker.css (100%) rename {modules => apps}/hudozka-writer/src/components/ImagePicker.tsx (100%) rename {modules => apps}/hudozka-writer/src/components/Login.css (100%) rename {modules => apps}/hudozka-writer/src/components/Login.tsx (100%) rename {modules => apps}/hudozka-writer/src/components/Toolbar.css (100%) rename {modules => apps}/hudozka-writer/src/components/Toolbar.tsx (100%) rename {modules => apps}/hudozka-writer/src/extensions/SlashCommands.css (100%) rename {modules => apps}/hudozka-writer/src/extensions/SlashCommands.tsx (100%) rename {modules => apps}/hudozka-writer/src/index.css (100%) rename {modules => apps}/hudozka-writer/src/lib/id.ts (100%) rename {modules => apps}/hudozka-writer/src/lib/markdown.ts (100%) rename {modules => apps}/hudozka-writer/src/lib/serialize.ts (100%) rename {modules => apps}/hudozka-writer/src/main.tsx (100%) rename {modules => apps}/hudozka-writer/src/nodes/CardGridBlock.tsx (100%) rename {modules => apps}/hudozka-writer/src/nodes/DocumentBlock.tsx (100%) rename {modules => apps}/hudozka-writer/src/nodes/EmbedBlock.tsx (100%) rename {modules => apps}/hudozka-writer/src/nodes/ImageBlock.tsx (100%) rename {modules => apps}/hudozka-writer/src/nodes/TextBlock.tsx (100%) rename {modules => apps}/hudozka-writer/src/nodes/WriterDocument.ts (100%) rename {modules => apps}/hudozka-writer/src/nodes/index.ts (100%) rename {modules => apps}/hudozka-writer/src/nodes/nodes.css (100%) rename {modules => apps}/hudozka-writer/src/pb.ts (100%) rename {modules => apps}/hudozka-writer/src/types.ts (100%) rename {modules => apps}/hudozka-writer/src/vite-env.d.ts (100%) rename {modules => apps}/hudozka-writer/tsconfig.json (57%) rename {modules => apps}/hudozka-writer/vite.config.ts (100%) rename .dockerignore => apps/hudozka/.dockerignore (100%) rename .env.example => apps/hudozka/.env.example (100%) rename .env.local => apps/hudozka/.env.local (100%) rename Dockerfile => apps/hudozka/Dockerfile (57%) rename next-env.d.ts => apps/hudozka/next-env.d.ts (100%) rename next.config.ts => apps/hudozka/next.config.ts (74%) create mode 100644 apps/hudozka/package.json rename {public => apps/hudozka/public}/static/animate.min.css (100%) rename {public => apps/hudozka/public}/static/graphics/favicon-114.png (100%) rename {public => apps/hudozka/public}/static/graphics/favicon-144.png (100%) rename {public => apps/hudozka/public}/static/graphics/favicon-32.png (100%) rename {public => apps/hudozka/public}/static/graphics/favicon-57.png (100%) rename {public => apps/hudozka/public}/static/graphics/favicon-72.png (100%) rename {public => apps/hudozka/public}/static/img/collective2017.jpg (100%) rename {public => apps/hudozka/public}/static/img/collective2019.jpg (100%) rename {public => apps/hudozka/public}/static/img/main.jpg (100%) rename {public => apps/hudozka/public}/static/likely.css (100%) rename {public => apps/hudozka/public}/static/likely.js (100%) rename {public => apps/hudozka/public}/static/shadow-font/Shadow-Regular.eot (100%) rename {public => apps/hudozka/public}/static/shadow-font/Shadow-Regular.svg (100%) rename {public => apps/hudozka/public}/static/shadow-font/Shadow-Regular.ttf (100%) rename {public => apps/hudozka/public}/static/shadow-font/Shadow-Regular.woff (100%) rename {src => apps/hudozka/src}/app/[...slug]/loading.tsx (100%) rename {src => apps/hudozka/src}/app/[...slug]/page.tsx (98%) rename {src => apps/hudozka/src}/app/feed.json/route.ts (100%) rename {src => apps/hudozka/src}/app/layout.tsx (100%) rename {src => apps/hudozka/src}/app/not-found.tsx (100%) rename {src => apps/hudozka/src}/app/page.tsx (100%) rename {src => apps/hudozka/src}/app/robots.ts (100%) rename {src => apps/hudozka/src}/app/sitemap.ts (100%) rename {src => apps/hudozka/src}/assets/graphics/lesson-canceled.svg (100%) rename {src => apps/hudozka/src}/assets/graphics/m-tape-bl.png (100%) rename {src => apps/hudozka/src}/assets/graphics/m-tape-br.png (100%) rename {src => apps/hudozka/src}/assets/graphics/m-tape-tl.png (100%) rename {src => apps/hudozka/src}/assets/graphics/m-tape-tr.png (100%) rename {src => apps/hudozka/src}/assets/graphics/skull.jpg (100%) rename {src => apps/hudozka/src}/assets/graphics/tape-lb.png (100%) rename {src => apps/hudozka/src}/assets/graphics/tape-rb.png (100%) rename {src => apps/hudozka/src}/assets/graphics/tape-tl.png (100%) rename {src => apps/hudozka/src}/assets/graphics/tape-tr.png (100%) rename {src => apps/hudozka/src}/assets/hudozka-logo.png (100%) rename {src => apps/hudozka/src}/components/AccessibilityButton/index.tsx (93%) rename {src => apps/hudozka/src}/components/AccessibilityPanel/index.tsx (100%) rename {src => apps/hudozka/src}/components/AccessibilityPanel/panel.module.css (100%) rename {src => apps/hudozka/src}/components/AccessibilityPanel/voice.tsx (100%) rename {src => apps/hudozka/src}/components/App/app.module.css (100%) rename {src => apps/hudozka/src}/components/App/index.tsx (96%) rename {src => apps/hudozka/src}/components/Author/index.tsx (100%) rename {src => apps/hudozka/src}/components/Breadcrumbs/Breadcrumbs.test.tsx (100%) rename {src => apps/hudozka/src}/components/Breadcrumbs/index.tsx (93%) rename {src => apps/hudozka/src}/components/Breadcrumbs/styles.module.css (100%) rename {src => apps/hudozka/src}/components/Card/index.tsx (100%) rename {src => apps/hudozka/src}/components/Card/styles.module.css (100%) rename {src => apps/hudozka/src}/components/CardGrid/index.tsx (100%) rename {src => apps/hudozka/src}/components/CardGrid/styles.module.css (100%) rename {src => apps/hudozka/src}/components/Contacts/contacts.module.css (100%) rename {src => apps/hudozka/src}/components/Contacts/index.tsx (93%) rename {src => apps/hudozka/src}/components/Content/index.tsx (87%) rename {src => apps/hudozka/src}/components/Copyright/index.tsx (100%) rename {src => apps/hudozka/src}/components/File/index.tsx (95%) rename {src => apps/hudozka/src}/components/File/styles.module.css (100%) rename {src => apps/hudozka/src}/components/FileCard/Signature.tsx (100%) rename {src => apps/hudozka/src}/components/FileCard/filecard.module.css (100%) rename {src => apps/hudozka/src}/components/FileCard/index.tsx (94%) rename {src => apps/hudozka/src}/components/FileCard/signature.module.css (100%) rename {src => apps/hudozka/src}/components/Footer/footer.module.css (100%) rename {src => apps/hudozka/src}/components/Footer/index.tsx (100%) rename {src => apps/hudozka/src}/components/Gosuslugi/index.tsx (100%) rename {src => apps/hudozka/src}/components/Gosuslugi/lib.ts (100%) rename {src => apps/hudozka/src}/components/HomeContent/index.tsx (93%) rename {src => apps/hudozka/src}/components/Html/index.tsx (100%) rename {src => apps/hudozka/src}/components/HudozkaLogo/index.tsx (100%) rename {src => apps/hudozka/src}/components/HudozkaLogo/logo.module.css (100%) rename {src => apps/hudozka/src}/components/HudozkaTitle/index.tsx (100%) rename {src => apps/hudozka/src}/components/HudozkaTitle/title.module.css (100%) rename {src => apps/hudozka/src}/components/Menu/ActiveLink.tsx (100%) rename {src => apps/hudozka/src}/components/Menu/index.tsx (100%) rename {src => apps/hudozka/src}/components/Menu/styles.module.css (100%) rename {src => apps/hudozka/src}/components/Navigation/DesktopNavigation.tsx (95%) rename {src => apps/hudozka/src}/components/Navigation/MobileNavigation.tsx (98%) rename {src => apps/hudozka/src}/components/Navigation/index.tsx (100%) rename {src => apps/hudozka/src}/components/Navigation/nav.module.css (100%) rename {src => apps/hudozka/src}/components/NotFound/error.module.css (100%) rename {src => apps/hudozka/src}/components/NotFound/index.tsx (100%) rename {src => apps/hudozka/src}/components/Page/index.tsx (100%) rename {src => apps/hudozka/src}/components/Page/styles.module.css (100%) rename {src => apps/hudozka/src}/components/PageContent/index.tsx (100%) rename {src => apps/hudozka/src}/components/PageGrid/Date.tsx (86%) rename {src => apps/hudozka/src}/components/PageGrid/date.module.css (100%) rename {src => apps/hudozka/src}/components/PageGrid/index.tsx (96%) rename {src => apps/hudozka/src}/components/PageMeta/index.tsx (91%) rename {src => apps/hudozka/src}/components/PageMeta/styles.module.css (100%) rename {src => apps/hudozka/src}/components/Share/index.tsx (100%) rename {src => apps/hudozka/src}/components/Spacer/index.tsx (100%) rename {src => apps/hudozka/src}/components/TagList/index.tsx (96%) rename {src => apps/hudozka/src}/components/TagList/styles.module.css (100%) rename {src => apps/hudozka/src}/components/Wrapper/index.tsx (100%) rename {src => apps/hudozka/src}/components/Wrapper/wrapper.module.css (100%) rename {src => apps/hudozka/src}/components/YMetrika.test.ts (100%) rename {src => apps/hudozka/src}/components/YMetrika.tsx (100%) rename {src => apps/hudozka/src}/components/Youtube/index.tsx (100%) rename {src => apps/hudozka/src}/const.ts (100%) rename {src => apps/hudozka/src}/global.d.ts (100%) rename {src => apps/hudozka/src}/hooks/useAccessibility.ts (100%) rename {src => apps/hudozka/src}/hooks/useDarkTheme.ts (65%) rename {src => apps/hudozka/src}/lib/meta.test.ts (100%) rename {src => apps/hudozka/src}/lib/meta.ts (100%) rename {src => apps/hudozka/src}/play.ts (100%) rename {src => apps/hudozka/src}/remote/api.test.ts (100%) rename {src => apps/hudozka/src}/remote/api.ts (100%) rename {src => apps/hudozka/src}/remote/doc.ts (100%) rename {src => apps/hudozka/src}/remote/factory.test.ts (100%) rename {src => apps/hudozka/src}/remote/factory.ts (99%) rename {src => apps/hudozka/src}/remote/pb.ts (100%) rename {src => apps/hudozka/src}/remote/types.ts (100%) rename {src => apps/hudozka/src}/store/play.ts (100%) rename {src => apps/hudozka/src}/store/theme.ts (98%) rename {src => apps/hudozka/src}/style/article.css (100%) rename {src => apps/hudozka/src}/style/content.css (100%) rename {src => apps/hudozka/src}/style/kazimir/video.css (100%) rename {src => apps/hudozka/src}/style/marker/marker-table.css (100%) rename {src => apps/hudozka/src}/style/style.css (100%) rename {src => apps/hudozka/src}/style/tape-viewer.scss (100%) rename {src => apps/hudozka/src}/style/vertical-image.scss (100%) rename {src => apps/hudozka/src}/types.ts (100%) rename {src => apps/hudozka/src}/ui/Picture/index.tsx (100%) rename {src => apps/hudozka/src}/ui/Picture/lib.tsx (100%) rename {src => apps/hudozka/src}/ui/Picture/picture.module.css (100%) rename {src => apps/hudozka/src}/ui/ThemeColor/index.tsx (100%) create mode 100644 apps/hudozka/tsconfig.json rename vitest.config.ts => apps/hudozka/vitest.config.ts (100%) rename vitest.setup.tsx => apps/hudozka/vitest.setup.tsx (100%) rename {modules => apps}/migrate-to-pocketbase/.env.example (100%) rename {modules => apps}/migrate-to-pocketbase/.gitignore (100%) rename {modules => apps}/migrate-to-pocketbase/README.md (100%) rename {modules => apps}/migrate-to-pocketbase/package-lock.json (100%) rename {modules => apps}/migrate-to-pocketbase/package.json (88%) rename {modules => apps}/migrate-to-pocketbase/src/blurhash.ts (100%) rename {modules => apps}/migrate-to-pocketbase/src/download.ts (100%) rename {modules => apps}/migrate-to-pocketbase/src/hash.ts (100%) rename {modules => apps}/migrate-to-pocketbase/src/index.ts (100%) rename {modules => apps}/migrate-to-pocketbase/src/strapi.ts (100%) rename {modules => apps}/migrate-to-pocketbase/src/wipe.ts (100%) create mode 100644 apps/migrate-to-pocketbase/tsconfig.json create mode 100644 docs/plans/20260424-monorepo.md rename docs/{superpowers/specs/2026-04-07-writer-draft-publish.md => specs/20260407-writer-draft-publish.md} (100%) rename docs/{superpowers/specs/2026-04-07-writer-editor-improvements.md => specs/20260407-writer-editor-improvements.md} (100%) create mode 100644 docs/specs/20260424-monorepo-design.md create mode 100644 packages/.gitkeep create mode 100644 packages/hooks/package.json create mode 100644 packages/hooks/src/index.ts rename {src/hooks => packages/hooks/src}/useLockBodyScroll.test.ts (100%) rename {src/hooks => packages/hooks/src}/useLockBodyScroll.ts (100%) rename {src/hooks => packages/hooks/src}/useMediaQuery.test.ts (100%) rename {src/hooks => packages/hooks/src}/useMediaQuery.ts (100%) rename {src/hooks => packages/hooks/src}/useMobile.ts (64%) rename {src/hooks => packages/hooks/src}/useReducedMotion.ts (67%) rename {src/hooks => packages/hooks/src}/useToggle.test.ts (100%) rename {src/hooks => packages/hooks/src}/useToggle.ts (100%) create mode 100644 packages/hooks/tsconfig.json create mode 100644 packages/hooks/vitest.config.ts create mode 100644 packages/hooks/vitest.setup.ts create mode 100644 packages/text/package.json create mode 100644 packages/text/src/index.ts rename {src/lib => packages/text/src}/text.test.ts (100%) rename {src/lib => packages/text/src}/text.ts (100%) create mode 100644 packages/text/tsconfig.json create mode 100644 packages/ui/package.json rename {src/ui => packages/ui/src}/Box/Box.test.tsx (100%) rename {src/ui => packages/ui/src}/Box/box.module.css (100%) rename {src/ui => packages/ui/src}/Box/index.tsx (100%) rename {src/ui => packages/ui/src}/Button/Button.test.tsx (100%) rename {src/ui => packages/ui/src}/Button/button.module.css (100%) rename {src/ui => packages/ui/src}/Button/index.tsx (100%) rename {src/ui => packages/ui/src}/Overlay/Transition.tsx (100%) rename {src/ui => packages/ui/src}/Overlay/index.tsx (88%) rename {src/ui => packages/ui/src}/Overlay/styles.module.css (100%) rename {src/ui => packages/ui/src}/Panel/Panel.test.tsx (100%) rename {src/ui => packages/ui/src}/Panel/index.tsx (100%) rename {src/ui => packages/ui/src}/Panel/panel.module.css (100%) rename {src/ui => packages/ui/src}/Title/index.tsx (100%) create mode 100644 packages/ui/src/index.ts create mode 100644 packages/ui/tsconfig.json create mode 100644 packages/ui/vitest.config.ts create mode 100644 packages/ui/vitest.setup.tsx create mode 100644 packages/utils/package.json rename {src/lib => packages/utils/src}/array.test.ts (100%) rename {src/lib => packages/utils/src}/array.ts (100%) rename {src/lib => packages/utils/src}/date.test.ts (100%) rename {src/lib => packages/utils/src}/date.ts (100%) rename {src/lib => packages/utils/src}/file.test.ts (100%) rename {src/lib => packages/utils/src}/file.ts (100%) rename {src/lib => packages/utils/src}/image.test.ts (100%) rename {src/lib => packages/utils/src}/image.ts (100%) create mode 100644 packages/utils/src/index.ts rename {src/lib => packages/utils/src}/math.test.ts (100%) rename {src/lib => packages/utils/src}/math.ts (100%) rename {src/lib => packages/utils/src}/string.test.ts (100%) rename {src/lib => packages/utils/src}/string.ts (100%) rename {src/lib => packages/utils/src}/url.test.ts (100%) rename {src/lib => packages/utils/src}/url.ts (100%) create mode 100644 packages/utils/tsconfig.json create mode 100644 packages/utils/vitest.config.ts create mode 100644 tsconfig.base.json delete mode 100644 tsconfig.json diff --git a/.github/workflows/build-app.yaml b/.github/workflows/build-app.yaml index e5274155..e664c14f 100644 --- a/.github/workflows/build-app.yaml +++ b/.github/workflows/build-app.yaml @@ -39,6 +39,7 @@ jobs: uses: docker/build-push-action@v5 with: context: ./ + file: ./apps/hudozka/Dockerfile push: true platforms: linux/amd64 tags: | diff --git a/.github/workflows/test-app.yml b/.github/workflows/test-app.yml index 20c64e72..af4773cd 100644 --- a/.github/workflows/test-app.yml +++ b/.github/workflows/test-app.yml @@ -59,7 +59,7 @@ jobs: run: npm ci - name: Type check - run: npx tsc --noEmit + run: npx tsc --noEmit -p apps/hudozka/tsconfig.json build: name: Build diff --git a/CLAUDE.md b/CLAUDE.md index 1afac5a6..7b0641d6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,15 +4,22 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -Website for the Art School of Shlisselburg ([art.shlisselburg.org](https://art.shlisselburg.org)). Content is in Russian. Built with Next.js using the App Router and Strapi headless CMS as the backend. The project has migrated through Express → Angular 1 → React class components → Next.js (Pages Router) → Next.js (App Router); some legacy artifacts remain. +Website for the Art School of Shlisselburg ([art.shlisselburg.org](https://art.shlisselburg.org)). Content is in Russian. Built with Next.js using the App Router and PocketBase as the backend (migrated from Strapi — see `pb/` and `src/remote/pb.ts`). The project has migrated through Express → Angular 1 → React class components → Next.js (Pages Router) → Next.js (App Router); some legacy artifacts remain. ## Commands +From repo root (delegates to `apps/hudozka`): + - `npm run dev` — dev server with Turbo (`next dev --turbo`) - `npm run build` — production build -- `npm run lint` — ESLint checks +- `npm run lint` — ESLint across the whole monorepo - `npm run format` — auto-fix lint issues -- `npm test` — run unit tests (Vitest, non-watch mode) +- `npm test` — run unit tests (Vitest) across every workspace + +Per-app: + +- `npm run dev -w apps/hudozka-writer` — writer UI (Vite) +- `npm run migrate -w apps/migrate-to-pocketbase` — one-off migration script Node 24 required (see `mise.toml`). Deployed on Vercel. @@ -22,9 +29,9 @@ Feature branches are named `issue-XXX` where `XXX` is the related GitHub issue n ## CI/CD -- `Dockerfile` — multi-stage Alpine build with standalone Next.js output, pushed to GHCR -- `.github/workflows/build-app.yaml` — builds and pushes Docker image on push to master -- `.github/workflows/test-app.yml` — runs lint, tests, and build on PRs (build runs only after lint and tests pass) +- `apps/hudozka/Dockerfile` — multi-stage Alpine build with standalone Next.js output, pushed to GHCR. Build context is the repo root so the workspace graph is available. +- `.github/workflows/build-app.yaml` — builds and pushes Docker image on push to master (`file: ./apps/hudozka/Dockerfile`, `context: ./`). +- `.github/workflows/test-app.yml` — runs lint, tests, typecheck, and build on PRs. ## Code Style @@ -48,29 +55,35 @@ When adding or updating packages in `package.json`, do not specify the PATCH ver ## Architecture -**App Router with SSG/ISR:** Pages are server components in the `app/` directory with `export const revalidate = 30`. The root layout (`app/layout.tsx`) fetches menu data and provides it via `MenuContext` through a `Providers` client component. The catch-all route `app/[...slug]/page.tsx` handles all dynamic content pages with `generateStaticParams` and `generateMetadata`. +**App Router with SSG/ISR:** Pages are server components under `src/app/` with `export const revalidate = 30`. The root layout (`src/app/layout.tsx`) fetches menu data and passes it down. The catch-all route `src/app/[...slug]/page.tsx` handles all dynamic content pages with `generateStaticParams` and `generateMetadata`. **Server/client boundary:** Layout and page components are server components that fetch data. The `App` component (`src/components/App`) is a `"use client"` component that provides the layout chrome (navigation, breadcrumbs, footer). Content is passed as `children` from server pages through the client boundary. Components using hooks (`PageGrid`, `HomeContent`, `PageContent`, `Menu`, `MobileNavigation`, `Wrapper`) are marked `"use client"`. -**Data flow:** Strapi CMS API (`hudozka.tmshv.com`) → `src/remote/api.ts` fetches data via `apiGet()` generic wrapper → `src/remote/factory.ts` normalizes CMS responses into app types (`src/types.ts`) → pages render token-based content blocks. The API layer uses a factory pattern with default-response fallbacks on failure. +**Data flow:** PocketBase API → `src/remote/api.ts` fetches data via `apiGet()` generic wrapper → `src/remote/factory.ts` normalizes responses into app types (`src/types.ts`) → pages render token-based content blocks. The API layer uses a factory pattern with default-response fallbacks on failure. The legacy Strapi integration was removed (see commit "rm strapi"); `src/remote/pb.ts` is the PocketBase client. **Metadata:** Uses the Next.js Metadata API (`generateMetadata`, `export const metadata`). The `buildMetadata()` helper in `src/lib/meta.ts` bridges the app's `Meta` type to Next.js `Metadata` format. **Content tokens:** Pages are composed of flexible content blocks (tokens): `text`, `image`, `file`, `html`, `youtube`, `instagram`, `grid`. The `Token` discriminated union is defined in `src/types.ts`. Each token type maps to a renderer component in the `PageContent` client component. -**State management:** Valtio for reactive state (`src/store/`). Theme/accessibility preferences in `theme` store, playback state in `play` store, school contact info and year range in `config` store. Navigation data passed via React Context (`src/context/MenuContext`), provided by the root layout. +**State management:** Valtio for reactive state (`src/store/`). Theme/accessibility preferences in `theme` store, playback state in `play` store. ## Key Directories -- `app/` — App Router pages, layouts, and route handlers (layout, home page, `[...slug]` catch-all, not-found, sitemap, robots, feed) -- `src/components/` — page-level and feature components (folder-per-component with `index.tsx` + `styles.module.css`) -- `src/ui/` — reusable primitives (Box, Button, Picture, Panel, Overlay, Title) -- `src/remote/` — Strapi API integration (`api.ts` for fetching, `factory.ts` for transforms, `types.ts` for CMS response shapes) -- `src/store/` — Valtio stores (theme/accessibility, playback config) -- `src/hooks/` — custom hooks (`useMobile`, `useAccessibility`, `useDarkTheme`, `useReducedMotion`) -- `src/lib/` — pure utility functions (date, string, url, image, file helpers) -- `src/style/` — global CSS with CSS custom properties for theming -- `modules/` — ancillary packages (strapi API client, sync tools, writer); excluded from tsconfig +- `apps/hudozka/src/app/` — App Router pages, layouts, route handlers (catch-all, not-found, sitemap, robots, feed) +- `apps/hudozka/src/components/` — page-level and feature components (folder-per-component + `styles.module.css`) +- `apps/hudozka/src/ui/` — Next/store-coupled UI that stays in the app: `Picture`, `ThemeColor` +- `apps/hudozka/src/remote/` — PocketBase API client (`api.ts`, `factory.ts`, `types.ts`, `pb.ts`) +- `apps/hudozka/src/store/` — Valtio stores (theme/accessibility, playback) +- `apps/hudozka/src/hooks/` — business-coupled hooks: `useDarkTheme`, `useAccessibility` +- `apps/hudozka/src/lib/` — app-only lib (currently just `meta.ts`) +- `apps/hudozka/src/style/` — global CSS with CSS custom properties for theming +- `packages/ui/` — `@hudozka/ui` — reusable primitives (Box, Button, Panel, Title, Overlay) +- `packages/hooks/` — `@hudozka/hooks` — generic React hooks (useToggle, useLockBodyScroll, useMediaQuery, useMobile, useReducedMotion) +- `packages/utils/` — `@hudozka/utils` — pure utilities (array, string, url, math, image, file, date) +- `packages/text/` — `@hudozka/text` — markdown + typograf rendering +- `apps/hudozka-writer/` — Vite-based Tiptap editor +- `apps/migrate-to-pocketbase/` — one-off Strapi → PocketBase migration script +- `pb/` — PocketBase instance (data, migrations) ## Styling @@ -78,7 +91,7 @@ CSS Modules (`.module.css`) for component-scoped styles. Global theme via CSS cu ## TypeScript -Path alias: `@/*` → `src/*`. Strict mode enabled. Target ES2020. `moduleDetection: "force"` in tsconfig (required because Next.js auto-generates `.next/types/validator.ts` without exports, which breaks `verbatimModuleSyntax`). Use `@/` path alias for all local imports, not `src/`. Legacy `src/` imports exist in the codebase but should not be used in new or modified code. +Path alias: `@/*` → `src/*`. Strict mode enabled. Target ES2020. `moduleDetection: "force"` in tsconfig (required because Next.js auto-generates `.next/types/validator.ts` without exports, which breaks `verbatimModuleSyntax`). Use `@/` path alias for all local imports, not `src/`. Legacy `src/` imports exist in the codebase but should not be used in new or modified code. The `@/*` path alias is only configured in `apps/hudozka/tsconfig.json`; packages under `packages/*` use short relative imports. `verbatimModuleSyntax` is enabled — use `import type` for type-only imports. When importing both values and types from the same module, use separate `import` and `import type` statements: @@ -108,3 +121,4 @@ Active modernization is tracked in GitHub issues #222–#238. Key items: - Two orphaned `.scss` files in `src/style/` (`tape-viewer.scss`, `vertical-image.scss`) — not imported anywhere, no SCSS preprocessor configured. - Some inline styles in `src/components/App/index.tsx` should be CSS modules. - `react-use` and `use-media` dependencies are unmaintained; only `useToggle` and `useLockBodyScroll` are used from `react-use`. + diff --git a/README.md b/README.md index a580a3b9..81346fc8 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,31 @@ # [ART.SHLISSELBURG.ORG](https://art.shlisselburg.org) -Website for the Art School of Shlisselburg. Built with Next.js and powered by Strapi CMS. +Website for the Art School of Shlisselburg. Built with Next.js and powered by PocketBase (migrated from Strapi). Font: [«Shadow»](http://mishapanfilov.ru/font_shadow.html) +## Repo layout + +This is an npm-workspaces monorepo: + +- `apps/hudozka` — main Next.js app (art.shlisselburg.org) +- `apps/hudozka-writer` — Vite-based content editor +- `apps/migrate-to-pocketbase` — one-off migration script (Strapi → PocketBase) +- `packages/ui` — `@hudozka/ui` — shared React primitives (Box, Button, Panel, Title, Overlay) +- `packages/hooks` — `@hudozka/hooks` — generic React hooks +- `packages/utils` — `@hudozka/utils` — pure utilities (array, string, url, math, image, file, date) +- `packages/text` — `@hudozka/text` — markdown + typograf rendering +- `pb/` — PocketBase instance (untouched by the monorepo migration) + +Common commands (from repo root): + +- `npm run dev` — run the web app dev server +- `npm run build` — production build of the web app +- `npm run lint` — lint the whole monorepo +- `npm test` — run tests across every workspace +- `npm run dev -w apps/hudozka-writer` — run the writer +- `npm run migrate -w apps/migrate-to-pocketbase` — run the migration script + ## Development ### Prerequisites @@ -25,7 +47,7 @@ npm run dev The development server runs at [http://localhost:3000](http://localhost:3000) with Turbo mode enabled. ## Content Management -Content is fetched from Strapi CMS and rendered using flexible content tokens: +Content is fetched from PocketBase and rendered using flexible content tokens: - `text` — Markdown with Russian typography (Typograf) - `image` — Responsive images with blur hash - `file` — Document downloads diff --git a/apps/.gitkeep b/apps/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/modules/hudozka-writer/.gitignore b/apps/hudozka-writer/.gitignore similarity index 100% rename from modules/hudozka-writer/.gitignore rename to apps/hudozka-writer/.gitignore diff --git a/modules/hudozka-writer/README.md b/apps/hudozka-writer/README.md similarity index 100% rename from modules/hudozka-writer/README.md rename to apps/hudozka-writer/README.md diff --git a/modules/hudozka-writer/index.html b/apps/hudozka-writer/index.html similarity index 100% rename from modules/hudozka-writer/index.html rename to apps/hudozka-writer/index.html diff --git a/modules/hudozka-writer/package-lock.json b/apps/hudozka-writer/package-lock.json similarity index 100% rename from modules/hudozka-writer/package-lock.json rename to apps/hudozka-writer/package-lock.json diff --git a/modules/hudozka-writer/package.json b/apps/hudozka-writer/package.json similarity index 100% rename from modules/hudozka-writer/package.json rename to apps/hudozka-writer/package.json diff --git a/modules/hudozka-writer/src/App.css b/apps/hudozka-writer/src/App.css similarity index 100% rename from modules/hudozka-writer/src/App.css rename to apps/hudozka-writer/src/App.css diff --git a/modules/hudozka-writer/src/App.tsx b/apps/hudozka-writer/src/App.tsx similarity index 100% rename from modules/hudozka-writer/src/App.tsx rename to apps/hudozka-writer/src/App.tsx diff --git a/modules/hudozka-writer/src/components/BlockActions.css b/apps/hudozka-writer/src/components/BlockActions.css similarity index 100% rename from modules/hudozka-writer/src/components/BlockActions.css rename to apps/hudozka-writer/src/components/BlockActions.css diff --git a/modules/hudozka-writer/src/components/BlockActions.tsx b/apps/hudozka-writer/src/components/BlockActions.tsx similarity index 100% rename from modules/hudozka-writer/src/components/BlockActions.tsx rename to apps/hudozka-writer/src/components/BlockActions.tsx diff --git a/modules/hudozka-writer/src/components/BlockInsert.css b/apps/hudozka-writer/src/components/BlockInsert.css similarity index 100% rename from modules/hudozka-writer/src/components/BlockInsert.css rename to apps/hudozka-writer/src/components/BlockInsert.css diff --git a/modules/hudozka-writer/src/components/BlockInsert.tsx b/apps/hudozka-writer/src/components/BlockInsert.tsx similarity index 100% rename from modules/hudozka-writer/src/components/BlockInsert.tsx rename to apps/hudozka-writer/src/components/BlockInsert.tsx diff --git a/modules/hudozka-writer/src/components/BlockMenu.css b/apps/hudozka-writer/src/components/BlockMenu.css similarity index 100% rename from modules/hudozka-writer/src/components/BlockMenu.css rename to apps/hudozka-writer/src/components/BlockMenu.css diff --git a/modules/hudozka-writer/src/components/BlockMenu.tsx b/apps/hudozka-writer/src/components/BlockMenu.tsx similarity index 100% rename from modules/hudozka-writer/src/components/BlockMenu.tsx rename to apps/hudozka-writer/src/components/BlockMenu.tsx diff --git a/modules/hudozka-writer/src/components/DraftBanner.css b/apps/hudozka-writer/src/components/DraftBanner.css similarity index 100% rename from modules/hudozka-writer/src/components/DraftBanner.css rename to apps/hudozka-writer/src/components/DraftBanner.css diff --git a/modules/hudozka-writer/src/components/DraftBanner.tsx b/apps/hudozka-writer/src/components/DraftBanner.tsx similarity index 100% rename from modules/hudozka-writer/src/components/DraftBanner.tsx rename to apps/hudozka-writer/src/components/DraftBanner.tsx diff --git a/modules/hudozka-writer/src/components/Editor.css b/apps/hudozka-writer/src/components/Editor.css similarity index 100% rename from modules/hudozka-writer/src/components/Editor.css rename to apps/hudozka-writer/src/components/Editor.css diff --git a/modules/hudozka-writer/src/components/Editor.tsx b/apps/hudozka-writer/src/components/Editor.tsx similarity index 100% rename from modules/hudozka-writer/src/components/Editor.tsx rename to apps/hudozka-writer/src/components/Editor.tsx diff --git a/modules/hudozka-writer/src/components/FilePicker.css b/apps/hudozka-writer/src/components/FilePicker.css similarity index 100% rename from modules/hudozka-writer/src/components/FilePicker.css rename to apps/hudozka-writer/src/components/FilePicker.css diff --git a/modules/hudozka-writer/src/components/FilePicker.tsx b/apps/hudozka-writer/src/components/FilePicker.tsx similarity index 100% rename from modules/hudozka-writer/src/components/FilePicker.tsx rename to apps/hudozka-writer/src/components/FilePicker.tsx diff --git a/modules/hudozka-writer/src/components/ImagePicker.css b/apps/hudozka-writer/src/components/ImagePicker.css similarity index 100% rename from modules/hudozka-writer/src/components/ImagePicker.css rename to apps/hudozka-writer/src/components/ImagePicker.css diff --git a/modules/hudozka-writer/src/components/ImagePicker.tsx b/apps/hudozka-writer/src/components/ImagePicker.tsx similarity index 100% rename from modules/hudozka-writer/src/components/ImagePicker.tsx rename to apps/hudozka-writer/src/components/ImagePicker.tsx diff --git a/modules/hudozka-writer/src/components/Login.css b/apps/hudozka-writer/src/components/Login.css similarity index 100% rename from modules/hudozka-writer/src/components/Login.css rename to apps/hudozka-writer/src/components/Login.css diff --git a/modules/hudozka-writer/src/components/Login.tsx b/apps/hudozka-writer/src/components/Login.tsx similarity index 100% rename from modules/hudozka-writer/src/components/Login.tsx rename to apps/hudozka-writer/src/components/Login.tsx diff --git a/modules/hudozka-writer/src/components/Toolbar.css b/apps/hudozka-writer/src/components/Toolbar.css similarity index 100% rename from modules/hudozka-writer/src/components/Toolbar.css rename to apps/hudozka-writer/src/components/Toolbar.css diff --git a/modules/hudozka-writer/src/components/Toolbar.tsx b/apps/hudozka-writer/src/components/Toolbar.tsx similarity index 100% rename from modules/hudozka-writer/src/components/Toolbar.tsx rename to apps/hudozka-writer/src/components/Toolbar.tsx diff --git a/modules/hudozka-writer/src/extensions/SlashCommands.css b/apps/hudozka-writer/src/extensions/SlashCommands.css similarity index 100% rename from modules/hudozka-writer/src/extensions/SlashCommands.css rename to apps/hudozka-writer/src/extensions/SlashCommands.css diff --git a/modules/hudozka-writer/src/extensions/SlashCommands.tsx b/apps/hudozka-writer/src/extensions/SlashCommands.tsx similarity index 100% rename from modules/hudozka-writer/src/extensions/SlashCommands.tsx rename to apps/hudozka-writer/src/extensions/SlashCommands.tsx diff --git a/modules/hudozka-writer/src/index.css b/apps/hudozka-writer/src/index.css similarity index 100% rename from modules/hudozka-writer/src/index.css rename to apps/hudozka-writer/src/index.css diff --git a/modules/hudozka-writer/src/lib/id.ts b/apps/hudozka-writer/src/lib/id.ts similarity index 100% rename from modules/hudozka-writer/src/lib/id.ts rename to apps/hudozka-writer/src/lib/id.ts diff --git a/modules/hudozka-writer/src/lib/markdown.ts b/apps/hudozka-writer/src/lib/markdown.ts similarity index 100% rename from modules/hudozka-writer/src/lib/markdown.ts rename to apps/hudozka-writer/src/lib/markdown.ts diff --git a/modules/hudozka-writer/src/lib/serialize.ts b/apps/hudozka-writer/src/lib/serialize.ts similarity index 100% rename from modules/hudozka-writer/src/lib/serialize.ts rename to apps/hudozka-writer/src/lib/serialize.ts diff --git a/modules/hudozka-writer/src/main.tsx b/apps/hudozka-writer/src/main.tsx similarity index 100% rename from modules/hudozka-writer/src/main.tsx rename to apps/hudozka-writer/src/main.tsx diff --git a/modules/hudozka-writer/src/nodes/CardGridBlock.tsx b/apps/hudozka-writer/src/nodes/CardGridBlock.tsx similarity index 100% rename from modules/hudozka-writer/src/nodes/CardGridBlock.tsx rename to apps/hudozka-writer/src/nodes/CardGridBlock.tsx diff --git a/modules/hudozka-writer/src/nodes/DocumentBlock.tsx b/apps/hudozka-writer/src/nodes/DocumentBlock.tsx similarity index 100% rename from modules/hudozka-writer/src/nodes/DocumentBlock.tsx rename to apps/hudozka-writer/src/nodes/DocumentBlock.tsx diff --git a/modules/hudozka-writer/src/nodes/EmbedBlock.tsx b/apps/hudozka-writer/src/nodes/EmbedBlock.tsx similarity index 100% rename from modules/hudozka-writer/src/nodes/EmbedBlock.tsx rename to apps/hudozka-writer/src/nodes/EmbedBlock.tsx diff --git a/modules/hudozka-writer/src/nodes/ImageBlock.tsx b/apps/hudozka-writer/src/nodes/ImageBlock.tsx similarity index 100% rename from modules/hudozka-writer/src/nodes/ImageBlock.tsx rename to apps/hudozka-writer/src/nodes/ImageBlock.tsx diff --git a/modules/hudozka-writer/src/nodes/TextBlock.tsx b/apps/hudozka-writer/src/nodes/TextBlock.tsx similarity index 100% rename from modules/hudozka-writer/src/nodes/TextBlock.tsx rename to apps/hudozka-writer/src/nodes/TextBlock.tsx diff --git a/modules/hudozka-writer/src/nodes/WriterDocument.ts b/apps/hudozka-writer/src/nodes/WriterDocument.ts similarity index 100% rename from modules/hudozka-writer/src/nodes/WriterDocument.ts rename to apps/hudozka-writer/src/nodes/WriterDocument.ts diff --git a/modules/hudozka-writer/src/nodes/index.ts b/apps/hudozka-writer/src/nodes/index.ts similarity index 100% rename from modules/hudozka-writer/src/nodes/index.ts rename to apps/hudozka-writer/src/nodes/index.ts diff --git a/modules/hudozka-writer/src/nodes/nodes.css b/apps/hudozka-writer/src/nodes/nodes.css similarity index 100% rename from modules/hudozka-writer/src/nodes/nodes.css rename to apps/hudozka-writer/src/nodes/nodes.css diff --git a/modules/hudozka-writer/src/pb.ts b/apps/hudozka-writer/src/pb.ts similarity index 100% rename from modules/hudozka-writer/src/pb.ts rename to apps/hudozka-writer/src/pb.ts diff --git a/modules/hudozka-writer/src/types.ts b/apps/hudozka-writer/src/types.ts similarity index 100% rename from modules/hudozka-writer/src/types.ts rename to apps/hudozka-writer/src/types.ts diff --git a/modules/hudozka-writer/src/vite-env.d.ts b/apps/hudozka-writer/src/vite-env.d.ts similarity index 100% rename from modules/hudozka-writer/src/vite-env.d.ts rename to apps/hudozka-writer/src/vite-env.d.ts diff --git a/modules/hudozka-writer/tsconfig.json b/apps/hudozka-writer/tsconfig.json similarity index 57% rename from modules/hudozka-writer/tsconfig.json rename to apps/hudozka-writer/tsconfig.json index d49b4406..a2ddbf84 100644 --- a/modules/hudozka-writer/tsconfig.json +++ b/apps/hudozka-writer/tsconfig.json @@ -1,18 +1,10 @@ { + "extends": "../../tsconfig.base.json", "compilerOptions": { - "target": "ES2020", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "bundler", "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true diff --git a/modules/hudozka-writer/vite.config.ts b/apps/hudozka-writer/vite.config.ts similarity index 100% rename from modules/hudozka-writer/vite.config.ts rename to apps/hudozka-writer/vite.config.ts diff --git a/.dockerignore b/apps/hudozka/.dockerignore similarity index 100% rename from .dockerignore rename to apps/hudozka/.dockerignore diff --git a/.env.example b/apps/hudozka/.env.example similarity index 100% rename from .env.example rename to apps/hudozka/.env.example diff --git a/.env.local b/apps/hudozka/.env.local similarity index 100% rename from .env.local rename to apps/hudozka/.env.local diff --git a/Dockerfile b/apps/hudozka/Dockerfile similarity index 57% rename from Dockerfile rename to apps/hudozka/Dockerfile index d7d50417..de163cc5 100644 --- a/Dockerfile +++ b/apps/hudozka/Dockerfile @@ -6,45 +6,47 @@ FROM base AS deps RUN apk add --no-cache libc6-compat WORKDIR /app -# Install dependencies based on the preferred package manager +# Copy workspace manifests (root + every workspace) to leverage npm ci caching. COPY package.json package-lock.json ./ +COPY apps/hudozka/package.json ./apps/hudozka/ +COPY apps/hudozka-writer/package.json ./apps/hudozka-writer/ +COPY apps/migrate-to-pocketbase/package.json ./apps/migrate-to-pocketbase/ +COPY packages/ ./packages/ + RUN npm ci -RUN npm install --os=linux --libc=musl --cpu=x64 sharp +RUN npm install --os=linux --libc=musl --cpu=x64 sharp --workspace apps/hudozka # Rebuild the source code only when needed FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules +COPY --from=deps /app/apps/hudozka/node_modules ./apps/hudozka/node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED=1 -RUN npm run build +RUN npm run build -w apps/hudozka # Production image, copy all the files and run next FROM base AS runner WORKDIR /app -# sharp is required to be installed in standalone mode for the image optimization to function correctly. -# Read more at: https://nextjs.org/docs/messages/sharp-missing-in-production -# RUN npm install sharp - ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs -COPY --from=builder --chown=nextjs:nodejs /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/apps/hudozka/public ./apps/hudozka/public # Set the correct permission for prerender cache -RUN mkdir .next -RUN chown nextjs:nodejs .next +RUN mkdir -p apps/hudozka/.next +RUN chown nextjs:nodejs apps/hudozka/.next # Automatically leverage output traces to reduce image size # https://nextjs.org/docs/advanced-features/output-file-tracing -COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +COPY --from=builder --chown=nextjs:nodejs /app/apps/hudozka/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/apps/hudozka/.next/static ./apps/hudozka/.next/static USER nextjs @@ -54,4 +56,5 @@ ENV PORT=3000 # server.js is created by next build from the standalone output # https://nextjs.org/docs/pages/api-reference/next-config-js/output ENV HOSTNAME="0.0.0.0" +WORKDIR /app/apps/hudozka CMD ["node", "server.js"] diff --git a/next-env.d.ts b/apps/hudozka/next-env.d.ts similarity index 100% rename from next-env.d.ts rename to apps/hudozka/next-env.d.ts diff --git a/next.config.ts b/apps/hudozka/next.config.ts similarity index 74% rename from next.config.ts rename to apps/hudozka/next.config.ts index 03a8e414..fdf8c122 100644 --- a/next.config.ts +++ b/apps/hudozka/next.config.ts @@ -1,9 +1,12 @@ import type { NextConfig } from "next" +import path from "node:path" const config: NextConfig = { output: "standalone", + outputFileTracingRoot: path.join(process.cwd(), "../../"), trailingSlash: false, images: { + unoptimized: true, remotePatterns: [ { hostname: "hudozka.shlisselburg.org" }, { hostname: "images.weserv.nl" }, diff --git a/apps/hudozka/package.json b/apps/hudozka/package.json new file mode 100644 index 00000000..5837d368 --- /dev/null +++ b/apps/hudozka/package.json @@ -0,0 +1,43 @@ +{ + "name": "hudozka", + "private": true, + "version": "7.0.0", + "type": "module", + "description": "", + "scripts": { + "start": "next start", + "dev": "next dev --turbo", + "build": "next build", + "lint": "eslint .", + "format": "eslint --fix .", + "test": "vitest run" + }, + "repository": { + "type": "git", + "url": "https://github.com/tmshv/hudozka" + }, + "author": "Roman Timashev", + "license": "ISC", + "dependencies": { + "@hudozka/hooks": "*", + "@hudozka/text": "*", + "@hudozka/ui": "*", + "@hudozka/utils": "*", + "@radix-ui/react-navigation-menu": "^1.2.0", + "@radix-ui/react-tooltip": "^1.2.0", + "classnames": "^2.5.0", + "dangerously-set-html-content": "^1.1.0", + "next": "^16.2.0", + "pocketbase": "^0.26.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-icons": "^5.5.0", + "sharp": "^0.34.0", + "valtio": "^2.3.0" + }, + "overrides": { + "dangerously-set-html-content": { + "react": "$react" + } + } +} diff --git a/public/static/animate.min.css b/apps/hudozka/public/static/animate.min.css similarity index 100% rename from public/static/animate.min.css rename to apps/hudozka/public/static/animate.min.css diff --git a/public/static/graphics/favicon-114.png b/apps/hudozka/public/static/graphics/favicon-114.png similarity index 100% rename from public/static/graphics/favicon-114.png rename to apps/hudozka/public/static/graphics/favicon-114.png diff --git a/public/static/graphics/favicon-144.png b/apps/hudozka/public/static/graphics/favicon-144.png similarity index 100% rename from public/static/graphics/favicon-144.png rename to apps/hudozka/public/static/graphics/favicon-144.png diff --git a/public/static/graphics/favicon-32.png b/apps/hudozka/public/static/graphics/favicon-32.png similarity index 100% rename from public/static/graphics/favicon-32.png rename to apps/hudozka/public/static/graphics/favicon-32.png diff --git a/public/static/graphics/favicon-57.png b/apps/hudozka/public/static/graphics/favicon-57.png similarity index 100% rename from public/static/graphics/favicon-57.png rename to apps/hudozka/public/static/graphics/favicon-57.png diff --git a/public/static/graphics/favicon-72.png b/apps/hudozka/public/static/graphics/favicon-72.png similarity index 100% rename from public/static/graphics/favicon-72.png rename to apps/hudozka/public/static/graphics/favicon-72.png diff --git a/public/static/img/collective2017.jpg b/apps/hudozka/public/static/img/collective2017.jpg similarity index 100% rename from public/static/img/collective2017.jpg rename to apps/hudozka/public/static/img/collective2017.jpg diff --git a/public/static/img/collective2019.jpg b/apps/hudozka/public/static/img/collective2019.jpg similarity index 100% rename from public/static/img/collective2019.jpg rename to apps/hudozka/public/static/img/collective2019.jpg diff --git a/public/static/img/main.jpg b/apps/hudozka/public/static/img/main.jpg similarity index 100% rename from public/static/img/main.jpg rename to apps/hudozka/public/static/img/main.jpg diff --git a/public/static/likely.css b/apps/hudozka/public/static/likely.css similarity index 100% rename from public/static/likely.css rename to apps/hudozka/public/static/likely.css diff --git a/public/static/likely.js b/apps/hudozka/public/static/likely.js similarity index 100% rename from public/static/likely.js rename to apps/hudozka/public/static/likely.js diff --git a/public/static/shadow-font/Shadow-Regular.eot b/apps/hudozka/public/static/shadow-font/Shadow-Regular.eot similarity index 100% rename from public/static/shadow-font/Shadow-Regular.eot rename to apps/hudozka/public/static/shadow-font/Shadow-Regular.eot diff --git a/public/static/shadow-font/Shadow-Regular.svg b/apps/hudozka/public/static/shadow-font/Shadow-Regular.svg similarity index 100% rename from public/static/shadow-font/Shadow-Regular.svg rename to apps/hudozka/public/static/shadow-font/Shadow-Regular.svg diff --git a/public/static/shadow-font/Shadow-Regular.ttf b/apps/hudozka/public/static/shadow-font/Shadow-Regular.ttf similarity index 100% rename from public/static/shadow-font/Shadow-Regular.ttf rename to apps/hudozka/public/static/shadow-font/Shadow-Regular.ttf diff --git a/public/static/shadow-font/Shadow-Regular.woff b/apps/hudozka/public/static/shadow-font/Shadow-Regular.woff similarity index 100% rename from public/static/shadow-font/Shadow-Regular.woff rename to apps/hudozka/public/static/shadow-font/Shadow-Regular.woff diff --git a/src/app/[...slug]/loading.tsx b/apps/hudozka/src/app/[...slug]/loading.tsx similarity index 100% rename from src/app/[...slug]/loading.tsx rename to apps/hudozka/src/app/[...slug]/loading.tsx diff --git a/src/app/[...slug]/page.tsx b/apps/hudozka/src/app/[...slug]/page.tsx similarity index 98% rename from src/app/[...slug]/page.tsx rename to apps/hudozka/src/app/[...slug]/page.tsx index ad403cb0..694af924 100644 --- a/src/app/[...slug]/page.tsx +++ b/apps/hudozka/src/app/[...slug]/page.tsx @@ -4,7 +4,7 @@ import { App } from "@/components/App" import { PageContent } from "@/components/PageContent" import { MetaBuilder, buildMetadata } from "@/lib/meta" import { getPageBySlug, getUrls } from "@/remote/api" -import { tail } from "@/lib/array" +import { tail } from "@hudozka/utils" export const revalidate = 30 export const dynamicParams = true diff --git a/src/app/feed.json/route.ts b/apps/hudozka/src/app/feed.json/route.ts similarity index 100% rename from src/app/feed.json/route.ts rename to apps/hudozka/src/app/feed.json/route.ts diff --git a/src/app/layout.tsx b/apps/hudozka/src/app/layout.tsx similarity index 100% rename from src/app/layout.tsx rename to apps/hudozka/src/app/layout.tsx diff --git a/src/app/not-found.tsx b/apps/hudozka/src/app/not-found.tsx similarity index 100% rename from src/app/not-found.tsx rename to apps/hudozka/src/app/not-found.tsx diff --git a/src/app/page.tsx b/apps/hudozka/src/app/page.tsx similarity index 100% rename from src/app/page.tsx rename to apps/hudozka/src/app/page.tsx diff --git a/src/app/robots.ts b/apps/hudozka/src/app/robots.ts similarity index 100% rename from src/app/robots.ts rename to apps/hudozka/src/app/robots.ts diff --git a/src/app/sitemap.ts b/apps/hudozka/src/app/sitemap.ts similarity index 100% rename from src/app/sitemap.ts rename to apps/hudozka/src/app/sitemap.ts diff --git a/src/assets/graphics/lesson-canceled.svg b/apps/hudozka/src/assets/graphics/lesson-canceled.svg similarity index 100% rename from src/assets/graphics/lesson-canceled.svg rename to apps/hudozka/src/assets/graphics/lesson-canceled.svg diff --git a/src/assets/graphics/m-tape-bl.png b/apps/hudozka/src/assets/graphics/m-tape-bl.png similarity index 100% rename from src/assets/graphics/m-tape-bl.png rename to apps/hudozka/src/assets/graphics/m-tape-bl.png diff --git a/src/assets/graphics/m-tape-br.png b/apps/hudozka/src/assets/graphics/m-tape-br.png similarity index 100% rename from src/assets/graphics/m-tape-br.png rename to apps/hudozka/src/assets/graphics/m-tape-br.png diff --git a/src/assets/graphics/m-tape-tl.png b/apps/hudozka/src/assets/graphics/m-tape-tl.png similarity index 100% rename from src/assets/graphics/m-tape-tl.png rename to apps/hudozka/src/assets/graphics/m-tape-tl.png diff --git a/src/assets/graphics/m-tape-tr.png b/apps/hudozka/src/assets/graphics/m-tape-tr.png similarity index 100% rename from src/assets/graphics/m-tape-tr.png rename to apps/hudozka/src/assets/graphics/m-tape-tr.png diff --git a/src/assets/graphics/skull.jpg b/apps/hudozka/src/assets/graphics/skull.jpg similarity index 100% rename from src/assets/graphics/skull.jpg rename to apps/hudozka/src/assets/graphics/skull.jpg diff --git a/src/assets/graphics/tape-lb.png b/apps/hudozka/src/assets/graphics/tape-lb.png similarity index 100% rename from src/assets/graphics/tape-lb.png rename to apps/hudozka/src/assets/graphics/tape-lb.png diff --git a/src/assets/graphics/tape-rb.png b/apps/hudozka/src/assets/graphics/tape-rb.png similarity index 100% rename from src/assets/graphics/tape-rb.png rename to apps/hudozka/src/assets/graphics/tape-rb.png diff --git a/src/assets/graphics/tape-tl.png b/apps/hudozka/src/assets/graphics/tape-tl.png similarity index 100% rename from src/assets/graphics/tape-tl.png rename to apps/hudozka/src/assets/graphics/tape-tl.png diff --git a/src/assets/graphics/tape-tr.png b/apps/hudozka/src/assets/graphics/tape-tr.png similarity index 100% rename from src/assets/graphics/tape-tr.png rename to apps/hudozka/src/assets/graphics/tape-tr.png diff --git a/src/assets/hudozka-logo.png b/apps/hudozka/src/assets/hudozka-logo.png similarity index 100% rename from src/assets/hudozka-logo.png rename to apps/hudozka/src/assets/hudozka-logo.png diff --git a/src/components/AccessibilityButton/index.tsx b/apps/hudozka/src/components/AccessibilityButton/index.tsx similarity index 93% rename from src/components/AccessibilityButton/index.tsx rename to apps/hudozka/src/components/AccessibilityButton/index.tsx index 36d60a43..ac9848fe 100644 --- a/src/components/AccessibilityButton/index.tsx +++ b/apps/hudozka/src/components/AccessibilityButton/index.tsx @@ -1,5 +1,5 @@ import { theme, toggleTheme } from "@/store/theme" -import { Button } from "@/ui/Button" +import { Button } from "@hudozka/ui" import { HiOutlineEye, HiOutlineEyeOff } from "react-icons/hi" import { useSnapshot } from "valtio" diff --git a/src/components/AccessibilityPanel/index.tsx b/apps/hudozka/src/components/AccessibilityPanel/index.tsx similarity index 100% rename from src/components/AccessibilityPanel/index.tsx rename to apps/hudozka/src/components/AccessibilityPanel/index.tsx diff --git a/src/components/AccessibilityPanel/panel.module.css b/apps/hudozka/src/components/AccessibilityPanel/panel.module.css similarity index 100% rename from src/components/AccessibilityPanel/panel.module.css rename to apps/hudozka/src/components/AccessibilityPanel/panel.module.css diff --git a/src/components/AccessibilityPanel/voice.tsx b/apps/hudozka/src/components/AccessibilityPanel/voice.tsx similarity index 100% rename from src/components/AccessibilityPanel/voice.tsx rename to apps/hudozka/src/components/AccessibilityPanel/voice.tsx diff --git a/src/components/App/app.module.css b/apps/hudozka/src/components/App/app.module.css similarity index 100% rename from src/components/App/app.module.css rename to apps/hudozka/src/components/App/app.module.css diff --git a/src/components/App/index.tsx b/apps/hudozka/src/components/App/index.tsx similarity index 96% rename from src/components/App/index.tsx rename to apps/hudozka/src/components/App/index.tsx index 5bfa43f4..2038ce8a 100644 --- a/src/components/App/index.tsx +++ b/apps/hudozka/src/components/App/index.tsx @@ -8,16 +8,14 @@ import { Content } from "../Content" import { Breadcrumbs } from "../Breadcrumbs" import type { BreadcrumbPart } from "@/types" import { usePathname } from "next/navigation" -import { useMobile } from "@/hooks/useMobile" +import { useMobile } from "@hudozka/hooks" import { Navigation } from "@/components/Navigation" -import { Box } from "@/ui/Box" +import { Box, Panel, Title } from "@hudozka/ui" import { AccessibilityButton } from "../AccessibilityButton" import { Copyright } from "@/components/Copyright" import { yearRange } from "@/const" import { Spacer } from "../Spacer" import { Author } from "../Author" -import { Title } from "@/ui/Title" -import { Panel } from "@/ui/Panel" import { Contacts } from "../Contacts" import dynamic from "next/dynamic" import Link from "next/link" diff --git a/src/components/Author/index.tsx b/apps/hudozka/src/components/Author/index.tsx similarity index 100% rename from src/components/Author/index.tsx rename to apps/hudozka/src/components/Author/index.tsx diff --git a/src/components/Breadcrumbs/Breadcrumbs.test.tsx b/apps/hudozka/src/components/Breadcrumbs/Breadcrumbs.test.tsx similarity index 100% rename from src/components/Breadcrumbs/Breadcrumbs.test.tsx rename to apps/hudozka/src/components/Breadcrumbs/Breadcrumbs.test.tsx diff --git a/src/components/Breadcrumbs/index.tsx b/apps/hudozka/src/components/Breadcrumbs/index.tsx similarity index 93% rename from src/components/Breadcrumbs/index.tsx rename to apps/hudozka/src/components/Breadcrumbs/index.tsx index 20b1708c..2f759e65 100644 --- a/src/components/Breadcrumbs/index.tsx +++ b/apps/hudozka/src/components/Breadcrumbs/index.tsx @@ -1,8 +1,8 @@ import s from "./styles.module.css" import type { BreadcrumbPart } from "@/types" -import { Button } from "@/ui/Button" -import { insertBetween } from "@/lib/array" +import { Button } from "@hudozka/ui" +import { insertBetween } from "@hudozka/utils" export type BreadcrumbsProps = { style?: React.CSSProperties diff --git a/src/components/Breadcrumbs/styles.module.css b/apps/hudozka/src/components/Breadcrumbs/styles.module.css similarity index 100% rename from src/components/Breadcrumbs/styles.module.css rename to apps/hudozka/src/components/Breadcrumbs/styles.module.css diff --git a/src/components/Card/index.tsx b/apps/hudozka/src/components/Card/index.tsx similarity index 100% rename from src/components/Card/index.tsx rename to apps/hudozka/src/components/Card/index.tsx diff --git a/src/components/Card/styles.module.css b/apps/hudozka/src/components/Card/styles.module.css similarity index 100% rename from src/components/Card/styles.module.css rename to apps/hudozka/src/components/Card/styles.module.css diff --git a/src/components/CardGrid/index.tsx b/apps/hudozka/src/components/CardGrid/index.tsx similarity index 100% rename from src/components/CardGrid/index.tsx rename to apps/hudozka/src/components/CardGrid/index.tsx diff --git a/src/components/CardGrid/styles.module.css b/apps/hudozka/src/components/CardGrid/styles.module.css similarity index 100% rename from src/components/CardGrid/styles.module.css rename to apps/hudozka/src/components/CardGrid/styles.module.css diff --git a/src/components/Contacts/contacts.module.css b/apps/hudozka/src/components/Contacts/contacts.module.css similarity index 100% rename from src/components/Contacts/contacts.module.css rename to apps/hudozka/src/components/Contacts/contacts.module.css diff --git a/src/components/Contacts/index.tsx b/apps/hudozka/src/components/Contacts/index.tsx similarity index 93% rename from src/components/Contacts/index.tsx rename to apps/hudozka/src/components/Contacts/index.tsx index 4f5df828..63e86ef8 100644 --- a/src/components/Contacts/index.tsx +++ b/apps/hudozka/src/components/Contacts/index.tsx @@ -1,7 +1,7 @@ import s from "./contacts.module.css" import Link from "next/link" -import { Title } from "@/ui/Title" +import { Title } from "@hudozka/ui" import { contacts } from "@/const" export const Contacts: React.FC = () => { diff --git a/src/components/Content/index.tsx b/apps/hudozka/src/components/Content/index.tsx similarity index 87% rename from src/components/Content/index.tsx rename to apps/hudozka/src/components/Content/index.tsx index 90f98b3e..738ed8fd 100644 --- a/src/components/Content/index.tsx +++ b/apps/hudozka/src/components/Content/index.tsx @@ -1,5 +1,5 @@ import cx from "classnames" -import { useReducedMotion } from "src/hooks/useReducedMotion" +import { useReducedMotion } from "@hudozka/hooks" export type ContentProps = { style?: React.CSSProperties diff --git a/src/components/Copyright/index.tsx b/apps/hudozka/src/components/Copyright/index.tsx similarity index 100% rename from src/components/Copyright/index.tsx rename to apps/hudozka/src/components/Copyright/index.tsx diff --git a/src/components/File/index.tsx b/apps/hudozka/src/components/File/index.tsx similarity index 95% rename from src/components/File/index.tsx rename to apps/hudozka/src/components/File/index.tsx index 58ed5f02..38a72c0b 100644 --- a/src/components/File/index.tsx +++ b/apps/hudozka/src/components/File/index.tsx @@ -1,7 +1,7 @@ import s from "./styles.module.css" import type { FileDefinition } from "@/types" -import { ext, size } from "src/lib/file" +import { ext, size } from "@hudozka/utils" import Link from "next/link" import Image from "next/image" diff --git a/src/components/File/styles.module.css b/apps/hudozka/src/components/File/styles.module.css similarity index 100% rename from src/components/File/styles.module.css rename to apps/hudozka/src/components/File/styles.module.css diff --git a/src/components/FileCard/Signature.tsx b/apps/hudozka/src/components/FileCard/Signature.tsx similarity index 100% rename from src/components/FileCard/Signature.tsx rename to apps/hudozka/src/components/FileCard/Signature.tsx diff --git a/src/components/FileCard/filecard.module.css b/apps/hudozka/src/components/FileCard/filecard.module.css similarity index 100% rename from src/components/FileCard/filecard.module.css rename to apps/hudozka/src/components/FileCard/filecard.module.css diff --git a/src/components/FileCard/index.tsx b/apps/hudozka/src/components/FileCard/index.tsx similarity index 94% rename from src/components/FileCard/index.tsx rename to apps/hudozka/src/components/FileCard/index.tsx index a7302a03..e0550083 100644 --- a/src/components/FileCard/index.tsx +++ b/apps/hudozka/src/components/FileCard/index.tsx @@ -3,10 +3,9 @@ import s from "./filecard.module.css" import Image from "next/image" import dynamic from "next/dynamic" import type { FileTokenData, Sign } from "@/types" -import { size, ext } from "@/lib/file" -import { getResizedUrl } from "@/lib/image" +import { ext, getResizedUrl, size } from "@hudozka/utils" import Link from "next/link" -import { Box } from "@/ui/Box" +import { Box } from "@hudozka/ui" const Signature = dynamic(() => import("./Signature").then(m => ({ default: m.Signature })), { ssr: false, diff --git a/src/components/FileCard/signature.module.css b/apps/hudozka/src/components/FileCard/signature.module.css similarity index 100% rename from src/components/FileCard/signature.module.css rename to apps/hudozka/src/components/FileCard/signature.module.css diff --git a/src/components/Footer/footer.module.css b/apps/hudozka/src/components/Footer/footer.module.css similarity index 100% rename from src/components/Footer/footer.module.css rename to apps/hudozka/src/components/Footer/footer.module.css diff --git a/src/components/Footer/index.tsx b/apps/hudozka/src/components/Footer/index.tsx similarity index 100% rename from src/components/Footer/index.tsx rename to apps/hudozka/src/components/Footer/index.tsx diff --git a/src/components/Gosuslugi/index.tsx b/apps/hudozka/src/components/Gosuslugi/index.tsx similarity index 100% rename from src/components/Gosuslugi/index.tsx rename to apps/hudozka/src/components/Gosuslugi/index.tsx diff --git a/src/components/Gosuslugi/lib.ts b/apps/hudozka/src/components/Gosuslugi/lib.ts similarity index 100% rename from src/components/Gosuslugi/lib.ts rename to apps/hudozka/src/components/Gosuslugi/lib.ts diff --git a/src/components/HomeContent/index.tsx b/apps/hudozka/src/components/HomeContent/index.tsx similarity index 93% rename from src/components/HomeContent/index.tsx rename to apps/hudozka/src/components/HomeContent/index.tsx index 70a67c2f..0760c1cb 100644 --- a/src/components/HomeContent/index.tsx +++ b/apps/hudozka/src/components/HomeContent/index.tsx @@ -2,7 +2,7 @@ import { HudozkaTitle } from "@/components/HudozkaTitle" import { PageGrid } from "@/components/PageGrid" -import { useMobile } from "@/hooks/useMobile" +import { useMobile } from "@hudozka/hooks" import type { PageCardDto } from "@/types" export type HomeContentProps = { diff --git a/src/components/Html/index.tsx b/apps/hudozka/src/components/Html/index.tsx similarity index 100% rename from src/components/Html/index.tsx rename to apps/hudozka/src/components/Html/index.tsx diff --git a/src/components/HudozkaLogo/index.tsx b/apps/hudozka/src/components/HudozkaLogo/index.tsx similarity index 100% rename from src/components/HudozkaLogo/index.tsx rename to apps/hudozka/src/components/HudozkaLogo/index.tsx diff --git a/src/components/HudozkaLogo/logo.module.css b/apps/hudozka/src/components/HudozkaLogo/logo.module.css similarity index 100% rename from src/components/HudozkaLogo/logo.module.css rename to apps/hudozka/src/components/HudozkaLogo/logo.module.css diff --git a/src/components/HudozkaTitle/index.tsx b/apps/hudozka/src/components/HudozkaTitle/index.tsx similarity index 100% rename from src/components/HudozkaTitle/index.tsx rename to apps/hudozka/src/components/HudozkaTitle/index.tsx diff --git a/src/components/HudozkaTitle/title.module.css b/apps/hudozka/src/components/HudozkaTitle/title.module.css similarity index 100% rename from src/components/HudozkaTitle/title.module.css rename to apps/hudozka/src/components/HudozkaTitle/title.module.css diff --git a/src/components/Menu/ActiveLink.tsx b/apps/hudozka/src/components/Menu/ActiveLink.tsx similarity index 100% rename from src/components/Menu/ActiveLink.tsx rename to apps/hudozka/src/components/Menu/ActiveLink.tsx diff --git a/src/components/Menu/index.tsx b/apps/hudozka/src/components/Menu/index.tsx similarity index 100% rename from src/components/Menu/index.tsx rename to apps/hudozka/src/components/Menu/index.tsx diff --git a/src/components/Menu/styles.module.css b/apps/hudozka/src/components/Menu/styles.module.css similarity index 100% rename from src/components/Menu/styles.module.css rename to apps/hudozka/src/components/Menu/styles.module.css diff --git a/src/components/Navigation/DesktopNavigation.tsx b/apps/hudozka/src/components/Navigation/DesktopNavigation.tsx similarity index 95% rename from src/components/Navigation/DesktopNavigation.tsx rename to apps/hudozka/src/components/Navigation/DesktopNavigation.tsx index 76e796d8..8b3e468e 100644 --- a/src/components/Navigation/DesktopNavigation.tsx +++ b/apps/hudozka/src/components/Navigation/DesktopNavigation.tsx @@ -1,5 +1,5 @@ import { Menu } from "../Menu" -import { Box } from "@/ui/Box" +import { Box } from "@hudozka/ui" import HudozkaLogo from "../HudozkaLogo" import Link from "next/link" import { AccessibilityButton } from "../AccessibilityButton" diff --git a/src/components/Navigation/MobileNavigation.tsx b/apps/hudozka/src/components/Navigation/MobileNavigation.tsx similarity index 98% rename from src/components/Navigation/MobileNavigation.tsx rename to apps/hudozka/src/components/Navigation/MobileNavigation.tsx index e9ba4098..ddf79d30 100644 --- a/src/components/Navigation/MobileNavigation.tsx +++ b/apps/hudozka/src/components/Navigation/MobileNavigation.tsx @@ -4,7 +4,7 @@ import s from "./nav.module.css" import { Menu } from "../Menu" import { useCallback, useEffect, useRef } from "react" -import { Box } from "@/ui/Box" +import { Box } from "@hudozka/ui" import { Spacer } from "../Spacer" import HudozkaLogo from "../HudozkaLogo" import { HudozkaTitle } from "../HudozkaTitle" diff --git a/src/components/Navigation/index.tsx b/apps/hudozka/src/components/Navigation/index.tsx similarity index 100% rename from src/components/Navigation/index.tsx rename to apps/hudozka/src/components/Navigation/index.tsx diff --git a/src/components/Navigation/nav.module.css b/apps/hudozka/src/components/Navigation/nav.module.css similarity index 100% rename from src/components/Navigation/nav.module.css rename to apps/hudozka/src/components/Navigation/nav.module.css diff --git a/src/components/NotFound/error.module.css b/apps/hudozka/src/components/NotFound/error.module.css similarity index 100% rename from src/components/NotFound/error.module.css rename to apps/hudozka/src/components/NotFound/error.module.css diff --git a/src/components/NotFound/index.tsx b/apps/hudozka/src/components/NotFound/index.tsx similarity index 100% rename from src/components/NotFound/index.tsx rename to apps/hudozka/src/components/NotFound/index.tsx diff --git a/src/components/Page/index.tsx b/apps/hudozka/src/components/Page/index.tsx similarity index 100% rename from src/components/Page/index.tsx rename to apps/hudozka/src/components/Page/index.tsx diff --git a/src/components/Page/styles.module.css b/apps/hudozka/src/components/Page/styles.module.css similarity index 100% rename from src/components/Page/styles.module.css rename to apps/hudozka/src/components/Page/styles.module.css diff --git a/src/components/PageContent/index.tsx b/apps/hudozka/src/components/PageContent/index.tsx similarity index 100% rename from src/components/PageContent/index.tsx rename to apps/hudozka/src/components/PageContent/index.tsx diff --git a/src/components/PageGrid/Date.tsx b/apps/hudozka/src/components/PageGrid/Date.tsx similarity index 86% rename from src/components/PageGrid/Date.tsx rename to apps/hudozka/src/components/PageGrid/Date.tsx index 0f6a2823..52c05189 100644 --- a/src/components/PageGrid/Date.tsx +++ b/apps/hudozka/src/components/PageGrid/Date.tsx @@ -1,6 +1,6 @@ import s from "./date.module.css" -import { dateFormat } from "src/lib/date" +import { dateFormat } from "@hudozka/utils" export type DateProps = { style?: React.CSSProperties diff --git a/src/components/PageGrid/date.module.css b/apps/hudozka/src/components/PageGrid/date.module.css similarity index 100% rename from src/components/PageGrid/date.module.css rename to apps/hudozka/src/components/PageGrid/date.module.css diff --git a/src/components/PageGrid/index.tsx b/apps/hudozka/src/components/PageGrid/index.tsx similarity index 96% rename from src/components/PageGrid/index.tsx rename to apps/hudozka/src/components/PageGrid/index.tsx index dacb6a68..5c6e1bf0 100644 --- a/src/components/PageGrid/index.tsx +++ b/apps/hudozka/src/components/PageGrid/index.tsx @@ -5,8 +5,8 @@ import { Card } from "../Card" import type { CardLayout } from "../Card" import { Date } from "./Date" import { Spacer } from "../Spacer" -import { Box } from "@/ui/Box" -import { useMobile } from "@/hooks/useMobile" +import { Box } from "@hudozka/ui" +import { useMobile } from "@hudozka/hooks" function itemColumn(item: PageCardDto): string { return item.featured ? "span 2" : "auto" diff --git a/src/components/PageMeta/index.tsx b/apps/hudozka/src/components/PageMeta/index.tsx similarity index 91% rename from src/components/PageMeta/index.tsx rename to apps/hudozka/src/components/PageMeta/index.tsx index fc2a0274..3b6e2ecf 100644 --- a/src/components/PageMeta/index.tsx +++ b/apps/hudozka/src/components/PageMeta/index.tsx @@ -1,11 +1,11 @@ import s from "./styles.module.css" import cx from "classnames" -import { dateFormat } from "@/lib/date" +import { dateFormat } from "@hudozka/utils" import type { Tag } from "@/types" import { TagList } from "../TagList" import type { Direction } from "../TagList" -import { useMobile } from "@/hooks/useMobile" +import { useMobile } from "@hudozka/hooks" export type PageMetaProps = { style?: React.CSSProperties diff --git a/src/components/PageMeta/styles.module.css b/apps/hudozka/src/components/PageMeta/styles.module.css similarity index 100% rename from src/components/PageMeta/styles.module.css rename to apps/hudozka/src/components/PageMeta/styles.module.css diff --git a/src/components/Share/index.tsx b/apps/hudozka/src/components/Share/index.tsx similarity index 100% rename from src/components/Share/index.tsx rename to apps/hudozka/src/components/Share/index.tsx diff --git a/src/components/Spacer/index.tsx b/apps/hudozka/src/components/Spacer/index.tsx similarity index 100% rename from src/components/Spacer/index.tsx rename to apps/hudozka/src/components/Spacer/index.tsx diff --git a/src/components/TagList/index.tsx b/apps/hudozka/src/components/TagList/index.tsx similarity index 96% rename from src/components/TagList/index.tsx rename to apps/hudozka/src/components/TagList/index.tsx index 1be90aae..85f1f48a 100644 --- a/src/components/TagList/index.tsx +++ b/apps/hudozka/src/components/TagList/index.tsx @@ -2,7 +2,7 @@ import s from "./styles.module.css" import cx from "classnames" import type { Tag } from "@/types" -import { Button } from "@/ui/Button" +import { Button } from "@hudozka/ui" const directionClass = { horizontal: "", diff --git a/src/components/TagList/styles.module.css b/apps/hudozka/src/components/TagList/styles.module.css similarity index 100% rename from src/components/TagList/styles.module.css rename to apps/hudozka/src/components/TagList/styles.module.css diff --git a/src/components/Wrapper/index.tsx b/apps/hudozka/src/components/Wrapper/index.tsx similarity index 100% rename from src/components/Wrapper/index.tsx rename to apps/hudozka/src/components/Wrapper/index.tsx diff --git a/src/components/Wrapper/wrapper.module.css b/apps/hudozka/src/components/Wrapper/wrapper.module.css similarity index 100% rename from src/components/Wrapper/wrapper.module.css rename to apps/hudozka/src/components/Wrapper/wrapper.module.css diff --git a/src/components/YMetrika.test.ts b/apps/hudozka/src/components/YMetrika.test.ts similarity index 100% rename from src/components/YMetrika.test.ts rename to apps/hudozka/src/components/YMetrika.test.ts diff --git a/src/components/YMetrika.tsx b/apps/hudozka/src/components/YMetrika.tsx similarity index 100% rename from src/components/YMetrika.tsx rename to apps/hudozka/src/components/YMetrika.tsx diff --git a/src/components/Youtube/index.tsx b/apps/hudozka/src/components/Youtube/index.tsx similarity index 100% rename from src/components/Youtube/index.tsx rename to apps/hudozka/src/components/Youtube/index.tsx diff --git a/src/const.ts b/apps/hudozka/src/const.ts similarity index 100% rename from src/const.ts rename to apps/hudozka/src/const.ts diff --git a/src/global.d.ts b/apps/hudozka/src/global.d.ts similarity index 100% rename from src/global.d.ts rename to apps/hudozka/src/global.d.ts diff --git a/src/hooks/useAccessibility.ts b/apps/hudozka/src/hooks/useAccessibility.ts similarity index 100% rename from src/hooks/useAccessibility.ts rename to apps/hudozka/src/hooks/useAccessibility.ts diff --git a/src/hooks/useDarkTheme.ts b/apps/hudozka/src/hooks/useDarkTheme.ts similarity index 65% rename from src/hooks/useDarkTheme.ts rename to apps/hudozka/src/hooks/useDarkTheme.ts index 14d4c81c..4b98bac2 100644 --- a/src/hooks/useDarkTheme.ts +++ b/apps/hudozka/src/hooks/useDarkTheme.ts @@ -1,4 +1,4 @@ -import { useMediaQuery } from "@/hooks/useMediaQuery" +import { useMediaQuery } from "@hudozka/hooks" export function useDarkTheme(): boolean { return useMediaQuery("(prefers-color-scheme: dark)") diff --git a/src/lib/meta.test.ts b/apps/hudozka/src/lib/meta.test.ts similarity index 100% rename from src/lib/meta.test.ts rename to apps/hudozka/src/lib/meta.test.ts diff --git a/src/lib/meta.ts b/apps/hudozka/src/lib/meta.ts similarity index 100% rename from src/lib/meta.ts rename to apps/hudozka/src/lib/meta.ts diff --git a/src/play.ts b/apps/hudozka/src/play.ts similarity index 100% rename from src/play.ts rename to apps/hudozka/src/play.ts diff --git a/src/remote/api.test.ts b/apps/hudozka/src/remote/api.test.ts similarity index 100% rename from src/remote/api.test.ts rename to apps/hudozka/src/remote/api.test.ts diff --git a/src/remote/api.ts b/apps/hudozka/src/remote/api.ts similarity index 100% rename from src/remote/api.ts rename to apps/hudozka/src/remote/api.ts diff --git a/src/remote/doc.ts b/apps/hudozka/src/remote/doc.ts similarity index 100% rename from src/remote/doc.ts rename to apps/hudozka/src/remote/doc.ts diff --git a/src/remote/factory.test.ts b/apps/hudozka/src/remote/factory.test.ts similarity index 100% rename from src/remote/factory.test.ts rename to apps/hudozka/src/remote/factory.test.ts diff --git a/src/remote/factory.ts b/apps/hudozka/src/remote/factory.ts similarity index 99% rename from src/remote/factory.ts rename to apps/hudozka/src/remote/factory.ts index c76afdec..601ff6c5 100644 --- a/src/remote/factory.ts +++ b/apps/hudozka/src/remote/factory.ts @@ -1,7 +1,7 @@ import type { MenuItem, Page, Tag, PageCardDto, Pic, Token, FeedPage } from "@/types" import type { PbPage, PbImage, PbFile, PbTag, PbHomeData, PbMenuData } from "./types" import type { DocV1Block } from "./doc" -import { typograf, markdownToHtml } from "@/lib/text" +import { typograf, markdownToHtml } from "@hudozka/text" import { pb } from "./pb" const md = (text: string) => typograf(markdownToHtml(text)) diff --git a/src/remote/pb.ts b/apps/hudozka/src/remote/pb.ts similarity index 100% rename from src/remote/pb.ts rename to apps/hudozka/src/remote/pb.ts diff --git a/src/remote/types.ts b/apps/hudozka/src/remote/types.ts similarity index 100% rename from src/remote/types.ts rename to apps/hudozka/src/remote/types.ts diff --git a/src/store/play.ts b/apps/hudozka/src/store/play.ts similarity index 100% rename from src/store/play.ts rename to apps/hudozka/src/store/play.ts diff --git a/src/store/theme.ts b/apps/hudozka/src/store/theme.ts similarity index 98% rename from src/store/theme.ts rename to apps/hudozka/src/store/theme.ts index 3b40d10c..bd044922 100644 --- a/src/store/theme.ts +++ b/apps/hudozka/src/store/theme.ts @@ -1,5 +1,5 @@ import { proxy, subscribe } from "valtio" -import { clamp } from "@/lib/math" +import { clamp } from "@hudozka/utils" export type ThemeName = "default" | "contrast" export type ColorSchemeVariant = "black-on-white" | "white-on-black" | "blue-on-blue" | "brown-on-yellow" | "green-on-brown" diff --git a/src/style/article.css b/apps/hudozka/src/style/article.css similarity index 100% rename from src/style/article.css rename to apps/hudozka/src/style/article.css diff --git a/src/style/content.css b/apps/hudozka/src/style/content.css similarity index 100% rename from src/style/content.css rename to apps/hudozka/src/style/content.css diff --git a/src/style/kazimir/video.css b/apps/hudozka/src/style/kazimir/video.css similarity index 100% rename from src/style/kazimir/video.css rename to apps/hudozka/src/style/kazimir/video.css diff --git a/src/style/marker/marker-table.css b/apps/hudozka/src/style/marker/marker-table.css similarity index 100% rename from src/style/marker/marker-table.css rename to apps/hudozka/src/style/marker/marker-table.css diff --git a/src/style/style.css b/apps/hudozka/src/style/style.css similarity index 100% rename from src/style/style.css rename to apps/hudozka/src/style/style.css diff --git a/src/style/tape-viewer.scss b/apps/hudozka/src/style/tape-viewer.scss similarity index 100% rename from src/style/tape-viewer.scss rename to apps/hudozka/src/style/tape-viewer.scss diff --git a/src/style/vertical-image.scss b/apps/hudozka/src/style/vertical-image.scss similarity index 100% rename from src/style/vertical-image.scss rename to apps/hudozka/src/style/vertical-image.scss diff --git a/src/types.ts b/apps/hudozka/src/types.ts similarity index 100% rename from src/types.ts rename to apps/hudozka/src/types.ts diff --git a/src/ui/Picture/index.tsx b/apps/hudozka/src/ui/Picture/index.tsx similarity index 100% rename from src/ui/Picture/index.tsx rename to apps/hudozka/src/ui/Picture/index.tsx diff --git a/src/ui/Picture/lib.tsx b/apps/hudozka/src/ui/Picture/lib.tsx similarity index 100% rename from src/ui/Picture/lib.tsx rename to apps/hudozka/src/ui/Picture/lib.tsx diff --git a/src/ui/Picture/picture.module.css b/apps/hudozka/src/ui/Picture/picture.module.css similarity index 100% rename from src/ui/Picture/picture.module.css rename to apps/hudozka/src/ui/Picture/picture.module.css diff --git a/src/ui/ThemeColor/index.tsx b/apps/hudozka/src/ui/ThemeColor/index.tsx similarity index 100% rename from src/ui/ThemeColor/index.tsx rename to apps/hudozka/src/ui/ThemeColor/index.tsx diff --git a/apps/hudozka/tsconfig.json b/apps/hudozka/tsconfig.json new file mode 100644 index 00000000..1640885e --- /dev/null +++ b/apps/hudozka/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + "ignoreDeprecations": "6.0", + "plugins": [ + { + "name": "next" + } + ] + }, + "exclude": [ + "node_modules" + ], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ] +} diff --git a/vitest.config.ts b/apps/hudozka/vitest.config.ts similarity index 100% rename from vitest.config.ts rename to apps/hudozka/vitest.config.ts diff --git a/vitest.setup.tsx b/apps/hudozka/vitest.setup.tsx similarity index 100% rename from vitest.setup.tsx rename to apps/hudozka/vitest.setup.tsx diff --git a/modules/migrate-to-pocketbase/.env.example b/apps/migrate-to-pocketbase/.env.example similarity index 100% rename from modules/migrate-to-pocketbase/.env.example rename to apps/migrate-to-pocketbase/.env.example diff --git a/modules/migrate-to-pocketbase/.gitignore b/apps/migrate-to-pocketbase/.gitignore similarity index 100% rename from modules/migrate-to-pocketbase/.gitignore rename to apps/migrate-to-pocketbase/.gitignore diff --git a/modules/migrate-to-pocketbase/README.md b/apps/migrate-to-pocketbase/README.md similarity index 100% rename from modules/migrate-to-pocketbase/README.md rename to apps/migrate-to-pocketbase/README.md diff --git a/modules/migrate-to-pocketbase/package-lock.json b/apps/migrate-to-pocketbase/package-lock.json similarity index 100% rename from modules/migrate-to-pocketbase/package-lock.json rename to apps/migrate-to-pocketbase/package-lock.json diff --git a/modules/migrate-to-pocketbase/package.json b/apps/migrate-to-pocketbase/package.json similarity index 88% rename from modules/migrate-to-pocketbase/package.json rename to apps/migrate-to-pocketbase/package.json index 18a2a5c5..11d23511 100644 --- a/modules/migrate-to-pocketbase/package.json +++ b/apps/migrate-to-pocketbase/package.json @@ -11,5 +11,8 @@ "nanoid": "^5.0.0", "pocketbase": "^0.26.0", "sharp": "^0.33.0" + }, + "devDependencies": { + "@types/node": "^25.0.0" } } diff --git a/modules/migrate-to-pocketbase/src/blurhash.ts b/apps/migrate-to-pocketbase/src/blurhash.ts similarity index 100% rename from modules/migrate-to-pocketbase/src/blurhash.ts rename to apps/migrate-to-pocketbase/src/blurhash.ts diff --git a/modules/migrate-to-pocketbase/src/download.ts b/apps/migrate-to-pocketbase/src/download.ts similarity index 100% rename from modules/migrate-to-pocketbase/src/download.ts rename to apps/migrate-to-pocketbase/src/download.ts diff --git a/modules/migrate-to-pocketbase/src/hash.ts b/apps/migrate-to-pocketbase/src/hash.ts similarity index 100% rename from modules/migrate-to-pocketbase/src/hash.ts rename to apps/migrate-to-pocketbase/src/hash.ts diff --git a/modules/migrate-to-pocketbase/src/index.ts b/apps/migrate-to-pocketbase/src/index.ts similarity index 100% rename from modules/migrate-to-pocketbase/src/index.ts rename to apps/migrate-to-pocketbase/src/index.ts diff --git a/modules/migrate-to-pocketbase/src/strapi.ts b/apps/migrate-to-pocketbase/src/strapi.ts similarity index 100% rename from modules/migrate-to-pocketbase/src/strapi.ts rename to apps/migrate-to-pocketbase/src/strapi.ts diff --git a/modules/migrate-to-pocketbase/src/wipe.ts b/apps/migrate-to-pocketbase/src/wipe.ts similarity index 100% rename from modules/migrate-to-pocketbase/src/wipe.ts rename to apps/migrate-to-pocketbase/src/wipe.ts diff --git a/apps/migrate-to-pocketbase/tsconfig.json b/apps/migrate-to-pocketbase/tsconfig.json new file mode 100644 index 00000000..c9b2f0a1 --- /dev/null +++ b/apps/migrate-to-pocketbase/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "lib": ["ES2023"], + "types": ["node"] + }, + "include": ["src"] +} diff --git a/docs/plans/20260424-monorepo.md b/docs/plans/20260424-monorepo.md new file mode 100644 index 00000000..39cb314f --- /dev/null +++ b/docs/plans/20260424-monorepo.md @@ -0,0 +1,1727 @@ +# Monorepo Migration — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Restructure hudozka into an npm-workspaces monorepo: move the Next app + writer + migrate script under `apps/`, extract shared UI / hooks / utils / text into `packages/` as `@hudozka/*`, keep `pb/` untouched. + +**Architecture:** npm workspaces, no build step for packages (TypeScript source consumed directly via `"exports": { ".": "./src/index.ts" }`), single root `eslint.config.js`, single `tsconfig.base.json`. No Turborepo. Spec: `docs/specs/20260424-monorepo-design.md`. + +**Tech Stack:** npm workspaces (npm 10+ / Node 24), Next.js 16, Vite 8 (writer), Node `--experimental-strip-types` (migrate), Vitest 4, ESLint 9 flat config, TypeScript 6. + +--- + +## Conventions used in this plan + +- **Repo root:** `/Users/tmshv/Workspace/__github_tmshv/hudozka`. All paths below are relative to this root unless absolute. +- **Working directory:** every `cd` command in this plan is idempotent — you can always `cd ` at the start of a step. +- **macOS sed:** `sed -i ''` (BSD flavor). The user's environment is Darwin. +- **Commit messages:** one-line only (user rule). No body, no trailer, no co-author. +- **`git mv`** used for every relocation so history is preserved. +- **Verification gates:** after each phase there is a `Gate` step that must pass before starting the next phase. + +--- + +## Phase 1 — Workspace skeleton + +Goal: repo recognises `apps/*` and `packages/*` as workspaces; `npm install` still works; nothing else changed. + +### Task 1.1: Create workspace directories + +**Files:** +- Create: `apps/.gitkeep` +- Create: `packages/.gitkeep` + +- [x] **Step 1:** Create the directories with placeholder files. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +mkdir -p apps packages +touch apps/.gitkeep packages/.gitkeep +``` + +- [x] **Step 2:** Verify directories exist. + +```bash +ls apps packages +``` +Expected: `.gitkeep` in each. + +- [x] **Step 3:** Commit. + +```bash +git add apps/.gitkeep packages/.gitkeep +git commit -m "Add empty apps and packages directories" +``` + +### Task 1.2: Create `tsconfig.base.json` + +**Files:** +- Create: `tsconfig.base.json` + +- [x] **Step 1:** Write the shared base config. + +```json +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["dom", "dom.iterable", "esnext"], + "module": "esnext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "allowJs": true, + "incremental": true + } +} +``` + +- [x] **Step 2:** Commit. + +```bash +git add tsconfig.base.json +git commit -m "Add shared tsconfig.base.json" +``` + +### Task 1.3: Add `workspaces` to root `package.json` + +**Files:** +- Modify: `package.json` + +- [x] **Step 1:** Add a top-level `"workspaces"` array right after `"type": "module"`. + +Edit `package.json` — insert this line after the `"type": "module",` line: + +```json + "workspaces": ["apps/*", "packages/*"], +``` + +Resulting top of `package.json`: + +```json +{ + "name": "hudozka", + "private": true, + "version": "7.0.0", + "type": "module", + "workspaces": ["apps/*", "packages/*"], + "description": "", + "scripts": { + ... +``` + +- [x] **Step 2:** Run `npm install` to verify npm accepts the workspaces config. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +npm install +``` +Expected: completes without errors. `node_modules/` is refreshed; no new workspace packages picked up yet (empty `apps/` and `packages/` — the `.gitkeep`s are ignored because they aren't packages). + +- [x] **Step 3:** Commit. + +```bash +git add package.json package-lock.json +git commit -m "Declare apps and packages as npm workspaces" +``` + +### Phase 1 Gate + +- [x] Run the full local pipeline at repo root: + +```bash +npm install +npm run lint +npm test +npm run build +``` +Expected: all four succeed. Behaviour identical to pre-migration. + +Notes: `npm install`, `npm test`, and `npm run build` all succeeded. `npm run lint` has pre-existing errors in `pb/pb_data/types.d.ts` (the root `eslint.config.js` globally ignores `pb_data/**` at the root level, not `pb/pb_data/**`). These failures existed before the workspaces change — verified by stashing and re-running. Phase 6 fixes the ESLint scope for `pb/` paths. + +--- + +## Phase 2 — Relocate `hudozka-writer` and `migrate-to-pocketbase` to `apps/` + +Goal: both modules live under `apps/`; their existing scripts still run; they participate in the workspace graph. + +### Task 2.1: Move `hudozka-writer` + +**Files:** +- Move: `modules/hudozka-writer/` → `apps/hudozka-writer/` +- Modify: `apps/hudozka-writer/tsconfig.json` (after move) + +- [x] **Step 1:** Move the directory. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +git mv modules/hudozka-writer apps/hudozka-writer +``` + +- [x] **Step 2:** Update `apps/hudozka-writer/tsconfig.json` to extend the base. Replace its entire contents with: + +```json +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "allowImportingTsExtensions": true, + "isolatedModules": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src", "vite.config.ts"] +} +``` + +- [x] **Step 3:** Install so the workspace picks up the new app. + +```bash +npm install +``` +Expected: npm symlinks `apps/hudozka-writer` into the workspace graph; its own `node_modules` from before may be consolidated into the root. + +- [x] **Step 4:** Verify the writer still starts. + +```bash +npm run dev -w apps/hudozka-writer +``` +Expected: Vite dev server starts (default port 5173). Stop with Ctrl+C. + +- [x] **Step 5:** Commit. + +```bash +git add apps/hudozka-writer package.json package-lock.json +git commit -m "Move hudozka-writer into apps/" +``` + +### Task 2.2: Move `migrate-to-pocketbase` + +**Files:** +- Move: `modules/migrate-to-pocketbase/` → `apps/migrate-to-pocketbase/` +- Create: `apps/migrate-to-pocketbase/tsconfig.json` + +- [x] **Step 1:** Move the directory. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +git mv modules/migrate-to-pocketbase apps/migrate-to-pocketbase +``` + +- [x] **Step 2:** Create `apps/migrate-to-pocketbase/tsconfig.json` (did not exist before; helps editor tooling): + +```json +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "lib": ["ES2023"], + "types": ["node"] + }, + "include": ["src"] +} +``` + +- [x] **Step 3:** Add `@types/node` to the app if not already hoisted; the root has `@types/node` but declare it locally to be explicit. + +Edit `apps/migrate-to-pocketbase/package.json` — add a `devDependencies` block: + +```json + "devDependencies": { + "@types/node": "^25.0.0" + } +``` + +- [x] **Step 4:** Install. + +```bash +npm install +``` + +- [x] **Step 5:** Sanity-check the script still loads (dry-run without `.env`). This must not crash on startup before complaining about env vars. + +```bash +npm run migrate -w apps/migrate-to-pocketbase --dry-run 2>&1 | head -20 || true +``` +Expected: either the script runs normally (if `.env` is present) or errors on a missing env var — NOT on a module-resolution issue. + +- [x] **Step 6:** Remove empty `modules/` directory. + +```bash +rmdir /Users/tmshv/Workspace/__github_tmshv/hudozka/modules +``` +If `rmdir` fails because the directory is not empty, run `ls modules/` — anything left is unintentional, investigate. + +- [x] **Step 7:** Commit. + +```bash +git add apps/migrate-to-pocketbase package.json package-lock.json +git commit -m "Move migrate-to-pocketbase into apps/" +``` + +Notes: The `.env` file was present so the migrate script ran as far as PocketBase validation errors (existing records), confirming module resolution works. Also had to exclude `apps/hudozka-writer` and `apps/migrate-to-pocketbase` from root `tsconfig.json` because the Next build's TypeScript pass otherwise picked up their sources (which use `.ts` extension imports requiring `allowImportingTsExtensions`). The root tsconfig is replaced entirely in Phase 3, so this is a temporary bridge. + +### Phase 2 Gate + +- [x] Verify the whole repo still installs, lints, tests, and builds. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +npm install +npm run lint +npm test +npm run build +``` +Expected: all pass. Note that `npm run lint` still uses the root `eslint.config.js` which currently globally-ignores `modules/**` — since `modules/` no longer exists that ignore is a no-op now but harmless. It will be cleaned up in Phase 6. + +Notes: `npm install`, `npm test`, and `npm run build` all succeeded. `npm run lint` still has the same pre-existing errors documented in Phase 1 Gate (pb/pb_data, pb/pb_migrations, hudozka-writer dist artifact, and two new doublequote warnings in `apps/migrate-to-pocketbase/src/index.ts`). All tracked for Phase 6 ESLint consolidation. Root `tsconfig.json` was updated to exclude the relocated apps from the Next build's TypeScript pass. + +--- + +## Phase 3 — Relocate Next app to `apps/hudozka` + +Goal: all Next artifacts live under `apps/hudozka/`; the app builds, tests, and runs exactly as before; Dockerfile and CI reference the new paths. + +### Task 3.1: Create `apps/hudozka` skeleton and move source + config files + +**Files:** +- Create: `apps/hudozka/` (directory) +- Move: the following from repo root into `apps/hudozka/`: + - `src/` (entire directory — includes `src/app/`, `src/components/`, etc.) + - `public/` + - `next.config.ts` + - `next-env.d.ts` + - `tsconfig.json` + - `vitest.config.ts` + - `vitest.setup.tsx` + - `Dockerfile` + - `.dockerignore` + - `.env.example` + +- [x] **Step 1:** Create the app directory. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +mkdir -p apps/hudozka +``` + +- [x] **Step 2:** Move source directories. + +```bash +git mv src apps/hudozka/src +git mv public apps/hudozka/public +``` + +- [x] **Step 3:** Move config files. + +```bash +git mv next.config.ts apps/hudozka/next.config.ts +git mv next-env.d.ts apps/hudozka/next-env.d.ts +git mv tsconfig.json apps/hudozka/tsconfig.json +git mv vitest.config.ts apps/hudozka/vitest.config.ts +git mv vitest.setup.tsx apps/hudozka/vitest.setup.tsx +git mv Dockerfile apps/hudozka/Dockerfile +git mv .dockerignore apps/hudozka/.dockerignore +git mv .env.example apps/hudozka/.env.example +``` + +Note: `.env.local` (if present) is typically untracked; move it too if it exists: + +```bash +test -f .env.local && mv .env.local apps/hudozka/.env.local || true +``` + +- [x] **Step 4:** Delete build artifacts from root — they'll regenerate under `apps/hudozka/` after the next build. + +```bash +rm -rf .next +rm -f tsconfig.tsbuildinfo +``` + +- [x] **Step 5:** Do **not** move `eslint.config.js` yet — the web app needs to keep linting in Phase 3 while the config still lives at the root. Phase 6 moves it properly. + +- [x] **Step 6:** Do **not** commit yet; continue to Task 3.2 which splits `package.json` (same phase, one commit at the end is cleaner because `package.json` changes and file moves land together). (Note: the external loop driving this plan commits one task per iteration, so these moves land as an intermediate commit here; Task 3.2 will follow with the package.json split.) + +### Task 3.2: Split `package.json` (root → app) + +**Files:** +- Modify: `package.json` (root) — keep workspaces + shared dev tooling +- Create: `apps/hudozka/package.json` — runtime deps + app scripts + +- [x] **Step 1:** Create `apps/hudozka/package.json` with the runtime deps and scripts previously at root: + +```json +{ + "name": "hudozka", + "private": true, + "version": "7.0.0", + "type": "module", + "description": "", + "scripts": { + "start": "next start", + "dev": "next dev --turbo", + "build": "next build", + "lint": "eslint .", + "format": "eslint --fix .", + "test": "vitest run" + }, + "repository": { + "type": "git", + "url": "https://github.com/tmshv/hudozka" + }, + "author": "Roman Timashev", + "license": "ISC", + "dependencies": { + "@radix-ui/react-navigation-menu": "^1.2.0", + "@radix-ui/react-tooltip": "^1.2.0", + "classnames": "^2.5.0", + "dangerously-set-html-content": "^1.1.0", + "date-fns": "^4.0.0", + "markdown-it": "^14.1.1", + "next": "^16.2.0", + "pocketbase": "^0.26.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-icons": "^5.5.0", + "sharp": "^0.34.0", + "typograf": "^7.6.0", + "valtio": "^2.3.0" + }, + "overrides": { + "dangerously-set-html-content": { + "react": "$react" + } + }, + "devDependencies": { + "@types/markdown-it": "^14.1.0" + } +} +``` + +Notes: +- `classnames`, `date-fns`, `markdown-it`, `typograf` stay here for now — Phase 4 and 5 move them to their respective packages. +- `@types/markdown-it` is a dev dep specific to the web app until Phase 5 moves text handling. + +- [x] **Step 2:** Replace root `package.json` with a workspace-only version: + +```json +{ + "name": "hudozka-monorepo", + "private": true, + "version": "7.0.0", + "type": "module", + "description": "", + "workspaces": ["apps/*", "packages/*"], + "scripts": { + "dev": "npm run dev -w apps/hudozka", + "build": "npm run build -w apps/hudozka", + "start": "npm run start -w apps/hudozka", + "lint": "eslint .", + "format": "eslint --fix .", + "test": "npm test --workspaces --if-present" + }, + "repository": { + "type": "git", + "url": "https://github.com/tmshv/hudozka" + }, + "author": "Roman Timashev", + "license": "ISC", + "devDependencies": { + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@types/node": "^25.2.0", + "@types/react": "^19.2.0", + "@typescript-eslint/eslint-plugin": "^8.59.0", + "@typescript-eslint/parser": "^8.59.0", + "eslint": "^9.39.0", + "eslint-config-next": "^16.2.0", + "globals": "^17.5.0", + "happy-dom": "^20.9.0", + "typescript": "^6.0.0", + "vitest": "^4.1.0" + } +} +``` + +Why the name changes: npm requires unique names across the workspace; the web app now uses `"hudozka"` and the root is renamed to `"hudozka-monorepo"`. + +- [x] **Step 3:** Install — npm will re-wire deps, hoist shared dev tooling to root, install runtime deps inside `apps/hudozka/node_modules`. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +rm -rf node_modules package-lock.json +npm install +``` + +Why delete the lockfile: the split of deps between root and app changes dependency resolution enough that regenerating the lockfile is simpler than trying to reconcile. This is a one-time step during the phase split. + +Expected: `npm install` completes; `ls apps/hudozka/node_modules/next` exists (runtime dep symlinked). + +### Task 3.3: Fix Next `tsconfig.json` to extend base + +**Files:** +- Modify: `apps/hudozka/tsconfig.json` + +- [x] **Step 1:** Replace the file contents with: + +```json +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + "ignoreDeprecations": "6.0", + "plugins": [ + { + "name": "next" + } + ] + }, + "exclude": [ + "node_modules" + ], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ] +} +``` + +Why simpler: shared compiler options (target, lib, strict, verbatimModuleSyntax, moduleDetection, etc.) come from the base. + +Note: `baseUrl` is deprecated in TypeScript 6.0 but remains required here because legacy `src/...` imports in the codebase rely on it. `ignoreDeprecations: "6.0"` silences the deprecation error. When those legacy imports are migrated to `@/*`, baseUrl can be dropped. + +### Task 3.4: Update `vitest.config.ts` paths (inside the app) + +**Files:** +- Modify: `apps/hudozka/vitest.config.ts` + +- [x] **Step 1:** Current file references `__dirname` and `./vitest.setup.tsx` — both already resolve relative to the file location. After the move, paths still work because `src/` and `vitest.setup.tsx` moved with the config. No content change needed; just verify. + +```bash +cat /Users/tmshv/Workspace/__github_tmshv/hudozka/apps/hudozka/vitest.config.ts +``` +Expected: unchanged from pre-move. Paths resolve to `apps/hudozka/src/` and `apps/hudozka/vitest.setup.tsx`. + +Verified: `npm test -w apps/hudozka` passes — 19 test files, 94 tests, all green. + +### Task 3.5: Rewrite Dockerfile for workspace layout + +**Files:** +- Modify: `apps/hudozka/Dockerfile` + +- [x] **Step 1:** Replace contents with: + +```dockerfile +FROM node:24.12.0-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Copy workspace manifests (root + every workspace) to leverage npm ci caching. +COPY package.json package-lock.json ./ +COPY apps/hudozka/package.json ./apps/hudozka/ +COPY apps/hudozka-writer/package.json ./apps/hudozka-writer/ +COPY apps/migrate-to-pocketbase/package.json ./apps/migrate-to-pocketbase/ +COPY packages/ ./packages/ + +RUN npm ci +RUN npm install --os=linux --libc=musl --cpu=x64 sharp --workspace apps/hudozka + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY --from=deps /app/apps/hudozka/node_modules ./apps/hudozka/node_modules +COPY . . + +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN npm run build -w apps/hudozka + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder --chown=nextjs:nodejs /app/apps/hudozka/public ./apps/hudozka/public + +# Set the correct permission for prerender cache +RUN mkdir -p apps/hudozka/.next +RUN chown nextjs:nodejs apps/hudozka/.next + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/apps/hudozka/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/apps/hudozka/.next/static ./apps/hudozka/.next/static + +USER nextjs + +EXPOSE 3000 +ENV PORT=3000 + +# server.js is created by next build from the standalone output +# https://nextjs.org/docs/pages/api-reference/next-config-js/output +ENV HOSTNAME="0.0.0.0" +WORKDIR /app/apps/hudozka +CMD ["node", "server.js"] +``` + +Key changes vs. pre-migration: +- Manifests for all workspaces are copied so `npm ci` can resolve the full workspace graph. +- `packages/` is copied wholesale because packages are consumed as TS source — their `package.json` + `src/` files are needed at install and build time. +- `sharp` reinstall scoped to `apps/hudozka` via `--workspace`. +- Build command: `npm run build -w apps/hudozka`. +- Standalone layout in Next 13+ for monorepos emits the server under `apps/hudozka/.next/standalone/apps/hudozka/server.js`, which is why the final `WORKDIR` is `/app/apps/hudozka` and the `COPY --from=builder ... /app/apps/hudozka/.next/standalone ./` lands the entire tree including `node_modules`. +- `public/` copied to `./apps/hudozka/public` so Next finds it at runtime relative to `WORKDIR`. + +If Next's standalone trace doesn't produce `apps/hudozka/server.js` because of a monorepo detection issue, add `outputFileTracingRoot: path.join(__dirname, "../../")` to `apps/hudozka/next.config.ts` — see the verification step below. + +- [x] **Step 2:** Add `outputFileTracingRoot` to `next.config.ts` to tell Next this is a monorepo. + +Replace `apps/hudozka/next.config.ts` with: + +```ts +import type { NextConfig } from "next" +import path from "node:path" +import { fileURLToPath } from "node:url" + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +const config: NextConfig = { + output: "standalone", + outputFileTracingRoot: path.join(__dirname, "../../"), + trailingSlash: false, + images: { + remotePatterns: [ + { hostname: "hudozka.shlisselburg.org" }, + { hostname: "images.weserv.nl" }, + { hostname: "127.0.0.1" }, + ], + }, +} + +export default config +``` + +Why: without `outputFileTracingRoot`, Next detects the workspace root incorrectly (finds a wrong `package.json`) and the standalone output ends up in a non-deterministic layout. Explicitly pointing it at the repo root makes the standalone tree predictable: `apps/hudozka/.next/standalone/apps/hudozka/server.js`. + +Notes: the `import.meta.url`/`fileURLToPath` approach from the original draft caused Next's TS-config compiler to emit CJS (`exports = ...`) into a `.js` file inside an ESM package (`"type": "module"`), which fails at load time. Switched to `path.join(process.cwd(), "../../")` — `next build` runs with cwd = the app workspace directory, so this resolves to the repo root without touching ESM-only features. Verified: `npm run build` at root succeeds and `apps/hudozka/.next/standalone/apps/hudozka/server.js` is produced. + +### Task 3.6: Update `.github/workflows/build-app.yaml` + +**Files:** +- Modify: `.github/workflows/build-app.yaml` + +- [x] **Step 1:** Change the `Build and push` step. Find this block: + +```yaml + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: ./ + push: true + platforms: linux/amd64 + tags: | + ghcr.io/tmshv/hudozka-app:latest + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new +``` + +Replace with: + +```yaml + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: ./ + file: ./apps/hudozka/Dockerfile + push: true + platforms: linux/amd64 + tags: | + ghcr.io/tmshv/hudozka-app:latest + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new +``` + +Only change: add `file: ./apps/hudozka/Dockerfile`. Context stays `./` so the build has access to the whole monorepo. + +### Task 3.7: Update `.github/workflows/test-app.yml` — `typecheck` job + +**Files:** +- Modify: `.github/workflows/test-app.yml` + +- [x] **Step 1:** The `typecheck` job runs `npx tsc --noEmit` at repo root. There's no longer a root `tsconfig.json`. Scope it to the web app. + +Find: + +```yaml + - name: Type check + run: npx tsc --noEmit +``` + +Replace with: + +```yaml + - name: Type check + run: npx tsc --noEmit -p apps/hudozka/tsconfig.json +``` + +- [x] **Step 2:** `lint` and `test` jobs: root scripts now delegate correctly (`npm run lint` still works via `eslint .`; `npm test` uses `--workspaces --if-present`). No change needed. + +- [x] **Step 3:** `build` job: `npm run build` at root now delegates to `apps/hudozka` via the root script. No change needed. + +Notes: Verified locally that `npx tsc --noEmit -p apps/hudozka/tsconfig.json` succeeds. Root `package.json` scripts confirmed: `lint`=`eslint .`, `test`=`npm test --workspaces --if-present`, `build`=`npm run build -w apps/hudozka` — all delegate correctly without workflow changes. + +### Task 3.8: Verify and commit Phase 3 + +- [x] **Step 1:** Verify dev works. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +npm run dev +``` +Expected: Next dev server starts, serves the home page at `http://localhost:3000`. Stop with Ctrl+C. + +- [x] **Step 2:** Verify tests. + +```bash +npm test +``` +Expected: vitest runs for `apps/hudozka` and any other workspace with a test script. All pass. + +- [x] **Step 3:** Verify production build. + +```bash +npm run build +``` +Expected: Next produces a standalone build at `apps/hudozka/.next/standalone/apps/hudozka/server.js`. Verify: + +```bash +ls apps/hudozka/.next/standalone/apps/hudozka/server.js +``` + +- [x] **Step 4:** Commit everything. + +```bash +git add apps/hudozka .github/workflows/build-app.yaml .github/workflows/test-app.yml package.json package-lock.json +git commit -m "Move Next app into apps/hudozka" +``` + +Notes: Verified — `npm test` passes (94/94 tests across 19 files), `npm run build` succeeds and produces `apps/hudozka/.next/standalone/apps/hudozka/server.js`, and `npm run dev` started and served `http://localhost:3000` with HTTP 200. Most Phase 3 file moves and config splits were already committed atomically by earlier iterations of the loop; the remaining `.github/workflows/test-app.yml` typecheck change from Task 3.7 lands in this commit alongside the Task 3.8 plan updates. + +### Phase 3 Gate + +- [x] Confirm `npm install && npm run lint && npm test && npm run build` all pass at root. +- [x] Confirm `docker build -f apps/hudozka/Dockerfile -t hudozka:test .` succeeds locally (optional but recommended — catches Dockerfile regressions before CI). [skipped — not automatable in this loop; CI exercises the same Dockerfile via `.github/workflows/build-app.yaml` and the standalone output layout was already verified by Task 3.8 Step 3] + +Notes: `npm install`, `npm test`, and `npm run build` all pass. `npm run lint` reports the same pre-existing failures documented in Phase 1 and Phase 2 Gates (`apps/hudozka-writer/dist/` build artifact, `pb/pb_data/`, `pb/pb_migrations/`, plus the two doublequote warnings in `apps/migrate-to-pocketbase/src/index.ts`). All of these are in scope for Phase 6 ESLint consolidation, which rewrites the global ignores to scope `pb_data/**` and `pb_migrations/**` under `pb/` and excludes built artifacts. Not a regression introduced by Phase 3. + +--- + +## Phase 4 — Extract `@hudozka/utils` + +Goal: utility modules (`array`, `string`, `url`, `math`, `image`, `file`, `date`) live in `packages/utils/`; `apps/hudozka` imports them from `@hudozka/utils`; build and tests pass. + +### Task 4.1: Scaffold `packages/utils` + +**Files:** +- Create: `packages/utils/package.json` +- Create: `packages/utils/tsconfig.json` +- Create: `packages/utils/vitest.config.ts` +- Create: `packages/utils/src/` (directory) + +- [x] **Step 1:** Create `packages/utils/package.json`: + +```json +{ + "name": "@hudozka/utils", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "test": "vitest run" + }, + "dependencies": { + "date-fns": "^4.0.0" + } +} +``` + +- [x] **Step 2:** Create `packages/utils/tsconfig.json`: + +```json +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"] +} +``` + +- [x] **Step 3:** Create `packages/utils/vitest.config.ts` (no DOM environment — these are pure utility tests): + +```ts +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + include: ["src/**/*.test.ts"], + }, +}) +``` + +- [x] **Step 4:** Create the src directory. + +```bash +mkdir -p /Users/tmshv/Workspace/__github_tmshv/hudozka/packages/utils/src +``` + +Notes: the `test` script uses `vitest run --passWithNoTests` so the workspace aggregator tolerates the scaffold state between Task 4.1 and Task 4.2 (no test files yet). Task 4.2 moves `.test.ts` files in; once present, vitest's default exit behavior kicks in. A `.gitkeep` is placed inside `src/` so the empty directory is trackable until files arrive; Task 4.2's `git mv` will populate it and the `.gitkeep` can be removed in that task. + +### Task 4.2: Move utility files + +**Files:** +- Move: `apps/hudozka/src/lib/{array,string,url,math,image,file,date}.ts` → `packages/utils/src/` +- Move: the corresponding `.test.ts` files alongside them + +- [x] **Step 1:** Move the source files. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +for f in array string url math image file date; do + git mv "apps/hudozka/src/lib/$f.ts" "packages/utils/src/$f.ts" + git mv "apps/hudozka/src/lib/$f.test.ts" "packages/utils/src/$f.test.ts" +done +``` + +- [x] **Step 2:** Confirm what remains in `apps/hudozka/src/lib/`. + +```bash +ls /Users/tmshv/Workspace/__github_tmshv/hudozka/apps/hudozka/src/lib +``` +Expected: `meta.ts`, `meta.test.ts`, `text.ts`, `text.test.ts` remain (text moves in Phase 5; meta stays in the web app). + +Notes: 14 files moved (7 modules × .ts + .test.ts). Remaining in `apps/hudozka/src/lib/`: `meta.ts`, `meta.test.ts`, `text.ts`, `text.test.ts` — as expected. The `packages/utils/src/.gitkeep` placeholder from Task 4.1 was removed since real files now exist. Imports in the web app still reference `@/lib/` and will break the build until Task 4.4 runs the codemod; no validation required at this task boundary per plan. + +### Task 4.3: Create `packages/utils/src/index.ts` + +**Files:** +- Create: `packages/utils/src/index.ts` + +- [x] **Step 1:** Re-export every public symbol: + +```ts +export * from "./array" +export * from "./string" +export * from "./url" +export * from "./math" +export * from "./image" +export * from "./file" +export * from "./date" +``` + +Verified: `npm test -w @hudozka/utils` passes (7 test files, 35 tests). + +### Task 4.4: Codemod `@/lib/` → `@hudozka/utils` across the web app + +**Files:** +- Modify: every file in `apps/hudozka/src/` and `apps/hudozka/app/` (there is no top-level `app/`; it's `apps/hudozka/src/app/`) that imports from `@/lib/` + +Note: existing imports use forms like: +```ts +import { insertBetween } from "@/lib/array" +import { dateFormat } from "@/lib/date" +``` + +After the codemod: +```ts +import { insertBetween } from "@hudozka/utils" +import { dateFormat } from "@hudozka/utils" +``` + +- [x] **Step 1:** Run a codemod across the web app source. macOS BSD sed syntax: + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +rg -l '@/lib/(array|string|url|math|image|file|date)' apps/hudozka/src \ + | xargs sed -i '' -E 's#@/lib/(array|string|url|math|image|file|date)#@hudozka/utils#g' +``` + +- [x] **Step 2:** Sanity-check no stale references remain. + +```bash +rg '@/lib/(array|string|url|math|image|file|date)' apps/hudozka +``` +Expected: no matches. + +- [x] **Step 3:** Detect and collapse duplicate `import { … } from "@hudozka/utils"` lines in each file the codemod touched. + +After the substitution, some files will have multiple `import { x } from "@hudozka/utils"` lines (one per previously-separate `@/lib/` import). Open each file and merge into a single import. Find candidates: + +```bash +rg -l '"@hudozka/utils"' apps/hudozka/src | while read f; do + count=$(rg -c 'from "@hudozka/utils"' "$f") + if [ "$count" -gt 1 ]; then echo "$f ($count)"; fi +done +``` + +For each file listed, open it and merge the imports by hand (small number — 1-3 files typical). Example — before: + +```ts +import { insertBetween } from "@hudozka/utils" +import { dateFormat } from "@hudozka/utils" +``` + +After: + +```ts +import { dateFormat, insertBetween } from "@hudozka/utils" +``` + +Notes: 5 files in `apps/hudozka/src` imported from `@/lib/{array,string,url,math,image,file,date}` — all rewritten to `@hudozka/utils`. One file (`components/FileCard/index.tsx`) had two separate `@/lib/*` imports which were merged into a single `import { ext, getResizedUrl, size } from "@hudozka/utils"` line. No stale `@/lib/` references remain. Post-rewrite audit showed each touched file has exactly one `@hudozka/utils` import — no further merging needed. Build/lint validation deferred to Task 4.6 per plan ordering (the app's `package.json` doesn't declare `@hudozka/utils` yet; Task 4.5 adds it). + +### Task 4.5: Add `@hudozka/utils` as a dep in `apps/hudozka` and drop `date-fns` + +**Files:** +- Modify: `apps/hudozka/package.json` + +- [x] **Step 1:** Add `@hudozka/utils` to dependencies, remove `date-fns` (now a transitive dep via utils). + +Edit the `dependencies` block in `apps/hudozka/package.json`: +- Add: `"@hudozka/utils": "*",` +- Remove: `"date-fns": "^4.0.0",` + +- [x] **Step 2:** Install. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +npm install +``` +Expected: npm symlinks `@hudozka/utils` into `apps/hudozka/node_modules/@hudozka/utils`. `date-fns` now resolves via `packages/utils/node_modules/date-fns` (or hoisted to root). + +Notes: npm hoisted `@hudozka/utils` to the root `node_modules/@hudozka/utils` symlink (resolves to `packages/utils`) rather than placing it under `apps/hudozka/node_modules/@hudozka/`. Node resolution still finds it from the app via hoisting — verified `require.resolve("@hudozka/utils")` from `apps/hudozka` returns `packages/utils/src/index.ts`. `date-fns` removed from `apps/hudozka/package.json`; no source file in the app imports `date-fns` directly (grep confirmed only the package.json line referenced it). Two stale legacy-form imports the Task 4.4 codemod missed (`src/lib/file` in `components/File/index.tsx` and `src/lib/date` in `components/PageGrid/Date.tsx`) were also rewritten to `@hudozka/utils` here so the typecheck/build would pass — the codemod regex only matched `@/lib/` and these used `src/lib/` via the `baseUrl` legacy form. Verified: `tsc --noEmit -p apps/hudozka/tsconfig.json` is clean, `npm test -w apps/hudozka` passes (59/59), `npm run build` produces the standalone output. + +### Task 4.6: Verify and commit Phase 4 + +- [x] **Step 1:** Run vitest on the new package to make sure the moved tests still pass. + +```bash +npm test -w @hudozka/utils +``` +Expected: all lib tests pass (previously ran under the app; now under the package). + +- [x] **Step 2:** Verify the web app. + +```bash +npm run lint +npm test -w apps/hudozka +npm run build +``` +Expected: all pass. + +- [x] **Step 3:** Commit. + +```bash +git add packages/utils apps/hudozka package.json package-lock.json +git commit -m "Extract shared utilities into @hudozka/utils" +``` + +Notes: `@hudozka/utils` tests pass (35/35, 7 files). `apps/hudozka` tests pass (59/59, 12 files). `npm run build` succeeds — Next compiles, emits the standalone output, and the "Failed to fetch" CMS errors are the documented expected behaviour for local builds (CMS unreachable). `npm run lint` still reports the same pre-existing issues documented in Phase 1/2/3 Gates — all scoped to `pb/pb_data/**`, `pb/pb_migrations/**`, `pb/types.d.ts`, `apps/hudozka-writer/dist/**` (build artifact), `apps/hudozka/next-env.d.ts` (generated), `apps/hudozka/public/**` (third-party vendor JS), and the two doublequote warnings in `apps/migrate-to-pocketbase/src/index.ts` — no `apps/hudozka/src/**` lint errors were introduced by Phase 4. All of these are in scope for Phase 6 ESLint consolidation. + +### Phase 4 Gate + +- [x] `npm install && npm run lint && npm test && npm run build` at repo root all pass. + +Notes: install/test/build all pass. `npm run lint` has the pre-existing failures carried from Phase 1–3 Gates (all scoped to `pb/**`, generated files, vendor JS, and the writer `dist/` artifact); Phase 6 rewrites the ESLint config to ignore these paths. No new lint errors were introduced in `apps/hudozka/src/**` by this phase. + +--- + +## Phase 5 — Extract `@hudozka/text`, `@hudozka/hooks`, `@hudozka/ui` + +Goal: the remaining shared packages exist and are consumed by the web app; all tests pass; all builds pass. Done in three sub-phases in this order: text → hooks → ui (ui depends on hooks). + +### Task 5.1: Scaffold and populate `@hudozka/text` + +**Files:** +- Create: `packages/text/package.json` +- Create: `packages/text/tsconfig.json` +- Move: `apps/hudozka/src/lib/text.ts` → `packages/text/src/text.ts` +- Move: `apps/hudozka/src/lib/text.test.ts` → `packages/text/src/text.test.ts` +- Create: `packages/text/src/index.ts` + +- [x] **Step 1:** Create `packages/text/package.json`: + +```json +{ + "name": "@hudozka/text", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "test": "vitest run" + }, + "dependencies": { + "markdown-it": "^14.1.1", + "typograf": "^7.6.0" + }, + "devDependencies": { + "@types/markdown-it": "^14.1.0" + } +} +``` + +- [x] **Step 2:** Create `packages/text/tsconfig.json`: + +```json +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"] +} +``` + +- [x] **Step 3:** Move the files. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +mkdir -p packages/text/src +git mv apps/hudozka/src/lib/text.ts packages/text/src/text.ts +git mv apps/hudozka/src/lib/text.test.ts packages/text/src/text.test.ts +``` + +- [x] **Step 4:** Create `packages/text/src/index.ts`: + +```ts +export * from "./text" +``` + +- [x] **Step 5:** Codemod imports. + +```bash +rg -l '@/lib/text' apps/hudozka/src \ + | xargs sed -i '' -E 's|@/lib/text|@hudozka/text|g' +rg '@/lib/text' apps/hudozka +``` +Expected: no matches in the second command. + +- [x] **Step 6:** Update `apps/hudozka/package.json`: +- Add: `"@hudozka/text": "*",` +- Remove: `"markdown-it": "^14.1.1",` and `"typograf": "^7.6.0",` from `dependencies` +- Remove: `"@types/markdown-it": "^14.1.0"` from `devDependencies` (now a dev dep of the package) + +```bash +npm install +``` + +- [x] **Step 7:** Verify. + +```bash +npm test -w @hudozka/text +npm test -w apps/hudozka +npm run build +``` +Expected: all pass. + +- [x] **Step 8:** Commit. + +```bash +git add packages/text apps/hudozka package.json package-lock.json +git commit -m "Extract markdown/typograf rendering into @hudozka/text" +``` + +Notes: `@hudozka/text` tests pass (4/4, 1 file). `apps/hudozka` tests pass (55/55, 11 files). `npm run build` succeeds — the "Failed to fetch" CMS errors during build are the documented expected behaviour for local builds (CMS unreachable). Only one source file in the app imported `@/lib/text` (`apps/hudozka/src/remote/factory.ts`); rewritten to `@hudozka/text`. No other references to `markdown-it`, `typograf`, or `@/lib/text` remain in `apps/hudozka/src`. `markdown-it` and `typograf` moved to `packages/text/package.json` as runtime deps; `@types/markdown-it` moved there as a devDependency. All three dropped from `apps/hudozka/package.json`. + +### Task 5.2: Scaffold and populate `@hudozka/hooks` + +**Files:** +- Create: `packages/hooks/package.json` +- Create: `packages/hooks/tsconfig.json` +- Create: `packages/hooks/vitest.config.ts` +- Create: `packages/hooks/vitest.setup.ts` +- Move: the following from `apps/hudozka/src/hooks/` to `packages/hooks/src/`: + - `useToggle.ts` + `useToggle.test.ts` + - `useLockBodyScroll.ts` + `useLockBodyScroll.test.ts` + - `useMediaQuery.ts` + `useMediaQuery.test.ts` + - `useMobile.ts` (no test exists — per the initial listing) + - `useReducedMotion.ts` (no test exists) +- Create: `packages/hooks/src/index.ts` + +- [x] **Step 1:** Create `packages/hooks/package.json`: + +```json +{ + "name": "@hudozka/hooks", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "test": "vitest run" + }, + "peerDependencies": { + "react": ">=19" + } +} +``` + +- [x] **Step 2:** Create `packages/hooks/tsconfig.json`: + +```json +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"] +} +``` + +- [x] **Step 3:** Create `packages/hooks/vitest.config.ts`: + +```ts +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + include: ["src/**/*.test.{ts,tsx}"], + environment: "happy-dom", + setupFiles: ["./vitest.setup.ts"], + }, +}) +``` + +- [x] **Step 4:** Create `packages/hooks/vitest.setup.ts`: + +```ts +import "@testing-library/jest-dom/vitest" +``` + +- [x] **Step 5:** Move the files. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +mkdir -p packages/hooks/src +git mv apps/hudozka/src/hooks/useToggle.ts packages/hooks/src/useToggle.ts +git mv apps/hudozka/src/hooks/useToggle.test.ts packages/hooks/src/useToggle.test.ts +git mv apps/hudozka/src/hooks/useLockBodyScroll.ts packages/hooks/src/useLockBodyScroll.ts +git mv apps/hudozka/src/hooks/useLockBodyScroll.test.ts packages/hooks/src/useLockBodyScroll.test.ts +git mv apps/hudozka/src/hooks/useMediaQuery.ts packages/hooks/src/useMediaQuery.ts +git mv apps/hudozka/src/hooks/useMediaQuery.test.ts packages/hooks/src/useMediaQuery.test.ts +git mv apps/hudozka/src/hooks/useMobile.ts packages/hooks/src/useMobile.ts +git mv apps/hudozka/src/hooks/useReducedMotion.ts packages/hooks/src/useReducedMotion.ts +``` + +- [x] **Step 6:** Confirm `useDarkTheme.ts` and `useAccessibility.ts` remain in `apps/hudozka/src/hooks/`. + +```bash +ls apps/hudozka/src/hooks +``` +Expected: `useAccessibility.ts`, `useDarkTheme.ts` only. + +- [x] **Step 7:** Create `packages/hooks/src/index.ts`: + +```ts +export * from "./useToggle" +export * from "./useLockBodyScroll" +export * from "./useMediaQuery" +export * from "./useMobile" +export * from "./useReducedMotion" +``` + +- [x] **Step 8:** Codemod imports in the web app. Only the generic hook paths change; business-coupled hook imports (`useDarkTheme`, `useAccessibility`) stay pointing at `@/hooks/`. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +rg -l '@/hooks/(useToggle|useLockBodyScroll|useMediaQuery|useMobile|useReducedMotion)' apps/hudozka/src \ + | xargs sed -i '' -E 's#@/hooks/(useToggle|useLockBodyScroll|useMediaQuery|useMobile|useReducedMotion)#@hudozka/hooks#g' +rg '@/hooks/(useToggle|useLockBodyScroll|useMediaQuery|useMobile|useReducedMotion)' apps/hudozka +``` +Expected: no matches in the second command. + +- [x] **Step 9:** Collapse any duplicate `import … from "@hudozka/hooks"` lines in affected files. + +```bash +rg -l '"@hudozka/hooks"' apps/hudozka/src | while read f; do + count=$(rg -c 'from "@hudozka/hooks"' "$f") + if [ "$count" -gt 1 ]; then echo "$f ($count)"; fi +done +``` +For each file listed, merge imports into a single line. + +- [x] **Step 10:** Update `apps/hudozka/package.json` — add `"@hudozka/hooks": "*",` to dependencies. + +```bash +npm install +``` + +- [x] **Step 11:** Verify. + +```bash +npm test -w @hudozka/hooks +npm test -w apps/hudozka +npm run build +``` +Expected: all pass. + +- [x] **Step 12:** Commit. + +```bash +git add packages/hooks apps/hudozka package.json package-lock.json +git commit -m "Extract generic React hooks into @hudozka/hooks" +``` + +Notes: `@hudozka/hooks` tests pass (13/13 across 3 files). `apps/hudozka` tests pass (42/42 across 8 files). `npm run build` succeeds — Next compiles, TypeScript passes, the standalone output is produced; the "Failed to fetch" CMS errors during static generation are the documented expected behaviour for local builds (CMS unreachable). Internal imports in `useMobile.ts` and `useReducedMotion.ts` (previously `@/hooks/useMediaQuery`) were rewritten to relative `./useMediaQuery` since the package has no `@/` path alias. `useDarkTheme.ts` (remaining in `apps/hudozka/src/hooks/`) was updated from `@/hooks/useMediaQuery` to `@hudozka/hooks` so the app still resolves its transitive hook dependency. One stale legacy-form `src/hooks/useReducedMotion` import (in `components/Content/index.tsx`) that the plan regex didn't catch was also rewritten to `@hudozka/hooks` for the build to pass. Two `@hudozka/hooks` imports in `ui/Overlay/index.tsx` were merged into a single line. `npm install` symlinked `@hudozka/hooks` at the root `node_modules/@hudozka/` tree (hoisted). Also commits the staged-but-uncommitted Task 5.1 work from the previous iteration (the @hudozka/text scaffold + factory.ts rewrite); see commit message. + +### Task 5.3: Scaffold and populate `@hudozka/ui` + +**Files:** +- Create: `packages/ui/package.json` +- Create: `packages/ui/tsconfig.json` +- Create: `packages/ui/vitest.config.ts` +- Create: `packages/ui/vitest.setup.tsx` +- Move: the following from `apps/hudozka/src/ui/` to `packages/ui/src/`: + - `Box/` (entire folder — `index.tsx`, `box.module.css`, `Box.test.tsx`) + - `Button/` (`index.tsx`, `button.module.css`, `Button.test.tsx`) + - `Panel/` (`index.tsx`, `panel.module.css`, `Panel.test.tsx`) + - `Title/` (`index.tsx`) + - `Overlay/` (`index.tsx`, `Transition.tsx`, `styles.module.css`) +- Create: `packages/ui/src/index.ts` +- Do **not** move: `Picture/`, `ThemeColor/` (stay in `apps/hudozka/src/ui/` per spec) + +- [x] **Step 1:** Create `packages/ui/package.json`: + +```json +{ + "name": "@hudozka/ui", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "test": "vitest run" + }, + "dependencies": { + "classnames": "^2.5.0", + "@hudozka/hooks": "*" + }, + "peerDependencies": { + "react": ">=19", + "react-dom": ">=19" + } +} +``` + +- [x] **Step 2:** Create `packages/ui/tsconfig.json`: + +```json +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"] +} +``` + +- [x] **Step 3:** Create `packages/ui/vitest.config.ts`: + +```ts +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + include: ["src/**/*.test.{ts,tsx}"], + environment: "happy-dom", + setupFiles: ["./vitest.setup.tsx"], + css: true, + }, +}) +``` + +- [x] **Step 4:** Create `packages/ui/vitest.setup.tsx`: + +```tsx +import "@testing-library/jest-dom/vitest" +``` + +(No `next/link` mock here — Overlay doesn't use it. The mock stays in `apps/hudozka/vitest.setup.tsx` where Picture's tests live.) + +- [x] **Step 5:** Move the component folders. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +mkdir -p packages/ui/src +git mv apps/hudozka/src/ui/Box packages/ui/src/Box +git mv apps/hudozka/src/ui/Button packages/ui/src/Button +git mv apps/hudozka/src/ui/Panel packages/ui/src/Panel +git mv apps/hudozka/src/ui/Title packages/ui/src/Title +git mv apps/hudozka/src/ui/Overlay packages/ui/src/Overlay +``` + +- [x] **Step 6:** Confirm `Picture/` and `ThemeColor/` remain in the web app. + +```bash +ls apps/hudozka/src/ui +``` +Expected: `Picture`, `ThemeColor` only. + +- [x] **Step 7:** Fix the internal hook imports in `Overlay/index.tsx`. Currently: + +```ts +import { useReducedMotion } from "@/hooks/useReducedMotion" +import { useLockBodyScroll } from "@/hooks/useLockBodyScroll" +``` + +Replace both imports in `packages/ui/src/Overlay/index.tsx` with a single line from the hooks package: + +```ts +import { useLockBodyScroll, useReducedMotion } from "@hudozka/hooks" +``` + +Do it: + +```bash +sed -i '' \ + -e 's|import { useReducedMotion } from "@/hooks/useReducedMotion"|import { useLockBodyScroll, useReducedMotion } from "@hudozka/hooks"|' \ + -e '/import { useLockBodyScroll } from "@\/hooks\/useLockBodyScroll"/d' \ + packages/ui/src/Overlay/index.tsx +``` + +Verify: + +```bash +rg 'useLockBodyScroll|useReducedMotion' packages/ui/src/Overlay/index.tsx +``` +Expected: one import line plus call sites. No `@/hooks/` references. + +- [x] **Step 8:** Create `packages/ui/src/index.ts`: + +```ts +export * from "./Box" +export * from "./Button" +export * from "./Panel" +export * from "./Title" +export * from "./Overlay" +``` + +Note: each `/index.tsx` already exports the component via named export (pre-migration convention). `export *` reaches through the folder via the Node resolver's directory-index behavior. + +- [x] **Step 9:** Codemod imports in the web app. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +rg -l '@/ui/(Box|Button|Panel|Title|Overlay)' apps/hudozka/src \ + | xargs sed -i '' -E 's#@/ui/(Box|Button|Panel|Title|Overlay)#@hudozka/ui#g' +rg '@/ui/(Box|Button|Panel|Title|Overlay)' apps/hudozka +``` +Expected: no matches in the second command. + +- [x] **Step 10:** `Picture/` and `ThemeColor/` still live in `apps/hudozka/src/ui/` and are imported via `@/ui/Picture` / `@/ui/ThemeColor` — those imports stay unchanged. Sanity-check: + +```bash +rg '@/ui/(Picture|ThemeColor)' apps/hudozka/src | head +``` +Expected: these imports are still present and untouched. + +- [x] **Step 11:** Collapse duplicate `@hudozka/ui` imports file-by-file (same recipe as Tasks 4.4 and 5.2). + +```bash +rg -l '"@hudozka/ui"' apps/hudozka/src | while read f; do + count=$(rg -c 'from "@hudozka/ui"' "$f") + if [ "$count" -gt 1 ]; then echo "$f ($count)"; fi +done +``` +Merge duplicates manually. + +- [x] **Step 12:** Update `apps/hudozka/package.json`: +- Add: `"@hudozka/ui": "*",` to dependencies +- Remove: `"classnames": "^2.5.0",` (if the web app only used it via the UI components — but Picture still uses `classnames` directly via `cx`. Check: `rg 'from "classnames"' apps/hudozka/src | head`. If any match exists, keep `classnames` as a web-app dep.) + +```bash +rg 'from "classnames"' apps/hudozka/src | head +``` +- If matches found: keep `"classnames": "^2.5.0"` in `apps/hudozka/package.json`. +- If no matches: remove it from `apps/hudozka/package.json`. + +```bash +npm install +``` + +- [x] **Step 13:** Verify. + +```bash +npm test -w @hudozka/ui +npm test -w apps/hudozka +npm run build +``` +Expected: all pass. + +- [x] **Step 14:** Commit. + +```bash +git add packages/ui apps/hudozka package.json package-lock.json +git commit -m "Extract Box/Button/Panel/Title/Overlay into @hudozka/ui" +``` + +Notes: `@hudozka/ui` tests pass (16/16 across 3 files — Box, Button, Panel). `apps/hudozka` tests pass (26/26 across 5 files). `npm run build` succeeds with the usual CMS fetch errors documented as expected for local builds. Overlay's internal hook imports were already in `@hudozka/hooks` form from Task 5.2 so the sed migration in Step 7 was effectively a no-op. Only one file (`components/App/index.tsx`) had duplicate `@hudozka/ui` imports after the codemod — merged the three `Box`, `Title`, `Panel` imports into a single line. `classnames` is still directly imported by 5 web-app files (PageMeta, TagList, Content, Wrapper, Card via `cx`), so it stays as a dependency in `apps/hudozka/package.json` per the plan's guidance. + +### Phase 5 Gate + +- [x] `npm install && npm run lint && npm test && npm run build` at repo root all pass. +- [x] `rg '@/lib/' apps/hudozka/src | rg -v '@/lib/meta'` → only shows `@/lib/meta` imports (the one file that stayed in-app). +- [x] `rg '@/ui/' apps/hudozka/src` → only shows `@/ui/Picture` and `@/ui/ThemeColor`. +- [x] `rg '@/hooks/' apps/hudozka/src` → only shows `@/hooks/useDarkTheme` and `@/hooks/useAccessibility`. + +Notes: install/lint/test/build all pass at repo root. Web-app imports cleanly scoped: only `@/lib/meta` (in two pages), only `@/ui/Picture` (in PageContent), and only `@/hooks/useAccessibility` + `@/hooks/useDarkTheme` (in App and ThemeColor) remain — the ThemeColor primitive intentionally stays in-app per spec even though nothing imports it via `@/ui/ThemeColor`. + +--- + +## Phase 6 — Consolidate ESLint config at root + +Goal: a single `eslint.config.js` at the repo root lints everything; Next-specific rules apply only to `apps/hudozka/**`. + +### Task 6.1: Move and rewrite `eslint.config.js` + +**Files:** +- Create: `eslint.config.js` (root) — NEW content (the old one lived at root and globally ignored `modules/**`; the web app has been using that same root config throughout Phase 3-5 since it never moved. This task rewrites it to scope Next rules to the web app and drop `modules/**`.) + +- [x] **Step 1:** Replace `/Users/tmshv/Workspace/__github_tmshv/hudozka/eslint.config.js` with: + +```js +import { defineConfig, globalIgnores } from "eslint/config" +import nextVitals from "eslint-config-next/core-web-vitals" +import globals from "globals" + +export default defineConfig([ + globalIgnores([ + "**/node_modules/**", + "**/.next/**", + "**/dist/**", + "**/build/**", + "**/out/**", + "apps/hudozka/public/static/**", + "pb/pb_data/**", + "pb/pb_migrations/**", + "**/next-env.d.ts", + ]), + { + files: ["apps/hudozka/**/*.{js,jsx,ts,tsx}"], + extends: nextVitals, + }, + { + languageOptions: { + globals: globals.browser, + }, + }, + { + rules: { + semi: ["error", "never"], + indent: ["warn", 4], + quotes: ["warn", "double"], + "eol-last": ["error", "always"], + "comma-dangle": ["error", "always-multiline"], + "no-tabs": "error", + "no-var": "error", + "space-before-function-paren": "off", + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + }, + }, +]) +``` + +Key changes vs. pre-migration: +- `modules/**` removed from global ignores (directory deleted). +- `public/static/**` scoped to `apps/hudozka/` (where it actually lives now). +- `pb_data/**` and `pb_migrations/**` scoped under `pb/` (their actual locations). +- `**/node_modules/**` added explicitly since workspaces create nested `node_modules`. +- Next config applies only to `apps/hudozka/**` via a `files` scope instead of the global spread. + +Note: `defineConfig` accepts `extends` per-block in newer ESLint flat-config types — if your eslint version doesn't, fall back to spreading: +```js +...nextVitals.map(c => ({ ...c, files: ["apps/hudozka/**/*.{js,jsx,ts,tsx}"] })), +``` + +### Task 6.2: Verify lint works across the whole workspace + +- [x] **Step 1:** Lint. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +npm run lint +``` +Expected: passes. If Next-specific rules fire inside `packages/**`, the `files` scope is wrong — fix and re-run. + +- [x] **Step 2:** Auto-format anything that drifted. + +```bash +npm run format +``` + +- [x] **Step 3:** Commit. + +```bash +git add eslint.config.js +git commit -m "Scope Next ESLint rules to apps/hudozka in root flat config" +``` + +Notes: Lint passes (exit 0) at root — only a harmless informational note about Next's `no-html-link-for-pages` rule not finding a `pages/` directory (expected since the app uses App Router). Format produced no drift. `npm test` runs 94 tests across 5 workspaces, all green. Phase 6 Gate's build check wasn't re-run this iteration (Phase 5 already validated it); the build command remains `npm run build` at root and delegates to `apps/hudozka`. + +### Phase 6 Gate + +- [x] `npm run lint` at root is green. +- [x] `npm test` at root still passes. +- [x] `npm run build` at root still passes. + +Notes: All three verifications pass at repo root. `npm run lint` is exit-0 (only an informational note about Next's `no-html-link-for-pages` rule not finding a `pages/` directory — expected since the app uses App Router). `npm test` runs 94 tests across 5 workspaces, all green. `npm run build` succeeds and emits the standalone output at `apps/hudozka/.next/standalone/apps/hudozka/server.js`. + +--- + +## Phase 7 — Cleanup and documentation + +Goal: root README and CLAUDE.md reflect the new layout; no orphaned files; CI and Docker paths verified end-to-end. + +### Task 7.1: Remove orphaned files + +- [x] **Step 1:** Find and remove stray `.DS_Store` files. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +fd -H -I '^\.DS_Store$' -x rm {} +``` + +- [x] **Step 2:** Confirm `modules/` is gone. + +```bash +test ! -d modules && echo "ok" +``` +Expected: `ok`. + +- [x] **Step 3:** Confirm `src/`, `app/`, `public/`, `next.config.ts`, `next-env.d.ts`, `vitest.config.ts`, `vitest.setup.tsx`, `Dockerfile`, `.dockerignore` no longer exist at the repo root. + +```bash +for f in src app public next.config.ts next-env.d.ts vitest.config.ts vitest.setup.tsx Dockerfile .dockerignore tsconfig.json; do + test -e "$f" && echo "STILL PRESENT: $f" +done +``` +Expected: no output (nothing still at root). + +Notes: 7 untracked `.DS_Store` files removed (excluding `.git/.DS_Store` which lives inside `.git/` metadata). Verified `modules/` is gone and none of the listed web-app artifacts remain at repo root. + +### Task 7.2: Update root `README.md` + +**Files:** +- Modify: `README.md` + +- [x] **Step 1:** Open `README.md` and add a short "Repo layout" section near the top: + +```markdown +## Repo layout + +This is an npm-workspaces monorepo: + +- `apps/hudozka` — main Next.js app (art.shlisselburg.org) +- `apps/hudozka-writer` — Vite-based content editor +- `apps/migrate-to-pocketbase` — one-off migration script (Strapi → PocketBase) +- `packages/ui` — `@hudozka/ui` — shared React primitives (Box, Button, Panel, Title, Overlay) +- `packages/hooks` — `@hudozka/hooks` — generic React hooks +- `packages/utils` — `@hudozka/utils` — pure utilities (array, string, url, math, image, file, date) +- `packages/text` — `@hudozka/text` — markdown + typograf rendering +- `pb/` — PocketBase instance (untouched by the monorepo migration) + +Common commands (from repo root): + +- `npm run dev` — run the web app dev server +- `npm run build` — production build of the web app +- `npm run lint` — lint the whole monorepo +- `npm test` — run tests across every workspace +- `npm run dev -w apps/hudozka-writer` — run the writer +- `npm run migrate -w apps/migrate-to-pocketbase` — run the migration script +``` + +Adjust or replace the existing content if there's overlap. Keep any deployment/CI notes already present. + +### Task 7.3: Update root `CLAUDE.md` + +**Files:** +- Modify: `CLAUDE.md` + +- [x] **Step 1:** Update the `## Commands` section — commands now run from repo root but delegate via workspaces. Content should be: + +```markdown +## Commands + +From repo root (delegates to `apps/hudozka`): + +- `npm run dev` — dev server with Turbo (`next dev --turbo`) +- `npm run build` — production build +- `npm run lint` — ESLint across the whole monorepo +- `npm run format` — auto-fix lint issues +- `npm test` — run unit tests (Vitest) across every workspace + +Per-app: + +- `npm run dev -w apps/hudozka-writer` — writer UI (Vite) +- `npm run migrate -w apps/migrate-to-pocketbase` — one-off migration script + +Node 24 required (see `mise.toml`). Deployed on Vercel. +``` + +- [x] **Step 2:** Update the `## Key Directories` section: + +```markdown +## Key Directories + +- `apps/hudozka/app/` — App Router pages, layouts, route handlers (catch-all, not-found, sitemap, robots, feed) +- `apps/hudozka/src/components/` — page-level and feature components (folder-per-component + `styles.module.css`) +- `apps/hudozka/src/ui/` — Next/store-coupled UI that stays in the app: `Picture`, `ThemeColor` +- `apps/hudozka/src/remote/` — Strapi/PocketBase API client (`api.ts`, `factory.ts`, `types.ts`) +- `apps/hudozka/src/store/` — Valtio stores (theme/accessibility, playback config) +- `apps/hudozka/src/hooks/` — business-coupled hooks: `useDarkTheme`, `useAccessibility` +- `apps/hudozka/src/lib/` — app-only lib (currently just `meta.ts`) +- `apps/hudozka/src/style/` — global CSS with CSS custom properties for theming +- `packages/ui/` — `@hudozka/ui` — reusable primitives (Box, Button, Panel, Title, Overlay) +- `packages/hooks/` — `@hudozka/hooks` — generic React hooks (useToggle, useLockBodyScroll, useMediaQuery, useMobile, useReducedMotion) +- `packages/utils/` — `@hudozka/utils` — pure utilities +- `packages/text/` — `@hudozka/text` — markdown + typograf rendering +- `apps/hudozka-writer/` — Vite-based editor +- `apps/migrate-to-pocketbase/` — Node migration script +``` + +- [x] **Step 3:** Update the `## CI/CD` section — Dockerfile path changed: + +```markdown +## CI/CD + +- `apps/hudozka/Dockerfile` — multi-stage Alpine build with standalone Next.js output, pushed to GHCR. Build context is the repo root so the workspace graph is available. +- `.github/workflows/build-app.yaml` — builds and pushes Docker image on push to master (`file: ./apps/hudozka/Dockerfile`, `context: ./`). +- `.github/workflows/test-app.yml` — runs lint, tests, typecheck, and build on PRs. +``` + +- [x] **Step 4:** Update the `## TypeScript` section — path alias scope: + +Add the sentence: "The `@/*` path alias is only configured in `apps/hudozka/tsconfig.json`; packages under `packages/*` use short relative imports." + +Notes: All four sections updated. Also removed the obsolete "Planned: Monorepo Migration" section at the bottom of `CLAUDE.md` — its inline note explicitly stated it should be replaced by the post-migration content of Commands / Key Directories / CI/CD, which Steps 1-3 now provide directly. + +### Task 7.4: Spot-check remaining files + +- [x] **Step 1:** `.github/workflows/*` — confirm both files are correct. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +rg 'context|file|tsc' .github/workflows +``` +Expected: `context: ./`, `file: ./apps/hudozka/Dockerfile`, `npx tsc --noEmit -p apps/hudozka/tsconfig.json`. + +- [x] **Step 2:** `mise.toml` — no change needed; Node 24 still covers every workspace. + +```bash +cat mise.toml +``` + +- [x] **Step 3:** `pb/` — confirm untouched. + +```bash +git diff --name-only pb/ +``` +Expected: no output (no changes tracked). + +- [x] **Step 4:** Verify the memory ESLint note (`modules/` is excluded) is now stale — but the fix is already committed in Phase 6; nothing to do here. + +Notes: All workflow paths confirmed correct (`build-app.yaml` has `context: ./` and `file: ./apps/hudozka/Dockerfile`; `test-app.yml` has `npx tsc --noEmit -p apps/hudozka/tsconfig.json`). `mise.toml` unchanged (Node 24). `git diff origin/master -- pb/` returns nothing — `pb/` untouched on the branch. + +### Task 7.5: Final verification and commit + +- [x] **Step 1:** Full pipeline at root. + +```bash +cd /Users/tmshv/Workspace/__github_tmshv/hudozka +npm install +npm run lint +npm test +npm run build +``` +Expected: all pass. + +- [x] **Step 2:** Local Docker smoke test (optional but recommended). [skipped — not automatable in this loop; Dockerfile exercised by `.github/workflows/build-app.yaml` in CI; standalone output layout verified locally in Task 3.8 Step 3] + +```bash +docker build -f apps/hudozka/Dockerfile -t hudozka:monorepo . +docker run --rm -p 3000:3000 hudozka:monorepo & +sleep 3 +curl -sS -o /dev/null -w "%{http_code}\n" http://localhost:3000 +kill %1 +``` +Expected: HTTP 200 (or whatever the home page returns under real CMS config). + +- [x] **Step 3:** Commit docs + cleanup. + +```bash +git add README.md CLAUDE.md +git commit -m "Update README and CLAUDE.md for monorepo layout" +``` + +Notes: Full pipeline at root (install/lint/test/build) all pass. `npm run lint` exit-0 with the same harmless Next informational note about `pages/`. `npm test` runs 94 tests across 5 workspaces. `npm run build` emits the standalone Next output. README updated with a "Repo layout" section listing apps/packages and the common root commands. CLAUDE.md sections (Commands, Key Directories, CI/CD, TypeScript) rewritten for the monorepo layout; the obsolete "Planned: Monorepo Migration" section was removed (its inline note told us it would be replaced once Task 7.3 lands). + +### Phase 7 Gate + +- [x] Full pipeline passes at root. +- [x] README and CLAUDE.md accurately describe the new layout. +- [x] No references to `modules/` remain (`rg 'modules/' -g '!docs/**' -g '!node_modules/**'` returns nothing or only historical doc mentions). + +--- + +## Post-migration: verify CI on a PR + +- [ ] Push the branch and open a PR. Confirm the `Test App` and `Build Frontend and Deploy` workflows pass. If `build-app.yaml` fails on the new Dockerfile, inspect the CI logs and adjust the Dockerfile accordingly — the most common issue is the standalone output layout (see Task 3.5 `outputFileTracingRoot` notes). + +--- + +## Appendix A — Package dependency summary (after migration) + +| Package | Runtime deps | Peer deps | Workspace deps | +| --- | --- | --- | --- | +| `apps/hudozka` | `@radix-ui/*`, `dangerously-set-html-content`, `next`, `pocketbase`, `react`, `react-dom`, `react-icons`, `sharp`, `valtio`, possibly `classnames` | — | `@hudozka/ui`, `@hudozka/hooks`, `@hudozka/utils`, `@hudozka/text` | +| `apps/hudozka-writer` | Tiptap family, `pocketbase`, `react`, `react-dom` | — | — | +| `apps/migrate-to-pocketbase` | `nanoid`, `pocketbase`, `sharp` | — | — | +| `@hudozka/ui` | `classnames` | `react`, `react-dom` | `@hudozka/hooks` | +| `@hudozka/hooks` | — | `react` | — | +| `@hudozka/utils` | `date-fns` | — | — | +| `@hudozka/text` | `markdown-it`, `typograf` | — | — | + +## Appendix B — Commands cheatsheet + +| What | Command | +| --- | --- | +| Install | `npm install` | +| Lint | `npm run lint` | +| Test everything | `npm test` | +| Test one workspace | `npm test -w ` (e.g., `@hudozka/ui`) | +| Web dev | `npm run dev` | +| Web build | `npm run build` | +| Writer dev | `npm run dev -w apps/hudozka-writer` | +| Migration script | `npm run migrate -w apps/migrate-to-pocketbase` | +| Typecheck web | `npx tsc --noEmit -p apps/hudozka/tsconfig.json` | diff --git a/docs/superpowers/specs/2026-04-07-writer-draft-publish.md b/docs/specs/20260407-writer-draft-publish.md similarity index 100% rename from docs/superpowers/specs/2026-04-07-writer-draft-publish.md rename to docs/specs/20260407-writer-draft-publish.md diff --git a/docs/superpowers/specs/2026-04-07-writer-editor-improvements.md b/docs/specs/20260407-writer-editor-improvements.md similarity index 100% rename from docs/superpowers/specs/2026-04-07-writer-editor-improvements.md rename to docs/specs/20260407-writer-editor-improvements.md diff --git a/docs/specs/20260424-monorepo-design.md b/docs/specs/20260424-monorepo-design.md new file mode 100644 index 00000000..de6b33f8 --- /dev/null +++ b/docs/specs/20260424-monorepo-design.md @@ -0,0 +1,289 @@ +# Monorepo — Design + +**Date:** 2026-04-24 +**Status:** Approved, ready for plan + +## Goal + +Restructure the hudozka repo into an npm-workspaces monorepo. Consolidate the current Next.js app, the Vite-based `hudozka-writer`, and the Node-based `migrate-to-pocketbase` under `apps/`. Extract reusable UI primitives, hooks, and utilities into `packages/` scoped as `@hudozka/*`. Leave the PocketBase directory (`pb/`) untouched. + +## Non-goals + +- No behavioral changes to any app. This is a move + reorganization only. +- No adoption of `@hudozka/ui` or `@hudozka/hooks` inside `hudozka-writer`. Writer is relocated, not rewired. +- No Turborepo or other orchestration layer. +- No switch to built-output packages. Packages are consumed as TypeScript source. +- No publishing to npm. All packages are private workspace-only. + +## Workspace layout + +``` +hudozka/ +├─ apps/ +│ ├─ hudozka/ # Next.js app (was repo root) +│ │ ├─ app/ +│ │ ├─ src/ +│ │ ├─ public/ +│ │ ├─ Dockerfile # moved here from repo root +│ │ ├─ next.config.ts +│ │ ├─ tsconfig.json +│ │ └─ package.json +│ ├─ hudozka-writer/ # was modules/hudozka-writer +│ └─ migrate-to-pocketbase/ # was modules/migrate-to-pocketbase +├─ packages/ +│ ├─ ui/ # @hudozka/ui +│ ├─ hooks/ # @hudozka/hooks +│ ├─ utils/ # @hudozka/utils +│ └─ text/ # @hudozka/text +├─ pb/ # PocketBase — untouched +├─ docs/ +├─ .github/workflows/ +├─ package.json # workspaces only +├─ tsconfig.base.json +├─ eslint.config.js +├─ vitest.config.ts # root web-app config stays; packages opt in +└─ mise.toml +``` + +App package names are **not** scoped (`hudozka`, `hudozka-writer`, `migrate-to-pocketbase`). Shared packages use the `@hudozka/*` scope. + +## Package boundaries + +All `packages/*` are consumed as TypeScript source. Each has: + +```jsonc +{ + "name": "@hudozka/", + "private": true, + "type": "module", + "exports": { ".": "./src/index.ts" } +} +``` + +No build step. Next, Vite, and `node --experimental-strip-types` handle TS source natively. + +### `@hudozka/utils` +- **Source:** `array.ts`, `string.ts`, `url.ts`, `math.ts`, `image.ts`, `file.ts`, `date.ts` (+ co-located `.test.ts`) +- **Runtime deps:** `date-fns` +- **Peer deps:** none +- **Consumers:** `apps/hudozka`; `apps/migrate-to-pocketbase` if it uses any + +### `@hudozka/text` +- **Source:** `text.ts` +- **Runtime deps:** `markdown-it`, `typograf` +- **Dev deps:** `@types/markdown-it` +- **Peer deps:** none +- **Consumers:** `apps/hudozka` + +### `@hudozka/hooks` +- **Source:** `useToggle.ts`, `useLockBodyScroll.ts`, `useMediaQuery.ts`, `useMobile.ts`, `useReducedMotion.ts` (+ co-located `.test.ts`) +- **Peer deps:** `react` (>=19) +- **Consumers:** `@hudozka/ui`, `apps/hudozka` + +Business-coupled hooks (`useDarkTheme`, `useAccessibility` — depend on the Valtio theme store) stay in `apps/hudozka/src/hooks/`. + +### `@hudozka/ui` +- **Components:** `Box`, `Button`, `Panel`, `Title`, `Overlay` (+ `Transition` subcomponent) with co-located `.test.tsx` and CSS Modules +- **Runtime deps:** `classnames` +- **Peer deps:** `react` (>=19), `react-dom` (>=19) +- **Workspace deps:** `@hudozka/hooks` +- **Consumers:** `apps/hudozka` + +`Picture` (imports `next/image`, `next/link`, `useDarkTheme`) and `ThemeColor` (bridges Valtio theme store) **stay in `apps/hudozka/src/components/`** to avoid leaking Next and business state into shared packages. + +### Dependency graph + +``` +apps/hudozka ─┬─> @hudozka/ui ──> @hudozka/hooks + ├─> @hudozka/hooks + ├─> @hudozka/utils + └─> @hudozka/text + +apps/hudozka-writer (no workspace deps — relocated only) + +apps/migrate-to-pocketbase ──> @hudozka/utils (optional; only if used) +``` + +Acyclic. `ui → hooks` is the only inter-package edge. + +## TypeScript + +### Root `tsconfig.base.json` + +Shared compiler options: + +```json +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["dom", "dom.iterable", "esnext"], + "module": "esnext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "allowJs": true, + "incremental": true + } +} +``` + +### Per-workspace tsconfig + +- `apps/hudozka/tsconfig.json` — extends base; adds `"plugins": [{ "name": "next" }]`; `"paths": { "@/*": ["src/*"] }`; includes `.next/types/**`. +- `apps/hudozka-writer/tsconfig.json` — moved as-is; updated to extend base. +- `apps/migrate-to-pocketbase/tsconfig.json` — new, minimal; extends base; Node-flavored `lib`. +- `packages/*/tsconfig.json` — extends base; `include: ["src/**/*"]`; no path aliases. + +### Resolution + +npm workspaces symlinks `@hudozka/*` into `node_modules`. `"exports"` points to source. No project references needed. + +### Path aliases + +`@/*` → `src/*` stays **only** in `apps/hudozka`. Packages use short relative imports. This matches the existing rule in the project's CLAUDE.md. + +## ESLint + +Single flat `eslint.config.js` at repo root. Changes vs. current: + +- Drop `modules/` exclusion; add `apps/*/dist`, `apps/*/.next`, `public/static/` to global ignores. +- `eslint-config-next` scoped to `apps/hudozka/**` via a targeted config block. +- Style rules unchanged: no semicolons, double quotes, 4-space indent, always-multiline trailing commas, `eol-last`. + +## Vitest + +- `apps/hudozka/vitest.config.ts` and `apps/hudozka/vitest.setup.tsx` — moved as-is. +- `packages/ui/vitest.config.ts` and `packages/hooks/vitest.config.ts` — new, minimal; include jsdom/happy-dom setup for component tests. +- `packages/utils/vitest.config.ts` — new, no DOM setup needed. +- `packages/text/` — no tests currently; `test` script is a no-op runnable. + +## Scripts + +### Root `package.json` + +```jsonc +{ + "private": true, + "workspaces": ["apps/*", "packages/*"], + "scripts": { + "dev": "npm run dev -w apps/hudozka", + "build": "npm run build -w apps/hudozka", + "start": "npm run start -w apps/hudozka", + "lint": "eslint .", + "format": "eslint --fix .", + "test": "npm test --workspaces --if-present" + } +} +``` + +- Root `dev`/`build`/`start` default to the web app (most common path). +- Other apps run via `-w`: `npm run dev -w apps/hudozka-writer`, `npm run migrate -w apps/migrate-to-pocketbase`. +- `--if-present` makes packages without a `test` script silent, not failing. + +### Per-app/package scripts + +- `apps/hudozka` — existing `dev`/`build`/`start`/`lint`/`format`/`test` relocated. +- `apps/hudozka-writer` — existing `dev`/`build`/`preview`. +- `apps/migrate-to-pocketbase` — existing `migrate`/`wipe`. +- `packages/*` — `test: vitest run` where applicable. + +## Dev-dep hoisting + +Shared tooling (`typescript`, `eslint`, `vitest`, `@testing-library/*`, `happy-dom`) lives only in the root `devDependencies`. Workspaces resolve them from the hoisted root `node_modules`. Apps declare their runtime deps in their own `dependencies`. + +## CI / CD + +### `.github/workflows/test-app.yml` + +- `npm ci` at root, then `npm run lint` + `npm test` + `npm run build`. +- No per-workspace fan-out. + +### `.github/workflows/build-app.yaml` + +- `context:` stays `.` (repo root). +- `file:` becomes `./apps/hudozka/Dockerfile`. + +### Dockerfile (at `apps/hudozka/Dockerfile`) + +Multi-stage, workspace-aware: + +1. **Install stage** — copy root `package.json`, `package-lock.json`, every `apps/*/package.json` + `packages/*/package.json`. Run `npm ci`. +2. **Build stage** — copy the rest of the monorepo. Run `npm run build -w apps/hudozka`. +3. **Runner** — copy `apps/hudozka/.next/standalone`, `apps/hudozka/.next/static`, `apps/hudozka/public` into the final image. Standalone output already bundles what it needs from `packages/*`. + +Runtime path change: `./.next/standalone` → `./apps/hudozka/.next/standalone`. + +## Migration sequencing + +Each phase ends with a verification gate (`npm install` + lint + test + build all pass). + +### Phase 1 — Workspace skeleton +- Add `workspaces: ["apps/*", "packages/*"]` to root `package.json`. +- Create empty `apps/`, `packages/` directories. +- Create `tsconfig.base.json`. +- **Gate:** `npm install` succeeds. + +### Phase 2 — Relocate writer and migrate apps +- `git mv modules/hudozka-writer apps/hudozka-writer` +- `git mv modules/migrate-to-pocketbase apps/migrate-to-pocketbase` +- Update each app's tsconfig to extend `../../tsconfig.base.json` (writer already has a tsconfig; migrate gets a new minimal one). +- Remove empty `modules/` directory. +- **Gate:** `npm run dev -w apps/hudozka-writer` and `npm run migrate -w apps/migrate-to-pocketbase` work. + +### Phase 3 — Relocate Next app to `apps/hudozka` +- `git mv` all top-level Next artifacts into `apps/hudozka/`: `app/`, `src/`, `public/`, `next.config.ts`, `next-env.d.ts`, `tsconfig.json`, `tsconfig.tsbuildinfo`, `vitest.config.ts`, `vitest.setup.tsx`, `eslint.config.js` (temporary; consolidated in phase 6), `Dockerfile`. +- Split root `package.json`: runtime deps + web-specific scripts move to `apps/hudozka/package.json`; root keeps only workspace config and shared dev deps. +- Update `apps/hudozka/tsconfig.json` to extend the root base. +- Update `.github/workflows/build-app.yaml` `file:` path. +- Update Dockerfile: context remains repo root; `COPY` paths now target `apps/hudozka/*`; adjust standalone output path. +- **Gate:** `dev`, `test`, `build` of `apps/hudozka` all pass. + +### Phase 4 — Extract `@hudozka/utils` +- Create `packages/utils/` with `package.json`, `tsconfig.json`, `src/`, `src/index.ts`. +- `git mv` `array.ts`, `string.ts`, `url.ts`, `math.ts`, `image.ts`, `file.ts`, `date.ts` (+ tests) from `apps/hudozka/src/lib/` to `packages/utils/src/`. +- Move `date-fns` from `apps/hudozka/package.json` to `packages/utils/package.json`. +- Codemod imports in `apps/hudozka`: `@/lib/array` → `@hudozka/utils`, etc. +- **Gate:** lint + test + build pass. + +### Phase 5 — Extract `@hudozka/text`, `@hudozka/hooks`, `@hudozka/ui` + +Order: text → hooks → ui (ui depends on hooks). + +- **`@hudozka/text`:** move `text.ts`, `markdown-it`, `typograf`, `@types/markdown-it`. Codemod imports. +- **`@hudozka/hooks`:** move `useToggle`, `useLockBodyScroll`, `useMediaQuery`, `useMobile`, `useReducedMotion` (+ tests). `useDarkTheme` and `useAccessibility` stay in `apps/hudozka`. Codemod imports. +- **`@hudozka/ui`:** move `Box`, `Button`, `Panel`, `Title`, `Overlay` (+ tests, CSS Modules). `Picture` and `ThemeColor` stay in `apps/hudozka/src/components/`. Move `classnames` dep. Codemod imports; Overlay's hook imports become `@hudozka/hooks`. +- **Gate after each package:** lint + test + build pass. + +### Phase 6 — Consolidate ESLint & Vitest configs +- Move `eslint.config.js` from `apps/hudozka/` back to repo root; scope Next config to `apps/hudozka/**`. +- Add minimal `vitest.config.ts` (and setup where needed) to `packages/ui` and `packages/hooks`; `packages/utils` gets a non-DOM config. +- Update root `package.json` scripts as above. +- **Gate:** `npm run lint` at root lints everything; `npm test` runs all workspace tests. + +### Phase 7 — Cleanup +- Delete orphaned files (stray `.DS_Store`, empty directories). +- Update root `README.md` with new layout (brief). +- Update root `CLAUDE.md` (Commands, Key Directories, Architecture paths). +- Spot-check `.github/workflows/*`, Dockerfile paths, `mise.toml`. + +## Risks and mitigations + +- **CSS Modules in `@hudozka/ui` consumed as source.** Next and Vite import `.module.css` natively; confirmed by current `src/ui/` usage. No build step needed. If a future consumer doesn't handle CSS, it must add that support itself. +- **Standalone Next output path change.** The Dockerfile runner copies a different path now. Explicit step in phase 3; guarded by the build gate. +- **Hoisted dev deps.** Different workspaces could theoretically disagree on major versions (e.g., `typescript`). Mitigation: all TS tooling lives only at the root; no workspace declares a conflicting version. +- **`experimental-strip-types` in `migrate-to-pocketbase`.** Still works when the script imports `@hudozka/utils` as source — Node strips types across the import boundary. Verified by the fact that utils has no non-TS-native syntax. +- **`eslint-config-next` globs.** Flat-config scoping must precisely target `apps/hudozka/**` to avoid leaking Next rules into packages. Phase 6 explicitly tests this by running `npm run lint` at root. + +## Deferred + +- Adopting `@hudozka/ui` or `@hudozka/hooks` in `apps/hudozka-writer`. +- Turborepo (revisit if build times or CI duration warrant it). +- Built-output packages + `.d.ts` emission (revisit if publishing or non-TS consumers appear). +- Further extraction from `apps/hudozka/src/` (e.g., splitting `remote/`, breaking up `components/`) — out of scope for this migration. diff --git a/eslint.config.js b/eslint.config.js index a8417834..83f1ae84 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -4,16 +4,24 @@ import globals from "globals" export default defineConfig([ globalIgnores([ - ".next/**", - "out/**", - "build/**", - "modules/**", - "public/static/**", - "pb_data/**", - "pb_migrations/**", - "next-env.d.ts", + "**/node_modules/**", + "**/.next/**", + "**/dist/**", + "**/build/**", + "**/out/**", + "apps/hudozka/public/static/**", + "pb/pb_data/**", + "pb/pb_migrations/**", + "**/next-env.d.ts", ]), - ...nextVitals, + { + files: ["apps/hudozka/**/*.{js,jsx,ts,tsx}"], + extends: nextVitals, + rules: { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + }, + }, { languageOptions: { globals: globals.browser, @@ -29,8 +37,6 @@ export default defineConfig([ "no-tabs": "error", "no-var": "error", "space-before-function-paren": "off", - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", }, }, ]) diff --git a/package-lock.json b/package-lock.json index e6b4990e..d9bf3023 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,33 +1,20 @@ { - "name": "hudozka", + "name": "hudozka-monorepo", "version": "7.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "hudozka", + "name": "hudozka-monorepo", "version": "7.0.0", "license": "ISC", - "dependencies": { - "@radix-ui/react-navigation-menu": "^1.2.0", - "@radix-ui/react-tooltip": "^1.2.0", - "classnames": "^2.5.0", - "dangerously-set-html-content": "^1.1.0", - "date-fns": "^4.0.0", - "markdown-it": "^14.1.1", - "next": "^16.2.0", - "pocketbase": "^0.26.0", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "react-icons": "^5.5.0", - "sharp": "^0.34.0", - "typograf": "^7.6.0", - "valtio": "^2.3.0" - }, + "workspaces": [ + "apps/*", + "packages/*" + ], "devDependencies": { "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "@types/markdown-it": "^14.1.0", "@types/node": "^25.2.0", "@types/react": "^19.2.0", "@typescript-eslint/eslint-plugin": "^8.59.0", @@ -40,6 +27,501 @@ "vitest": "^4.1.0" } }, + "apps/hudozka": { + "version": "7.0.0", + "license": "ISC", + "dependencies": { + "@hudozka/hooks": "*", + "@hudozka/text": "*", + "@hudozka/ui": "*", + "@hudozka/utils": "*", + "@radix-ui/react-navigation-menu": "^1.2.0", + "@radix-ui/react-tooltip": "^1.2.0", + "classnames": "^2.5.0", + "dangerously-set-html-content": "^1.1.0", + "next": "^16.2.0", + "pocketbase": "^0.26.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-icons": "^5.5.0", + "sharp": "^0.34.0", + "valtio": "^2.3.0" + } + }, + "apps/hudozka-writer": { + "version": "0.1.0", + "dependencies": { + "@tiptap/core": "^3.0.0", + "@tiptap/extension-blockquote": "^3.0.0", + "@tiptap/extension-bold": "^3.0.0", + "@tiptap/extension-bullet-list": "^3.0.0", + "@tiptap/extension-code": "^3.0.0", + "@tiptap/extension-code-block": "^3.0.0", + "@tiptap/extension-document": "^3.0.0", + "@tiptap/extension-hard-break": "^3.0.0", + "@tiptap/extension-heading": "^3.0.0", + "@tiptap/extension-horizontal-rule": "^3.0.0", + "@tiptap/extension-italic": "^3.0.0", + "@tiptap/extension-link": "^3.0.0", + "@tiptap/extension-list-item": "^3.0.0", + "@tiptap/extension-ordered-list": "^3.0.0", + "@tiptap/extension-paragraph": "^3.0.0", + "@tiptap/extension-strike": "^3.0.0", + "@tiptap/extension-text": "^3.0.0", + "@tiptap/markdown": "^3.0.0", + "@tiptap/pm": "^3.0.0", + "@tiptap/react": "^3.0.0", + "@tiptap/starter-kit": "^3.0.0", + "@tiptap/suggestion": "^3.22.2", + "pocketbase": "^0.25.0", + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "@types/react": "^19.1.0", + "@types/react-dom": "^19.1.0", + "@vitejs/plugin-react": "^5.2.0", + "typescript": "^5.9.0", + "vite": "^8.0.0" + } + }, + "apps/hudozka-writer/node_modules/pocketbase": { + "version": "0.25.2", + "license": "MIT" + }, + "apps/hudozka-writer/node_modules/typescript": { + "version": "5.9.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "apps/migrate-to-pocketbase": { + "version": "1.0.0", + "dependencies": { + "nanoid": "^5.0.0", + "pocketbase": "^0.26.0", + "sharp": "^0.33.0" + }, + "devDependencies": { + "@types/node": "^25.0.0" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "apps/migrate-to-pocketbase/node_modules/nanoid": { + "version": "5.1.9", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "apps/migrate-to-pocketbase/node_modules/sharp": { + "version": "0.33.5", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, "node_modules/@adobe/css-tools": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", @@ -199,6 +681,16 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -259,6 +751,38 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/runtime": { "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", @@ -604,6 +1128,22 @@ "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", "license": "MIT" }, + "node_modules/@hudozka/hooks": { + "resolved": "packages/hooks", + "link": true + }, + "node_modules/@hudozka/text": { + "resolved": "packages/text", + "link": true + }, + "node_modules/@hudozka/ui": { + "resolved": "packages/ui", + "link": true + }, + "node_modules/@hudozka/utils": { + "resolved": "packages/utils", + "link": true + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -2283,6 +2823,465 @@ } } }, + "node_modules/@tiptap/core": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.22.4.tgz", + "integrity": "sha512-vGIGm/HpqLg8EAAQXQ+koV+/S828OEpzocfWcPOwo1u2QUVf9dQG47Yy6JJ8zFFaJwfv4dBcOXli+7BrJwsxDQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "3.22.4" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.22.4.tgz", + "integrity": "sha512-7/61kNPbGFhMgM//zMknD0pSb69rGdRIkpulXOWS1JBrFHkH6hjZDfrOETNzgKkO+NlmzVl9rXSTv0xauS3lzA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.22.4.tgz", + "integrity": "sha512-jIaPKfNOQu2lhpbLDvtwlQqM+mjF+Kk+auHpzYjBnsuwUli1Cl5ZOau7RH+rru/SQvZe1DtpQlANujDywugZAA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.22.4.tgz", + "integrity": "sha512-v4pux5Ql3THAEjaLMY4ldtdy/Xy2qU7PJLBkq8ugLp8qicaKC+tpqxp6sGif4vLIjz7Ap5hurRbTNbXzszyyHA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4", + "@tiptap/pm": "3.22.4" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.22.4.tgz", + "integrity": "sha512-TB+d3fGcTixYjO7coKqTr1mGTJuqr8hjDCPUFgzuvKyJnBhqWITmBzQ/8CLq4rr6mihgGURbD3N+xkQuPAKFiw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.22.4" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.22.4.tgz", + "integrity": "sha512-cnbxmVhAcc7X3G81QUYEmKP0ve2hRmvAiFXBuuv9RUtQlBiRnzmhHoJOMgkX0CsMR7+8kMRpTfeDUYq2xp5s5w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.22.4.tgz", + "integrity": "sha512-MEurzNXfMET3rhjpoPJYUgMfxTdTqbzT9+ToFrqNGAHocdXVm6m1hhO2frVC7fEtHPnxXKsn0Z3NUbCRkRTLuA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4", + "@tiptap/pm": "3.22.4" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.22.4.tgz", + "integrity": "sha512-XQKla1+703FqQJC48tPDVgt9ucGiFbIEmQdOg5L5o07z9a6/NzuaZAc+1zJ7NxcUZzy+z6wBn1PrVMTiqiSXlw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.22.4.tgz", + "integrity": "sha512-N9/yMDC35jJp0V/naL0+6gi4gUDUIcPpWEzFdCDWUSYBA8mt41c1kI1ZU7UTKYIBzTClenhYHRc2XKZxxx0+LQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "3.22.4" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.22.4.tgz", + "integrity": "sha512-DFuyYxgaZPgxum5z1yvJPbfYCvDdO8geXsdyqt0qYYdiat3aGE4ncJhiLRIFDhSHBhaZg5eCgu/YPYAN6jZnrA==", + "license": "MIT", + "optional": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@floating-ui/dom": "^1.0.0", + "@tiptap/core": "3.22.4", + "@tiptap/pm": "3.22.4" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.22.4.tgz", + "integrity": "sha512-UYBEUj3SFpKINIE7AdzcyeS3xICK+ee+YLBbuqNXyHStYChjJOohzJehqiqhjR16A88KQQ+ZjgyDcItKGygSog==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "3.22.4" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.22.4.tgz", + "integrity": "sha512-xq+a4dE7T6VwApCkh/yU3p30gn3F8g8Arb9CyEZm58/WIJUIGvHSTjDdHmvU16+kiWSBg+wOOsaFHhYjJjxcKA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.22.4.tgz", + "integrity": "sha512-TUaj5f0Ir5qy9HKKt2ocnwfXKpZDYeHgbbP9gshKFzdq5PLe1RbIgkjfy6bnoI865cYjmPYWRjcT7XsKyIcb9Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.22.4.tgz", + "integrity": "sha512-cCI1HekGQwhY/MbgaKQ0R/7HcH5ZM1oFAyI/J72QGLC0XnF403S/OXoHMuBWr1mCu8hNiQWCzeNRJUty0iytNw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4", + "@tiptap/pm": "3.22.4" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.22.4.tgz", + "integrity": "sha512-fVSDx5AYXgDI3v2zZIqb7V8EewthwM2NJ/ZCX+XaxRsqNEpnjVhgHs7UlvDqK1wj2OJ6zmUNjPtVlAFRxwT+HQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.22.4.tgz", + "integrity": "sha512-uoP3yus02uwGPVzW2QaEPJWVIrUb/r5nKm6c8DiJv9fNSX1+gykZZMg42c6GwRFLZ/vyfWjVCbAE03VMUqafgA==", + "license": "MIT", + "dependencies": { + "linkifyjs": "^4.3.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4", + "@tiptap/pm": "3.22.4" + } + }, + "node_modules/@tiptap/extension-list": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.22.4.tgz", + "integrity": "sha512-Xe8UFvvHmyp/c/TJsFwlwU9CWACYbBirNsluJ3U1+H8BTu1wqdrT/AXR5uIXeyCl5kiWKgX5q71eHWbYFOrqrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4", + "@tiptap/pm": "3.22.4" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.22.4.tgz", + "integrity": "sha512-H659KXTvggSypIDWSOJBZ37jh9pKjQriDDvYPYvOZCdfij0D0hsDXN/wXoypArneUkoBdgruHfTtMkFOaQlgkw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.22.4" + } + }, + "node_modules/@tiptap/extension-list-keymap": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.22.4.tgz", + "integrity": "sha512-t/zhker4oIS78AIGYDdFFfZC6zSBlszfD7z/zqFLGCg5PHNNgkZK5hKj6Vyix6D2SapRn/ajnx+8mhbKIUH5eA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.22.4" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.22.4.tgz", + "integrity": "sha512-w77hPVf7pcHt97vfrybg/l0t5CimCd4y75OJKuHuo3CfgM5xbUP/gaPNMDyLLe7MYole/UHi/XvG3XjgzqTzAw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.22.4" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.22.4.tgz", + "integrity": "sha512-de6dFkIhigiENESY6rNJ3yTVS/337ybfP30dNPudTwGe9oAu9ZCS+04j6QCvXSjhlI3ULiv7wiSHqrP26Gd+Hw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.22.4.tgz", + "integrity": "sha512-aRHWQj42HiailXSC9LkKYM3jWMcSeGwOjbqM4PiuxQZmHVDRFmeHkfJItOdn2cSHaO0vuEVK+TvrWUWsBFi3pg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.22.4.tgz", + "integrity": "sha512-mM69uUW5cSxIhyEpWXi/YcfyupcJMDLCPEfYi62awH0iOP/LRoCv/nHjJq4Hyj/KxRJbe8HKwIUnqaCUf7m5Pg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.22.4.tgz", + "integrity": "sha512-08kGdbhIrA6h10GWXqOkqIveaBj5tmxclK208/nUIAlonI9hPd739vu7fmVtpnmqCnSSNpoRtU4u6Gj5at0ZpA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4" + } + }, + "node_modules/@tiptap/extensions": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.22.4.tgz", + "integrity": "sha512-fOe8VptJvLPs32bNdUYo8SRyljwqKNQVXWW056VoXIc5en/59OdJlJQVeHI0jRRciH3MtrqODi/gfJR0VHNZ8A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4", + "@tiptap/pm": "3.22.4" + } + }, + "node_modules/@tiptap/markdown": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/markdown/-/markdown-3.22.4.tgz", + "integrity": "sha512-gcoLOq5TBntw13QdeWMy7yc2X+b9yTplcFb5kpMQNgNoxlu0+jlX4LiBR+E6lOV+m+AtkprHHltTYyKG23bdOw==", + "license": "MIT", + "dependencies": { + "marked": "^17.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4", + "@tiptap/pm": "3.22.4" + } + }, + "node_modules/@tiptap/pm": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.22.4.tgz", + "integrity": "sha512-hj8Qka6WcHRllHUdeSjDnq2XaisUo4KsoGJc1WcFpoa1Yd+OeD861zUMnV7DFVGdZRy45Obht0CUYJpXQ4yA4w==", + "license": "MIT", + "dependencies": { + "prosemirror-changeset": "^2.3.0", + "prosemirror-commands": "^1.6.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-keymap": "^1.2.2", + "prosemirror-model": "^1.24.1", + "prosemirror-schema-list": "^1.5.0", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.6.4", + "prosemirror-transform": "^1.10.2", + "prosemirror-view": "^1.38.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/react": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.22.4.tgz", + "integrity": "sha512-XIQZPwLakR1t8+Q1UeCpr+kUHDWxpJzGy9r2xUi3mpPd6Wh8dtNltScBkUlCcr0sqc6J1GF6Is02JJVQGmCZMA==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "fast-equals": "^5.3.3", + "use-sync-external-store": "^1.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "optionalDependencies": { + "@tiptap/extension-bubble-menu": "^3.22.4", + "@tiptap/extension-floating-menu": "^3.22.4" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4", + "@tiptap/pm": "3.22.4", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.22.4.tgz", + "integrity": "sha512-qWjw+vfdin1rzMRpRU4cC5tLTwMJtUpXeQukv+6mOqqvhptuwuZBjUHImVEJaSPoHXS7+1ut+nTnrLyWyEuE5Q==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^3.22.4", + "@tiptap/extension-blockquote": "^3.22.4", + "@tiptap/extension-bold": "^3.22.4", + "@tiptap/extension-bullet-list": "^3.22.4", + "@tiptap/extension-code": "^3.22.4", + "@tiptap/extension-code-block": "^3.22.4", + "@tiptap/extension-document": "^3.22.4", + "@tiptap/extension-dropcursor": "^3.22.4", + "@tiptap/extension-gapcursor": "^3.22.4", + "@tiptap/extension-hard-break": "^3.22.4", + "@tiptap/extension-heading": "^3.22.4", + "@tiptap/extension-horizontal-rule": "^3.22.4", + "@tiptap/extension-italic": "^3.22.4", + "@tiptap/extension-link": "^3.22.4", + "@tiptap/extension-list": "^3.22.4", + "@tiptap/extension-list-item": "^3.22.4", + "@tiptap/extension-list-keymap": "^3.22.4", + "@tiptap/extension-ordered-list": "^3.22.4", + "@tiptap/extension-paragraph": "^3.22.4", + "@tiptap/extension-strike": "^3.22.4", + "@tiptap/extension-text": "^3.22.4", + "@tiptap/extension-underline": "^3.22.4", + "@tiptap/extensions": "^3.22.4", + "@tiptap/pm": "^3.22.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/suggestion": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@tiptap/suggestion/-/suggestion-3.22.4.tgz", + "integrity": "sha512-1buvLZemITTeKmPf2wGFWvvhRFKjdQ+JgMqc67xBraOKeDd8wQi1e2XlhCYAtlVMm5f6j+qlLC/MvwuHI2jHeQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.22.4", + "@tiptap/pm": "3.22.4" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -2302,6 +3301,51 @@ "license": "MIT", "peer": true }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -2380,12 +3424,26 @@ "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" } }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@types/whatwg-mimetype": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", @@ -2905,6 +3963,34 @@ "win32" ] }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@vitejs/plugin-react/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@vitest/expect": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", @@ -3525,11 +4611,23 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -3542,9 +4640,18 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3585,7 +4692,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -4644,6 +5750,15 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -5109,6 +6224,14 @@ "hermes-estree": "0.25.1" } }, + "node_modules/hudozka": { + "resolved": "apps/hudozka", + "link": true + }, + "node_modules/hudozka-writer": { + "resolved": "apps/hudozka-writer", + "link": true + }, "node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -5189,6 +6312,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT" + }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -6002,6 +7131,12 @@ "uc.micro": "^2.0.0" } }, + "node_modules/linkifyjs": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz", + "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6098,6 +7233,18 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/marked": { + "version": "17.0.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.6.tgz", + "integrity": "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6138,6 +7285,10 @@ "node": ">=8.6" } }, + "node_modules/migrate-to-pocketbase": { + "resolved": "apps/migrate-to-pocketbase", + "link": true + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -6275,6 +7426,34 @@ } } }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/node-exports-info": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", @@ -6463,6 +7642,12 @@ "node": ">= 0.8.0" } }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "license": "MIT" + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -6596,9 +7781,10 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6615,9 +7801,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -6682,6 +7868,135 @@ "dev": true, "license": "MIT" }, + "node_modules/prosemirror-changeset": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.1.tgz", + "integrity": "sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==", + "license": "MIT", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", + "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz", + "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz", + "integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz", + "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", + "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.25.4", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", + "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", + "license": "MIT", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz", + "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", + "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz", + "integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.2.3", + "prosemirror-model": "^1.25.4", + "prosemirror-state": "^1.4.4", + "prosemirror-transform": "^1.10.5", + "prosemirror-view": "^1.41.4" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.12.0.tgz", + "integrity": "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.41.8", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.8.tgz", + "integrity": "sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, "node_modules/proxy-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-3.0.1.tgz", @@ -6766,6 +8081,16 @@ "license": "MIT", "peer": true }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -6913,6 +8238,12 @@ "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" } }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7209,6 +8540,15 @@ "dev": true, "license": "ISC" }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -7830,6 +9170,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/valtio": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/valtio/-/valtio-2.3.1.tgz", @@ -7945,35 +9294,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/vite/node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/vitest": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", @@ -8077,6 +9397,12 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/whatwg-mimetype": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", @@ -8283,6 +9609,43 @@ "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } + }, + "packages/hooks": { + "name": "@hudozka/hooks", + "version": "0.0.0", + "peerDependencies": { + "react": ">=19" + } + }, + "packages/text": { + "name": "@hudozka/text", + "version": "0.0.0", + "dependencies": { + "markdown-it": "^14.1.0", + "typograf": "^7.6.0" + }, + "devDependencies": { + "@types/markdown-it": "^14.1.0" + } + }, + "packages/ui": { + "name": "@hudozka/ui", + "version": "0.0.0", + "dependencies": { + "@hudozka/hooks": "*", + "classnames": "^2.5.0" + }, + "peerDependencies": { + "react": ">=19", + "react-dom": ">=19" + } + }, + "packages/utils": { + "name": "@hudozka/utils", + "version": "0.0.0", + "dependencies": { + "date-fns": "^4.0.0" + } } } } diff --git a/package.json b/package.json index 72a1bf2d..456caf5f 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,17 @@ { - "name": "hudozka", + "name": "hudozka-monorepo", "private": true, "version": "7.0.0", "type": "module", "description": "", + "workspaces": ["apps/*", "packages/*"], "scripts": { - "start": "next start", - "dev": "next dev --turbo", - "build": "next build", + "dev": "npm run dev -w apps/hudozka", + "build": "npm run build -w apps/hudozka", + "start": "npm run start -w apps/hudozka", "lint": "eslint .", "format": "eslint --fix .", - "test": "vitest run" + "test": "npm test --workspaces --if-present" }, "repository": { "type": "git", @@ -18,31 +19,9 @@ }, "author": "Roman Timashev", "license": "ISC", - "dependencies": { - "@radix-ui/react-navigation-menu": "^1.2.0", - "@radix-ui/react-tooltip": "^1.2.0", - "classnames": "^2.5.0", - "dangerously-set-html-content": "^1.1.0", - "date-fns": "^4.0.0", - "markdown-it": "^14.1.1", - "next": "^16.2.0", - "pocketbase": "^0.26.0", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "react-icons": "^5.5.0", - "sharp": "^0.34.0", - "typograf": "^7.6.0", - "valtio": "^2.3.0" - }, - "overrides": { - "dangerously-set-html-content": { - "react": "$react" - } - }, "devDependencies": { "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "@types/markdown-it": "^14.1.0", "@types/node": "^25.2.0", "@types/react": "^19.2.0", "@typescript-eslint/eslint-plugin": "^8.59.0", diff --git a/packages/.gitkeep b/packages/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/hooks/package.json b/packages/hooks/package.json new file mode 100644 index 00000000..022f15cf --- /dev/null +++ b/packages/hooks/package.json @@ -0,0 +1,15 @@ +{ + "name": "@hudozka/hooks", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "test": "vitest run" + }, + "peerDependencies": { + "react": ">=19" + } +} diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts new file mode 100644 index 00000000..f844381a --- /dev/null +++ b/packages/hooks/src/index.ts @@ -0,0 +1,5 @@ +export * from "./useToggle" +export * from "./useLockBodyScroll" +export * from "./useMediaQuery" +export * from "./useMobile" +export * from "./useReducedMotion" diff --git a/src/hooks/useLockBodyScroll.test.ts b/packages/hooks/src/useLockBodyScroll.test.ts similarity index 100% rename from src/hooks/useLockBodyScroll.test.ts rename to packages/hooks/src/useLockBodyScroll.test.ts diff --git a/src/hooks/useLockBodyScroll.ts b/packages/hooks/src/useLockBodyScroll.ts similarity index 100% rename from src/hooks/useLockBodyScroll.ts rename to packages/hooks/src/useLockBodyScroll.ts diff --git a/src/hooks/useMediaQuery.test.ts b/packages/hooks/src/useMediaQuery.test.ts similarity index 100% rename from src/hooks/useMediaQuery.test.ts rename to packages/hooks/src/useMediaQuery.test.ts diff --git a/src/hooks/useMediaQuery.ts b/packages/hooks/src/useMediaQuery.ts similarity index 100% rename from src/hooks/useMediaQuery.ts rename to packages/hooks/src/useMediaQuery.ts diff --git a/src/hooks/useMobile.ts b/packages/hooks/src/useMobile.ts similarity index 64% rename from src/hooks/useMobile.ts rename to packages/hooks/src/useMobile.ts index 6bb83254..22175e60 100644 --- a/src/hooks/useMobile.ts +++ b/packages/hooks/src/useMobile.ts @@ -1,4 +1,4 @@ -import { useMediaQuery } from "@/hooks/useMediaQuery" +import { useMediaQuery } from "./useMediaQuery" export function useMobile(): boolean { return useMediaQuery("(max-width: 31.25em)", false) diff --git a/src/hooks/useReducedMotion.ts b/packages/hooks/src/useReducedMotion.ts similarity index 67% rename from src/hooks/useReducedMotion.ts rename to packages/hooks/src/useReducedMotion.ts index 14cdf96d..223ef649 100644 --- a/src/hooks/useReducedMotion.ts +++ b/packages/hooks/src/useReducedMotion.ts @@ -1,4 +1,4 @@ -import { useMediaQuery } from "@/hooks/useMediaQuery" +import { useMediaQuery } from "./useMediaQuery" export function useReducedMotion(): boolean { return useMediaQuery("(prefers-reduced-motion: reduce)") diff --git a/src/hooks/useToggle.test.ts b/packages/hooks/src/useToggle.test.ts similarity index 100% rename from src/hooks/useToggle.test.ts rename to packages/hooks/src/useToggle.test.ts diff --git a/src/hooks/useToggle.ts b/packages/hooks/src/useToggle.ts similarity index 100% rename from src/hooks/useToggle.ts rename to packages/hooks/src/useToggle.ts diff --git a/packages/hooks/tsconfig.json b/packages/hooks/tsconfig.json new file mode 100644 index 00000000..20f4beb5 --- /dev/null +++ b/packages/hooks/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"] +} diff --git a/packages/hooks/vitest.config.ts b/packages/hooks/vitest.config.ts new file mode 100644 index 00000000..73ed9bc1 --- /dev/null +++ b/packages/hooks/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + include: ["src/**/*.test.{ts,tsx}"], + environment: "happy-dom", + setupFiles: ["./vitest.setup.ts"], + }, +}) diff --git a/packages/hooks/vitest.setup.ts b/packages/hooks/vitest.setup.ts new file mode 100644 index 00000000..b9e76229 --- /dev/null +++ b/packages/hooks/vitest.setup.ts @@ -0,0 +1 @@ +import "@testing-library/jest-dom/vitest" diff --git a/packages/text/package.json b/packages/text/package.json new file mode 100644 index 00000000..296c37db --- /dev/null +++ b/packages/text/package.json @@ -0,0 +1,19 @@ +{ + "name": "@hudozka/text", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "test": "vitest run" + }, + "dependencies": { + "markdown-it": "^14.1.0", + "typograf": "^7.6.0" + }, + "devDependencies": { + "@types/markdown-it": "^14.1.0" + } +} diff --git a/packages/text/src/index.ts b/packages/text/src/index.ts new file mode 100644 index 00000000..6417a56d --- /dev/null +++ b/packages/text/src/index.ts @@ -0,0 +1 @@ +export * from "./text" diff --git a/src/lib/text.test.ts b/packages/text/src/text.test.ts similarity index 100% rename from src/lib/text.test.ts rename to packages/text/src/text.test.ts diff --git a/src/lib/text.ts b/packages/text/src/text.ts similarity index 100% rename from src/lib/text.ts rename to packages/text/src/text.ts diff --git a/packages/text/tsconfig.json b/packages/text/tsconfig.json new file mode 100644 index 00000000..20f4beb5 --- /dev/null +++ b/packages/text/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"] +} diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 00000000..b8845aeb --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,20 @@ +{ + "name": "@hudozka/ui", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "test": "vitest run" + }, + "dependencies": { + "classnames": "^2.5.0", + "@hudozka/hooks": "*" + }, + "peerDependencies": { + "react": ">=19", + "react-dom": ">=19" + } +} diff --git a/src/ui/Box/Box.test.tsx b/packages/ui/src/Box/Box.test.tsx similarity index 100% rename from src/ui/Box/Box.test.tsx rename to packages/ui/src/Box/Box.test.tsx diff --git a/src/ui/Box/box.module.css b/packages/ui/src/Box/box.module.css similarity index 100% rename from src/ui/Box/box.module.css rename to packages/ui/src/Box/box.module.css diff --git a/src/ui/Box/index.tsx b/packages/ui/src/Box/index.tsx similarity index 100% rename from src/ui/Box/index.tsx rename to packages/ui/src/Box/index.tsx diff --git a/src/ui/Button/Button.test.tsx b/packages/ui/src/Button/Button.test.tsx similarity index 100% rename from src/ui/Button/Button.test.tsx rename to packages/ui/src/Button/Button.test.tsx diff --git a/src/ui/Button/button.module.css b/packages/ui/src/Button/button.module.css similarity index 100% rename from src/ui/Button/button.module.css rename to packages/ui/src/Button/button.module.css diff --git a/src/ui/Button/index.tsx b/packages/ui/src/Button/index.tsx similarity index 100% rename from src/ui/Button/index.tsx rename to packages/ui/src/Button/index.tsx diff --git a/src/ui/Overlay/Transition.tsx b/packages/ui/src/Overlay/Transition.tsx similarity index 100% rename from src/ui/Overlay/Transition.tsx rename to packages/ui/src/Overlay/Transition.tsx diff --git a/src/ui/Overlay/index.tsx b/packages/ui/src/Overlay/index.tsx similarity index 88% rename from src/ui/Overlay/index.tsx rename to packages/ui/src/Overlay/index.tsx index 8676854d..2f35a7a9 100644 --- a/src/ui/Overlay/index.tsx +++ b/packages/ui/src/Overlay/index.tsx @@ -1,8 +1,7 @@ import s from "./styles.module.css" import { Transition } from "./Transition" -import { useReducedMotion } from "@/hooks/useReducedMotion" -import { useLockBodyScroll } from "@/hooks/useLockBodyScroll" +import { useLockBodyScroll, useReducedMotion } from "@hudozka/hooks" export type OverlayProps = { children?: React.ReactNode diff --git a/src/ui/Overlay/styles.module.css b/packages/ui/src/Overlay/styles.module.css similarity index 100% rename from src/ui/Overlay/styles.module.css rename to packages/ui/src/Overlay/styles.module.css diff --git a/src/ui/Panel/Panel.test.tsx b/packages/ui/src/Panel/Panel.test.tsx similarity index 100% rename from src/ui/Panel/Panel.test.tsx rename to packages/ui/src/Panel/Panel.test.tsx diff --git a/src/ui/Panel/index.tsx b/packages/ui/src/Panel/index.tsx similarity index 100% rename from src/ui/Panel/index.tsx rename to packages/ui/src/Panel/index.tsx diff --git a/src/ui/Panel/panel.module.css b/packages/ui/src/Panel/panel.module.css similarity index 100% rename from src/ui/Panel/panel.module.css rename to packages/ui/src/Panel/panel.module.css diff --git a/src/ui/Title/index.tsx b/packages/ui/src/Title/index.tsx similarity index 100% rename from src/ui/Title/index.tsx rename to packages/ui/src/Title/index.tsx diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts new file mode 100644 index 00000000..aec2c3c7 --- /dev/null +++ b/packages/ui/src/index.ts @@ -0,0 +1,5 @@ +export * from "./Box" +export * from "./Button" +export * from "./Panel" +export * from "./Title" +export * from "./Overlay" diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json new file mode 100644 index 00000000..20f4beb5 --- /dev/null +++ b/packages/ui/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"] +} diff --git a/packages/ui/vitest.config.ts b/packages/ui/vitest.config.ts new file mode 100644 index 00000000..942927ed --- /dev/null +++ b/packages/ui/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + include: ["src/**/*.test.{ts,tsx}"], + environment: "happy-dom", + setupFiles: ["./vitest.setup.tsx"], + css: true, + }, +}) diff --git a/packages/ui/vitest.setup.tsx b/packages/ui/vitest.setup.tsx new file mode 100644 index 00000000..b9e76229 --- /dev/null +++ b/packages/ui/vitest.setup.tsx @@ -0,0 +1 @@ +import "@testing-library/jest-dom/vitest" diff --git a/packages/utils/package.json b/packages/utils/package.json new file mode 100644 index 00000000..76797038 --- /dev/null +++ b/packages/utils/package.json @@ -0,0 +1,15 @@ +{ + "name": "@hudozka/utils", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "test": "vitest run --passWithNoTests" + }, + "dependencies": { + "date-fns": "^4.0.0" + } +} diff --git a/src/lib/array.test.ts b/packages/utils/src/array.test.ts similarity index 100% rename from src/lib/array.test.ts rename to packages/utils/src/array.test.ts diff --git a/src/lib/array.ts b/packages/utils/src/array.ts similarity index 100% rename from src/lib/array.ts rename to packages/utils/src/array.ts diff --git a/src/lib/date.test.ts b/packages/utils/src/date.test.ts similarity index 100% rename from src/lib/date.test.ts rename to packages/utils/src/date.test.ts diff --git a/src/lib/date.ts b/packages/utils/src/date.ts similarity index 100% rename from src/lib/date.ts rename to packages/utils/src/date.ts diff --git a/src/lib/file.test.ts b/packages/utils/src/file.test.ts similarity index 100% rename from src/lib/file.test.ts rename to packages/utils/src/file.test.ts diff --git a/src/lib/file.ts b/packages/utils/src/file.ts similarity index 100% rename from src/lib/file.ts rename to packages/utils/src/file.ts diff --git a/src/lib/image.test.ts b/packages/utils/src/image.test.ts similarity index 100% rename from src/lib/image.test.ts rename to packages/utils/src/image.test.ts diff --git a/src/lib/image.ts b/packages/utils/src/image.ts similarity index 100% rename from src/lib/image.ts rename to packages/utils/src/image.ts diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts new file mode 100644 index 00000000..27829d9c --- /dev/null +++ b/packages/utils/src/index.ts @@ -0,0 +1,7 @@ +export * from "./array" +export * from "./string" +export * from "./url" +export * from "./math" +export * from "./image" +export * from "./file" +export * from "./date" diff --git a/src/lib/math.test.ts b/packages/utils/src/math.test.ts similarity index 100% rename from src/lib/math.test.ts rename to packages/utils/src/math.test.ts diff --git a/src/lib/math.ts b/packages/utils/src/math.ts similarity index 100% rename from src/lib/math.ts rename to packages/utils/src/math.ts diff --git a/src/lib/string.test.ts b/packages/utils/src/string.test.ts similarity index 100% rename from src/lib/string.test.ts rename to packages/utils/src/string.test.ts diff --git a/src/lib/string.ts b/packages/utils/src/string.ts similarity index 100% rename from src/lib/string.ts rename to packages/utils/src/string.ts diff --git a/src/lib/url.test.ts b/packages/utils/src/url.test.ts similarity index 100% rename from src/lib/url.test.ts rename to packages/utils/src/url.test.ts diff --git a/src/lib/url.ts b/packages/utils/src/url.ts similarity index 100% rename from src/lib/url.ts rename to packages/utils/src/url.ts diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json new file mode 100644 index 00000000..20f4beb5 --- /dev/null +++ b/packages/utils/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"] +} diff --git a/packages/utils/vitest.config.ts b/packages/utils/vitest.config.ts new file mode 100644 index 00000000..f1dff742 --- /dev/null +++ b/packages/utils/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + include: ["src/**/*.test.ts"], + }, +}) diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000..c818c6fd --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["dom", "dom.iterable", "esnext"], + "module": "esnext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "allowJs": true, + "incremental": true + } +} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 3db94b3a..00000000 --- a/tsconfig.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@/*": [ - "src/*" - ] - }, - "target": "ES2020", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "jsx": "react-jsx", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ] - }, - "exclude": [ - "node_modules", - "modules" - ], - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts", - ".next/dev/types/**/*.ts" - ] -}