@pghoya2956/livemap 1.0.0 → 1.1.0

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.
@@ -6,35 +6,57 @@
6
6
  - 산출물은 프로젝트 `map/.out/overview-1440.png`와 `map/.out/budget-results/`다.
7
7
  - 대상은 엔진 `serve`(포트 `MAP_PORT`, 기본 4181)다. `LIVEMAP_BUDGET_STATIC=<export 폴더>`면 `serve --static`을 잰다.
8
8
 
9
+ ## 첫 화면이 답하는 질문
10
+
11
+ 개요는 처음 보는 동료·외부 독자가 30초 안에 읽는 화면이다. 읽는 순서대로 다음 네 질문에 답하고, 답하지 않는 패널은 첫 화면에 못 들어온다.
12
+
13
+ 1. 지금 어느 마일스톤이고 얼마나 왔나: 진척 계기(화면에서 가장 큰 글자)와 마일스톤 패널.
14
+ 2. 무엇이 동작하고 무엇이 남았나: 기능 지도(가장 넓은 패널)와 기능 현황.
15
+ 3. 무엇을 기다리고, 이상은 없나: 특보와 상단 바 알약.
16
+ 4. 최근 얼마나 바뀌었나: 최근 변경, 기능별 변경, 전광판.
17
+
18
+ 운영자 신호(계획 항목 수, 열린 질문, 등급, 미분류, 어댑터 상태)는 전광판 숫자와 더보기에 둔다.
19
+
9
20
  ## 규칙과 이유
10
21
 
11
22
  | 규칙 | 값(`config.budget`) | 이유 |
12
23
  |---|---|---|
13
- | 화면은 세 질문만 | 어디까지 됐나 · 지금 무엇을 하나 · 무엇이 바뀌었나 | 셋에 답하지 않는 패널은 화면에 못 들어온다 |
14
- | 스크롤 0 | viewport 1440×900 | 눈에 전체 상태. 한국 상황판 문법 |
15
- | 패널 수 | ≤ 8 | 지금 5. 더하려면 하나를 뺀다 |
16
- | 목록 패널 행 수 | ≤ 6 (목록이 둘이면 각각) | 훑어 읽을 수 있는 한계 |
17
- | 여정 매트릭스 행 | ≤ 12 | 여정이 그보다 많으면 레인을 합친다 |
18
- | 시스템 식별자 | 0 | 첫 화면은 사용자 어휘만. 경로·파일명·sha는 상세 층 |
19
- | 내비 | ≤ 5 | 개요·여정·작업·변화·더보기 |
20
- | 깊이 | 3 | 개요목록 → 상세. 화면·API·DB 표는 장면 상세에서만 도달 |
21
- | CSP 아래 렌더 | 콘솔 오류 0, 사이드바 배경 적용 | 인라인 의존 회귀 방지 |
24
+ | 스크롤 0 | `viewport` 1440×900 | 눈에 전체 상태. 한국 상황판 문법 |
25
+ | 숨은 스크롤 0 | 모든 `.panel`, 그 안의 `.pb`·`.rows`, `overflow-y`가 `auto`·`scroll`인 자손에서 `scrollHeight − clientHeight ≤ 1`. 줄 수 제한(`-webkit-line-clamp`) 글자와 기능 지도(`.mapwrap`)·캡처(`.live`) 상자는 뺀다 | 패널 스크롤은 검사가 수 없는 곳에서 행을 자르고 공유 스크린샷에 잘린 행이 실린다. 목록은 들어가는 행만 그리고 나머지는 머리줄 "외 n →" 링크로 보낸다 |
26
+ | 패널 수 | ≤ 8 (`maxPanels`) | 지금 8. 더하려면 하나를 뺀다 |
27
+ | 목록 패널 행 수 | ≤ 6 (`maxRowsPerPanel`, 목록 묶음 `.rows`마다) | 훑어 읽을 수 있는 한계 |
28
+ | 기능 지도 행 | ≤ 12 (`.jrow`) | 기능이 13개부터는 완성 기능이 지도에서 접히고 바닥 칩 "완성 기능 n 접힘"이 기능 화면으로 이어진다. 기능 화면에는 전부 보인다 |
29
+ | 시스템 식별자 | 0 (`#root` 글자 전체) | 첫 화면은 사용자 어휘만. 경로·파일명·sha는 상세 층 |
30
+ | 내비 | ≤ 5 (`navItems`), 모두 보임 | 개요·기능·로드맵·작업·더보기 |
31
+ | 깊이 | 3 | 기능 지도 기능 선택 기능 현황의 "기능 화면 " → 단계 카드 → 단계 상세. 화면·API·DB 표는 단계 상세에서만 도달 |
32
+ | CSP 아래 렌더 | 콘솔 오류·CSP 위반 0, `body` 배경이 `--bg`(`rgb(5, 7, 10)`) | 인라인 의존 회귀 방지(`hosting-and-csp.md`) |
33
+ | 모션 줄임 | `reducedMotion: 'reduce'`에서 `document.getAnimations()` 0 | 전광판·캡처 회전·연결선 흐름이 멈추는지 |
34
+ | 상태 모양 | 기능 지도 범례(레이어 버튼) 표식 4종이 각각 동작 = 채운 원, 목업 = 반원 채움 + 실선, 계획 = 채움 없음 + 실선, 구상 = 채움 없음 + 점선. 기능이 없어 범례가 없으면 건너뛴다 | 다크 상태 색끼리는 휘도 차가 작아 색을 못 보는 독자에게 모양이 구분을 맡는다 |
35
+
36
+ 스크린샷은 모션 줄임 컨텍스트에서 `document.fonts.ready` 뒤에 찍는다. 새로 더한 숨은 스크롤·모션 줄임·상태 모양 검사는 엔진 화면 요소만 보므로, 프로젝트 자료가 달라서 새로 실패하지 않는다. 내부 용어·글자 대비·글자 크기 집합은 프로젝트 문구와 브라우저에 따라 달라져 예산 검사에 넣지 않는다.
22
37
 
