@taskmark/ui 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -6
- package/app/api/taskmark-reload/route.ts +103 -0
- package/app/board/page.tsx +6 -4
- package/app/layout.tsx +2 -0
- package/bin/lib/watch-board-markdown.mjs +65 -0
- package/bin/taskmark.js +190 -56
- package/components/board/app-bar.tsx +31 -34
- package/components/board/board-dev-reloader.tsx +49 -0
- package/components/board/epic-list.tsx +2 -4
- package/components/board/overall-work-items-list.tsx +2 -1
- package/components/board/static-board-app.tsx +0 -1
- package/components/board/story-list.tsx +2 -1
- package/components/board/task-list.tsx +2 -1
- package/components/board/work-item-sheet.tsx +15 -2
- package/components/board/work-items-list.tsx +2 -2
- package/components/setup/setup-wizard.tsx +2 -6
- package/dist/prod-next/BUILD_ID +1 -0
- package/dist/{standalone/.next → prod-next}/app-path-routes-manifest.json +3 -2
- package/dist/{standalone/.next → prod-next}/build-manifest.json +3 -3
- package/dist/prod-next/export-marker.json +6 -0
- package/dist/prod-next/images-manifest.json +68 -0
- package/dist/prod-next/next-minimal-server.js.nft.json +1 -0
- package/dist/prod-next/next-server.js.nft.json +1 -0
- package/dist/{standalone/.next → prod-next}/prerender-manifest.json +3 -3
- package/dist/{standalone/.next → prod-next}/react-loadable-manifest.json +2 -2
- package/dist/prod-next/required-server-files.js +337 -0
- package/dist/{standalone/.next → prod-next}/required-server-files.json +0 -7
- package/dist/{standalone/.next → prod-next}/routes-manifest.json +6 -0
- package/dist/prod-next/server/app/_global-error/page.js +32 -0
- package/dist/prod-next/server/app/_global-error/page.js.nft.json +1 -0
- package/dist/prod-next/server/app/_global-error/page_client-reference-manifest.js +1 -0
- package/dist/{standalone/.next → prod-next}/server/app/_global-error.html +1 -1
- package/dist/{standalone/.next → prod-next}/server/app/_global-error.rsc +1 -1
- package/dist/{standalone/.next → prod-next}/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/{standalone/.next → prod-next}/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/{standalone/.next → prod-next}/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/{standalone/.next → prod-next}/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/{standalone/.next → prod-next}/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/{standalone/.next → prod-next}/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/prod-next/server/app/_not-found/page.js +2 -0
- package/dist/prod-next/server/app/_not-found/page.js.nft.json +1 -0
- package/dist/prod-next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/dist/prod-next/server/app/_not-found.html +1 -0
- package/dist/prod-next/server/app/_not-found.rsc +21 -0
- package/dist/prod-next/server/app/_not-found.segments/_full.segment.rsc +21 -0
- package/dist/{standalone/.next → prod-next}/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/{standalone/.next → prod-next}/server/app/_not-found.segments/_index.segment.rsc +5 -4
- package/dist/{standalone/.next → prod-next}/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/{standalone/.next → prod-next}/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/{standalone/.next → prod-next}/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/prod-next/server/app/api/taskmark-reload/route.js +1 -0
- package/dist/prod-next/server/app/api/taskmark-reload/route.js.nft.json +1 -0
- package/dist/prod-next/server/app/api/taskmark-reload/route_client-reference-manifest.js +1 -0
- package/dist/{standalone/.next → prod-next}/server/app/board/page.js +14 -14
- package/dist/prod-next/server/app/board/page.js.nft.json +1 -0
- package/dist/prod-next/server/app/board/page_client-reference-manifest.js +1 -0
- package/dist/{standalone/.next → prod-next}/server/app/favicon.ico/route.js +1 -1
- package/dist/prod-next/server/app/favicon.ico/route.js.nft.json +1 -0
- package/dist/prod-next/server/app/page.js +2 -0
- package/dist/prod-next/server/app/page.js.nft.json +1 -0
- package/dist/prod-next/server/app/page_client-reference-manifest.js +1 -0
- package/dist/prod-next/server/app/setup/page.js +2 -0
- package/dist/prod-next/server/app/setup/page.js.nft.json +1 -0
- package/dist/prod-next/server/app/setup/page_client-reference-manifest.js +1 -0
- package/dist/{standalone/.next → prod-next}/server/app-paths-manifest.json +3 -2
- package/dist/{standalone/.next → prod-next}/server/chunks/319.js +1 -1
- package/dist/{standalone/.next → prod-next}/server/chunks/490.js +1 -1
- package/dist/prod-next/server/chunks/57.js +19 -0
- package/dist/prod-next/server/chunks/616.js +1 -0
- package/dist/prod-next/server/chunks/708.js +1 -0
- package/dist/prod-next/server/chunks/724.js +1 -0
- package/dist/{standalone/.next/server/chunks/445.js → prod-next/server/chunks/741.js} +2 -2
- package/dist/prod-next/server/functions-config-manifest.json +6 -0
- package/dist/prod-next/server/interception-route-rewrite-manifest.js +1 -0
- package/dist/{standalone/.next → prod-next}/server/middleware-build-manifest.js +1 -1
- package/dist/prod-next/server/middleware-react-loadable-manifest.js +1 -0
- package/dist/prod-next/server/pages/404.html +1 -0
- package/dist/{standalone/.next → prod-next}/server/pages/500.html +1 -1
- package/dist/prod-next/server/server-reference-manifest.js +1 -0
- package/dist/prod-next/server/server-reference-manifest.json +1 -0
- package/dist/prod-next/static/chunks/51.e9139a9ea6eb36b9.js +1 -0
- package/dist/{standalone/.next/static/chunks/781-f9f201ddc8c8591a.js → prod-next/static/chunks/613-6f794154d28bb75a.js} +1 -1
- package/dist/prod-next/static/chunks/app/_global-error/page-d34451c04262dd7d.js +1 -0
- package/dist/prod-next/static/chunks/app/api/taskmark-reload/route-d34451c04262dd7d.js +1 -0
- package/dist/prod-next/static/chunks/app/board/page-cd1d9be0c0127225.js +1 -0
- package/dist/prod-next/static/chunks/app/layout-856e8a282e69e7d0.js +1 -0
- package/dist/prod-next/static/chunks/app/page-d34451c04262dd7d.js +1 -0
- package/dist/prod-next/static/chunks/app/setup/page-c3003359e8becc84.js +1 -0
- package/dist/prod-next/static/chunks/next/dist/client/components/builtin/app-error-d34451c04262dd7d.js +1 -0
- package/dist/prod-next/static/chunks/next/dist/client/components/builtin/forbidden-d34451c04262dd7d.js +1 -0
- package/dist/prod-next/static/chunks/next/dist/client/components/builtin/not-found-d34451c04262dd7d.js +1 -0
- package/dist/prod-next/static/chunks/next/dist/client/components/builtin/unauthorized-d34451c04262dd7d.js +1 -0
- package/dist/{standalone/.next/static/chunks/webpack-b59eaf276b7fb38f.js → prod-next/static/chunks/webpack-bfe8992962004e2b.js} +1 -1
- package/dist/prod-next/static/wbcgYS3Jw87KqhnA9ttOL/_buildManifest.js +1 -0
- package/dist/public/taskmark-snapshot.json +1 -0
- package/lib/display-path.ts +6 -0
- package/lib/taskmark/dev-reload-token.ts +5 -0
- package/lib/taskmark/project-label.ts +3 -2
- package/lib/taskmark/validate.ts +4 -4
- package/next.config.ts +3 -8
- package/package.json +5 -4
- package/public/taskmark-snapshot.json +1 -1
- package/scripts/build-static.mjs +10 -1
- package/scripts/prepare-standalone.mjs +53 -20
- package/dist/standalone/.next/BUILD_ID +0 -1
- package/dist/standalone/.next/server/app/_global-error/page.js +0 -32
- package/dist/standalone/.next/server/app/_global-error/page.js.nft.json +0 -1
- package/dist/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +0 -1
- package/dist/standalone/.next/server/app/_not-found/page.js +0 -2
- package/dist/standalone/.next/server/app/_not-found/page.js.nft.json +0 -1
- package/dist/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
- package/dist/standalone/.next/server/app/_not-found.html +0 -1
- package/dist/standalone/.next/server/app/_not-found.rsc +0 -20
- package/dist/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +0 -20
- package/dist/standalone/.next/server/app/board/page.js.nft.json +0 -1
- package/dist/standalone/.next/server/app/board/page_client-reference-manifest.js +0 -1
- package/dist/standalone/.next/server/app/favicon.ico/route.js.nft.json +0 -1
- package/dist/standalone/.next/server/app/page.js +0 -2
- package/dist/standalone/.next/server/app/page.js.nft.json +0 -1
- package/dist/standalone/.next/server/app/page_client-reference-manifest.js +0 -1
- package/dist/standalone/.next/server/app/setup/page.js +0 -2
- package/dist/standalone/.next/server/app/setup/page.js.nft.json +0 -1
- package/dist/standalone/.next/server/app/setup/page_client-reference-manifest.js +0 -1
- package/dist/standalone/.next/server/chunks/164.js +0 -1
- package/dist/standalone/.next/server/chunks/57.js +0 -19
- package/dist/standalone/.next/server/chunks/570.js +0 -1
- package/dist/standalone/.next/server/chunks/666.js +0 -1
- package/dist/standalone/.next/server/functions-config-manifest.json +0 -4
- package/dist/standalone/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/dist/standalone/.next/server/pages/404.html +0 -1
- package/dist/standalone/.next/server/server-reference-manifest.js +0 -1
- package/dist/standalone/.next/server/server-reference-manifest.json +0 -1
- package/dist/standalone/.next/static/chunks/211.a43663747d668728.js +0 -1
- package/dist/standalone/.next/static/chunks/app/_global-error/page-82d456ade9f47021.js +0 -1
- package/dist/standalone/.next/static/chunks/app/board/page-6db8dec814516c28.js +0 -1
- package/dist/standalone/.next/static/chunks/app/layout-3745e06d87691845.js +0 -1
- package/dist/standalone/.next/static/chunks/app/page-82d456ade9f47021.js +0 -1
- package/dist/standalone/.next/static/chunks/app/setup/page-8a54c6f26633bdca.js +0 -1
- package/dist/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-82d456ade9f47021.js +0 -1
- package/dist/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-82d456ade9f47021.js +0 -1
- package/dist/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-82d456ade9f47021.js +0 -1
- package/dist/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-82d456ade9f47021.js +0 -1
- package/dist/standalone/.next/static/jm3kxf8c6qGe0cYKCktaO/_buildManifest.js +0 -1
- package/dist/standalone/node_modules/next/dist/compiled/jest-worker/processChild.js +0 -1
- package/dist/standalone/node_modules/next/dist/compiled/jest-worker/threadChild.js +0 -1
- package/dist/standalone/node_modules/next/dist/server/lib/start-server.js +0 -494
- package/dist/standalone/node_modules/next/dist/server/next-server.js +0 -1429
- package/dist/standalone/node_modules/next/dist/server/next.js +0 -455
- package/dist/standalone/node_modules/next/dist/server/require-hook.js +0 -82
- package/dist/standalone/node_modules/next/dist/server/route-modules/app-page/module.compiled.js +0 -36
- package/dist/standalone/node_modules/next/dist/server/route-modules/pages/module.compiled.js +0 -20
- package/dist/standalone/node_modules/styled-jsx/index.js +0 -1
- package/dist/standalone/node_modules/styled-jsx/package.json +0 -132
- package/dist/standalone/node_modules/styled-jsx/style.js +0 -1
- package/dist/standalone/package.json +0 -77
- package/dist/standalone/public/taskmark-snapshot.json +0 -1
- package/dist/standalone/server.js +0 -44
- /package/dist/{standalone/.next → prod-next}/package.json +0 -0
- /package/dist/{standalone/.next → prod-next}/server/app/_global-error.meta +0 -0
- /package/dist/{standalone/.next → prod-next}/server/app/_not-found.meta +0 -0
- /package/dist/{standalone/.next → prod-next}/server/app/favicon.ico.body +0 -0
- /package/dist/{standalone/.next → prod-next}/server/app/favicon.ico.meta +0 -0
- /package/dist/{standalone/.next → prod-next}/server/chunks/318.js +0 -0
- /package/dist/{standalone/.next → prod-next}/server/middleware-manifest.json +0 -0
- /package/dist/{standalone/.next → prod-next}/server/next-font-manifest.js +0 -0
- /package/dist/{standalone/.next → prod-next}/server/next-font-manifest.json +0 -0
- /package/dist/{standalone/.next → prod-next}/server/pages-manifest.json +0 -0
- /package/dist/{standalone/.next → prod-next}/server/prefetch-hints.json +0 -0
- /package/dist/{standalone/.next → prod-next}/server/webpack-runtime.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/chunks/4bd1b696-deb4a0a1da1923b0.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/chunks/669-8103441c67b185da.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/chunks/701-df3ee5f1cf837b1a.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/chunks/794-25668765560cb197.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/chunks/app/_not-found/page-0b3785af737e7968.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/chunks/framework-b29f9083d3cda3b2.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/chunks/main-app-bfb36455d24b3e5a.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/chunks/main-bb33c46a9c25dee8.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/chunks/next/dist/client/components/builtin/global-error-dc53ab29e0491fe4.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/chunks/polyfills-42372ed130431b0a.js +0 -0
- /package/dist/{standalone/.next → prod-next}/static/css/2e6ec4c80894e3c0.css +0 -0
- /package/dist/{standalone/.next → prod-next}/static/css/c593627ceef17a4b.css +0 -0
- /package/dist/{standalone/.next → prod-next}/static/media/36966cca54120369-s.p.woff2 +0 -0
- /package/dist/{standalone/.next → prod-next}/static/media/982ceffe7b733b3b-s.p.woff2 +0 -0
- /package/dist/{standalone/.next → prod-next}/static/media/b7387a63dd068245-s.woff2 +0 -0
- /package/dist/{standalone/.next → prod-next}/static/media/b8bfb68ab9ab617c-s.woff2 +0 -0
- /package/dist/{standalone/.next → prod-next}/static/media/e1aab0933260df4d-s.woff2 +0 -0
- /package/dist/{standalone/.next/static/jm3kxf8c6qGe0cYKCktaO → prod-next/static/wbcgYS3Jw87KqhnA9ttOL}/_ssgManifest.js +0 -0
- /package/dist/{standalone/public → public}/apple-touch-icon.png +0 -0
- /package/dist/{standalone/public → public}/favicon-96x96.png +0 -0
- /package/dist/{standalone/public → public}/favicon.ico +0 -0
- /package/dist/{standalone/public → public}/favicon.svg +0 -0
- /package/dist/{standalone/public → public}/file.svg +0 -0
- /package/dist/{standalone/public → public}/globe.svg +0 -0
- /package/dist/{standalone/public → public}/next.svg +0 -0
- /package/dist/{standalone/public → public}/og/og-default.png +0 -0
- /package/dist/{standalone/public → public}/site.webmanifest +0 -0
- /package/dist/{standalone/public → public}/tm_dark.png +0 -0
- /package/dist/{standalone/public → public}/tm_light.png +0 -0
- /package/dist/{standalone/public → public}/vercel.svg +0 -0
- /package/dist/{standalone/public → public}/web-app-manifest-192x192.png +0 -0
- /package/dist/{standalone/public → public}/web-app-manifest-512x512.png +0 -0
- /package/dist/{standalone/public → public}/window.svg +0 -0
package/README.md
CHANGED
|
@@ -12,6 +12,15 @@ npx taskmark serve
|
|
|
12
12
|
|
|
13
13
|
Opens [http://localhost:8275](http://localhost:8275) bound to **this** board.
|
|
14
14
|
|
|
15
|
+
### Development (live board reload)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx taskmark dev
|
|
19
|
+
# or: npm run dev / yarn dev
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Runs Next.js in development on port **8275** and refreshes the UI when board `.md` files change.
|
|
23
|
+
|
|
15
24
|
Without a local install (once published):
|
|
16
25
|
|
|
17
26
|
```bash
|
|
@@ -49,16 +58,17 @@ You do **not** need to clone `taskmark-frontend`.
|
|
|
49
58
|
"scripts": {
|
|
50
59
|
"start": "taskmark serve --no-open",
|
|
51
60
|
"serve": "taskmark serve",
|
|
61
|
+
"dev": "taskmark dev",
|
|
52
62
|
"build": "taskmark build --board . --out out",
|
|
53
63
|
"preview": "taskmark preview"
|
|
54
64
|
},
|
|
55
65
|
"dependencies": {
|
|
56
|
-
"@taskmark/ui": "^0.2.
|
|
66
|
+
"@taskmark/ui": "^0.2.5"
|
|
57
67
|
}
|
|
58
68
|
}
|
|
59
69
|
```
|
|
60
70
|
|
|
61
|
-
Then `npm install && npm run serve` (or `npm start`). For **Vercel**, use `npm run build` (static HTML in `out/`) — see the board-ui-stub `vercel.json`.
|
|
71
|
+
Then `npm install && npm run serve` (or `npm start`). Use `npm run dev` while editing board markdown. For **Vercel**, use `npm run build` (static HTML in `out/`) — see the board-ui-stub `vercel.json`.
|
|
62
72
|
|
|
63
73
|
### Static production build
|
|
64
74
|
|
|
@@ -78,14 +88,15 @@ Favicon, webmanifest, and Open Graph assets ship in `public/` and are included i
|
|
|
78
88
|
```bash
|
|
79
89
|
npm install
|
|
80
90
|
npm run build # next standalone + dist/ for the CLI
|
|
81
|
-
npm run serve #
|
|
82
|
-
npm run dev #
|
|
91
|
+
npm run serve # production standalone against cwd/env board
|
|
92
|
+
npm run dev # taskmark dev (Next + markdown live reload) on :8275
|
|
83
93
|
```
|
|
84
94
|
|
|
85
|
-
Zero-config for `npm run dev`: `TASKMARK_BOARD` / `TASKMARK_MASTER` / cwd (see above). Cookie multi-master setup still works when unbound.
|
|
95
|
+
Zero-config for `npm run dev` / `serve`: `TASKMARK_BOARD` / `TASKMARK_MASTER` / cwd (see above). Cookie multi-master setup still works when unbound.
|
|
86
96
|
|
|
87
97
|
## Stack
|
|
88
98
|
|
|
89
|
-
- Next.js
|
|
99
|
+
- Next.js production server packaged for `taskmark serve` (`next start` + slim `dist/prod-next`)
|
|
100
|
+
- `taskmark dev` for live board markdown reload
|
|
90
101
|
- Tailwind CSS v4
|
|
91
102
|
- Local markdown boards only (no hosted sync)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import fs from "node:fs"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
|
|
4
|
+
import { cwdCandidates } from "@/lib/taskmark/autoconfig"
|
|
5
|
+
import { resolveBoardAtPath } from "@/lib/taskmark/discover"
|
|
6
|
+
|
|
7
|
+
export const dynamic = "force-dynamic"
|
|
8
|
+
export const runtime = "nodejs"
|
|
9
|
+
|
|
10
|
+
function resolveBoardRoot(): string | null {
|
|
11
|
+
const env = process.env.TASKMARK_BOARD?.trim()
|
|
12
|
+
if (env) {
|
|
13
|
+
const board = resolveBoardAtPath(path.resolve(env))
|
|
14
|
+
if (board) return board
|
|
15
|
+
}
|
|
16
|
+
for (const candidate of cwdCandidates()) {
|
|
17
|
+
const board = resolveBoardAtPath(candidate)
|
|
18
|
+
if (board) return board
|
|
19
|
+
}
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function walkMarkdownFiles(root: string, out: string[] = []): string[] {
|
|
24
|
+
let entries: fs.Dirent[]
|
|
25
|
+
try {
|
|
26
|
+
entries = fs.readdirSync(root, { withFileTypes: true })
|
|
27
|
+
} catch {
|
|
28
|
+
return out
|
|
29
|
+
}
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "out") {
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
const full = path.join(root, entry.name)
|
|
35
|
+
if (entry.isDirectory()) {
|
|
36
|
+
walkMarkdownFiles(full, out)
|
|
37
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
38
|
+
out.push(full)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return out
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function latestMarkdownMtime(boardRoot: string): number {
|
|
45
|
+
let latest = 0
|
|
46
|
+
for (const file of walkMarkdownFiles(boardRoot)) {
|
|
47
|
+
try {
|
|
48
|
+
const mtime = fs.statSync(file).mtimeMs
|
|
49
|
+
if (mtime > latest) latest = mtime
|
|
50
|
+
} catch {
|
|
51
|
+
// ignore transient deletes
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return latest
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* SSE stream that emits `reload` when any board `.md` mtime changes.
|
|
59
|
+
* Used only by `taskmark dev` / Next development mode.
|
|
60
|
+
*/
|
|
61
|
+
export async function GET() {
|
|
62
|
+
if (process.env.NODE_ENV !== "development") {
|
|
63
|
+
return new Response("Not available", { status: 404 })
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const boardRoot = resolveBoardRoot()
|
|
67
|
+
if (!boardRoot) {
|
|
68
|
+
return new Response("No board", { status: 404 })
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const encoder = new TextEncoder()
|
|
72
|
+
let last = latestMarkdownMtime(boardRoot)
|
|
73
|
+
let interval: ReturnType<typeof setInterval> | null = null
|
|
74
|
+
|
|
75
|
+
const stream = new ReadableStream({
|
|
76
|
+
start(controller) {
|
|
77
|
+
const send = (event: string, data: string) => {
|
|
78
|
+
controller.enqueue(encoder.encode(`event: ${event}\ndata: ${data}\n\n`))
|
|
79
|
+
}
|
|
80
|
+
send("hello", String(Date.now()))
|
|
81
|
+
interval = setInterval(() => {
|
|
82
|
+
const next = latestMarkdownMtime(boardRoot)
|
|
83
|
+
if (next > last) {
|
|
84
|
+
last = next
|
|
85
|
+
send("reload", String(Date.now()))
|
|
86
|
+
} else {
|
|
87
|
+
send("ping", String(Date.now()))
|
|
88
|
+
}
|
|
89
|
+
}, 750)
|
|
90
|
+
},
|
|
91
|
+
cancel() {
|
|
92
|
+
if (interval) clearInterval(interval)
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
return new Response(stream, {
|
|
97
|
+
headers: {
|
|
98
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
99
|
+
"Cache-Control": "no-cache, no-transform",
|
|
100
|
+
Connection: "keep-alive",
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
}
|
package/app/board/page.tsx
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
parseListViewMode,
|
|
24
24
|
type ListViewMode,
|
|
25
25
|
} from "@/lib/taskmark/list-view-mode"
|
|
26
|
+
import { DEV_RELOAD_TOKEN } from "@/lib/taskmark/dev-reload-token"
|
|
26
27
|
import { loadSnapshotFromPublic } from "@/lib/taskmark/load-snapshot-server"
|
|
27
28
|
import { parseEpicsForProject } from "@/lib/taskmark/parse-epics"
|
|
28
29
|
import {
|
|
@@ -38,6 +39,11 @@ import {
|
|
|
38
39
|
import { isStaticRuntime } from "@/lib/taskmark/static-mode"
|
|
39
40
|
import { loadWorkspace } from "@/lib/taskmark/workspace"
|
|
40
41
|
|
|
42
|
+
export const dynamic = "force-dynamic"
|
|
43
|
+
|
|
44
|
+
// Keep this import so `taskmark dev` can touch the token module and refresh RSC.
|
|
45
|
+
void DEV_RELOAD_TOKEN
|
|
46
|
+
|
|
41
47
|
type BoardPageProps = {
|
|
42
48
|
searchParams: Promise<{
|
|
43
49
|
view?: string | string[]
|
|
@@ -143,7 +149,6 @@ export default async function BoardPage({ searchParams }: BoardPageProps) {
|
|
|
143
149
|
<AppBar
|
|
144
150
|
projects={workspace.projects}
|
|
145
151
|
activeProjectId={activeProject.id}
|
|
146
|
-
masterCount={workspace.masters.length}
|
|
147
152
|
autoconfig={workspace.autoconfig}
|
|
148
153
|
/>
|
|
149
154
|
|
|
@@ -177,9 +182,6 @@ export default async function BoardPage({ searchParams }: BoardPageProps) {
|
|
|
177
182
|
? ` · story ${selectedStoryId} not in list`
|
|
178
183
|
: ""}
|
|
179
184
|
</p>
|
|
180
|
-
<p className="mt-1 font-mono text-xs text-muted-foreground">
|
|
181
|
-
{activeProject.boardPath}
|
|
182
|
-
</p>
|
|
183
185
|
</div>
|
|
184
186
|
<ListViewSwitcher
|
|
185
187
|
activeView={activeView}
|
package/app/layout.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Metadata, Viewport } from "next"
|
|
2
2
|
import { Archivo_Black, Space_Grotesk } from "next/font/google"
|
|
3
|
+
import { BoardDevReloader } from "@/components/board/board-dev-reloader"
|
|
3
4
|
import { Toaster } from "@/components/ui/sonner"
|
|
4
5
|
import { SITE } from "@/lib/site"
|
|
5
6
|
import "./globals.css"
|
|
@@ -80,6 +81,7 @@ export default function RootLayout({
|
|
|
80
81
|
className={`${archivoBlack.variable} ${spaceGrotesk.variable} font-sans antialiased`}
|
|
81
82
|
>
|
|
82
83
|
<Toaster />
|
|
84
|
+
<BoardDevReloader />
|
|
83
85
|
{children}
|
|
84
86
|
</body>
|
|
85
87
|
</html>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import fs from "node:fs"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
|
|
4
|
+
const IGNORE_DIRS = new Set([
|
|
5
|
+
"node_modules",
|
|
6
|
+
".git",
|
|
7
|
+
"out",
|
|
8
|
+
".next",
|
|
9
|
+
"dist",
|
|
10
|
+
".taskmark-ui-build",
|
|
11
|
+
])
|
|
12
|
+
|
|
13
|
+
function shouldIgnoreDir(name) {
|
|
14
|
+
return IGNORE_DIRS.has(name) || name.startsWith(".")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function latestMarkdownMtime(boardRoot) {
|
|
18
|
+
let latest = 0
|
|
19
|
+
const walk = (dir) => {
|
|
20
|
+
let entries
|
|
21
|
+
try {
|
|
22
|
+
entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
23
|
+
} catch {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
const full = path.join(dir, entry.name)
|
|
28
|
+
if (entry.isDirectory()) {
|
|
29
|
+
if (!shouldIgnoreDir(entry.name)) walk(full)
|
|
30
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
31
|
+
try {
|
|
32
|
+
latest = Math.max(latest, fs.statSync(full).mtimeMs)
|
|
33
|
+
} catch {
|
|
34
|
+
// ignore races
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
walk(boardRoot)
|
|
40
|
+
return latest
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Poll a board directory for `.md` mtime changes.
|
|
45
|
+
* Uses polling (not recursive fs.watch) to avoid EMFILE on large boards.
|
|
46
|
+
* Returns a stop function.
|
|
47
|
+
*/
|
|
48
|
+
export function watchBoardMarkdown(boardRoot, onChange) {
|
|
49
|
+
let stopped = false
|
|
50
|
+
let lastStamp = latestMarkdownMtime(boardRoot)
|
|
51
|
+
|
|
52
|
+
const interval = setInterval(() => {
|
|
53
|
+
if (stopped) return
|
|
54
|
+
const next = latestMarkdownMtime(boardRoot)
|
|
55
|
+
if (next > lastStamp) {
|
|
56
|
+
lastStamp = next
|
|
57
|
+
onChange()
|
|
58
|
+
}
|
|
59
|
+
}, 750)
|
|
60
|
+
|
|
61
|
+
return () => {
|
|
62
|
+
stopped = true
|
|
63
|
+
clearInterval(interval)
|
|
64
|
+
}
|
|
65
|
+
}
|
package/bin/taskmark.js
CHANGED
|
@@ -3,23 +3,28 @@ import { spawn } from "node:child_process"
|
|
|
3
3
|
import fs from "node:fs"
|
|
4
4
|
import http from "node:http"
|
|
5
5
|
import path from "node:path"
|
|
6
|
+
import { createRequire } from "node:module"
|
|
6
7
|
import { fileURLToPath } from "node:url"
|
|
7
8
|
|
|
8
9
|
import { resolveServeBoard } from "./lib/resolve-board.mjs"
|
|
9
10
|
import { startStaticServer } from "./lib/static-preview.mjs"
|
|
11
|
+
import { watchBoardMarkdown } from "./lib/watch-board-markdown.mjs"
|
|
10
12
|
|
|
11
13
|
const DEFAULT_PORT = 8275
|
|
12
14
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
13
15
|
const packageRoot = path.resolve(__dirname, "..")
|
|
16
|
+
const requireFromPackage = createRequire(path.join(packageRoot, "package.json"))
|
|
14
17
|
|
|
15
18
|
function printHelp() {
|
|
16
19
|
console.log(`Usage:
|
|
17
20
|
taskmark serve [options]
|
|
21
|
+
taskmark dev [options]
|
|
18
22
|
taskmark build [options]
|
|
19
23
|
taskmark preview [options]
|
|
20
24
|
|
|
21
25
|
Commands:
|
|
22
26
|
serve Start the prebuilt local UI server (default port ${DEFAULT_PORT})
|
|
27
|
+
dev Next.js development server with board markdown live reload
|
|
23
28
|
build Production static HTML export for Vercel / static hosting
|
|
24
29
|
preview Serve an existing static export (default: <board>/out)
|
|
25
30
|
|
|
@@ -27,7 +32,7 @@ Options:
|
|
|
27
32
|
--port, -p <n> Listen port (default ${DEFAULT_PORT}; env PORT / TASKMARK_PORT)
|
|
28
33
|
--board <path> Board or product root (sets TASKMARK_BOARD)
|
|
29
34
|
--out <dir> Static output directory for build/preview (default: <board>/out)
|
|
30
|
-
--no-open Do not open a browser (serve / preview)
|
|
35
|
+
--no-open Do not open a browser (serve / preview / dev)
|
|
31
36
|
--help, -h Show help
|
|
32
37
|
|
|
33
38
|
Board resolution (same as the UI):
|
|
@@ -38,6 +43,7 @@ Board resolution (same as the UI):
|
|
|
38
43
|
|
|
39
44
|
Examples:
|
|
40
45
|
npm i @taskmark/ui --save && npx taskmark serve
|
|
46
|
+
npx taskmark dev --board .
|
|
41
47
|
npx taskmark build --board .
|
|
42
48
|
npm run preview
|
|
43
49
|
npx taskmark preview --port 9000
|
|
@@ -97,15 +103,73 @@ function resolvePort(raw) {
|
|
|
97
103
|
return n
|
|
98
104
|
}
|
|
99
105
|
|
|
100
|
-
function
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
function resolveNextBin() {
|
|
107
|
+
try {
|
|
108
|
+
return requireFromPackage.resolve("next/dist/bin/next")
|
|
109
|
+
} catch {
|
|
110
|
+
const fallback = path.join(
|
|
111
|
+
packageRoot,
|
|
112
|
+
"node_modules",
|
|
113
|
+
"next",
|
|
114
|
+
"dist",
|
|
115
|
+
"bin",
|
|
116
|
+
"next"
|
|
117
|
+
)
|
|
118
|
+
if (fs.existsSync(fallback)) return fallback
|
|
119
|
+
throw new Error(
|
|
120
|
+
"next binary not found. Reinstall @taskmark/ui (next is required for serve/dev)."
|
|
121
|
+
)
|
|
107
122
|
}
|
|
108
|
-
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Ensure packageRoot/.next points at a usable production build.
|
|
127
|
+
* Published packages ship dist/prod-next (npm cannot pack nested node_modules
|
|
128
|
+
* used by Next standalone).
|
|
129
|
+
*/
|
|
130
|
+
function ensureProdNext() {
|
|
131
|
+
const nextDir = path.join(packageRoot, ".next")
|
|
132
|
+
const shipped = path.join(packageRoot, "dist", "prod-next")
|
|
133
|
+
const hasLocalServer = fs.existsSync(path.join(nextDir, "server"))
|
|
134
|
+
const hasShippedServer = fs.existsSync(path.join(shipped, "server"))
|
|
135
|
+
|
|
136
|
+
if (hasLocalServer) return nextDir
|
|
137
|
+
if (!hasShippedServer) {
|
|
138
|
+
throw new Error(`Prebuilt UI not found.
|
|
139
|
+
|
|
140
|
+
Expected ${shipped} (or a local .next/server from npm run build).
|
|
141
|
+
Reinstall @taskmark/ui or rebuild the package.`)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (fs.existsSync(nextDir)) {
|
|
145
|
+
const st = fs.lstatSync(nextDir)
|
|
146
|
+
if (st.isSymbolicLink() || st.isDirectory()) {
|
|
147
|
+
fs.rmSync(nextDir, { recursive: true, force: true })
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const type = process.platform === "win32" ? "junction" : "dir"
|
|
153
|
+
fs.symlinkSync(shipped, nextDir, type)
|
|
154
|
+
} catch {
|
|
155
|
+
fs.cpSync(shipped, nextDir, { recursive: true })
|
|
156
|
+
}
|
|
157
|
+
return nextDir
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function touchDevReloadToken() {
|
|
161
|
+
const tokenFile = path.join(
|
|
162
|
+
packageRoot,
|
|
163
|
+
"lib",
|
|
164
|
+
"taskmark",
|
|
165
|
+
"dev-reload-token.ts"
|
|
166
|
+
)
|
|
167
|
+
const stamp = String(Date.now())
|
|
168
|
+
fs.writeFileSync(
|
|
169
|
+
tokenFile,
|
|
170
|
+
`/**\n * Touched by \`taskmark dev\` when board markdown changes so Next Fast Refresh\n * re-runs server components that import this module.\n */\nexport const DEV_RELOAD_TOKEN = "${stamp}"\n`,
|
|
171
|
+
"utf8"
|
|
172
|
+
)
|
|
109
173
|
}
|
|
110
174
|
|
|
111
175
|
function openBrowser(url) {
|
|
@@ -155,8 +219,8 @@ function waitForServer(port, timeoutMs = 60_000) {
|
|
|
155
219
|
})
|
|
156
220
|
}
|
|
157
221
|
|
|
158
|
-
|
|
159
|
-
const resolved = resolveServeBoard(
|
|
222
|
+
function resolveBoardOrExit(boardArg, command) {
|
|
223
|
+
const resolved = resolveServeBoard(boardArg)
|
|
160
224
|
if (!resolved) {
|
|
161
225
|
console.error(`No Taskmark board found.
|
|
162
226
|
|
|
@@ -166,50 +230,126 @@ Looked for:
|
|
|
166
230
|
- cwd layouts: nested ./taskmark/ or dedicated *-taskmark root
|
|
167
231
|
|
|
168
232
|
Fix: cd into a product repo (with ./taskmark/) or a board root, or set TASKMARK_BOARD.
|
|
169
|
-
Then re-run: npx taskmark
|
|
170
|
-
(or: npx -p @taskmark/ui taskmark
|
|
233
|
+
Then re-run: npx taskmark ${command}
|
|
234
|
+
(or: npx -p @taskmark/ui taskmark ${command})`)
|
|
171
235
|
process.exit(1)
|
|
172
236
|
}
|
|
237
|
+
return resolved
|
|
238
|
+
}
|
|
173
239
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
240
|
+
function attachChildLifecycle(child, onShutdown) {
|
|
241
|
+
const shutdown = (signal) => {
|
|
242
|
+
onShutdown?.()
|
|
243
|
+
if (!child.killed) child.kill(signal)
|
|
244
|
+
}
|
|
245
|
+
process.on("SIGINT", () => shutdown("SIGINT"))
|
|
246
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"))
|
|
247
|
+
child.on("exit", (code, signal) => {
|
|
248
|
+
onShutdown?.()
|
|
249
|
+
if (signal) process.exit(1)
|
|
250
|
+
process.exit(code ?? 0)
|
|
251
|
+
})
|
|
252
|
+
return shutdown
|
|
253
|
+
}
|
|
177
254
|
|
|
178
|
-
|
|
179
|
-
|
|
255
|
+
async function serve(args) {
|
|
256
|
+
const resolved = resolveBoardOrExit(args.board, "serve")
|
|
257
|
+
const port = resolvePort(args.port)
|
|
258
|
+
const url = `http://localhost:${port}`
|
|
259
|
+
const nextBin = resolveNextBin()
|
|
180
260
|
|
|
181
|
-
|
|
261
|
+
try {
|
|
262
|
+
ensureProdNext()
|
|
263
|
+
} catch (err) {
|
|
264
|
+
console.error(String(err?.message || err))
|
|
182
265
|
process.exit(1)
|
|
183
266
|
}
|
|
184
267
|
|
|
268
|
+
console.log(`Taskmark UI`)
|
|
269
|
+
console.log(` board: ${resolved.boardPath} (${resolved.source})`)
|
|
270
|
+
console.log(` listen: ${url}`)
|
|
271
|
+
|
|
272
|
+
const child = spawn(
|
|
273
|
+
process.execPath,
|
|
274
|
+
[nextBin, "start", "-H", "0.0.0.0", "-p", String(port)],
|
|
275
|
+
{
|
|
276
|
+
cwd: packageRoot,
|
|
277
|
+
env: {
|
|
278
|
+
...process.env,
|
|
279
|
+
PORT: String(port),
|
|
280
|
+
HOSTNAME: "0.0.0.0",
|
|
281
|
+
TASKMARK_BOARD: resolved.boardPath,
|
|
282
|
+
NODE_ENV: "production",
|
|
283
|
+
},
|
|
284
|
+
stdio: "inherit",
|
|
285
|
+
}
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
const shutdown = attachChildLifecycle(child)
|
|
289
|
+
|
|
290
|
+
const hosted =
|
|
291
|
+
Boolean(process.env.VERCEL) ||
|
|
292
|
+
Boolean(process.env.NOW_REGION) ||
|
|
293
|
+
process.env.CI === "true"
|
|
294
|
+
const shouldOpen = args.open && !hosted
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
await waitForServer(port)
|
|
298
|
+
if (shouldOpen) {
|
|
299
|
+
openBrowser(url)
|
|
300
|
+
} else {
|
|
301
|
+
console.log(`Ready: ${url}`)
|
|
302
|
+
}
|
|
303
|
+
} catch (err) {
|
|
304
|
+
console.error(String(err?.message || err))
|
|
305
|
+
shutdown("SIGTERM")
|
|
306
|
+
process.exit(1)
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function dev(args) {
|
|
311
|
+
const resolved = resolveBoardOrExit(args.board, "dev")
|
|
185
312
|
const port = resolvePort(args.port)
|
|
186
313
|
const url = `http://localhost:${port}`
|
|
187
|
-
const
|
|
314
|
+
const nextBin = resolveNextBin()
|
|
188
315
|
|
|
189
|
-
console.log(`Taskmark UI`)
|
|
316
|
+
console.log(`Taskmark UI (dev)`)
|
|
190
317
|
console.log(` board: ${resolved.boardPath} (${resolved.source})`)
|
|
191
318
|
console.log(` listen: ${url}`)
|
|
319
|
+
console.log(` watch: **/*.md under board`)
|
|
192
320
|
|
|
193
|
-
const child = spawn(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
321
|
+
const child = spawn(
|
|
322
|
+
process.execPath,
|
|
323
|
+
[nextBin, "dev", "--webpack", "-H", "0.0.0.0", "-p", String(port)],
|
|
324
|
+
{
|
|
325
|
+
cwd: packageRoot,
|
|
326
|
+
env: {
|
|
327
|
+
...process.env,
|
|
328
|
+
PORT: String(port),
|
|
329
|
+
TASKMARK_BOARD: resolved.boardPath,
|
|
330
|
+
TASKMARK_CWD: resolved.boardPath,
|
|
331
|
+
NODE_ENV: "development",
|
|
332
|
+
},
|
|
333
|
+
stdio: "inherit",
|
|
334
|
+
}
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
let debounce = null
|
|
338
|
+
const stopWatch = watchBoardMarkdown(resolved.boardPath, () => {
|
|
339
|
+
if (debounce) clearTimeout(debounce)
|
|
340
|
+
debounce = setTimeout(() => {
|
|
341
|
+
try {
|
|
342
|
+
touchDevReloadToken()
|
|
343
|
+
console.log(`[taskmark] board markdown changed — refreshing`)
|
|
344
|
+
} catch (err) {
|
|
345
|
+
console.warn(`[taskmark] reload touch failed: ${err?.message || err}`)
|
|
346
|
+
}
|
|
347
|
+
}, 200)
|
|
202
348
|
})
|
|
203
349
|
|
|
204
|
-
const shutdown = (
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
process.on("SIGINT", () => shutdown("SIGINT"))
|
|
208
|
-
process.on("SIGTERM", () => shutdown("SIGTERM"))
|
|
209
|
-
|
|
210
|
-
child.on("exit", (code, signal) => {
|
|
211
|
-
if (signal) process.exit(1)
|
|
212
|
-
process.exit(code ?? 0)
|
|
350
|
+
const shutdown = attachChildLifecycle(child, () => {
|
|
351
|
+
stopWatch()
|
|
352
|
+
if (debounce) clearTimeout(debounce)
|
|
213
353
|
})
|
|
214
354
|
|
|
215
355
|
const hosted =
|
|
@@ -219,7 +359,7 @@ Or install a published build that includes dist/standalone.`)
|
|
|
219
359
|
const shouldOpen = args.open && !hosted
|
|
220
360
|
|
|
221
361
|
try {
|
|
222
|
-
await waitForServer(port)
|
|
362
|
+
await waitForServer(port, 120_000)
|
|
223
363
|
if (shouldOpen) {
|
|
224
364
|
openBrowser(url)
|
|
225
365
|
} else {
|
|
@@ -241,19 +381,7 @@ async function preview(args) {
|
|
|
241
381
|
}
|
|
242
382
|
|
|
243
383
|
const boardArg = resolveCallerPath(args.board)
|
|
244
|
-
const resolved =
|
|
245
|
-
if (!resolved) {
|
|
246
|
-
console.error(`No Taskmark board found.
|
|
247
|
-
|
|
248
|
-
Looked for:
|
|
249
|
-
- --board / TASKMARK_BOARD
|
|
250
|
-
- TASKMARK_MASTER
|
|
251
|
-
- cwd layouts: nested ./taskmark/ or dedicated *-taskmark root
|
|
252
|
-
|
|
253
|
-
Fix: cd into a product repo (with ./taskmark/) or a board root, or set TASKMARK_BOARD.
|
|
254
|
-
Then re-run: npx taskmark preview`)
|
|
255
|
-
process.exit(1)
|
|
256
|
-
}
|
|
384
|
+
const resolved = resolveBoardOrExit(boardArg, "preview")
|
|
257
385
|
|
|
258
386
|
const outDir = args.out
|
|
259
387
|
? resolveCallerPath(args.out)
|
|
@@ -308,6 +436,15 @@ async function main() {
|
|
|
308
436
|
}
|
|
309
437
|
return
|
|
310
438
|
}
|
|
439
|
+
if (args.command === "dev") {
|
|
440
|
+
try {
|
|
441
|
+
await dev(args)
|
|
442
|
+
} catch (err) {
|
|
443
|
+
console.error(String(err?.message || err))
|
|
444
|
+
process.exit(1)
|
|
445
|
+
}
|
|
446
|
+
return
|
|
447
|
+
}
|
|
311
448
|
if (args.command === "preview") {
|
|
312
449
|
try {
|
|
313
450
|
await preview(args)
|
|
@@ -319,14 +456,11 @@ async function main() {
|
|
|
319
456
|
}
|
|
320
457
|
if (args.command === "build") {
|
|
321
458
|
const buildScript = path.join(packageRoot, "scripts", "build-static.mjs")
|
|
322
|
-
// Resolve relative paths against the caller's cwd (npm/yarn script dir),
|
|
323
|
-
// not packageRoot — the child runs with cwd = @taskmark/ui.
|
|
324
459
|
const callerCwd = process.env.INIT_CWD || process.cwd()
|
|
325
460
|
const childArgs = [buildScript]
|
|
326
461
|
if (args.board) {
|
|
327
462
|
childArgs.push("--board", path.resolve(callerCwd, args.board))
|
|
328
463
|
} else {
|
|
329
|
-
// Prefer the invoke directory as the board when no --board flag.
|
|
330
464
|
childArgs.push("--board", callerCwd)
|
|
331
465
|
}
|
|
332
466
|
if (args.out) {
|