chore: scaffold Vite + React 19 + Tailwind v4 app on HMDesign

Start the React rebuild of the DWG/DXF viewer (#1).

- HMDesign is a git submodule (vendor/HMDesign, main) and its
  packages/ui is an npm workspace, so @hm-design/ui resolves to the
  submodule and shares this repo's node_modules.
- scripts/setup-hmdesign.mjs (postinstall) builds the package dist and
  points vendor/HMDesign/node_modules at ours with a junction, because
  HMDesign's build script reads typescript/shadcn from its own root.
  Nothing inside the submodule is modified.
- Versions follow HMDesign (react 19.3.0, vite 8.3.0, tailwindcss
  4.3.3, typescript 7.0.2) and the sample (three 0.185.1, dxf-parser
  1.1.2, opentype.js 2.0.0).
- CSS entry: tailwindcss + @hm-design/ui/tailwind.css (index.css).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
minsungandClaude Opus 5.5 committed 2026-10-12 18:48:53 +09:00
1 parent 1fd0b00df3
commit 948021c7ac
12 files changed
+8328

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
*.wasm binary
*.dwg binary
*.ttf binary
*.dxf -text
+6
View File
@@ -0,0 +1,6 @@
node_modules/
dist/
*.log
*.tsbuildinfo
.DS_Store
.vite/
+4
View File
@@ -0,0 +1,4 @@
[submodule "vendor/HMDesign"]
path = vendor/HMDesign
url = https://gitea.hmac.kr/b25027/HMDesign.git
branch = main
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DWG · DXF 뷰어</title>
<link rel="icon" href="data:," />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+8164
View File
File diff suppressed because it is too large. Load diff
+42
View File
@@ -0,0 +1,42 @@
{
"name": "dwg-dxf-viewer-hm",
"private": true,
"version": "0.2.0",
"description": "DWG/DXF 2D viewer: the dwg-dxf-viewer-sample engine (Viewer2D + acadrust-dwg + dxf-parser) inside a React 19 app built on HMDesign (@hm-design/ui).",
"type": "module",
"workspaces": [
"vendor/HMDesign/packages/ui"
],
"scripts": {
"postinstall": "node scripts/setup-hmdesign.mjs",
"hm:build": "node scripts/setup-hmdesign.mjs --force",
"dev": "vite --port 5173 --strictPort",
"build": "tsc -p tsconfig.json && vite build",
"preview": "vite preview --port 4173 --strictPort",
"typecheck": "tsc -p tsconfig.json",
"guard": "node vendor/HMDesign/scripts/ai-generation-guard.mjs --target . --scope FULL_SCREEN --mode GNB_ONLY --result index.html"
},
"dependencies": {
"@hm-design/ui": "*",
"dxf-parser": "1.1.2",
"lucide-react": "1.49.0",
"opentype.js": "2.0.0",
"react": "19.3.0",
"react-dom": "19.3.0",
"three": "0.185.1"
},
"devDependencies": {
"@babel/parser": "7.29.9",
"@tailwindcss/vite": "4.3.3",
"@types/node": "24.7.0",
"@types/react": "19.3.0",
"@types/react-dom": "19.3.0",
"@types/three": "0.185.1",
"@vitejs/plugin-react": "6.1.1",
"fast-glob": "3.3.3",
"postcss": "8.5.28",
"tailwindcss": "4.3.3",
"typescript": "7.0.2",
"vite": "8.3.0"
}
}
+48
View File
@@ -0,0 +1,48 @@
/**
* Prepare the HMDesign submodule for this app (runs as `postinstall`, or `npm run hm:build`).
*
* HMDesign is consumed as a git submodule (vendor/HMDesign) whose `packages/ui` is an npm workspace of this
* repo, so `@hm-design/ui` resolves to vendor/HMDesign/packages/ui and its dependencies are installed into
* this repo's node_modules. The package exports compiled `dist/` (JS, types, theme CSS, fonts), which Git
* does not carry, so it has to be built once after install.
*
* HMDesign's own package build (vendor/HMDesign/scripts/build-package.mjs) reads TypeScript and shadcn from
* `<HMDesign root>/node_modules/...` by absolute path. Instead of a second full `npm ci` inside the submodule
* (Storybook and friends), point that folder at this repo's node_modules with a directory junction. The
* submodule's .gitignore already ignores node_modules/, so its working tree stays clean.
*
* Nothing inside the submodule is edited.
*/
import { existsSync, lstatSync, symlinkSync, statSync } from "node:fs"
import path from "node:path"
import { fileURLToPath } from "node:url"
import { execFileSync } from "node:child_process"
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
const hm = path.join(root, "vendor", "HMDesign")
const uiPkg = path.join(hm, "packages", "ui")
const force = process.argv.includes("--force")
if (!existsSync(path.join(uiPkg, "package.json"))) {
console.error("[setup-hmdesign] vendor/HMDesign is empty. Run: git submodule update --init --recursive")
process.exit(1)
}
const hmNodeModules = path.join(hm, "node_modules")
const rootNodeModules = path.join(root, "node_modules")
if (!existsSync(hmNodeModules)) {
symlinkSync(rootNodeModules, hmNodeModules, "junction")
console.log("[setup-hmdesign] linked vendor/HMDesign/node_modules -> node_modules")
} else if (!lstatSync(hmNodeModules).isSymbolicLink()) {
console.log("[setup-hmdesign] vendor/HMDesign/node_modules is a real folder; using it as is")
}
const distEntry = path.join(uiPkg, "dist", "index.js")
const srcIndex = path.join(uiPkg, "src", "index.ts")
const stale = !existsSync(distEntry) || statSync(srcIndex).mtimeMs > statSync(distEntry).mtimeMs
if (force || stale) {
console.log("[setup-hmdesign] building @hm-design/ui (vendor/HMDesign/packages/ui/dist)")
execFileSync(process.execPath, [path.join(hm, "scripts", "build-package.mjs"), "ui"], { cwd: uiPkg, stdio: "inherit" })
} else {
console.log("[setup-hmdesign] @hm-design/ui dist is up to date")
}
+4
View File
@@ -0,0 +1,4 @@
/* Tailwind v4 + the public HM Tailwind setup (runtime-portability.md §2.1). The HM theme follows in hm-theme.css. */
@import "tailwindcss" source("./");
@import "@hm-design/ui/tailwind.css";
@source "../index.html";
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+19
View File
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "Bundler",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"isolatedModules": true,
"allowJs": true,
"resolveJsonModule": true,
"noFallthroughCasesInSwitch": true,
"types": ["vite/client"]
},
"include": ["src", "vite.config.ts"]
}
Vendored Submodule
+1
Submodule vendor/HMDesign added at d18e3c3380.
+22
View File
@@ -0,0 +1,22 @@
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import tailwindcss from "@tailwindcss/vite"
export default defineConfig({
plugins: [react(), tailwindcss()],
server: {
port: 5173,
strictPort: true,
},
resolve: {
// @hm-design/ui is a linked workspace (vendor/HMDesign/packages/ui); keep one React instance.
dedupe: ["react", "react-dom"],
},
optimizeDeps: {
// Only crawl this app's entry; vendor/HMDesign carries its own HTML entries (Storybook exports).
entries: ["index.html"],
include: ["three", "dxf-parser", "opentype.js"],
},
// acadrust-dwg WASM is imported with `?url` (src/engine/viewer2d/acadrustParser.ts).
assetsInclude: ["**/*.wasm"],
})