23
38
  ## 개요 조각
24
39
 
25
- 개요는 `overview.json`만 읽는다. `derive.mjs`의 `overviewSlice()`가 경로·파일명·sha를 뺀 조각을 만들고, 검사는 개요 본문에서 `/api/`·`.tsx`·`.mjs`·`.sql`·`web/src`·7자 이상 16진수를 찾아 하나라도 있으면 실패한다. 개요에 무언가를 더할 때 이 조각을 거치지 않으면 식별자가 새어 들어온다.
40
+ 개요는 `overview.json`만 읽고 다시 계산하지 않는다. `derive.mjs`의 `overviewSlice(d, opts)`가 경로·파일명·sha를 뺀 조각을 만들고, 검사는 개요 본문에서 `/api/`·`.tsx`·`.mjs`·`.sql`·`web/src`·7자 이상 16진수를 찾아 하나라도 있으면 실패한다. 개요에 무언가를 더할 때 이 조각을 거치지 않으면 식별자가 새어 들어온다.
41
+
42
+ 프로젝트가 적은 문구도 개요에 나온다. 커밋 제목은 `overviewSlice`가 관례 접두어·내부 ID·식별자를 지우고 사람 커밋만 싣는다(작성자가 `[bot]`으로 끝나면 자동 커밋으로 따로 센다). 로드맵 항목·마일스톤의 목표와 결정 대기, 작업 제목, 기능·단계 이름은 엔진이 고치지 않는다. 여기에 경로나 7자 이상 16진수를 적으면 식별자 검사가 실패하므로 그 문구를 고친다.
43
+
44
+ `overview.json` 크기는 기능 수와 커밋 수에 따라 1.0.1보다 커진다. 엔진 픽스처는 3,697B이고, 기능 9개·14일 커밋 200여 건인 실제 프로젝트 하나에서 25,637B였다.
26
45
 
