@things-factory/station 10.1.33
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 +124 -0
- package/dist-client/index.d.ts +11 -0
- package/dist-client/index.js +13 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/station/scene/index.d.ts +11 -0
- package/dist-client/station/scene/index.js +12 -0
- package/dist-client/station/scene/index.js.map +1 -0
- package/dist-client/station/scene/station-context-binding.d.ts +25 -0
- package/dist-client/station/scene/station-context-binding.js +39 -0
- package/dist-client/station/scene/station-context-binding.js.map +1 -0
- package/dist-client/station/scene/station-context-scene.d.ts +28 -0
- package/dist-client/station/scene/station-context-scene.js +138 -0
- package/dist-client/station/scene/station-context-scene.js.map +1 -0
- package/dist-client/station/scene/station-counts-scene.d.ts +6 -0
- package/dist-client/station/scene/station-counts-scene.js +51 -0
- package/dist-client/station/scene/station-counts-scene.js.map +1 -0
- package/dist-client/station/scene/station-field-scene.d.ts +6 -0
- package/dist-client/station/scene/station-field-scene.js +89 -0
- package/dist-client/station/scene/station-field-scene.js.map +1 -0
- package/dist-client/station/scene/station-icons.d.ts +82 -0
- package/dist-client/station/scene/station-icons.js +131 -0
- package/dist-client/station/scene/station-icons.js.map +1 -0
- package/dist-client/station/scene/station-numpad-scene.d.ts +9 -0
- package/dist-client/station/scene/station-numpad-scene.js +144 -0
- package/dist-client/station/scene/station-numpad-scene.js.map +1 -0
- package/dist-client/station/scene/station-scene-style.d.ts +51 -0
- package/dist-client/station/scene/station-scene-style.js +108 -0
- package/dist-client/station/scene/station-scene-style.js.map +1 -0
- package/dist-client/station/scene/station-scene.d.ts +295 -0
- package/dist-client/station/scene/station-scene.js +68 -0
- package/dist-client/station/scene/station-scene.js.map +1 -0
- package/dist-client/station/scene/station-who-scene.d.ts +6 -0
- package/dist-client/station/scene/station-who-scene.js +48 -0
- package/dist-client/station/scene/station-who-scene.js.map +1 -0
- package/dist-client/station/scene/station-work-panel-scene.d.ts +28 -0
- package/dist-client/station/scene/station-work-panel-scene.js +37 -0
- package/dist-client/station/scene/station-work-panel-scene.js.map +1 -0
- package/dist-client/station/station-board-binding.d.ts +16 -0
- package/dist-client/station/station-board-binding.js +20 -0
- package/dist-client/station/station-board-binding.js.map +1 -0
- package/dist-client/station/station-context.d.ts +178 -0
- package/dist-client/station/station-context.js +30 -0
- package/dist-client/station/station-context.js.map +1 -0
- package/dist-client/station/station-palette.d.ts +34 -0
- package/dist-client/station/station-palette.js +119 -0
- package/dist-client/station/station-palette.js.map +1 -0
- package/dist-client/station/station-runtime.d.ts +47 -0
- package/dist-client/station/station-runtime.js +47 -0
- package/dist-client/station/station-runtime.js.map +1 -0
- package/dist-client/station/station-scene-context.d.ts +68 -0
- package/dist-client/station/station-scene-context.js +62 -0
- package/dist-client/station/station-scene-context.js.map +1 -0
- package/dist-client/station/station-screen-flow.d.ts +16 -0
- package/dist-client/station/station-screen-flow.js +58 -0
- package/dist-client/station/station-screen-flow.js.map +1 -0
- package/dist-client/station/station-tile-registry.d.ts +89 -0
- package/dist-client/station/station-tile-registry.js +86 -0
- package/dist-client/station/station-tile-registry.js.map +1 -0
- package/dist-client/station/station-work-panel-view.d.ts +23 -0
- package/dist-client/station/station-work-panel-view.js +125 -0
- package/dist-client/station/station-work-panel-view.js.map +1 -0
- package/dist-client/station/station-work-runtime.d.ts +47 -0
- package/dist-client/station/station-work-runtime.js +161 -0
- package/dist-client/station/station-work-runtime.js.map +1 -0
- package/dist-client/station/station-work-scenarios.d.ts +41 -0
- package/dist-client/station/station-work-scenarios.js +59 -0
- package/dist-client/station/station-work-scenarios.js.map +1 -0
- package/dist-client/station/task-counts.d.ts +32 -0
- package/dist-client/station/task-counts.js +26 -0
- package/dist-client/station/task-counts.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/activities/activity-station-screen-control-withdrawal.d.ts +62 -0
- package/dist-server/activities/activity-station-screen-control-withdrawal.js +147 -0
- package/dist-server/activities/activity-station-screen-control-withdrawal.js.map +1 -0
- package/dist-server/activities/activity-station-screen-release.d.ts +81 -0
- package/dist-server/activities/activity-station-screen-release.js +147 -0
- package/dist-server/activities/activity-station-screen-release.js.map +1 -0
- package/dist-server/activities/index.d.ts +49 -0
- package/dist-server/activities/index.js +21 -0
- package/dist-server/activities/index.js.map +1 -0
- package/dist-server/index.d.ts +3 -0
- package/dist-server/index.js +8 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/service/index.d.ts +23 -0
- package/dist-server/service/index.js +41 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/station/deletion-changes.d.ts +14 -0
- package/dist-server/service/station/deletion-changes.js +25 -0
- package/dist-server/service/station/deletion-changes.js.map +1 -0
- package/dist-server/service/station/index.d.ts +13 -0
- package/dist-server/service/station/index.js +25 -0
- package/dist-server/service/station/index.js.map +1 -0
- package/dist-server/service/station/reversibility.d.ts +21 -0
- package/dist-server/service/station/reversibility.js +24 -0
- package/dist-server/service/station/reversibility.js.map +1 -0
- package/dist-server/service/station/screen-model-digest.d.ts +60 -0
- package/dist-server/service/station/screen-model-digest.js +97 -0
- package/dist-server/service/station/screen-model-digest.js.map +1 -0
- package/dist-server/service/station/staged-material.d.ts +62 -0
- package/dist-server/service/station/staged-material.js +44 -0
- package/dist-server/service/station/staged-material.js.map +1 -0
- package/dist-server/service/station/station-argument-sources.d.ts +31 -0
- package/dist-server/service/station/station-argument-sources.js +66 -0
- package/dist-server/service/station/station-argument-sources.js.map +1 -0
- package/dist-server/service/station/station-assignment-choice.d.ts +81 -0
- package/dist-server/service/station/station-assignment-choice.js +105 -0
- package/dist-server/service/station/station-assignment-choice.js.map +1 -0
- package/dist-server/service/station/station-assignment-resolver.d.ts +104 -0
- package/dist-server/service/station/station-assignment-resolver.js +426 -0
- package/dist-server/service/station/station-assignment-resolver.js.map +1 -0
- package/dist-server/service/station/station-assignment-service.d.ts +33 -0
- package/dist-server/service/station/station-assignment-service.js +64 -0
- package/dist-server/service/station/station-assignment-service.js.map +1 -0
- package/dist-server/service/station/station-axes.d.ts +9 -0
- package/dist-server/service/station/station-axes.js +16 -0
- package/dist-server/service/station/station-axes.js.map +1 -0
- package/dist-server/service/station/station-flow.d.ts +156 -0
- package/dist-server/service/station/station-flow.js +255 -0
- package/dist-server/service/station/station-flow.js.map +1 -0
- package/dist-server/service/station/station-generate.d.ts +76 -0
- package/dist-server/service/station/station-generate.js +178 -0
- package/dist-server/service/station/station-generate.js.map +1 -0
- package/dist-server/service/station/station-product.d.ts +123 -0
- package/dist-server/service/station/station-product.js +77 -0
- package/dist-server/service/station/station-product.js.map +1 -0
- package/dist-server/service/station/station-runtime-binding.d.ts +6 -0
- package/dist-server/service/station/station-runtime-binding.js +46 -0
- package/dist-server/service/station/station-runtime-binding.js.map +1 -0
- package/dist-server/service/station/station-screen-assignment.d.ts +94 -0
- package/dist-server/service/station/station-screen-assignment.js +154 -0
- package/dist-server/service/station/station-screen-assignment.js.map +1 -0
- package/dist-server/service/station/station-screen-control-withdrawal-resolver.d.ts +37 -0
- package/dist-server/service/station/station-screen-control-withdrawal-resolver.js +130 -0
- package/dist-server/service/station/station-screen-control-withdrawal-resolver.js.map +1 -0
- package/dist-server/service/station/station-screen-control-withdrawal.d.ts +104 -0
- package/dist-server/service/station/station-screen-control-withdrawal.js +128 -0
- package/dist-server/service/station/station-screen-control-withdrawal.js.map +1 -0
- package/dist-server/service/station/station-screen-draft-resolver.d.ts +47 -0
- package/dist-server/service/station/station-screen-draft-resolver.js +153 -0
- package/dist-server/service/station/station-screen-draft-resolver.js.map +1 -0
- package/dist-server/service/station/station-screen-layout.d.ts +119 -0
- package/dist-server/service/station/station-screen-layout.js +202 -0
- package/dist-server/service/station/station-screen-layout.js.map +1 -0
- package/dist-server/service/station/station-screen-list-resolver.d.ts +69 -0
- package/dist-server/service/station/station-screen-list-resolver.js +215 -0
- package/dist-server/service/station/station-screen-list-resolver.js.map +1 -0
- package/dist-server/service/station/station-screen-publish-subscriber.d.ts +22 -0
- package/dist-server/service/station/station-screen-publish-subscriber.js +90 -0
- package/dist-server/service/station/station-screen-publish-subscriber.js.map +1 -0
- package/dist-server/service/station/station-screen-release-gate.d.ts +66 -0
- package/dist-server/service/station/station-screen-release-gate.js +67 -0
- package/dist-server/service/station/station-screen-release-gate.js.map +1 -0
- package/dist-server/service/station/station-screen-release-record.d.ts +62 -0
- package/dist-server/service/station/station-screen-release-record.js +139 -0
- package/dist-server/service/station/station-screen-release-record.js.map +1 -0
- package/dist-server/service/station/station-screen-release-signature.d.ts +23 -0
- package/dist-server/service/station/station-screen-release-signature.js +32 -0
- package/dist-server/service/station/station-screen-release-signature.js.map +1 -0
- package/dist-server/service/station/station-screen-scorecard.d.ts +15 -0
- package/dist-server/service/station/station-screen-scorecard.js +35 -0
- package/dist-server/service/station/station-screen-scorecard.js.map +1 -0
- package/dist-server/service/station/station-screen-spec-resolver.d.ts +36 -0
- package/dist-server/service/station/station-screen-spec-resolver.js +202 -0
- package/dist-server/service/station/station-screen-spec-resolver.js.map +1 -0
- package/dist-server/service/station/station-screen-spec.d.ts +104 -0
- package/dist-server/service/station/station-screen-spec.js +114 -0
- package/dist-server/service/station/station-screen-spec.js.map +1 -0
- package/dist-server/service/station/station-screen-thumbnail.d.ts +11 -0
- package/dist-server/service/station/station-screen-thumbnail.js +36 -0
- package/dist-server/service/station/station-screen-thumbnail.js.map +1 -0
- package/dist-server/service/station/station-service-resolver.d.ts +27 -0
- package/dist-server/service/station/station-service-resolver.js +95 -0
- package/dist-server/service/station/station-service-resolver.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +44 -0
- package/shared/station-axes.d.ts +4 -0
- package/shared/station-axes.js +155 -0
- package/shared/station-screen-flow-check.d.ts +12 -0
- package/shared/station-screen-flow-check.js +74 -0
- package/things-scene.config.js +15 -0
- package/translations/en.json +42 -0
- package/translations/ja.json +42 -0
- package/translations/ko.json +42 -0
- package/translations/ms.json +42 -0
- package/translations/zh.json +42 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 팔레트 항목의 그림 — **없으면 열 칸이 다 「no-image」로 뜬다.**
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
5
|
+
* ## 그리는 자리가 있다
|
|
6
|
+
*
|
|
7
|
+
* 앞서 이 자리에 「`@operato/board` 안에 템플릿 아이콘을 그리는 자리가 없다」고 적고 `icon` 을 비워
|
|
8
|
+
* 두었다. **틀렸다.** 있다.
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* [읽었음] component-menu.js:101 templateIcon(t) { return t.icon || noImage }
|
|
12
|
+
* [읽었음] component-menu.js:171 [template] img { width: …; height: … }
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* `<img src>` 다. 그래서 비워 두면 오류 없이 **열 칸이 전부 같은 회색 네모**로 뜬다. 그것이 우리
|
|
16
|
+
* 그룹을 팔레트에서 제일 안 친절한 칸으로 만들고 있었다.
|
|
17
|
+
*
|
|
18
|
+
* ## 왜 색이 한 벌인가 — **테마를 못 따라간다**
|
|
19
|
+
*
|
|
20
|
+
* `<img>` 안의 SVG 는 독립 문서라 바깥 CSS 변수가 안 닿는다. 그래서 처음에는 `prefers-color-scheme`
|
|
21
|
+
* 으로 두 벌을 넣었고, 그것이 **동작하기까지 했다.**
|
|
22
|
+
*
|
|
23
|
+
* ```
|
|
24
|
+
* [확인함] 다크로 놓고 캔버스에 그려 가운데 픽셀을 읽었다 → 다크용 색이 나왔다
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* 그런데 **그 신호가 우리 신호가 아니다.** 이 앱의 테마는 `body` 의 클래스이고 OS 설정과 별개다.
|
|
28
|
+
*
|
|
29
|
+
* ```
|
|
30
|
+
* [확인함] prefersDark: true · body.className: "light" ← 같은 순간에 이랬다
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* 라이트 테마로 쓰는 사람이 OS 를 다크로 두면 **밝은 아이콘이 밝은 팔레트에 얹힌다.** 그 자리를 재
|
|
34
|
+
* 봤다.
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* [확인함] 팔레트 바탕 라이트 rgb(228,232,237) · 다크 rgb(50,58,68)
|
|
38
|
+
* #3f4a52 라이트 7.38:1 · 다크 1.27:1 ← 다크에서 안 보인다
|
|
39
|
+
* #dbe4e9 라이트 1.05:1 · 다크 8.93:1 ← 라이트에서 안 보인다
|
|
40
|
+
* #7d848c 라이트 3.07:1 · 다크 3.04:1 ← 양쪽에서 읽힌다
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* 그래서 **한 벌로 간다.** 어느 테마에서도 도형 대비 기준(3:1)을 넘는 중간 톤이고, 어긋날 때 사라지는
|
|
44
|
+
* 쪽보다 늘 조금 얌전한 쪽이 낫다. 테마를 따라 바뀌게 하려면 팔레트가 이 그림을 `<img>` 로 그리는
|
|
45
|
+
* 것부터 바뀌어야 하고, 그것은 보드 쪽 일이다.
|
|
46
|
+
*
|
|
47
|
+
* ## 붓
|
|
48
|
+
*
|
|
49
|
+
* figure 저작 도구의 팔레트를 본떴다 — 그쪽은 Material Symbols 글리프를 쓴다. 여기는 `<img>` 라
|
|
50
|
+
* `md-icon` 을 못 넣으므로, **같은 결로 직접 그린다**: 24 격자, 채운 덩어리가 주인공, 윤곽은 가늘게,
|
|
51
|
+
* 부차적인 것은 옅게. 보드의 기존 그룹 아이콘(가는 선 CAD 풍)과는 일부러 다르다 — 작업대 부품은
|
|
52
|
+
* 저작자가 한눈에 고르는 것이지 도면에서 찾는 것이 아니다.
|
|
53
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
54
|
+
*/
|
|
55
|
+
/** 값 하나 — 화면틀 안에 굵은 값 한 줄과 옅은 이름표 한 줄. */
|
|
56
|
+
export declare const ICON_FIELD: string;
|
|
57
|
+
/** 누가 서 있나 — 사람. */
|
|
58
|
+
export declare const ICON_WHO: string;
|
|
59
|
+
/** 무슨 지시를 도나 — 집게가 달린 판. */
|
|
60
|
+
export declare const ICON_ORDER: string;
|
|
61
|
+
/** 얼마나 했나 — 양품과 부적합, 두 막대. 부적합이 옅다. */
|
|
62
|
+
export declare const ICON_COUNTS: string;
|
|
63
|
+
/** 자판 없이 세는 자리 — 아홉 칸. */
|
|
64
|
+
export declare const ICON_NUMPAD: string;
|
|
65
|
+
/** 눌러서 사실을 남기는 자리 — 누르는 시늉. */
|
|
66
|
+
export declare const ICON_ACTION: string;
|
|
67
|
+
/** 대 놓은 자재 — 쌓인 상자. */
|
|
68
|
+
export declare const ICON_STAGED: string;
|
|
69
|
+
/** 무엇을 어떻게 하나 — 차례가 있는 글. */
|
|
70
|
+
export declare const ICON_DIRECTIVE: string;
|
|
71
|
+
/**
|
|
72
|
+
* 손볼 때가 됐나 — 톱니.
|
|
73
|
+
*
|
|
74
|
+
* 처음에는 스패너로 그렸다(둥근 머리 + 비스듬한 자루). 팔레트 크기로 놓고 보니 **돋보기로 읽혔다** —
|
|
75
|
+
* 「손볼 때가 됐나」가 「찾기」로 보이면 고른 사람이 다른 것을 놓는다. 톱니는 그 자리에서 안 헷갈린다.
|
|
76
|
+
*
|
|
77
|
+
* 이가 여덟 개인 것은 여섯이면 성기고 열이면 24 격자에서 뭉치기 때문이다. 자리는 계산으로 놓는다 —
|
|
78
|
+
* 손으로 적으면 하나가 어긋나도 눈에 안 띈다.
|
|
79
|
+
*/
|
|
80
|
+
export declare const ICON_MAINTENANCE: string;
|
|
81
|
+
/** 안 그려지는 부품 — 무엇을 얼마나 자주 읽나. 시계로 말한다. */
|
|
82
|
+
export declare const ICON_CONTEXT: string;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 팔레트 항목의 그림 — **없으면 열 칸이 다 「no-image」로 뜬다.**
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
5
|
+
* ## 그리는 자리가 있다
|
|
6
|
+
*
|
|
7
|
+
* 앞서 이 자리에 「`@operato/board` 안에 템플릿 아이콘을 그리는 자리가 없다」고 적고 `icon` 을 비워
|
|
8
|
+
* 두었다. **틀렸다.** 있다.
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* [읽었음] component-menu.js:101 templateIcon(t) { return t.icon || noImage }
|
|
12
|
+
* [읽었음] component-menu.js:171 [template] img { width: …; height: … }
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* `<img src>` 다. 그래서 비워 두면 오류 없이 **열 칸이 전부 같은 회색 네모**로 뜬다. 그것이 우리
|
|
16
|
+
* 그룹을 팔레트에서 제일 안 친절한 칸으로 만들고 있었다.
|
|
17
|
+
*
|
|
18
|
+
* ## 왜 색이 한 벌인가 — **테마를 못 따라간다**
|
|
19
|
+
*
|
|
20
|
+
* `<img>` 안의 SVG 는 독립 문서라 바깥 CSS 변수가 안 닿는다. 그래서 처음에는 `prefers-color-scheme`
|
|
21
|
+
* 으로 두 벌을 넣었고, 그것이 **동작하기까지 했다.**
|
|
22
|
+
*
|
|
23
|
+
* ```
|
|
24
|
+
* [확인함] 다크로 놓고 캔버스에 그려 가운데 픽셀을 읽었다 → 다크용 색이 나왔다
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* 그런데 **그 신호가 우리 신호가 아니다.** 이 앱의 테마는 `body` 의 클래스이고 OS 설정과 별개다.
|
|
28
|
+
*
|
|
29
|
+
* ```
|
|
30
|
+
* [확인함] prefersDark: true · body.className: "light" ← 같은 순간에 이랬다
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* 라이트 테마로 쓰는 사람이 OS 를 다크로 두면 **밝은 아이콘이 밝은 팔레트에 얹힌다.** 그 자리를 재
|
|
34
|
+
* 봤다.
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* [확인함] 팔레트 바탕 라이트 rgb(228,232,237) · 다크 rgb(50,58,68)
|
|
38
|
+
* #3f4a52 라이트 7.38:1 · 다크 1.27:1 ← 다크에서 안 보인다
|
|
39
|
+
* #dbe4e9 라이트 1.05:1 · 다크 8.93:1 ← 라이트에서 안 보인다
|
|
40
|
+
* #7d848c 라이트 3.07:1 · 다크 3.04:1 ← 양쪽에서 읽힌다
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* 그래서 **한 벌로 간다.** 어느 테마에서도 도형 대비 기준(3:1)을 넘는 중간 톤이고, 어긋날 때 사라지는
|
|
44
|
+
* 쪽보다 늘 조금 얌전한 쪽이 낫다. 테마를 따라 바뀌게 하려면 팔레트가 이 그림을 `<img>` 로 그리는
|
|
45
|
+
* 것부터 바뀌어야 하고, 그것은 보드 쪽 일이다.
|
|
46
|
+
*
|
|
47
|
+
* ## 붓
|
|
48
|
+
*
|
|
49
|
+
* figure 저작 도구의 팔레트를 본떴다 — 그쪽은 Material Symbols 글리프를 쓴다. 여기는 `<img>` 라
|
|
50
|
+
* `md-icon` 을 못 넣으므로, **같은 결로 직접 그린다**: 24 격자, 채운 덩어리가 주인공, 윤곽은 가늘게,
|
|
51
|
+
* 부차적인 것은 옅게. 보드의 기존 그룹 아이콘(가는 선 CAD 풍)과는 일부러 다르다 — 작업대 부품은
|
|
52
|
+
* 저작자가 한눈에 고르는 것이지 도면에서 찾는 것이 아니다.
|
|
53
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
54
|
+
*/
|
|
55
|
+
/** 양쪽 테마에서 읽히는 한 벌 잉크. 고른 근거는 머리말의 잰 값이다. */
|
|
56
|
+
const INK = '#7d848c';
|
|
57
|
+
/** 24 격자에 그린 몸통을 그림 주소로 만든다. */
|
|
58
|
+
function iconOf(body) {
|
|
59
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">` +
|
|
60
|
+
`<style>` +
|
|
61
|
+
`:root{--c:${INK}}` +
|
|
62
|
+
/* 채운 것이 기본. 윤곽(.o)과 굵은 획(.o2)은 골라 쓴다. */
|
|
63
|
+
`*{fill:var(--c)}` +
|
|
64
|
+
`.o{fill:none;stroke:var(--c);stroke-width:1.7;stroke-linejoin:round}` +
|
|
65
|
+
`.o2{fill:none;stroke:var(--c);stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round}` +
|
|
66
|
+
`.dim{opacity:.45}` +
|
|
67
|
+
`</style>${body}</svg>`;
|
|
68
|
+
/*
|
|
69
|
+
* base64 가 아니라 퍼센트 부호화를 쓴다. 저장소에 남는 글이 읽히는 편이 낫고, `#` 만 살리면
|
|
70
|
+
* 그대로 통한다 — base64 로 접으면 색 하나 고치는 데도 사람이 못 읽는 덩어리가 된다.
|
|
71
|
+
*/
|
|
72
|
+
return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
|
|
73
|
+
}
|
|
74
|
+
/** 값 하나 — 화면틀 안에 굵은 값 한 줄과 옅은 이름표 한 줄. */
|
|
75
|
+
export const ICON_FIELD = iconOf(`<rect class="o" x="2.6" y="5" width="18.8" height="14" rx="2.6"/>` +
|
|
76
|
+
`<rect x="5.6" y="8.8" width="9" height="3.4" rx="1.5"/>` +
|
|
77
|
+
`<rect class="dim" x="5.6" y="14" width="6" height="1.9" rx="0.95"/>`);
|
|
78
|
+
/** 누가 서 있나 — 사람. */
|
|
79
|
+
export const ICON_WHO = iconOf(`<circle cx="12" cy="8" r="3.7"/>` + `<path d="M4.4 19.8c0-3.7 3.4-5.8 7.6-5.8s7.6 2.1 7.6 5.8z"/>`);
|
|
80
|
+
/** 무슨 지시를 도나 — 집게가 달린 판. */
|
|
81
|
+
export const ICON_ORDER = iconOf(`<rect class="o" x="5" y="4.2" width="14" height="16.6" rx="2.5"/>` +
|
|
82
|
+
`<rect x="9" y="2.4" width="6" height="3.6" rx="1.3"/>` +
|
|
83
|
+
`<rect x="8.2" y="10" width="7.6" height="1.9" rx="0.95"/>` +
|
|
84
|
+
`<rect class="dim" x="8.2" y="14" width="5.2" height="1.9" rx="0.95"/>`);
|
|
85
|
+
/** 얼마나 했나 — 양품과 부적합, 두 막대. 부적합이 옅다. */
|
|
86
|
+
export const ICON_COUNTS = iconOf(`<rect x="4.6" y="9.6" width="5" height="9.4" rx="1.7"/>` +
|
|
87
|
+
`<rect class="dim" x="14.4" y="14" width="5" height="5" rx="1.7"/>` +
|
|
88
|
+
`<rect x="2.6" y="20.2" width="18.8" height="1.9" rx="0.95"/>`);
|
|
89
|
+
/** 자판 없이 세는 자리 — 아홉 칸. */
|
|
90
|
+
export const ICON_NUMPAD = iconOf([7, 12, 17]
|
|
91
|
+
.map(y => [7, 12, 17].map(x => `<circle cx="${x}" cy="${y}" r="1.95"/>`).join(''))
|
|
92
|
+
.join(''));
|
|
93
|
+
/** 눌러서 사실을 남기는 자리 — 누르는 시늉. */
|
|
94
|
+
export const ICON_ACTION = iconOf(`<circle cx="12" cy="13.6" r="4.2"/>` + `<path class="o2" d="M6.6 8.8a7.4 7.4 0 0 1 10.8 0"/>`);
|
|
95
|
+
/** 대 놓은 자재 — 쌓인 상자. */
|
|
96
|
+
export const ICON_STAGED = iconOf(`<rect x="8" y="3.4" width="8" height="7.6" rx="1.4"/>` +
|
|
97
|
+
`<rect x="2.8" y="12.8" width="8" height="7.6" rx="1.4"/>` +
|
|
98
|
+
`<rect class="dim" x="13.2" y="12.8" width="8" height="7.6" rx="1.4"/>`);
|
|
99
|
+
/** 무엇을 어떻게 하나 — 차례가 있는 글. */
|
|
100
|
+
export const ICON_DIRECTIVE = iconOf(`<rect class="o" x="3.4" y="4" width="17.2" height="16" rx="2.5"/>` +
|
|
101
|
+
`<circle cx="7.6" cy="9.2" r="1.6"/>` +
|
|
102
|
+
`<rect x="10.6" y="8.2" width="7" height="2" rx="1"/>` +
|
|
103
|
+
`<circle cx="7.6" cy="14.8" r="1.6"/>` +
|
|
104
|
+
`<rect class="dim" x="10.6" y="13.8" width="5" height="2" rx="1"/>`);
|
|
105
|
+
/**
|
|
106
|
+
* 손볼 때가 됐나 — 톱니.
|
|
107
|
+
*
|
|
108
|
+
* 처음에는 스패너로 그렸다(둥근 머리 + 비스듬한 자루). 팔레트 크기로 놓고 보니 **돋보기로 읽혔다** —
|
|
109
|
+
* 「손볼 때가 됐나」가 「찾기」로 보이면 고른 사람이 다른 것을 놓는다. 톱니는 그 자리에서 안 헷갈린다.
|
|
110
|
+
*
|
|
111
|
+
* 이가 여덟 개인 것은 여섯이면 성기고 열이면 24 격자에서 뭉치기 때문이다. 자리는 계산으로 놓는다 —
|
|
112
|
+
* 손으로 적으면 하나가 어긋나도 눈에 안 띈다.
|
|
113
|
+
*/
|
|
114
|
+
export const ICON_MAINTENANCE = iconOf(Array.from({ length: 8 }, (_, i) => {
|
|
115
|
+
const angle = (i * Math.PI) / 4;
|
|
116
|
+
/*
|
|
117
|
+
* `toFixed` 를 안 쓴다. 이 자리에서는 맞는 값을 내지만, **화면에 수를 보일 때 쓰면 로케일이
|
|
118
|
+
* 쉼표를 쓰는 자리에서 점을 찍는다** — 그래서 이 패키지에 그것을 통째로 막는 시험이 있고,
|
|
119
|
+
* 예외를 하나 뚫으면 그 시험이 무뎌진다. 자릿수는 반올림으로 줄인다.
|
|
120
|
+
*/
|
|
121
|
+
const round = (value) => Math.round(value * 100) / 100;
|
|
122
|
+
return `<circle cx="${round(12 + Math.cos(angle) * 8.4)}" cy="${round(12 + Math.sin(angle) * 8.4)}" r="2.1"/>`;
|
|
123
|
+
}).join('') +
|
|
124
|
+
/*
|
|
125
|
+
* 몸통은 **채우지 않고 굵게 두른다.** 채우면 가운데 구멍이 없어지고, 구멍 없는 톱니는 단추처럼
|
|
126
|
+
* 보인다. 배경색으로 구멍을 덮는 수는 못 쓴다 — 팔레트 바탕이 테마마다 다르다.
|
|
127
|
+
*/
|
|
128
|
+
`<circle cx="12" cy="12" r="5" style="fill:none;stroke:var(--c);stroke-width:3.6"/>`);
|
|
129
|
+
/** 안 그려지는 부품 — 무엇을 얼마나 자주 읽나. 시계로 말한다. */
|
|
130
|
+
export const ICON_CONTEXT = iconOf(`<circle class="o" cx="12" cy="12" r="8.6"/>` + `<path class="o2" d="M12 6.9v5.4l3.6 2.2"/>`);
|
|
131
|
+
//# sourceMappingURL=station-icons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"station-icons.js","sourceRoot":"","sources":["../../../client/station/scene/station-icons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,6CAA6C;AAC7C,MAAM,GAAG,GAAG,SAAS,CAAA;AAErB,gCAAgC;AAChC,SAAS,MAAM,CAAC,IAAY;IAC1B,MAAM,GAAG,GACP,8DAA8D;QAC9D,SAAS;QACT,aAAa,GAAG,GAAG;QACnB,yCAAyC;QACzC,kBAAkB;QAClB,sEAAsE;QACtE,4FAA4F;QAC5F,mBAAmB;QACnB,WAAW,IAAI,QAAQ,CAAA;IAEzB;;;OAGG;IACH,OAAO,mCAAmC,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAA;AACtE,CAAC;AAED,0CAA0C;AAC1C,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAC9B,mEAAmE;IACjE,yDAAyD;IACzD,qEAAqE,CACxE,CAAA;AAED,oBAAoB;AACpB,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAC5B,kCAAkC,GAAG,8DAA8D,CACpG,CAAA;AAED,4BAA4B;AAC5B,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAC9B,mEAAmE;IACjE,uDAAuD;IACvD,2DAA2D;IAC3D,uEAAuE,CAC1E,CAAA;AAED,uCAAuC;AACvC,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAC/B,yDAAyD;IACvD,mEAAmE;IACnE,8DAA8D,CACjE,CAAA;AAED,0BAA0B;AAC1B,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAC/B,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC;KACR,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;KACjF,IAAI,CAAC,EAAE,CAAC,CACZ,CAAA;AAED,+BAA+B;AAC/B,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,qCAAqC,GAAG,sDAAsD,CAAC,CAAA;AAEjI,uBAAuB;AACvB,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAC/B,uDAAuD;IACrD,0DAA0D;IAC1D,uEAAuE,CAC1E,CAAA;AAED,6BAA6B;AAC7B,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAClC,mEAAmE;IACjE,qCAAqC;IACrC,sDAAsD;IACtD,sCAAsC;IACtC,mEAAmE,CACtE,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CACpC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;IACjC,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IAE/B;;;;OAIG;IACH,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAA;IAE9D,OAAO,eAAe,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,SAAS,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,aAAa,CAAA;AAChH,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;IACT;;;OAGG;IACH,oFAAoF,CACvF,CAAA;AAED,0CAA0C;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAChC,6CAA6C,GAAG,4CAA4C,CAC7F,CAAA","sourcesContent":["/**\n * 팔레트 항목의 그림 — **없으면 열 칸이 다 「no-image」로 뜬다.**\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ## 그리는 자리가 있다\n *\n * 앞서 이 자리에 「`@operato/board` 안에 템플릿 아이콘을 그리는 자리가 없다」고 적고 `icon` 을 비워\n * 두었다. **틀렸다.** 있다.\n *\n * ```\n * [읽었음] component-menu.js:101 templateIcon(t) { return t.icon || noImage }\n * [읽었음] component-menu.js:171 [template] img { width: …; height: … }\n * ```\n *\n * `<img src>` 다. 그래서 비워 두면 오류 없이 **열 칸이 전부 같은 회색 네모**로 뜬다. 그것이 우리\n * 그룹을 팔레트에서 제일 안 친절한 칸으로 만들고 있었다.\n *\n * ## 왜 색이 한 벌인가 — **테마를 못 따라간다**\n *\n * `<img>` 안의 SVG 는 독립 문서라 바깥 CSS 변수가 안 닿는다. 그래서 처음에는 `prefers-color-scheme`\n * 으로 두 벌을 넣었고, 그것이 **동작하기까지 했다.**\n *\n * ```\n * [확인함] 다크로 놓고 캔버스에 그려 가운데 픽셀을 읽었다 → 다크용 색이 나왔다\n * ```\n *\n * 그런데 **그 신호가 우리 신호가 아니다.** 이 앱의 테마는 `body` 의 클래스이고 OS 설정과 별개다.\n *\n * ```\n * [확인함] prefersDark: true · body.className: \"light\" ← 같은 순간에 이랬다\n * ```\n *\n * 라이트 테마로 쓰는 사람이 OS 를 다크로 두면 **밝은 아이콘이 밝은 팔레트에 얹힌다.** 그 자리를 재\n * 봤다.\n *\n * ```\n * [확인함] 팔레트 바탕 라이트 rgb(228,232,237) · 다크 rgb(50,58,68)\n * #3f4a52 라이트 7.38:1 · 다크 1.27:1 ← 다크에서 안 보인다\n * #dbe4e9 라이트 1.05:1 · 다크 8.93:1 ← 라이트에서 안 보인다\n * #7d848c 라이트 3.07:1 · 다크 3.04:1 ← 양쪽에서 읽힌다\n * ```\n *\n * 그래서 **한 벌로 간다.** 어느 테마에서도 도형 대비 기준(3:1)을 넘는 중간 톤이고, 어긋날 때 사라지는\n * 쪽보다 늘 조금 얌전한 쪽이 낫다. 테마를 따라 바뀌게 하려면 팔레트가 이 그림을 `<img>` 로 그리는\n * 것부터 바뀌어야 하고, 그것은 보드 쪽 일이다.\n *\n * ## 붓\n *\n * figure 저작 도구의 팔레트를 본떴다 — 그쪽은 Material Symbols 글리프를 쓴다. 여기는 `<img>` 라\n * `md-icon` 을 못 넣으므로, **같은 결로 직접 그린다**: 24 격자, 채운 덩어리가 주인공, 윤곽은 가늘게,\n * 부차적인 것은 옅게. 보드의 기존 그룹 아이콘(가는 선 CAD 풍)과는 일부러 다르다 — 작업대 부품은\n * 저작자가 한눈에 고르는 것이지 도면에서 찾는 것이 아니다.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n\n/** 양쪽 테마에서 읽히는 한 벌 잉크. 고른 근거는 머리말의 잰 값이다. */\nconst INK = '#7d848c'\n\n/** 24 격자에 그린 몸통을 그림 주소로 만든다. */\nfunction iconOf(body: string): string {\n const svg =\n `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">` +\n `<style>` +\n `:root{--c:${INK}}` +\n /* 채운 것이 기본. 윤곽(.o)과 굵은 획(.o2)은 골라 쓴다. */\n `*{fill:var(--c)}` +\n `.o{fill:none;stroke:var(--c);stroke-width:1.7;stroke-linejoin:round}` +\n `.o2{fill:none;stroke:var(--c);stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round}` +\n `.dim{opacity:.45}` +\n `</style>${body}</svg>`\n\n /*\n * base64 가 아니라 퍼센트 부호화를 쓴다. 저장소에 남는 글이 읽히는 편이 낫고, `#` 만 살리면\n * 그대로 통한다 — base64 로 접으면 색 하나 고치는 데도 사람이 못 읽는 덩어리가 된다.\n */\n return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg)\n}\n\n/** 값 하나 — 화면틀 안에 굵은 값 한 줄과 옅은 이름표 한 줄. */\nexport const ICON_FIELD = iconOf(\n `<rect class=\"o\" x=\"2.6\" y=\"5\" width=\"18.8\" height=\"14\" rx=\"2.6\"/>` +\n `<rect x=\"5.6\" y=\"8.8\" width=\"9\" height=\"3.4\" rx=\"1.5\"/>` +\n `<rect class=\"dim\" x=\"5.6\" y=\"14\" width=\"6\" height=\"1.9\" rx=\"0.95\"/>`\n)\n\n/** 누가 서 있나 — 사람. */\nexport const ICON_WHO = iconOf(\n `<circle cx=\"12\" cy=\"8\" r=\"3.7\"/>` + `<path d=\"M4.4 19.8c0-3.7 3.4-5.8 7.6-5.8s7.6 2.1 7.6 5.8z\"/>`\n)\n\n/** 무슨 지시를 도나 — 집게가 달린 판. */\nexport const ICON_ORDER = iconOf(\n `<rect class=\"o\" x=\"5\" y=\"4.2\" width=\"14\" height=\"16.6\" rx=\"2.5\"/>` +\n `<rect x=\"9\" y=\"2.4\" width=\"6\" height=\"3.6\" rx=\"1.3\"/>` +\n `<rect x=\"8.2\" y=\"10\" width=\"7.6\" height=\"1.9\" rx=\"0.95\"/>` +\n `<rect class=\"dim\" x=\"8.2\" y=\"14\" width=\"5.2\" height=\"1.9\" rx=\"0.95\"/>`\n)\n\n/** 얼마나 했나 — 양품과 부적합, 두 막대. 부적합이 옅다. */\nexport const ICON_COUNTS = iconOf(\n `<rect x=\"4.6\" y=\"9.6\" width=\"5\" height=\"9.4\" rx=\"1.7\"/>` +\n `<rect class=\"dim\" x=\"14.4\" y=\"14\" width=\"5\" height=\"5\" rx=\"1.7\"/>` +\n `<rect x=\"2.6\" y=\"20.2\" width=\"18.8\" height=\"1.9\" rx=\"0.95\"/>`\n)\n\n/** 자판 없이 세는 자리 — 아홉 칸. */\nexport const ICON_NUMPAD = iconOf(\n [7, 12, 17]\n .map(y => [7, 12, 17].map(x => `<circle cx=\"${x}\" cy=\"${y}\" r=\"1.95\"/>`).join(''))\n .join('')\n)\n\n/** 눌러서 사실을 남기는 자리 — 누르는 시늉. */\nexport const ICON_ACTION = iconOf(`<circle cx=\"12\" cy=\"13.6\" r=\"4.2\"/>` + `<path class=\"o2\" d=\"M6.6 8.8a7.4 7.4 0 0 1 10.8 0\"/>`)\n\n/** 대 놓은 자재 — 쌓인 상자. */\nexport const ICON_STAGED = iconOf(\n `<rect x=\"8\" y=\"3.4\" width=\"8\" height=\"7.6\" rx=\"1.4\"/>` +\n `<rect x=\"2.8\" y=\"12.8\" width=\"8\" height=\"7.6\" rx=\"1.4\"/>` +\n `<rect class=\"dim\" x=\"13.2\" y=\"12.8\" width=\"8\" height=\"7.6\" rx=\"1.4\"/>`\n)\n\n/** 무엇을 어떻게 하나 — 차례가 있는 글. */\nexport const ICON_DIRECTIVE = iconOf(\n `<rect class=\"o\" x=\"3.4\" y=\"4\" width=\"17.2\" height=\"16\" rx=\"2.5\"/>` +\n `<circle cx=\"7.6\" cy=\"9.2\" r=\"1.6\"/>` +\n `<rect x=\"10.6\" y=\"8.2\" width=\"7\" height=\"2\" rx=\"1\"/>` +\n `<circle cx=\"7.6\" cy=\"14.8\" r=\"1.6\"/>` +\n `<rect class=\"dim\" x=\"10.6\" y=\"13.8\" width=\"5\" height=\"2\" rx=\"1\"/>`\n)\n\n/**\n * 손볼 때가 됐나 — 톱니.\n *\n * 처음에는 스패너로 그렸다(둥근 머리 + 비스듬한 자루). 팔레트 크기로 놓고 보니 **돋보기로 읽혔다** —\n * 「손볼 때가 됐나」가 「찾기」로 보이면 고른 사람이 다른 것을 놓는다. 톱니는 그 자리에서 안 헷갈린다.\n *\n * 이가 여덟 개인 것은 여섯이면 성기고 열이면 24 격자에서 뭉치기 때문이다. 자리는 계산으로 놓는다 —\n * 손으로 적으면 하나가 어긋나도 눈에 안 띈다.\n */\nexport const ICON_MAINTENANCE = iconOf(\n Array.from({ length: 8 }, (_, i) => {\n const angle = (i * Math.PI) / 4\n\n /*\n * `toFixed` 를 안 쓴다. 이 자리에서는 맞는 값을 내지만, **화면에 수를 보일 때 쓰면 로케일이\n * 쉼표를 쓰는 자리에서 점을 찍는다** — 그래서 이 패키지에 그것을 통째로 막는 시험이 있고,\n * 예외를 하나 뚫으면 그 시험이 무뎌진다. 자릿수는 반올림으로 줄인다.\n */\n const round = (value: number) => Math.round(value * 100) / 100\n\n return `<circle cx=\"${round(12 + Math.cos(angle) * 8.4)}\" cy=\"${round(12 + Math.sin(angle) * 8.4)}\" r=\"2.1\"/>`\n }).join('') +\n /*\n * 몸통은 **채우지 않고 굵게 두른다.** 채우면 가운데 구멍이 없어지고, 구멍 없는 톱니는 단추처럼\n * 보인다. 배경색으로 구멍을 덮는 수는 못 쓴다 — 팔레트 바탕이 테마마다 다르다.\n */\n `<circle cx=\"12\" cy=\"12\" r=\"5\" style=\"fill:none;stroke:var(--c);stroke-width:3.6\"/>`\n)\n\n/** 안 그려지는 부품 — 무엇을 얼마나 자주 읽나. 시계로 말한다. */\nexport const ICON_CONTEXT = iconOf(\n `<circle class=\"o\" cx=\"12\" cy=\"12\" r=\"8.6\"/>` + `<path class=\"o2\" d=\"M12 6.9v5.4l3.6 2.2\"/>`\n)\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ComponentNature } from '@hatiolab/things-scene';
|
|
2
|
+
import { StationScene } from './station-scene';
|
|
3
|
+
export default class StationNumpadScene extends StationScene {
|
|
4
|
+
/** What has been keyed in but not yet sent. Deliberately not in the model — it is not the board's. */
|
|
5
|
+
private typed;
|
|
6
|
+
render(context: CanvasRenderingContext2D): void;
|
|
7
|
+
onmouseup(event: any): void;
|
|
8
|
+
get nature(): ComponentNature;
|
|
9
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { sceneComponent } from '@hatiolab/things-scene';
|
|
3
|
+
import { STATION_COLOR, drawCard, drawDetached, drawHeading, withinBox } from './station-scene-style';
|
|
4
|
+
import { StationScene } from './station-scene';
|
|
5
|
+
/**
|
|
6
|
+
* On a board · putting a number in.
|
|
7
|
+
*
|
|
8
|
+
* ── Why counting needs its own component ───────────────────────────────────
|
|
9
|
+
* The action list is closed, and every action but this one is a bare verb — start, pause, resume,
|
|
10
|
+
* finish. Counting carries a number, and a plain button has nowhere to get one. Putting `count` on
|
|
11
|
+
* the button component would mean baking the quantity into the board at authoring time, so every
|
|
12
|
+
* press of it would report the same figure whatever came off the machine.
|
|
13
|
+
*
|
|
14
|
+
* ── Why not a text field ───────────────────────────────────────────────────
|
|
15
|
+
* There is no DOM here to hold an input, and the floor is wearing gloves. Big keys the operator can
|
|
16
|
+
* hit without looking are the reason this is drawn rather than typed.
|
|
17
|
+
*
|
|
18
|
+
* ── The total, not the difference ──────────────────────────────────────────
|
|
19
|
+
* The same task reported twice is an **update**, so what goes out is the running total, not what was
|
|
20
|
+
* added. Increments lose the plot the moment one envelope goes missing, and the sequence number
|
|
21
|
+
* would tell us something was dropped without being able to put the total back.
|
|
22
|
+
*/
|
|
23
|
+
const NATURE = {
|
|
24
|
+
mutable: false,
|
|
25
|
+
resizable: true,
|
|
26
|
+
rotatable: false,
|
|
27
|
+
properties: [
|
|
28
|
+
{ type: 'string', label: 'equipment', name: 'equipment', property: 'equipment' },
|
|
29
|
+
{ type: 'string', label: 'label', name: 'label', property: 'label' },
|
|
30
|
+
{
|
|
31
|
+
type: 'select',
|
|
32
|
+
label: 'counts',
|
|
33
|
+
name: 'counts',
|
|
34
|
+
property: { options: ['good', 'scrap'] }
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
};
|
|
38
|
+
const KEYS = ['7', '8', '9', '4', '5', '6', '1', '2', '3', 'C', '0', '✓'];
|
|
39
|
+
let StationNumpadScene = class StationNumpadScene extends StationScene {
|
|
40
|
+
constructor() {
|
|
41
|
+
super(...arguments);
|
|
42
|
+
/** What has been keyed in but not yet sent. Deliberately not in the model — it is not the board's. */
|
|
43
|
+
this.typed = '';
|
|
44
|
+
}
|
|
45
|
+
render(context) {
|
|
46
|
+
const state = this.state;
|
|
47
|
+
const { left, top, width, height } = state;
|
|
48
|
+
const snapshot = this.snapshot;
|
|
49
|
+
if (!snapshot) {
|
|
50
|
+
drawDetached(context, 'not connected', left, top, width, height);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const kind = state.counts === 'scrap' ? 'scrap' : 'good';
|
|
54
|
+
drawCard(context, left, top, width, height);
|
|
55
|
+
const bodyTop = drawHeading(context, state.label || `${kind} count`, left, top, width);
|
|
56
|
+
withinBox(context, left, bodyTop, width, top + height - bodyTop, () => {
|
|
57
|
+
/* What is on the pad now, and what is already recorded — the operator needs both to trust it. */
|
|
58
|
+
const readout = 30;
|
|
59
|
+
context.textBaseline = 'middle';
|
|
60
|
+
context.textAlign = 'right';
|
|
61
|
+
context.font = 'bold 24px sans-serif';
|
|
62
|
+
context.fillStyle = this.typed ? STATION_COLOR.ink : STATION_COLOR.quiet;
|
|
63
|
+
context.fillText(this.typed || String(snapshot.today[kind]), left + width - 8, bodyTop + readout / 2);
|
|
64
|
+
const gridTop = bodyTop + readout;
|
|
65
|
+
const cellWidth = width / 3;
|
|
66
|
+
const cellHeight = (top + height - gridTop) / 4;
|
|
67
|
+
KEYS.forEach((key, index) => {
|
|
68
|
+
const x = left + (index % 3) * cellWidth;
|
|
69
|
+
const y = gridTop + Math.floor(index / 3) * cellHeight;
|
|
70
|
+
context.beginPath();
|
|
71
|
+
context.rect(x + 1, y + 1, cellWidth - 2, cellHeight - 2);
|
|
72
|
+
context.fillStyle = key === '✓' ? STATION_COLOR.action : key === 'C' ? STATION_COLOR.line : STATION_COLOR.paper;
|
|
73
|
+
context.fill();
|
|
74
|
+
context.strokeStyle = STATION_COLOR.line;
|
|
75
|
+
context.stroke();
|
|
76
|
+
context.textAlign = 'center';
|
|
77
|
+
context.fillStyle = key === '✓' ? '#ffffff' : STATION_COLOR.ink;
|
|
78
|
+
context.font = `bold ${Math.max(14, Math.min(26, cellHeight * 0.4))}px sans-serif`;
|
|
79
|
+
context.fillText(key, x + cellWidth / 2, y + cellHeight / 2);
|
|
80
|
+
});
|
|
81
|
+
context.textAlign = 'left';
|
|
82
|
+
context.textBaseline = 'alphabetic';
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
onmouseup(event) {
|
|
86
|
+
/* Pressed while drawing the screen would leave real facts behind. */
|
|
87
|
+
if (this.app?.isViewMode === false) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const state = this.state;
|
|
91
|
+
const context = this.context;
|
|
92
|
+
const snapshot = context?.snapshot;
|
|
93
|
+
if (!context || !snapshot || state.disabled || context.readOnly) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const point = this.transcoordC2S(event.offsetX, event.offsetY);
|
|
97
|
+
const bodyTop = state.top + 22;
|
|
98
|
+
const gridTop = bodyTop + 30;
|
|
99
|
+
const column = Math.floor(((point.x - state.left) / state.width) * 3);
|
|
100
|
+
const row = Math.floor(((point.y - gridTop) / (state.top + state.height - gridTop)) * 4);
|
|
101
|
+
const key = KEYS[row * 3 + column];
|
|
102
|
+
if (!key || row < 0 || column < 0 || column > 2 || row > 3) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (key === 'C') {
|
|
106
|
+
this.typed = '';
|
|
107
|
+
this.invalidate();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (key !== '✓') {
|
|
111
|
+
/* Four digits is more than any station reports in one window, and it stops a leaned-on key. */
|
|
112
|
+
this.typed = (this.typed + key).slice(0, 4);
|
|
113
|
+
this.invalidate();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (!this.typed) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const kind = state.counts === 'scrap' ? 'scrap' : 'good';
|
|
120
|
+
const typed = this.typed;
|
|
121
|
+
context.act({ kind: 'count', [kind]: Number(typed) }).then(() => {
|
|
122
|
+
/*
|
|
123
|
+
* Only clear once it actually went in.
|
|
124
|
+
*
|
|
125
|
+
* Clearing regardless was how a refused count vanished: the badge had not been scanned, the
|
|
126
|
+
* runtime declined, and the pad wiped the number the operator had just keyed. They key it
|
|
127
|
+
* again, it is refused again, and nothing on the screen ever says why.
|
|
128
|
+
*/
|
|
129
|
+
if (!this.context?.snapshot.problem) {
|
|
130
|
+
this.typed = '';
|
|
131
|
+
}
|
|
132
|
+
this.invalidate();
|
|
133
|
+
});
|
|
134
|
+
this.invalidate();
|
|
135
|
+
}
|
|
136
|
+
get nature() {
|
|
137
|
+
return NATURE;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
StationNumpadScene = __decorate([
|
|
141
|
+
sceneComponent('station-numpad')
|
|
142
|
+
], StationNumpadScene);
|
|
143
|
+
export default StationNumpadScene;
|
|
144
|
+
//# sourceMappingURL=station-numpad-scene.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"station-numpad-scene.js","sourceRoot":"","sources":["../../../client/station/scene/station-numpad-scene.ts"],"names":[],"mappings":"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","sourcesContent":["import { ComponentNature, sceneComponent } from '@hatiolab/things-scene'\n\nimport { STATION_COLOR, drawCard, drawDetached, drawHeading, withinBox } from './station-scene-style'\nimport { StationScene } from './station-scene'\n\n/**\n * On a board · putting a number in.\n *\n * ── Why counting needs its own component ───────────────────────────────────\n * The action list is closed, and every action but this one is a bare verb — start, pause, resume,\n * finish. Counting carries a number, and a plain button has nowhere to get one. Putting `count` on\n * the button component would mean baking the quantity into the board at authoring time, so every\n * press of it would report the same figure whatever came off the machine.\n *\n * ── Why not a text field ───────────────────────────────────────────────────\n * There is no DOM here to hold an input, and the floor is wearing gloves. Big keys the operator can\n * hit without looking are the reason this is drawn rather than typed.\n *\n * ── The total, not the difference ──────────────────────────────────────────\n * The same task reported twice is an **update**, so what goes out is the running total, not what was\n * added. Increments lose the plot the moment one envelope goes missing, and the sequence number\n * would tell us something was dropped without being able to put the total back.\n */\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: false,\n properties: [\n { type: 'string', label: 'equipment', name: 'equipment', property: 'equipment' },\n { type: 'string', label: 'label', name: 'label', property: 'label' },\n {\n type: 'select',\n label: 'counts',\n name: 'counts',\n property: { options: ['good', 'scrap'] }\n }\n ]\n}\n\nconst KEYS = ['7', '8', '9', '4', '5', '6', '1', '2', '3', 'C', '0', '✓']\n\n@sceneComponent('station-numpad')\nexport default class StationNumpadScene extends StationScene {\n /** What has been keyed in but not yet sent. Deliberately not in the model — it is not the board's. */\n private typed = ''\n\n render(context: CanvasRenderingContext2D) {\n const state: any = this.state\n const { left, top, width, height } = state\n const snapshot = this.snapshot\n\n if (!snapshot) {\n drawDetached(context, 'not connected', left, top, width, height)\n return\n }\n\n const kind = state.counts === 'scrap' ? 'scrap' : 'good'\n\n drawCard(context, left, top, width, height)\n\n const bodyTop = drawHeading(context, state.label || `${kind} count`, left, top, width)\n\n withinBox(context, left, bodyTop, width, top + height - bodyTop, () => {\n /* What is on the pad now, and what is already recorded — the operator needs both to trust it. */\n const readout = 30\n context.textBaseline = 'middle'\n context.textAlign = 'right'\n context.font = 'bold 24px sans-serif'\n context.fillStyle = this.typed ? STATION_COLOR.ink : STATION_COLOR.quiet\n context.fillText(this.typed || String(snapshot.today[kind]), left + width - 8, bodyTop + readout / 2)\n\n const gridTop = bodyTop + readout\n const cellWidth = width / 3\n const cellHeight = (top + height - gridTop) / 4\n\n KEYS.forEach((key, index) => {\n const x = left + (index % 3) * cellWidth\n const y = gridTop + Math.floor(index / 3) * cellHeight\n\n context.beginPath()\n context.rect(x + 1, y + 1, cellWidth - 2, cellHeight - 2)\n context.fillStyle = key === '✓' ? STATION_COLOR.action : key === 'C' ? STATION_COLOR.line : STATION_COLOR.paper\n context.fill()\n context.strokeStyle = STATION_COLOR.line\n context.stroke()\n\n context.textAlign = 'center'\n context.fillStyle = key === '✓' ? '#ffffff' : STATION_COLOR.ink\n context.font = `bold ${Math.max(14, Math.min(26, cellHeight * 0.4))}px sans-serif`\n context.fillText(key, x + cellWidth / 2, y + cellHeight / 2)\n })\n\n context.textAlign = 'left'\n context.textBaseline = 'alphabetic'\n })\n }\n\n onmouseup(event: any) {\n /* Pressed while drawing the screen would leave real facts behind. */\n if ((this as any).app?.isViewMode === false) {\n return\n }\n\n const state: any = this.state\n const context = this.context\n const snapshot = context?.snapshot\n\n if (!context || !snapshot || state.disabled || context.readOnly) {\n return\n }\n\n const point = this.transcoordC2S(event.offsetX, event.offsetY)\n const bodyTop = state.top + 22\n const gridTop = bodyTop + 30\n const column = Math.floor(((point.x - state.left) / state.width) * 3)\n const row = Math.floor(((point.y - gridTop) / (state.top + state.height - gridTop)) * 4)\n const key = KEYS[row * 3 + column]\n\n if (!key || row < 0 || column < 0 || column > 2 || row > 3) {\n return\n }\n\n if (key === 'C') {\n this.typed = ''\n this.invalidate()\n return\n }\n\n if (key !== '✓') {\n /* Four digits is more than any station reports in one window, and it stops a leaned-on key. */\n this.typed = (this.typed + key).slice(0, 4)\n this.invalidate()\n return\n }\n\n if (!this.typed) {\n return\n }\n\n const kind = state.counts === 'scrap' ? 'scrap' : 'good'\n const typed = this.typed\n\n context.act({ kind: 'count', [kind]: Number(typed) } as any).then(() => {\n /*\n * Only clear once it actually went in.\n *\n * Clearing regardless was how a refused count vanished: the badge had not been scanned, the\n * runtime declined, and the pad wiped the number the operator had just keyed. They key it\n * again, it is refused again, and nothing on the screen ever says why.\n */\n if (!this.context?.snapshot.problem) {\n this.typed = ''\n }\n\n this.invalidate()\n })\n\n this.invalidate()\n }\n\n get nature() {\n return NATURE\n }\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 현장 씬 컴포넌트가 함께 쓰는 겉모습 — 자리와 색만.
|
|
3
|
+
*
|
|
4
|
+
* 컴포넌트마다 크기와 색을 다시 정하면 한 보드 안에서 글씨 크기가 셋이 된다.
|
|
5
|
+
*/
|
|
6
|
+
export declare const STATION_COLOR: {
|
|
7
|
+
ink: string;
|
|
8
|
+
quiet: string;
|
|
9
|
+
line: string;
|
|
10
|
+
paper: string;
|
|
11
|
+
good: string;
|
|
12
|
+
scrap: string;
|
|
13
|
+
warn: string;
|
|
14
|
+
action: string;
|
|
15
|
+
};
|
|
16
|
+
/** 카드 바탕 하나 — 모든 현장 컴포넌트가 같은 테두리를 쓴다. */
|
|
17
|
+
export declare function drawCard(context: CanvasRenderingContext2D, left: number, top: number, width: number, height: number, accent?: string): void;
|
|
18
|
+
export declare function drawHeading(context: CanvasRenderingContext2D, text: string, left: number, top: number, width: number): number;
|
|
19
|
+
/**
|
|
20
|
+
* 아직 런타임이 없을 때 그리는 것.
|
|
21
|
+
*
|
|
22
|
+
* 0 을 그리지 않는다 — 그 0 은 「아직 안 붙었다」가 아니라 「오늘 아무것도 안 나왔다」로 읽힌다.
|
|
23
|
+
*/
|
|
24
|
+
export declare function drawDetached(context: CanvasRenderingContext2D, text: string, left: number, top: number, width: number, height: number): void;
|
|
25
|
+
/**
|
|
26
|
+
* Keep a component's drawing inside its own box.
|
|
27
|
+
*
|
|
28
|
+
* Rows and long names run past the edge otherwise, and on a canvas there is no clipping by default —
|
|
29
|
+
* a staged list with eight rows would draw over the button below it, and the operator would press
|
|
30
|
+
* text instead of the button.
|
|
31
|
+
*/
|
|
32
|
+
export declare function withinBox<T>(context: CanvasRenderingContext2D, left: number, top: number, width: number, height: number, draw: () => T): T;
|
|
33
|
+
/** How tall one row of a list is. Sized for a gloved finger, not for a mouse. */
|
|
34
|
+
export declare const STATION_ROW_HEIGHT = 34;
|
|
35
|
+
/**
|
|
36
|
+
* Which row a tap landed on, or nothing when it landed below the last one.
|
|
37
|
+
*
|
|
38
|
+
* Returning a row for empty space is how a list ends up removing the last item when someone taps
|
|
39
|
+
* the blank area under it.
|
|
40
|
+
*/
|
|
41
|
+
export declare function rowAt(y: number, listTop: number, rows: number): number | undefined;
|
|
42
|
+
/** One line of a list — name on the left, a short note on the right. */
|
|
43
|
+
export declare function drawRow(context: CanvasRenderingContext2D, left: number, top: number, width: number, name: string, note: string, color: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* What a list says when it has nothing in it.
|
|
46
|
+
*
|
|
47
|
+
* Deliberately not blank. An empty area reads as a component that failed to draw; a sentence reads
|
|
48
|
+
* as a fact — and on a shop-floor screen the difference decides whether someone goes looking for a
|
|
49
|
+
* broken tablet.
|
|
50
|
+
*/
|
|
51
|
+
export declare function drawEmptyList(context: CanvasRenderingContext2D, text: string, left: number, top: number, width: number): void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 현장 씬 컴포넌트가 함께 쓰는 겉모습 — 자리와 색만.
|
|
3
|
+
*
|
|
4
|
+
* 컴포넌트마다 크기와 색을 다시 정하면 한 보드 안에서 글씨 크기가 셋이 된다.
|
|
5
|
+
*/
|
|
6
|
+
export const STATION_COLOR = {
|
|
7
|
+
ink: '#17242c',
|
|
8
|
+
quiet: '#8a959c',
|
|
9
|
+
line: '#d5dce0',
|
|
10
|
+
paper: '#ffffff',
|
|
11
|
+
good: '#1a8f4c',
|
|
12
|
+
scrap: '#c93f30',
|
|
13
|
+
warn: '#b4762a',
|
|
14
|
+
action: '#2f6fb0'
|
|
15
|
+
};
|
|
16
|
+
/** 카드 바탕 하나 — 모든 현장 컴포넌트가 같은 테두리를 쓴다. */
|
|
17
|
+
export function drawCard(context, left, top, width, height, accent = STATION_COLOR.line) {
|
|
18
|
+
context.beginPath();
|
|
19
|
+
context.rect(left, top, width, height);
|
|
20
|
+
context.fillStyle = STATION_COLOR.paper;
|
|
21
|
+
context.fill();
|
|
22
|
+
context.lineWidth = 1;
|
|
23
|
+
context.strokeStyle = accent;
|
|
24
|
+
context.stroke();
|
|
25
|
+
}
|
|
26
|
+
export function drawHeading(context, text, left, top, width) {
|
|
27
|
+
context.textBaseline = 'top';
|
|
28
|
+
context.textAlign = 'left';
|
|
29
|
+
context.fillStyle = STATION_COLOR.quiet;
|
|
30
|
+
context.font = 'bold 10px sans-serif';
|
|
31
|
+
context.fillText(String(text), left + 8, top + 6);
|
|
32
|
+
return top + 22;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 아직 런타임이 없을 때 그리는 것.
|
|
36
|
+
*
|
|
37
|
+
* 0 을 그리지 않는다 — 그 0 은 「아직 안 붙었다」가 아니라 「오늘 아무것도 안 나왔다」로 읽힌다.
|
|
38
|
+
*/
|
|
39
|
+
export function drawDetached(context, text, left, top, width, height) {
|
|
40
|
+
drawCard(context, left, top, width, height, STATION_COLOR.line);
|
|
41
|
+
context.textAlign = 'center';
|
|
42
|
+
context.textBaseline = 'middle';
|
|
43
|
+
context.fillStyle = STATION_COLOR.quiet;
|
|
44
|
+
context.font = '11px sans-serif';
|
|
45
|
+
context.fillText(String(text), left + width / 2, top + height / 2);
|
|
46
|
+
context.textAlign = 'left';
|
|
47
|
+
context.textBaseline = 'alphabetic';
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Keep a component's drawing inside its own box.
|
|
51
|
+
*
|
|
52
|
+
* Rows and long names run past the edge otherwise, and on a canvas there is no clipping by default —
|
|
53
|
+
* a staged list with eight rows would draw over the button below it, and the operator would press
|
|
54
|
+
* text instead of the button.
|
|
55
|
+
*/
|
|
56
|
+
export function withinBox(context, left, top, width, height, draw) {
|
|
57
|
+
context.save();
|
|
58
|
+
context.beginPath();
|
|
59
|
+
context.rect(left, top, width, height);
|
|
60
|
+
context.clip();
|
|
61
|
+
try {
|
|
62
|
+
return draw();
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
context.restore();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** How tall one row of a list is. Sized for a gloved finger, not for a mouse. */
|
|
69
|
+
export const STATION_ROW_HEIGHT = 34;
|
|
70
|
+
/**
|
|
71
|
+
* Which row a tap landed on, or nothing when it landed below the last one.
|
|
72
|
+
*
|
|
73
|
+
* Returning a row for empty space is how a list ends up removing the last item when someone taps
|
|
74
|
+
* the blank area under it.
|
|
75
|
+
*/
|
|
76
|
+
export function rowAt(y, listTop, rows) {
|
|
77
|
+
const index = Math.floor((y - listTop) / STATION_ROW_HEIGHT);
|
|
78
|
+
return index >= 0 && index < rows ? index : undefined;
|
|
79
|
+
}
|
|
80
|
+
/** One line of a list — name on the left, a short note on the right. */
|
|
81
|
+
export function drawRow(context, left, top, width, name, note, color) {
|
|
82
|
+
context.textBaseline = 'middle';
|
|
83
|
+
context.textAlign = 'left';
|
|
84
|
+
context.fillStyle = color;
|
|
85
|
+
context.font = 'bold 13px sans-serif';
|
|
86
|
+
context.fillText(name, left + 8, top + STATION_ROW_HEIGHT / 2);
|
|
87
|
+
context.fillStyle = STATION_COLOR.quiet;
|
|
88
|
+
context.font = '11px sans-serif';
|
|
89
|
+
context.textAlign = 'right';
|
|
90
|
+
context.fillText(note, left + width - 8, top + STATION_ROW_HEIGHT / 2);
|
|
91
|
+
context.textAlign = 'left';
|
|
92
|
+
context.textBaseline = 'alphabetic';
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* What a list says when it has nothing in it.
|
|
96
|
+
*
|
|
97
|
+
* Deliberately not blank. An empty area reads as a component that failed to draw; a sentence reads
|
|
98
|
+
* as a fact — and on a shop-floor screen the difference decides whether someone goes looking for a
|
|
99
|
+
* broken tablet.
|
|
100
|
+
*/
|
|
101
|
+
export function drawEmptyList(context, text, left, top, width) {
|
|
102
|
+
context.fillStyle = STATION_COLOR.quiet;
|
|
103
|
+
context.font = '12px sans-serif';
|
|
104
|
+
context.textBaseline = 'middle';
|
|
105
|
+
context.fillText(text, left + 8, top + STATION_ROW_HEIGHT / 2);
|
|
106
|
+
context.textBaseline = 'alphabetic';
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=station-scene-style.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"station-scene-style.js","sourceRoot":"","sources":["../../../client/station/scene/station-scene-style.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,GAAG,EAAE,SAAS;IACd,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;CAClB,CAAA;AAED,yCAAyC;AACzC,MAAM,UAAU,QAAQ,CAAC,OAAiC,EAAE,IAAY,EAAE,GAAW,EAAE,KAAa,EAAE,MAAc,EAAE,SAAiB,aAAa,CAAC,IAAI;IACvJ,OAAO,CAAC,SAAS,EAAE,CAAA;IACnB,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,GAAG,aAAa,CAAC,KAAK,CAAA;IACvC,OAAO,CAAC,IAAI,EAAE,CAAA;IACd,OAAO,CAAC,SAAS,GAAG,CAAC,CAAA;IACrB,OAAO,CAAC,WAAW,GAAG,MAAM,CAAA;IAC5B,OAAO,CAAC,MAAM,EAAE,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAiC,EAAE,IAAY,EAAE,IAAY,EAAE,GAAW,EAAE,KAAa;IACnH,OAAO,CAAC,YAAY,GAAG,KAAK,CAAA;IAC5B,OAAO,CAAC,SAAS,GAAG,MAAM,CAAA;IAC1B,OAAO,CAAC,SAAS,GAAG,aAAa,CAAC,KAAK,CAAA;IACvC,OAAO,CAAC,IAAI,GAAG,sBAAsB,CAAA;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAA;IAEjD,OAAO,GAAG,GAAG,EAAE,CAAA;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAAiC,EAAE,IAAY,EAAE,IAAY,EAAE,GAAW,EAAE,KAAa,EAAE,MAAc;IACpI,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,IAAI,CAAC,CAAA;IAC/D,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAA;IAC5B,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAA;IAC/B,OAAO,CAAC,SAAS,GAAG,aAAa,CAAC,KAAK,CAAA;IACvC,OAAO,CAAC,IAAI,GAAG,iBAAiB,CAAA;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,CAAC,EAAE,GAAG,GAAG,MAAM,GAAG,CAAC,CAAC,CAAA;IAClE,OAAO,CAAC,SAAS,GAAG,MAAM,CAAA;IAC1B,OAAO,CAAC,YAAY,GAAG,YAAY,CAAA;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAI,OAAiC,EAAE,IAAY,EAAE,GAAW,EAAE,KAAa,EAAE,MAAc,EAAE,IAAa;IACrI,OAAO,CAAC,IAAI,EAAE,CAAA;IACd,OAAO,CAAC,SAAS,EAAE,CAAA;IACnB,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;IACtC,OAAO,CAAC,IAAI,EAAE,CAAA;IAEd,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAA;IACf,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,OAAO,EAAE,CAAA;IACnB,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAA;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAC,CAAS,EAAE,OAAe,EAAE,IAAY;IAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,kBAAkB,CAAC,CAAA;IAE5D,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AACvD,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,OAAO,CAAC,OAAiC,EAAE,IAAY,EAAE,GAAW,EAAE,KAAa,EAAE,IAAY,EAAE,IAAY,EAAE,KAAa;IAC5I,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAA;IAC/B,OAAO,CAAC,SAAS,GAAG,MAAM,CAAA;IAE1B,OAAO,CAAC,SAAS,GAAG,KAAK,CAAA;IACzB,OAAO,CAAC,IAAI,GAAG,sBAAsB,CAAA;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,EAAE,GAAG,GAAG,kBAAkB,GAAG,CAAC,CAAC,CAAA;IAE9D,OAAO,CAAC,SAAS,GAAG,aAAa,CAAC,KAAK,CAAA;IACvC,OAAO,CAAC,IAAI,GAAG,iBAAiB,CAAA;IAChC,OAAO,CAAC,SAAS,GAAG,OAAO,CAAA;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,KAAK,GAAG,CAAC,EAAE,GAAG,GAAG,kBAAkB,GAAG,CAAC,CAAC,CAAA;IAEtE,OAAO,CAAC,SAAS,GAAG,MAAM,CAAA;IAC1B,OAAO,CAAC,YAAY,GAAG,YAAY,CAAA;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,OAAiC,EAAE,IAAY,EAAE,IAAY,EAAE,GAAW,EAAE,KAAa;IACrH,OAAO,CAAC,SAAS,GAAG,aAAa,CAAC,KAAK,CAAA;IACvC,OAAO,CAAC,IAAI,GAAG,iBAAiB,CAAA;IAChC,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAA;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,EAAE,GAAG,GAAG,kBAAkB,GAAG,CAAC,CAAC,CAAA;IAC9D,OAAO,CAAC,YAAY,GAAG,YAAY,CAAA;AACrC,CAAC","sourcesContent":["/**\n * 현장 씬 컴포넌트가 함께 쓰는 겉모습 — 자리와 색만.\n *\n * 컴포넌트마다 크기와 색을 다시 정하면 한 보드 안에서 글씨 크기가 셋이 된다.\n */\n\nexport const STATION_COLOR = {\n ink: '#17242c',\n quiet: '#8a959c',\n line: '#d5dce0',\n paper: '#ffffff',\n good: '#1a8f4c',\n scrap: '#c93f30',\n warn: '#b4762a',\n action: '#2f6fb0'\n}\n\n/** 카드 바탕 하나 — 모든 현장 컴포넌트가 같은 테두리를 쓴다. */\nexport function drawCard(context: CanvasRenderingContext2D, left: number, top: number, width: number, height: number, accent: string = STATION_COLOR.line): void {\n context.beginPath()\n context.rect(left, top, width, height)\n context.fillStyle = STATION_COLOR.paper\n context.fill()\n context.lineWidth = 1\n context.strokeStyle = accent\n context.stroke()\n}\n\nexport function drawHeading(context: CanvasRenderingContext2D, text: string, left: number, top: number, width: number): number {\n context.textBaseline = 'top'\n context.textAlign = 'left'\n context.fillStyle = STATION_COLOR.quiet\n context.font = 'bold 10px sans-serif'\n context.fillText(String(text), left + 8, top + 6)\n\n return top + 22\n}\n\n/**\n * 아직 런타임이 없을 때 그리는 것.\n *\n * 0 을 그리지 않는다 — 그 0 은 「아직 안 붙었다」가 아니라 「오늘 아무것도 안 나왔다」로 읽힌다.\n */\nexport function drawDetached(context: CanvasRenderingContext2D, text: string, left: number, top: number, width: number, height: number): void {\n drawCard(context, left, top, width, height, STATION_COLOR.line)\n context.textAlign = 'center'\n context.textBaseline = 'middle'\n context.fillStyle = STATION_COLOR.quiet\n context.font = '11px sans-serif'\n context.fillText(String(text), left + width / 2, top + height / 2)\n context.textAlign = 'left'\n context.textBaseline = 'alphabetic'\n}\n\n/**\n * Keep a component's drawing inside its own box.\n *\n * Rows and long names run past the edge otherwise, and on a canvas there is no clipping by default —\n * a staged list with eight rows would draw over the button below it, and the operator would press\n * text instead of the button.\n */\nexport function withinBox<T>(context: CanvasRenderingContext2D, left: number, top: number, width: number, height: number, draw: () => T): T {\n context.save()\n context.beginPath()\n context.rect(left, top, width, height)\n context.clip()\n\n try {\n return draw()\n } finally {\n context.restore()\n }\n}\n\n/** How tall one row of a list is. Sized for a gloved finger, not for a mouse. */\nexport const STATION_ROW_HEIGHT = 34\n\n/**\n * Which row a tap landed on, or nothing when it landed below the last one.\n *\n * Returning a row for empty space is how a list ends up removing the last item when someone taps\n * the blank area under it.\n */\nexport function rowAt(y: number, listTop: number, rows: number): number | undefined {\n const index = Math.floor((y - listTop) / STATION_ROW_HEIGHT)\n\n return index >= 0 && index < rows ? index : undefined\n}\n\n/** One line of a list — name on the left, a short note on the right. */\nexport function drawRow(context: CanvasRenderingContext2D, left: number, top: number, width: number, name: string, note: string, color: string): void {\n context.textBaseline = 'middle'\n context.textAlign = 'left'\n\n context.fillStyle = color\n context.font = 'bold 13px sans-serif'\n context.fillText(name, left + 8, top + STATION_ROW_HEIGHT / 2)\n\n context.fillStyle = STATION_COLOR.quiet\n context.font = '11px sans-serif'\n context.textAlign = 'right'\n context.fillText(note, left + width - 8, top + STATION_ROW_HEIGHT / 2)\n\n context.textAlign = 'left'\n context.textBaseline = 'alphabetic'\n}\n\n/**\n * What a list says when it has nothing in it.\n *\n * Deliberately not blank. An empty area reads as a component that failed to draw; a sentence reads\n * as a fact — and on a shop-floor screen the difference decides whether someone goes looking for a\n * broken tablet.\n */\nexport function drawEmptyList(context: CanvasRenderingContext2D, text: string, left: number, top: number, width: number): void {\n context.fillStyle = STATION_COLOR.quiet\n context.font = '12px sans-serif'\n context.textBaseline = 'middle'\n context.fillText(text, left + 8, top + STATION_ROW_HEIGHT / 2)\n context.textBaseline = 'alphabetic'\n}\n"]}
|