27
46
  ## 다시 보게 만드는 것
28
47
 
29
- - 마지막 방문 시각을 브라우저 안에 기억해(밖으로 보내지 않는다) 그 뒤 바뀐 커밋·장면에 빨간 점을 찍는다.
30
- - 상단 한 줄: 동작 장면 수·실데이터 화면 수·14일 커밋·미배포·경고·다음 걸음. 들어오자마자 읽을 문장.
48
+ - 마지막 방문 시각을 브라우저 안에 기억해(`localStorage` `map:lastVisit`, 밖으로 보내지 않는다) 그 뒤 생긴 커밋 행에 점을 찍는다.
49
+ - 전광판 한 줄: 동작 단계·완성 기능·로드맵 완료·14일 변경·규모 숫자·기능별 변경. 들어오자마자 읽는 줄이고, 정지 버튼과 모션 줄임으로 멈춘다.
31
50
  - 2주 뒤 실제로 연 뷰만 남긴다. 안 연 패널은 지운다.
32
51
 
33
52
  ## 패널을 바꾸는 절차
34
53
 
35
- 화면 파일은 엔진 소유다. 패널을 바꾸는 일은 엔진 저장소에서 하고 릴리스로 내보낸다.
54
+ 화면 원본은 엔진 저장소 `ui/`(React)이고 `site/`의 `index.html`·`map.css`·`map.js`는 그 번들 산출물이다. 패널을 바꾸는 일은 엔진 저장소에서 하고 릴리스로 내보낸다.
36
55
 
37
- 1. 답하려는 질문이 질문 중 무엇인지 적는다. 없으면 상세 층으로 간다.
56
+ 1. 답하려는 질문이 질문 중 무엇인지 적는다. 없으면 하위 화면으로 간다.
38
57
  2. 뺄 패널을 정한다.
39
- 3. 엔진 저장소 `site/map.js`의 `overview()`에서 `<section class="panel" data-budget="list|matrix">`로 만든다. 인라인 style 금지, 폭은 `data-w`.
40
- 4. 엔진 CI의 tarball 스모크(픽스처)와, 쓰는 프로젝트에 `npm install --no-save --install-links <엔진 저장소>`로 끼운 `npm run map:budget`으로 스크롤·행·식별자를 잰다. 스크린샷 `map/.out/overview-1440.png`을 사용자에게 보인다.
58
+ 3. `ui/components/panels.jsx`에 컴포넌트를 만들고 `ui/Overview.jsx` 격자에 놓는다. 틀은 `ui/components/primitives.jsx`의 `Panel`이고, 목록 패널은 `budget="list"`와 `.rows > .row`, 행 수는 `ui/lib/fit.js`의 `useFitRows`로 정한다. 마크업의 `style` 속성·`<style>` 주입은 쓰지 않고 크기·위치만 React `style` prop으로 준다. 프로젝트가 적은 문구를 담는 요소에는 `data-text="project"`, 커밋 제목에는 `data-text="commit"`을 단다.
59
+ 4. 자료가 필요하면 `overviewSlice`에 필드를 더한다(추가만 한다. 1.0.1 필드의 이름·형·값은 바꾸지 않는다).
60
+ 5. `node scripts/build-ui.mjs`로 `site/`를 다시 만들어 함께 커밋한다. CI는 다시 빌드한 결과가 커밋과 같은지 본다.
61
+ 6. 누르거나 이동하는 요소를 더하거나 바꿨으면 `scripts/click-targets.json`에 행을 맞춘다. 표에 없는 대화형 요소가 있으면 클릭 경로 크롤이 실패한다. 엔진 저장소에서 `node scripts/route-crawl.mjs --url http://127.0.0.1:<포트>/map/ --targets scripts/click-targets.json`(한 화면만 `--only <overview|journeys|roadmap|tasks|more>`, 이동 없이 개요만 `--overview-only`)로 확인한다. 크롤러는 팩에 없어 엔진 저장소에서만 돈다.
62
+ 7. 엔진 CI의 스모크(픽스처: 예산, 하위 화면 콘솔 0, 설정 경로를 바꾼 복사본의 화면 문구, 클릭 경로)와, 쓰는 프로젝트에 `npm install --no-save --install-links <엔진 저장소>`로 끼운 `npm run map:budget`으로 스크롤·숨은 스크롤·행·식별자를 잰다. 스크린샷 `map/.out/overview-1440.png`을 사용자에게 보인다.
package/package.json CHANGED
@@ -1,19 +1,49 @@
1
1
  {
2
2
  "name": "@pghoya2956/livemap",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Project status board engine: scans a repository into a graph and serves a one-screen map of journeys, screens, APIs, tests and work.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "bin": { "livemap": "bin/livemap.mjs" },
8
- "files": ["bin/", "src/", "site/", "budget/", "templates/", "docs/", "CHANGELOG.md"],
9
- "engines": { "node": ">=22" },
7
+ "bin": {
8
+ "livemap": "bin/livemap.mjs"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "src/",
13
+ "site/",
14
+ "budget/",
15
+ "templates/",
16
+ "docs/",
17
+ "CHANGELOG.md"
18
+ ],
19
+ "engines": {
20
+ "node": ">=22"
21
+ },
10
22
  "scripts": {
11
- "test": "node --test test/*.test.mjs"
23
+ "test": "node --test test/*.test.mjs",
24
+ "build:ui": "node scripts/build-ui.mjs"
25
+ },
26
+ "peerDependencies": {
27
+ "@playwright/test": ">=1.63.0 <2"
28
+ },
29
+ "peerDependenciesMeta": {
30
+ "@playwright/test": {
31
+ "optional": true
32
+ }
33
+ },
34
+ "devDependencies": {
35
+ "@playwright/test": "1.63.0",
36
+ "@types/react": "19.3.0",
37
+ "esbuild": "0.28.2",
38
+ "react": "19.3.0",
39
+ "react-dom": "19.3.0"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/pghoya2956/livemap.git"
12
44
  },
13
- "peerDependencies": { "@playwright/test": ">=1.63.0 <2" },
14
- "peerDependenciesMeta": { "@playwright/test": { "optional": true } },
15
- "devDependencies": { "@playwright/test": "1.63.0" },
16
- "repository": { "type": "git", "url": "git+https://github.com/pghoya2956/livemap.git" },
17
45
  "homepage": "https://github.com/pghoya2956/livemap#readme",
18
- "publishConfig": { "access": "public" }
46
+ "publishConfig": {
47
+ "access": "public"
48
+ }
19
49
  }
package/site/index.html CHANGED
@@ -4,22 +4,10 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>상황판</title>
7
- <!-- 서빙 CSP(default-src 'self')에 맞춰 스타일·스크립트·서체는 같은 출처의 /map/ 아래 파일만 쓴다. 인라인은 넣지 않는다. -->
8
- <link rel="stylesheet" href="/map/map.css">
7
+ <link rel="stylesheet" href="map.css">
9
8
  </head>
10
9
  <body>
11
-
12
- <div class="app">
13
- <aside class="side">
14
- <div class="brand"><span id="brandName">상황판</span><small id="brandSha"></small></div>
15
- <nav class="nav" id="nav"></nav>
16
- <div class="foot" id="sideFoot"></div>
17
- </aside>
18
- <main class="main">
19
- <div class="head"><h1 id="title"></h1><span class="sub" id="subtitle"></span><div class="meta" id="meta"></div></div>
20
- <div id="view"><p class="empty">불러오는 중…</p></div>
21
- </main>
22
- </div>
23
- <script src="/map/map.js"></script>
10
+ <div id="root"></div>
11
+ <script src="map.js"></script>
24
12
  </body>
25
13
  </html>