deel-local-cli 1.5.3 → 1.5.4
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.en.md +4 -3
- package/README.md +4 -3
- package/package.json +1 -1
- package/src/repl.js +9 -0
- package/src/ui/inputbox.js +16 -1
- package/src/ui/office.js +146 -14
- package/src/ui/screen.js +340 -336
package/README.en.md
CHANGED
|
@@ -100,7 +100,7 @@ This page is the **summary**. Each section links to the detail behind it.
|
|
|
100
100
|
| [Speed and spend](docs/en/tuning.md) | Per-stage effort · the prefix cache · context length |
|
|
101
101
|
| [Safety and corporate review](docs/en/safety.md) | Undo · working scope · audit log · the review package |
|
|
102
102
|
| [Configuration](docs/en/config.md) · [Development](docs/en/develop.md) | Env vars · run flags · running the tests · folder layout |
|
|
103
|
-
| [Release notes](docs/en/releases.md) | [1.5.3](docs/en/releases.md#153) · [1.5.2](docs/en/releases.md#152) · [1.5.1](docs/en/releases.md#151) · [1.5.0](docs/en/releases.md#150) · [1.4.3](docs/en/releases.md#143) · [1.4.2](docs/en/releases.md#142) · [1.4.1](docs/en/releases.md#141) · [1.4.0](docs/en/releases.md#140) · [1.3.0](docs/en/releases.md#130) · [1.2.0](docs/en/releases.md#120) |
|
|
103
|
+
| [Release notes](docs/en/releases.md) | [1.5.4](docs/en/releases.md#154) · [1.5.3](docs/en/releases.md#153) · [1.5.2](docs/en/releases.md#152) · [1.5.1](docs/en/releases.md#151) · [1.5.0](docs/en/releases.md#150) · [1.4.3](docs/en/releases.md#143) · [1.4.2](docs/en/releases.md#142) · [1.4.1](docs/en/releases.md#141) · [1.4.0](docs/en/releases.md#140) · [1.3.0](docs/en/releases.md#130) · [1.2.0](docs/en/releases.md#120) |
|
|
104
104
|
|
|
105
105
|
---
|
|
106
106
|
|
|
@@ -929,7 +929,7 @@ Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder tak
|
|
|
929
929
|
## Development
|
|
930
930
|
|
|
931
931
|
```bash
|
|
932
|
-
npm test Full suite (3,
|
|
932
|
+
npm test Full suite (3,681 checks)
|
|
933
933
|
npm run coverage Which lines the tests actually execute
|
|
934
934
|
npm run verify Import + network checks only
|
|
935
935
|
npm run bench Edit success rate
|
|
@@ -981,7 +981,8 @@ so one run tells you everything.
|
|
|
981
981
|
|
|
982
982
|
| Version | What changed |
|
|
983
983
|
|---|---|
|
|
984
|
-
| **[1.5.
|
|
984
|
+
| **[1.5.4](docs/en/releases.md#154)** | The office fills up and a day passes in it — "only one person" was a bug, not a design |
|
|
985
|
+
| [1.5.3](docs/en/releases.md#153) | Three things a Mac user tripped over — newline hint, office stays put, one `/motion` |
|
|
985
986
|
| [1.5.2](docs/en/releases.md#152) | Everything else the adversarial reviews turned up — where the screen was lying |
|
|
986
987
|
| [1.5.1](docs/en/releases.md#151) | Two of the things 1.5.0 added shipped broken. This fixes them |
|
|
987
988
|
| [1.5.0](docs/en/releases.md#150) | Line breaks exist now, the screen got fun, and it came out lighter than before |
|
package/README.md
CHANGED
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
| [속도와 씀씀이](docs/ko/tuning.md) | 단계별 추론 강도 · 프리픽스 캐시 · 컨텍스트 길이 |
|
|
102
102
|
| [안전망과 사내 반입](docs/ko/safety.md) | 되돌리기 · 작업 범위 · 감사기록 · 심사 서류 |
|
|
103
103
|
| [설정](docs/ko/config.md) · [개발](docs/ko/develop.md) | 환경변수 · 실행 옵션 · 검사 돌리기 · 폴더 구조 |
|
|
104
|
-
| [릴리스 노트](docs/ko/releases.md) | [1.5.3](docs/ko/releases.md#153) · [1.5.2](docs/ko/releases.md#152) · [1.5.1](docs/ko/releases.md#151) · [1.5.0](docs/ko/releases.md#150) · [1.4.3](docs/ko/releases.md#143) · [1.4.2](docs/ko/releases.md#142) · [1.4.1](docs/ko/releases.md#141) · [1.4.0](docs/ko/releases.md#140) · [1.3.0](docs/ko/releases.md#130) · [1.2.0](docs/ko/releases.md#120) |
|
|
104
|
+
| [릴리스 노트](docs/ko/releases.md) | [1.5.4](docs/ko/releases.md#154) · [1.5.3](docs/ko/releases.md#153) · [1.5.2](docs/ko/releases.md#152) · [1.5.1](docs/ko/releases.md#151) · [1.5.0](docs/ko/releases.md#150) · [1.4.3](docs/ko/releases.md#143) · [1.4.2](docs/ko/releases.md#142) · [1.4.1](docs/ko/releases.md#141) · [1.4.0](docs/ko/releases.md#140) · [1.3.0](docs/ko/releases.md#130) · [1.2.0](docs/ko/releases.md#120) |
|
|
105
105
|
|
|
106
106
|
---
|
|
107
107
|
|
|
@@ -907,7 +907,7 @@ deel sbom --only sbom # SBOM 한 장만
|
|
|
907
907
|
## 개발
|
|
908
908
|
|
|
909
909
|
```bash
|
|
910
|
-
npm test 전체 검증 (3,
|
|
910
|
+
npm test 전체 검증 (3,681항목)
|
|
911
911
|
npm run coverage 검사가 소스의 어디를 밟았는지
|
|
912
912
|
npm run verify 반입·통신 검증만
|
|
913
913
|
npm run bench 편집 성공률 측정
|
|
@@ -972,7 +972,8 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
|
|
|
972
972
|
|
|
973
973
|
| 판 | 무엇이 바뀌었나 |
|
|
974
974
|
|---|---|
|
|
975
|
-
| **[1.5.
|
|
975
|
+
| **[1.5.4](docs/ko/releases.md#154)** | 사무실에 사람이 차고, 방에 하루가 흐른다 — 「한 명뿐」은 고정이 아니라 버그였다 |
|
|
976
|
+
| [1.5.3](docs/ko/releases.md#153) | 맥에서 걸린 세 가지 — 줄바꿈 안내 · 사무실이 안 사라짐 · `/motion` 하나로 |
|
|
976
977
|
| [1.5.2](docs/ko/releases.md#152) | 적대적 리뷰가 남긴 나머지를 전부 정리했다 — 화면이 거짓말하던 자리들 |
|
|
977
978
|
| [1.5.1](docs/ko/releases.md#151) | 1.5.0 이 넣은 것 두 개가 망가진 채 나갔다. 그것을 고친다 |
|
|
978
979
|
| [1.5.0](docs/ko/releases.md#150) | 줄바꿈이 생겼고, 화면이 재미있어졌고, 그러면서 오히려 가벼워졌다 |
|
package/package.json
CHANGED
package/src/repl.js
CHANGED
|
@@ -1118,12 +1118,17 @@ export async function chatLoop(opts = {}) {
|
|
|
1118
1118
|
clearThinking();
|
|
1119
1119
|
if (streamed) { 답비우기(); say(''); streamed = false; }
|
|
1120
1120
|
화면.일바꿈(갈래고르기(ev.names?.[0]));
|
|
1121
|
+
// 사무실은 이만큼 자리를 채운다 — 이것이 진짜로 동시에 도는 수다.
|
|
1122
|
+
화면.함께갱신(ev.count);
|
|
1121
1123
|
say('');
|
|
1122
1124
|
say(` ${c.gray(`${ev.count}개를 함께 돌립니다`)} ${c.gray('·')} ${c.gray(ev.names.join(' '))}`);
|
|
1123
1125
|
break;
|
|
1124
1126
|
|
|
1125
1127
|
case 'tool':
|
|
1126
1128
|
tools++;
|
|
1129
|
+
// 결과가 오면 그 묶음은 끝난 것이다. 안 지우면 다음 한 개짜리
|
|
1130
|
+
// 도구까지 여럿이 도는 것처럼 보인다 — 자리 수가 거짓이 된다.
|
|
1131
|
+
화면.함께갱신(0);
|
|
1127
1132
|
// 걸러져 나온 것(인자가 잘렸거나, 모르는 도구거나, 거부된 것)은
|
|
1128
1133
|
// '시작' 을 안 거쳤다. 그래서 '생각 중…' 줄이 안 지워진 채로 결과가
|
|
1129
1134
|
// 그 줄 뒤에 가서 붙고, 이름도 없이 "└ 인자가 잘렸습니다" 만 남는다.
|
|
@@ -1426,6 +1431,10 @@ export async function chatLoop(opts = {}) {
|
|
|
1426
1431
|
// 어떻게 끝났든 일하는 표시는 반드시 걷는다. 오류로 빠져나온 길에서
|
|
1427
1432
|
// 안 걷으면 돌아가는 표시가 화면에 붙박이로 남고, 시계도 계속 돈다.
|
|
1428
1433
|
화면.일끝();
|
|
1434
|
+
// 중간에 끊겨도 자리는 비운다. 안 비우면 다음 턴이 시작될 때까지
|
|
1435
|
+
// 아무도 안 도는 방에 사람이 앉아 있다.
|
|
1436
|
+
화면.함께갱신(0);
|
|
1437
|
+
화면.하위갱신(0);
|
|
1429
1438
|
clearInterval(제목시계);
|
|
1430
1439
|
/*
|
|
1431
1440
|
* 오래 걸린 턴만 알린다. "안녕" 에 1초 만에 답할 때마다 딩 소리가 나면
|
package/src/ui/inputbox.js
CHANGED
|
@@ -355,6 +355,16 @@ export class InputBox {
|
|
|
355
355
|
this.할일들 = [];
|
|
356
356
|
// 지금 도는 하위 작업 수 (사무실 자리 수). 모르면 0 이다.
|
|
357
357
|
this.도는하위 = 0;
|
|
358
|
+
/*
|
|
359
|
+
* 지금 **한꺼번에** 도는 도구 수.
|
|
360
|
+
*
|
|
361
|
+
* 하위 작업은 순서대로 돈다(loop.js 가 하위를 `for await` 로 기다린다).
|
|
362
|
+
* 그래서 하위 수만 보면 자리는 거의 늘 하나다 — 방이 늘 한 명짜리로
|
|
363
|
+
* 보이던 진짜 이유가 이것이다. 실제로 동시에 도는 것은 읽기 도구 묶음
|
|
364
|
+
* 쪽이고(loop.js 의 Promise.all), 그 개수는 이미 알고 있었는데 방만
|
|
365
|
+
* 안 보고 있었다.
|
|
366
|
+
*/
|
|
367
|
+
this.함께도는 = 0;
|
|
358
368
|
}
|
|
359
369
|
|
|
360
370
|
/** 할 일이 바뀌었다. 사무실 화이트보드가 이걸 본다. */
|
|
@@ -367,6 +377,11 @@ export class InputBox {
|
|
|
367
377
|
this.도는하위 = Number.isFinite(n) && n > 0 ? Math.trunc(n) : 0;
|
|
368
378
|
}
|
|
369
379
|
|
|
380
|
+
/** 한꺼번에 도는 도구 수. 묶음이 끝나면 0 으로 돌아온다. */
|
|
381
|
+
함께갱신(n) {
|
|
382
|
+
this.함께도는 = Number.isFinite(n) && n > 1 ? Math.trunc(n) : 0;
|
|
383
|
+
}
|
|
384
|
+
|
|
370
385
|
/** 대화 글이 줄 중간까지 쓰였다. 줄이 끝날 때까지 상자를 안 세운다. */
|
|
371
386
|
줄중간표시() {
|
|
372
387
|
this.지우기();
|
|
@@ -526,7 +541,7 @@ export class InputBox {
|
|
|
526
541
|
this.그린높이 = 높이;
|
|
527
542
|
}
|
|
528
543
|
const 방 = 사무실켜나(process.stdout.rows, 칸수)
|
|
529
|
-
? 사무실줄들(사무실상태(this.session, this.일감, this.틱, this.할일들, this
|
|
544
|
+
? 사무실줄들(사무실상태(this.session, this.일감, this.틱, this.할일들, this.도는하위, this.함께도는), 폭)
|
|
530
545
|
: null;
|
|
531
546
|
|
|
532
547
|
const 짜기 = (방줄들) => 프레임({
|
package/src/ui/office.js
CHANGED
|
@@ -78,9 +78,18 @@ const C = {
|
|
|
78
78
|
화면: 45, 화면꺼짐: 239, 화면글: 123, 살: 223,
|
|
79
79
|
머리: [52, 240, 94, 130], 옷: [67, 131, 71, 97],
|
|
80
80
|
밤: 17, 별: 226, 낮: 111, 구름: 255,
|
|
81
|
+
새벽: 153, 노을: 173, 노을구름: 217, 별흐림: 250,
|
|
82
|
+
해: 226, 아침해: 229, 노을해: 208, 달: 230,
|
|
83
|
+
시계판: 253, 머그: 254, 커피: 94,
|
|
81
84
|
종이: 255, 종이어둠: 250, 쪽지: 227, 쪽지끝: 120, 초록: 46, 넘침: 208,
|
|
82
85
|
};
|
|
83
86
|
|
|
87
|
+
// 하루가 가면 방도 저문다. [벽, 바닥]
|
|
88
|
+
const 방색 = {
|
|
89
|
+
아침: [C.벽, C.바닥], 낮: [C.벽, C.바닥],
|
|
90
|
+
노을: [237, 137], 밤: [236, C.바닥줄],
|
|
91
|
+
};
|
|
92
|
+
|
|
84
93
|
// ── 픽셀 판 ─────────────────────────────────────────────────────────────
|
|
85
94
|
class 판 {
|
|
86
95
|
constructor(가로, 세로칸) {
|
|
@@ -187,16 +196,27 @@ function 자리색(자세, 머리, 옷) {
|
|
|
187
196
|
export function 사무실줄들(상태, 폭) {
|
|
188
197
|
const {
|
|
189
198
|
일꾼들 = [], 읽은파일 = 0, 고친파일 = 0, 할일 = 0, 끝난일 = 0,
|
|
190
|
-
부른횟수 = 0, 창 = 0, 틱 = 0,
|
|
199
|
+
부른횟수 = 0, 창 = 0, 틱 = 0, 걸린초 = 0,
|
|
191
200
|
} = 상태;
|
|
192
201
|
|
|
193
202
|
const 가로 = Math.max(60, 폭);
|
|
194
203
|
const p = new 판(가로, 세로);
|
|
195
|
-
p.칠하기(0, 0, 가로, 벽, C.벽);
|
|
196
|
-
p.칠하기(0, 벽, 가로, 1, C.굽도리);
|
|
197
|
-
p.칠하기(0, 벽 + 1, 가로, 세로 - 벽 - 1, C.바닥);
|
|
198
204
|
|
|
199
|
-
|
|
205
|
+
// 컨텍스트가 차는 만큼 하루가 간다. 넷으로 나눈다 — 둘이면 5% 와 50% 가
|
|
206
|
+
// 똑같은 대낮이라 창이 아무 말도 안 하게 된다.
|
|
207
|
+
const 하늘 = 창 >= 0.8 ? '밤' : 창 >= 0.55 ? '노을' : 창 >= 0.25 ? '낮' : '아침';
|
|
208
|
+
|
|
209
|
+
/*
|
|
210
|
+
* 방 안도 같이 저문다. 창만 어두워지고 벽은 대낮이면 창이 그림 액자처럼
|
|
211
|
+
* 보인다 — 저 창이 이 방의 창이라는 것이 안 읽힌다.
|
|
212
|
+
*
|
|
213
|
+
* 사람과 책상은 안 어둡게 한다. 이 방을 켠 이유가 자리를 읽는 것인데,
|
|
214
|
+
* 밤이라고 그게 안 보이면 분위기를 위해 본문을 버린 셈이 된다.
|
|
215
|
+
*/
|
|
216
|
+
const [벽색, 바닥색] = 방색[하늘];
|
|
217
|
+
p.칠하기(0, 0, 가로, 벽, 벽색);
|
|
218
|
+
p.칠하기(0, 벽, 가로, 1, C.굽도리);
|
|
219
|
+
p.칠하기(0, 벽 + 1, 가로, 세로 - 벽 - 1, 바닥색);
|
|
200
220
|
|
|
201
221
|
// 화이트보드 18×5 — 할 일. 끝난 것은 초록, 남은 것은 노랑.
|
|
202
222
|
{
|
|
@@ -246,25 +266,100 @@ export function 사무실줄들(상태, 폭) {
|
|
|
246
266
|
p.올리기(가로 - 15, 0, 그림, { R: C.검정, L: C.초록, l: C.쇠진, O: C.넘침 });
|
|
247
267
|
}
|
|
248
268
|
|
|
249
|
-
|
|
269
|
+
/*
|
|
270
|
+
* ── 창밖 — 컨텍스트가 차는 만큼 하루가 간다 ──────────────────────────
|
|
271
|
+
*
|
|
272
|
+
* 아침 → 낮 → 노을 → 밤. 넷은 꾸밈이 아니라 **컨텍스트가 얼마나 찼는가**
|
|
273
|
+
* 하나를 네 칸으로 나눈 것이다. 노을이 들면 슬슬 접을 때고, 밤이면 이미
|
|
274
|
+
* 늦은 것이다 — 숫자를 안 읽고 곁눈으로 봐도 그게 보인다.
|
|
275
|
+
*
|
|
276
|
+
* 예전에는 낮/밤 둘뿐이라 5% 와 50% 가 똑같이 환한 대낮이었다. 컨텍스트가
|
|
277
|
+
* 반이나 찬 것과 거의 안 찬 것이 같은 그림이면, 이 창은 아무 말도 안 한다.
|
|
278
|
+
*/
|
|
250
279
|
{
|
|
251
280
|
const 시작 = 21;
|
|
252
281
|
const 남 = 가로 - 15 - 시작;
|
|
253
282
|
const 창수 = Math.max(0, Math.floor(남 / 18));
|
|
283
|
+
|
|
284
|
+
/*
|
|
285
|
+
* 해는 동쪽에서 낮게 떠 한낮에 높이 오고, 서쪽으로 기울었다가 달로 바뀐다.
|
|
286
|
+
* 자리가 곧 시각이라, 세 칸짜리 하늘에서도 하루가 도는 것이 보인다.
|
|
287
|
+
* 3×2 로 그린다 — 2×1 짜리 점은 구름과 구별이 안 됐다.
|
|
288
|
+
*/
|
|
289
|
+
const 하늘것 = {
|
|
290
|
+
아침: { k: 2, r: 1 }, 낮: { k: 4, r: 0 }, 노을: { k: 9, r: 1 }, 밤: { k: 10, r: 0 },
|
|
291
|
+
}[하늘];
|
|
292
|
+
const 칠 = {
|
|
293
|
+
아침: { F: C.쇠어둠, S: C.새벽, c: C.구름, o: C.아침해 },
|
|
294
|
+
낮: { F: C.쇠어둠, S: C.낮, c: C.구름, o: C.해 },
|
|
295
|
+
노을: { F: C.쇠어둠, S: C.노을, c: C.노을구름, o: C.노을해 },
|
|
296
|
+
밤: { F: C.쇠어둠, S: C.밤, c: C.별, o: C.달 },
|
|
297
|
+
}[하늘];
|
|
298
|
+
|
|
299
|
+
/*
|
|
300
|
+
* 구름은 흐르고 별은 깜빡인다. 이 둘만은 숫자가 아니다 — 방이 멈춰 있지
|
|
301
|
+
* 않다는 것만 말한다. 그래서 아주 느리게 둔다(구름 14판, 별 9판에 한 번).
|
|
302
|
+
* 빨리 움직이면 눈이 그쪽으로 끌려가서, 정작 읽어야 할 자리·쪽지·불빛을
|
|
303
|
+
* 안 보게 된다.
|
|
304
|
+
*
|
|
305
|
+
* 구름은 **덩어리**여야 한다. 칸마다 따로 주사위를 굴렸더니 흰 점이
|
|
306
|
+
* 흩뿌려져서, 구름이 아니라 화면 잡티로 보였다. 가운데 줄을 넓게 잡아
|
|
307
|
+
* 뭉게구름 모양을 만든다.
|
|
308
|
+
*/
|
|
309
|
+
const 흐름 = Math.floor(틱 / 14);
|
|
310
|
+
const 깜빡 = Math.floor(틱 / 9);
|
|
311
|
+
const 구름폭 = [1, 3, 2];
|
|
312
|
+
const 구름인가 = (i, r, k) => {
|
|
313
|
+
for (const b of [0, 1]) {
|
|
314
|
+
const 중심 = ((i * 9 + b * 11 + 흐름) % 22) - 4;
|
|
315
|
+
if (Math.abs(k - 중심) <= 구름폭[r]) return true;
|
|
316
|
+
}
|
|
317
|
+
return false;
|
|
318
|
+
};
|
|
319
|
+
|
|
254
320
|
for (let i = 0; i < 창수; i++) {
|
|
255
321
|
const 그림 = ['FFFFFFFFFFFFFFFF'];
|
|
256
322
|
for (let r = 0; r < 3; r++) {
|
|
257
323
|
let 줄 = 'F';
|
|
258
324
|
for (let k = 0; k < 14; k++) {
|
|
259
|
-
|
|
260
|
-
|
|
325
|
+
if (k === 7) { 줄 += 'F'; continue; } // 창틀 세로대
|
|
326
|
+
const 해자리 = (r === 하늘것.r || r === 하늘것.r + 1)
|
|
327
|
+
&& k >= 하늘것.k && k < 하늘것.k + 3;
|
|
328
|
+
if (해자리) { 줄 += 'o'; continue; }
|
|
329
|
+
if (하늘 === '밤') {
|
|
330
|
+
const h = (i * 37 + r * 19 + k * 11) % 31;
|
|
331
|
+
줄 += (h < 4 && (h + 깜빡) % 4 !== 0) ? 'c' : 'S';
|
|
332
|
+
} else {
|
|
333
|
+
줄 += 구름인가(i, r, k) ? 'c' : 'S';
|
|
334
|
+
}
|
|
261
335
|
}
|
|
262
336
|
그림.push(줄 + 'F');
|
|
263
337
|
}
|
|
264
338
|
그림.push('FFFFFFFFFFFFFFFF');
|
|
265
|
-
p.올리기(시작 + i * 18, 0, 그림,
|
|
266
|
-
|
|
267
|
-
|
|
339
|
+
p.올리기(시작 + i * 18, 0, 그림, 칠);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/*
|
|
343
|
+
* 벽시계 — 이번 턴이 얼마나 걸렸나.
|
|
344
|
+
*
|
|
345
|
+
* 상자 오른쪽 끝에 걸린 시간이 이미 초로 찍히지만, 그건 읽어야 보인다.
|
|
346
|
+
* 바늘은 안 읽어도 보인다 — 한 바퀴가 1분이라, 두 바퀴째면 "오래 걸리는
|
|
347
|
+
* 중이구나" 가 숫자를 세기 전에 온다.
|
|
348
|
+
*
|
|
349
|
+
* 창을 놓고 남는 자리에만 건다. 좁은 터미널에서 시계 때문에 창이 하나
|
|
350
|
+
* 줄면 그건 손해다 — 창이 이 방의 본문이다.
|
|
351
|
+
*/
|
|
352
|
+
const 남은칸 = 남 - 창수 * 18;
|
|
353
|
+
if (남은칸 >= 8) {
|
|
354
|
+
const cx = 시작 + 창수 * 18 + Math.floor((남은칸 - 7) / 2);
|
|
355
|
+
p.올리기(cx, 0, [' TTTTT ', 'TWWWWWT', 'TWWWWWT', 'TWWWWWT', ' TTTTT '],
|
|
356
|
+
{ T: C.검정, W: C.시계판 });
|
|
357
|
+
// 한 바퀴에 1분. 7.5초마다 한 칸씩 — 도는 것이 보이면서도 매 판 다시
|
|
358
|
+
// 그릴 만큼 부산하지는 않다.
|
|
359
|
+
const 칸 = Math.floor((걸린초 % 60) / 7.5) % 8;
|
|
360
|
+
const [dx, dy] = [[0, -2], [1, -1], [2, 0], [1, 1], [0, 2], [-1, 1], [-2, 0], [-1, -1]][칸];
|
|
361
|
+
p.찍기(cx + 3, 2, C.검정);
|
|
362
|
+
p.찍기(cx + 3 + dx, 2 + dy, C.검정);
|
|
268
363
|
}
|
|
269
364
|
}
|
|
270
365
|
|
|
@@ -310,10 +405,29 @@ export function 사무실줄들(상태, 폭) {
|
|
|
310
405
|
if (넘친자리 && i === 자리수 - 1) p.칠하기(x + 자리폭 - 2, y + 자리높이 - 3, 2, 2, C.넘침);
|
|
311
406
|
// 조는 사람 머리 위 z
|
|
312
407
|
if (자세 === '졸기' && 짝) p.칠하기(x + 13, y + 2, 2, 1, C.종이);
|
|
313
|
-
|
|
314
|
-
|
|
408
|
+
/*
|
|
409
|
+
* 조는 자리에는 식은 커피가 있다.
|
|
410
|
+
*
|
|
411
|
+
* 이것도 지어낸 것이 아니라 조는 것과 **같은 수**(45초 넘김)를 한 번 더
|
|
412
|
+
* 다르게 보인 것이다. 자는 사람이 화면 뒤로 반쯤 숨는 자세라, 좁은
|
|
413
|
+
* 터미널에서는 조는지 자리를 비운 것인지 헷갈렸다. 머그는 안 숨는다.
|
|
414
|
+
*/
|
|
415
|
+
if (자세 === '졸기') {
|
|
416
|
+
p.칠하기(x + 2, y + 10, 2, 2, C.머그);
|
|
417
|
+
p.칠하기(x + 2, y + 10, 2, 1, C.커피);
|
|
418
|
+
}
|
|
419
|
+
/*
|
|
420
|
+
* 고친 파일은 책상 위 종이로. 한 자리에 두 장까지.
|
|
421
|
+
*
|
|
422
|
+
* 전에는 자리마다 한 장이라 다섯 자리짜리 방에서 다섯 장이 상한이었고,
|
|
423
|
+
* 여섯 개를 고치나 스무 개를 고치나 같은 그림이었다. 자리당 두 장으로
|
|
424
|
+
* 두면 눈금이 두 배가 된다 — 없는 자리를 만들지 않으면서.
|
|
425
|
+
*/
|
|
426
|
+
const 종이몫 = Math.min(2, Math.max(0, 고친파일 - i * 2));
|
|
427
|
+
for (let n = 0; n < 종이몫; n++) p.칠하기(x + 13 - n * 4, y + 9, 3, 1, C.종이);
|
|
315
428
|
}
|
|
316
429
|
|
|
430
|
+
|
|
317
431
|
return p.줄들();
|
|
318
432
|
}
|
|
319
433
|
|
|
@@ -386,7 +500,7 @@ export function 켜나(rows = process.stdout.rows, cols = process.stdout.columns
|
|
|
386
500
|
* @param {object} 일감 지금 도는 일 (없으면 null)
|
|
387
501
|
* @param {number} 틱
|
|
388
502
|
*/
|
|
389
|
-
export function 상태만들기(session, 일감, 틱 = 0, 할일들 = [], 도는하위 = 0) {
|
|
503
|
+
export function 상태만들기(session, 일감, 틱 = 0, 할일들 = [], 도는하위 = 0, 함께도는 = 0) {
|
|
390
504
|
const s = session ?? {};
|
|
391
505
|
const 쓴것 = s.usage ?? {};
|
|
392
506
|
const 총토큰 = (쓴것.in ?? 0) + (쓴것.out ?? 0);
|
|
@@ -399,6 +513,8 @@ export function 상태만들기(session, 일감, 틱 = 0, 할일들 = [], 도는
|
|
|
399
513
|
* 것을 그대로 쓴다 — motion.js 가 쓰는 그 갈래다.
|
|
400
514
|
*/
|
|
401
515
|
const 일꾼들 = [];
|
|
516
|
+
// 벽시계가 볼 값. 일이 없으면 0 — 바늘이 12 시에 선다.
|
|
517
|
+
let 걸린초 = 0;
|
|
402
518
|
if (일감) {
|
|
403
519
|
/*
|
|
404
520
|
* 오래 끌면 존다.
|
|
@@ -411,6 +527,7 @@ export function 상태만들기(session, 일감, 틱 = 0, 할일들 = [], 도는
|
|
|
411
527
|
*/
|
|
412
528
|
const 지난 = Number.isFinite(일감.시작) ? Date.now() - 일감.시작 : 0;
|
|
413
529
|
const 오래됐나 = 일감.갈래 === '느긋' || 지난 >= 느긋해질때;
|
|
530
|
+
걸린초 = Math.floor(지난 / 1000);
|
|
414
531
|
const 갈래 = 오래됐나 ? '졸기'
|
|
415
532
|
: ['읽기', '웹'].includes(일감.갈래) ? '읽기'
|
|
416
533
|
: ['쓰기', '명령', '답'].includes(일감.갈래) ? '쓰기' : '대기';
|
|
@@ -427,11 +544,26 @@ export function 상태만들기(session, 일감, 틱 = 0, 할일들 = [], 도는
|
|
|
427
544
|
* 모른다고 두는 편이 낫다 — 자리가 찼다는 사실은 그대로 보인다.
|
|
428
545
|
*/
|
|
429
546
|
일꾼들.push({ 갈래 });
|
|
547
|
+
/*
|
|
548
|
+
* 읽기 도구를 한꺼번에 돌리면 그만큼 진짜로 동시에 돈다.
|
|
549
|
+
*
|
|
550
|
+
* 방이 늘 한 명짜리로 보이던 진짜 이유가 여기 있었다. 하위 작업은
|
|
551
|
+
* loop.js 가 `for await` 로 기다리므로 **순서대로** 돈다 — 도는하위 는
|
|
552
|
+
* 사실상 0 아니면 1 이고, 겹치는 것은 하위가 또 하위를 떼어 낼 때뿐이다.
|
|
553
|
+
* 정작 동시에 도는 것은 읽기 묶음(loop.js 의 Promise.all)인데, 그 개수는
|
|
554
|
+
* 화면에 「3개를 함께 돌립니다」로 이미 찍히고 있었는데도 방만 안 봤다.
|
|
555
|
+
*
|
|
556
|
+
* 자세는 지어내는 것이 아니다. 묶어서 돌리는 것은 읽기 전용 도구뿐이라
|
|
557
|
+
* (Read·Glob·Grep·Skill·WebFetch) 저 자리는 전부 읽는 중인 게 맞다.
|
|
558
|
+
* 첫 자리는 이미 위에서 넣었으므로 하나를 뺀다.
|
|
559
|
+
*/
|
|
560
|
+
for (let i = 1; i < Math.max(0, 함께도는); i++) 일꾼들.push({ 갈래: '읽기' });
|
|
430
561
|
for (let i = 0; i < Math.max(0, 도는하위); i++) 일꾼들.push({ 갈래: '대기' });
|
|
431
562
|
}
|
|
432
563
|
|
|
433
564
|
return {
|
|
434
565
|
일꾼들,
|
|
566
|
+
걸린초,
|
|
435
567
|
읽은파일: s.filesRead?.size ?? 0,
|
|
436
568
|
고친파일: s.changes?.size ?? 0,
|
|
437
569
|
끝난일: 할일들.filter((t) => t.state === 'done').length,
|
package/src/ui/screen.js
CHANGED
|
@@ -1,336 +1,340 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 화면 한 장.
|
|
3
|
-
*
|
|
4
|
-
* 왜 이 파일이 생겼나:
|
|
5
|
-
* repl.js 안에 say()·process.stdout.write 가 79군데 흩어져 있었다. 그 상태로는
|
|
6
|
-
* 화면을 하나 더 얹을 수가 없다 — 어디로 무엇이 나가는지 한 군데서 못 잡으니
|
|
7
|
-
* 두 화면이 같은 코드를 나눠 쓰려다 결국 둘 다 어중간해진다.
|
|
8
|
-
*
|
|
9
|
-
* 그래서 '무엇을 그린다' 와 '어떻게 그린다' 를 가른다. repl.js 는 이제
|
|
10
|
-
* `화면.줄(...)`·`화면.답조각(...)` 처럼 **뜻**만 말하고, 그것을 줄로 흘릴지
|
|
11
|
-
* 칸에 담아 다시 그릴지는 여기서 정한다.
|
|
12
|
-
*
|
|
13
|
-
* 두 가지 구현이 있다:
|
|
14
|
-
* 줄화면(LineScreen) 지금까지의 그 화면. 위에서 아래로 흘러간다.
|
|
15
|
-
* 파이프·기록·CI·`deel run`·검사가 전부 이것을 읽는다.
|
|
16
|
-
* 상자화면(BoxScreen) ui/inputbox.js. 사람이 터미널 앞에 앉아 있을 때만.
|
|
17
|
-
* **대화는 똑같이 흘려보내고** 맨 아래 입력 상자만
|
|
18
|
-
* 우리가 지우고 다시 그린다.
|
|
19
|
-
*
|
|
20
|
-
* ── 한 번 틀렸던 길 ─────────────────────────────────────────────────────
|
|
21
|
-
*
|
|
22
|
-
* 처음에는 터미널을 통째로 빌려(대체 화면) 대화 칸·파일 칸·할 일 칸을 나눠
|
|
23
|
-
* 그렸다. 보기에는 그럴듯했는데 **슬래시 명령이 전부 먹통**이 됐다.
|
|
24
|
-
* commands.js 를 비롯한 여섯 모듈이 화면 객체를 안 거치고 stdout 에 바로 쓰는데,
|
|
25
|
-
* 전체화면은 매번 자기 버퍼에서 화면을 다시 그리므로 그 글이 찍히자마자
|
|
26
|
-
* 덮여 사라졌다. 명령이 안 도는 게 아니라 결과가 안 보이는 것이라 더 나빴다.
|
|
27
|
-
*
|
|
28
|
-
* 고치려면 stdout 에 쓰는 자리를 전부 화면 객체로 꿰야 하는데, 지금 여섯 곳이고
|
|
29
|
-
* 앞으로 늘 것이며, 하나라도 빠뜨리면 같은 증상이 조용히 돌아온다. 그래서
|
|
30
|
-
* 대화는 그냥 흘려보내기로 했다. 그러면 stdout 에 쓰는 모든 코드가 손 안 대고
|
|
31
|
-
* 그대로 맞다. 스크롤·복사·`| tee` 도 그대로다.
|
|
32
|
-
*/
|
|
33
|
-
import { c, say, cursor, box } from './ansi.js';
|
|
34
|
-
import { statusLine, contextWarning } from './status.js';
|
|
35
|
-
import { spin } from './spinner.js';
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 지금 이 자리에서 입력 상자를 써도 되는가.
|
|
39
|
-
*
|
|
40
|
-
* 하나라도 아니면 줄화면이다 — 애매하면 줄화면이 맞다. 잘못 켜면 사용자
|
|
41
|
-
* 화면이 깨지지만, 잘못 안 켜면 그냥 지금까지의 화면일 뿐이다.
|
|
42
|
-
*
|
|
43
|
-
* 상자는 커서를 위로 올려 가며 자기가 그린 줄을 지운다. 그 앞자리가
|
|
44
|
-
* 터미널이 아니면(파이프·기록) 제어문자가 그대로 글에 섞인다.
|
|
45
|
-
*/
|
|
46
|
-
export function 상자쓸까({ tui = null } = {}) {
|
|
47
|
-
if (tui === false) return false; // deel --no-tui
|
|
48
|
-
if (!process.stdout.isTTY) return false; // 파이프·기록·CI
|
|
49
|
-
if (!process.stdin.isTTY) return false; // 입력이 파이프로 들어옴 (검사·데모)
|
|
50
|
-
if (process.env.TERM === 'dumb') return false;
|
|
51
|
-
if (process.env.CI) return false;
|
|
52
|
-
// 너무 좁으면 상자 안에 남는 자리가 없다. 테두리만 남고 글이 안 보인다.
|
|
53
|
-
if ((process.stdout.columns ?? 0) < 40) return false;
|
|
54
|
-
if (tui === true) return true;
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 줄로 흘려보내는 화면 — 지금까지의 그 화면.
|
|
60
|
-
*
|
|
61
|
-
* 여기 있는 메서드 하나하나가 전에 repl.js 에 흩어져 있던 출력 한 줄과
|
|
62
|
-
* **글자 하나까지 같아야 한다.** 검사가 전부 이 화면을 글로 읽고 있어서,
|
|
63
|
-
* 다르면 그 자리에서 잡힌다. 그게 이 갈라내기가 맞는지 재는 자다.
|
|
64
|
-
*/
|
|
65
|
-
export class LineScreen {
|
|
66
|
-
constructor() {
|
|
67
|
-
this.kind = 'line';
|
|
68
|
-
// 곧 지워질 줄이 화면에 있나. \r 로 커서만 앞으로 보내 놓고 안 지우면
|
|
69
|
-
// 다음에 오는 짧은 글이 그 줄 위에 겹쳐 찍힌다.
|
|
70
|
-
this.임시중 = false;
|
|
71
|
-
this.돌림 = null;
|
|
72
|
-
/*
|
|
73
|
-
* 하위 작업 안쪽인가 — 그렇다면 몇 겹인가.
|
|
74
|
-
*
|
|
75
|
-
* 하위 작업(tools/task.js)은 제 대화에서 따로 돌지만 화면은 하나다.
|
|
76
|
-
* 표시가 없으면 하위가 부른 도구 줄이 부모 것과 똑같이 찍혀서, 사람은
|
|
77
|
-
* 부모가 파일 열두 개를 읽은 줄로 본다. 한 단 들여 그으면 어디부터
|
|
78
|
-
* 어디까지가 떼어 낸 일인지가 그냥 보인다.
|
|
79
|
-
*
|
|
80
|
-
* 여기 한 군데서만 붙인다. repl.js 의 say() 자리 예순 곳을 고치는 대신
|
|
81
|
-
* 이벤트가 들고 온 깊이를 화면에 알려 주는 쪽이 안 빠뜨린다.
|
|
82
|
-
*/
|
|
83
|
-
this.깊이 = 0;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ── 흘러가는 글 ────────────────────────────────────────────────────────
|
|
87
|
-
|
|
88
|
-
/** 지금부터 찍는 줄이 몇 겹 안쪽인지. 0 이면 평소대로. */
|
|
89
|
-
들여쓰기(n) { this.깊이 = Math.max(0, Number(n) || 0); }
|
|
90
|
-
|
|
91
|
-
/*
|
|
92
|
-
* 하위 작업 구간에 세로줄을 세운다.
|
|
93
|
-
*
|
|
94
|
-
* 빈 칸으로만 밀면 어디서 시작해 어디서 끝났는지가 안 보인다 — 특히 하위가
|
|
95
|
-
* 파일을 열 개 만지고 나면, 스크롤을 올려 여는 줄을 찾아야 그게 하위였는지
|
|
96
|
-
* 안다. 세로줄 한 칸이면 훑는 것만으로 구간이 잡힌다.
|
|
97
|
-
*/
|
|
98
|
-
#안내() { return this.깊이 ? c.gray(' │').repeat(this.깊이) : ''; }
|
|
99
|
-
|
|
100
|
-
/** 한 줄. 이미 색이 입혀진 글을 받는다. */
|
|
101
|
-
줄(s = '') { this.임시지움(); say(this.깊이 && s ? this.#안내() + s : s); }
|
|
102
|
-
|
|
103
|
-
/** 줄바꿈 없이 이어 붙인다 — 스트리밍으로 오는 답. */
|
|
104
|
-
붙임(s) { this.임시지움(); process.stdout.write(s); }
|
|
105
|
-
|
|
106
|
-
// ── 곧 지워질 표시 ─────────────────────────────────────────────────────
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 기다리는 중. \r 로 커서만 앞으로 보낸다.
|
|
110
|
-
*
|
|
111
|
-
* 파이프일 때도 찍는다 — 데모·기록에서 '무엇을 기다리는 중이었나' 가
|
|
112
|
-
* 남아야 한다. 다만 지워야 할 줄로 세는 것은 터미널일 때만이다.
|
|
113
|
-
*/
|
|
114
|
-
기다림(s) {
|
|
115
|
-
process.stdout.write(` ${s}\r`);
|
|
116
|
-
if (process.stdout.isTTY) this.임시중 = true;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/** 생각하는 중. 글자 수가 계속 바뀌므로 터미널일 때만 그린다. */
|
|
120
|
-
생각(s) {
|
|
121
|
-
if (!process.stdout.isTTY) return;
|
|
122
|
-
cursor.clearLine();
|
|
123
|
-
process.stdout.write(` ${s}`);
|
|
124
|
-
this.임시중 = true;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
임시지움() {
|
|
128
|
-
if (this.임시중) { cursor.clearLine(); this.임시중 = false; }
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* 사람이 치고 있던 입력 줄을 지운다.
|
|
133
|
-
*
|
|
134
|
-
* Shift+Tab 으로 승인 방식을 돌릴 때처럼, 입력을 기다리는 도중에 화면에
|
|
135
|
-
* 한 줄을 끼워 넣어야 하는 자리가 있다. 안 지우면 `❯ ` 뒤에 새 글이 붙는다.
|
|
136
|
-
* 상자화면에서는 상자를 통째로 걷어낸다(BoxScreen 이 갈아 끼운다).
|
|
137
|
-
*/
|
|
138
|
-
입력지움() { cursor.clearLine(); }
|
|
139
|
-
|
|
140
|
-
/** 오래 걸리는 일에 돌아가는 표시를 세운다. */
|
|
141
|
-
돌리기(label) { this.돌림 = spin(label); return this.돌림; }
|
|
142
|
-
|
|
143
|
-
// spinner 가 멈추면서 이미 줄을 지우고 커서를 되살린다. 여기서 또 지우지 않는다 —
|
|
144
|
-
// 화면에 나가는 제어문자가 한 벌이라도 달라지면 그게 갈라내기의 흔적이 된다.
|
|
145
|
-
돌림멈춤(finalLine) {
|
|
146
|
-
if (!this.돌림) return;
|
|
147
|
-
this.돌림.stop(finalLine);
|
|
148
|
-
this.돌림 = null;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// ── 자리를 차지하는 것 ─────────────────────────────────────────────────
|
|
152
|
-
|
|
153
|
-
/** 켤 때 한 번 그리는 머리말 상자. */
|
|
154
|
-
머리말(lines) {
|
|
155
|
-
say('');
|
|
156
|
-
for (const l of box(lines, { tone: c.gray })) say(' ' + l);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* 입력 자리. 위에 상태줄을 한 줄 깔고 그 아래에 커서를 둔다.
|
|
161
|
-
*
|
|
162
|
-
* 줄화면에서는 이게 매번 새로 찍힌다 — 대화가 길어지면 상태줄이 화면에
|
|
163
|
-
* 여러 번 남는다. 그게 흘러가는 화면의 성질이고, 기록으로 읽을 때는
|
|
164
|
-
* 오히려 그때그때 상태를 알 수 있어 낫다.
|
|
165
|
-
*/
|
|
166
|
-
입력자리(session) {
|
|
167
|
-
say('');
|
|
168
|
-
say(statusLine(session));
|
|
169
|
-
const w = contextWarning(session);
|
|
170
|
-
if (w) say(` ${w}`);
|
|
171
|
-
process.stdout.write(` ${c.hcyan('❯')} `);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** 사람이 치는 중인 글이 바뀌었다. 줄화면은 readline 이 알아서 되비춘다. */
|
|
175
|
-
입력갱신() { }
|
|
176
|
-
|
|
177
|
-
/** 일하는 중 미리 치는 글. 줄화면은 상자가 없어 보여 줄 자리가 없다. */
|
|
178
|
-
대기갱신() { }
|
|
179
|
-
|
|
180
|
-
/** 줄화면은 붙여 쓰기 전에 임시글만 지우면 되므로 따로 셀 것이 없다. */
|
|
181
|
-
줄중간인가() { return false; }
|
|
182
|
-
|
|
183
|
-
/*
|
|
184
|
-
* 일하는 중 표시. 줄화면은 안 그린다.
|
|
185
|
-
*
|
|
186
|
-
* 줄화면은 흘러가는 화면이라 '한 자리에서 도는 표시' 를 둘 데가 없다.
|
|
187
|
-
* 대신 기다림()·생각() 이 그 자리를 이미 맡고 있다 — 여기서 또 그리면
|
|
188
|
-
* 같은 말이 두 번 나간다. 파이프·기록에서는 돌아가는 표시 자체가 잡음이다.
|
|
189
|
-
*/
|
|
190
|
-
일시작() { }
|
|
191
|
-
일바꿈() { }
|
|
192
|
-
일끝() { }
|
|
193
|
-
// 줄 화면에는 사무실이 없다 — 받아만 두고 아무것도 안 한다.
|
|
194
|
-
할일갱신() { }
|
|
195
|
-
하위갱신() { }
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
this
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
this
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
this
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
this
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
*
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 화면 한 장.
|
|
3
|
+
*
|
|
4
|
+
* 왜 이 파일이 생겼나:
|
|
5
|
+
* repl.js 안에 say()·process.stdout.write 가 79군데 흩어져 있었다. 그 상태로는
|
|
6
|
+
* 화면을 하나 더 얹을 수가 없다 — 어디로 무엇이 나가는지 한 군데서 못 잡으니
|
|
7
|
+
* 두 화면이 같은 코드를 나눠 쓰려다 결국 둘 다 어중간해진다.
|
|
8
|
+
*
|
|
9
|
+
* 그래서 '무엇을 그린다' 와 '어떻게 그린다' 를 가른다. repl.js 는 이제
|
|
10
|
+
* `화면.줄(...)`·`화면.답조각(...)` 처럼 **뜻**만 말하고, 그것을 줄로 흘릴지
|
|
11
|
+
* 칸에 담아 다시 그릴지는 여기서 정한다.
|
|
12
|
+
*
|
|
13
|
+
* 두 가지 구현이 있다:
|
|
14
|
+
* 줄화면(LineScreen) 지금까지의 그 화면. 위에서 아래로 흘러간다.
|
|
15
|
+
* 파이프·기록·CI·`deel run`·검사가 전부 이것을 읽는다.
|
|
16
|
+
* 상자화면(BoxScreen) ui/inputbox.js. 사람이 터미널 앞에 앉아 있을 때만.
|
|
17
|
+
* **대화는 똑같이 흘려보내고** 맨 아래 입력 상자만
|
|
18
|
+
* 우리가 지우고 다시 그린다.
|
|
19
|
+
*
|
|
20
|
+
* ── 한 번 틀렸던 길 ─────────────────────────────────────────────────────
|
|
21
|
+
*
|
|
22
|
+
* 처음에는 터미널을 통째로 빌려(대체 화면) 대화 칸·파일 칸·할 일 칸을 나눠
|
|
23
|
+
* 그렸다. 보기에는 그럴듯했는데 **슬래시 명령이 전부 먹통**이 됐다.
|
|
24
|
+
* commands.js 를 비롯한 여섯 모듈이 화면 객체를 안 거치고 stdout 에 바로 쓰는데,
|
|
25
|
+
* 전체화면은 매번 자기 버퍼에서 화면을 다시 그리므로 그 글이 찍히자마자
|
|
26
|
+
* 덮여 사라졌다. 명령이 안 도는 게 아니라 결과가 안 보이는 것이라 더 나빴다.
|
|
27
|
+
*
|
|
28
|
+
* 고치려면 stdout 에 쓰는 자리를 전부 화면 객체로 꿰야 하는데, 지금 여섯 곳이고
|
|
29
|
+
* 앞으로 늘 것이며, 하나라도 빠뜨리면 같은 증상이 조용히 돌아온다. 그래서
|
|
30
|
+
* 대화는 그냥 흘려보내기로 했다. 그러면 stdout 에 쓰는 모든 코드가 손 안 대고
|
|
31
|
+
* 그대로 맞다. 스크롤·복사·`| tee` 도 그대로다.
|
|
32
|
+
*/
|
|
33
|
+
import { c, say, cursor, box } from './ansi.js';
|
|
34
|
+
import { statusLine, contextWarning } from './status.js';
|
|
35
|
+
import { spin } from './spinner.js';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 지금 이 자리에서 입력 상자를 써도 되는가.
|
|
39
|
+
*
|
|
40
|
+
* 하나라도 아니면 줄화면이다 — 애매하면 줄화면이 맞다. 잘못 켜면 사용자
|
|
41
|
+
* 화면이 깨지지만, 잘못 안 켜면 그냥 지금까지의 화면일 뿐이다.
|
|
42
|
+
*
|
|
43
|
+
* 상자는 커서를 위로 올려 가며 자기가 그린 줄을 지운다. 그 앞자리가
|
|
44
|
+
* 터미널이 아니면(파이프·기록) 제어문자가 그대로 글에 섞인다.
|
|
45
|
+
*/
|
|
46
|
+
export function 상자쓸까({ tui = null } = {}) {
|
|
47
|
+
if (tui === false) return false; // deel --no-tui
|
|
48
|
+
if (!process.stdout.isTTY) return false; // 파이프·기록·CI
|
|
49
|
+
if (!process.stdin.isTTY) return false; // 입력이 파이프로 들어옴 (검사·데모)
|
|
50
|
+
if (process.env.TERM === 'dumb') return false;
|
|
51
|
+
if (process.env.CI) return false;
|
|
52
|
+
// 너무 좁으면 상자 안에 남는 자리가 없다. 테두리만 남고 글이 안 보인다.
|
|
53
|
+
if ((process.stdout.columns ?? 0) < 40) return false;
|
|
54
|
+
if (tui === true) return true;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 줄로 흘려보내는 화면 — 지금까지의 그 화면.
|
|
60
|
+
*
|
|
61
|
+
* 여기 있는 메서드 하나하나가 전에 repl.js 에 흩어져 있던 출력 한 줄과
|
|
62
|
+
* **글자 하나까지 같아야 한다.** 검사가 전부 이 화면을 글로 읽고 있어서,
|
|
63
|
+
* 다르면 그 자리에서 잡힌다. 그게 이 갈라내기가 맞는지 재는 자다.
|
|
64
|
+
*/
|
|
65
|
+
export class LineScreen {
|
|
66
|
+
constructor() {
|
|
67
|
+
this.kind = 'line';
|
|
68
|
+
// 곧 지워질 줄이 화면에 있나. \r 로 커서만 앞으로 보내 놓고 안 지우면
|
|
69
|
+
// 다음에 오는 짧은 글이 그 줄 위에 겹쳐 찍힌다.
|
|
70
|
+
this.임시중 = false;
|
|
71
|
+
this.돌림 = null;
|
|
72
|
+
/*
|
|
73
|
+
* 하위 작업 안쪽인가 — 그렇다면 몇 겹인가.
|
|
74
|
+
*
|
|
75
|
+
* 하위 작업(tools/task.js)은 제 대화에서 따로 돌지만 화면은 하나다.
|
|
76
|
+
* 표시가 없으면 하위가 부른 도구 줄이 부모 것과 똑같이 찍혀서, 사람은
|
|
77
|
+
* 부모가 파일 열두 개를 읽은 줄로 본다. 한 단 들여 그으면 어디부터
|
|
78
|
+
* 어디까지가 떼어 낸 일인지가 그냥 보인다.
|
|
79
|
+
*
|
|
80
|
+
* 여기 한 군데서만 붙인다. repl.js 의 say() 자리 예순 곳을 고치는 대신
|
|
81
|
+
* 이벤트가 들고 온 깊이를 화면에 알려 주는 쪽이 안 빠뜨린다.
|
|
82
|
+
*/
|
|
83
|
+
this.깊이 = 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── 흘러가는 글 ────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
/** 지금부터 찍는 줄이 몇 겹 안쪽인지. 0 이면 평소대로. */
|
|
89
|
+
들여쓰기(n) { this.깊이 = Math.max(0, Number(n) || 0); }
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
* 하위 작업 구간에 세로줄을 세운다.
|
|
93
|
+
*
|
|
94
|
+
* 빈 칸으로만 밀면 어디서 시작해 어디서 끝났는지가 안 보인다 — 특히 하위가
|
|
95
|
+
* 파일을 열 개 만지고 나면, 스크롤을 올려 여는 줄을 찾아야 그게 하위였는지
|
|
96
|
+
* 안다. 세로줄 한 칸이면 훑는 것만으로 구간이 잡힌다.
|
|
97
|
+
*/
|
|
98
|
+
#안내() { return this.깊이 ? c.gray(' │').repeat(this.깊이) : ''; }
|
|
99
|
+
|
|
100
|
+
/** 한 줄. 이미 색이 입혀진 글을 받는다. */
|
|
101
|
+
줄(s = '') { this.임시지움(); say(this.깊이 && s ? this.#안내() + s : s); }
|
|
102
|
+
|
|
103
|
+
/** 줄바꿈 없이 이어 붙인다 — 스트리밍으로 오는 답. */
|
|
104
|
+
붙임(s) { this.임시지움(); process.stdout.write(s); }
|
|
105
|
+
|
|
106
|
+
// ── 곧 지워질 표시 ─────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 기다리는 중. \r 로 커서만 앞으로 보낸다.
|
|
110
|
+
*
|
|
111
|
+
* 파이프일 때도 찍는다 — 데모·기록에서 '무엇을 기다리는 중이었나' 가
|
|
112
|
+
* 남아야 한다. 다만 지워야 할 줄로 세는 것은 터미널일 때만이다.
|
|
113
|
+
*/
|
|
114
|
+
기다림(s) {
|
|
115
|
+
process.stdout.write(` ${s}\r`);
|
|
116
|
+
if (process.stdout.isTTY) this.임시중 = true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** 생각하는 중. 글자 수가 계속 바뀌므로 터미널일 때만 그린다. */
|
|
120
|
+
생각(s) {
|
|
121
|
+
if (!process.stdout.isTTY) return;
|
|
122
|
+
cursor.clearLine();
|
|
123
|
+
process.stdout.write(` ${s}`);
|
|
124
|
+
this.임시중 = true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
임시지움() {
|
|
128
|
+
if (this.임시중) { cursor.clearLine(); this.임시중 = false; }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 사람이 치고 있던 입력 줄을 지운다.
|
|
133
|
+
*
|
|
134
|
+
* Shift+Tab 으로 승인 방식을 돌릴 때처럼, 입력을 기다리는 도중에 화면에
|
|
135
|
+
* 한 줄을 끼워 넣어야 하는 자리가 있다. 안 지우면 `❯ ` 뒤에 새 글이 붙는다.
|
|
136
|
+
* 상자화면에서는 상자를 통째로 걷어낸다(BoxScreen 이 갈아 끼운다).
|
|
137
|
+
*/
|
|
138
|
+
입력지움() { cursor.clearLine(); }
|
|
139
|
+
|
|
140
|
+
/** 오래 걸리는 일에 돌아가는 표시를 세운다. */
|
|
141
|
+
돌리기(label) { this.돌림 = spin(label); return this.돌림; }
|
|
142
|
+
|
|
143
|
+
// spinner 가 멈추면서 이미 줄을 지우고 커서를 되살린다. 여기서 또 지우지 않는다 —
|
|
144
|
+
// 화면에 나가는 제어문자가 한 벌이라도 달라지면 그게 갈라내기의 흔적이 된다.
|
|
145
|
+
돌림멈춤(finalLine) {
|
|
146
|
+
if (!this.돌림) return;
|
|
147
|
+
this.돌림.stop(finalLine);
|
|
148
|
+
this.돌림 = null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ── 자리를 차지하는 것 ─────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
/** 켤 때 한 번 그리는 머리말 상자. */
|
|
154
|
+
머리말(lines) {
|
|
155
|
+
say('');
|
|
156
|
+
for (const l of box(lines, { tone: c.gray })) say(' ' + l);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 입력 자리. 위에 상태줄을 한 줄 깔고 그 아래에 커서를 둔다.
|
|
161
|
+
*
|
|
162
|
+
* 줄화면에서는 이게 매번 새로 찍힌다 — 대화가 길어지면 상태줄이 화면에
|
|
163
|
+
* 여러 번 남는다. 그게 흘러가는 화면의 성질이고, 기록으로 읽을 때는
|
|
164
|
+
* 오히려 그때그때 상태를 알 수 있어 낫다.
|
|
165
|
+
*/
|
|
166
|
+
입력자리(session) {
|
|
167
|
+
say('');
|
|
168
|
+
say(statusLine(session));
|
|
169
|
+
const w = contextWarning(session);
|
|
170
|
+
if (w) say(` ${w}`);
|
|
171
|
+
process.stdout.write(` ${c.hcyan('❯')} `);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** 사람이 치는 중인 글이 바뀌었다. 줄화면은 readline 이 알아서 되비춘다. */
|
|
175
|
+
입력갱신() { }
|
|
176
|
+
|
|
177
|
+
/** 일하는 중 미리 치는 글. 줄화면은 상자가 없어 보여 줄 자리가 없다. */
|
|
178
|
+
대기갱신() { }
|
|
179
|
+
|
|
180
|
+
/** 줄화면은 붙여 쓰기 전에 임시글만 지우면 되므로 따로 셀 것이 없다. */
|
|
181
|
+
줄중간인가() { return false; }
|
|
182
|
+
|
|
183
|
+
/*
|
|
184
|
+
* 일하는 중 표시. 줄화면은 안 그린다.
|
|
185
|
+
*
|
|
186
|
+
* 줄화면은 흘러가는 화면이라 '한 자리에서 도는 표시' 를 둘 데가 없다.
|
|
187
|
+
* 대신 기다림()·생각() 이 그 자리를 이미 맡고 있다 — 여기서 또 그리면
|
|
188
|
+
* 같은 말이 두 번 나간다. 파이프·기록에서는 돌아가는 표시 자체가 잡음이다.
|
|
189
|
+
*/
|
|
190
|
+
일시작() { }
|
|
191
|
+
일바꿈() { }
|
|
192
|
+
일끝() { }
|
|
193
|
+
// 줄 화면에는 사무실이 없다 — 받아만 두고 아무것도 안 한다.
|
|
194
|
+
할일갱신() { }
|
|
195
|
+
하위갱신() { }
|
|
196
|
+
함께갱신() { }
|
|
197
|
+
|
|
198
|
+
close() {
|
|
199
|
+
this.돌림멈춤();
|
|
200
|
+
this.임시지움();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* 대화는 그대로 흘려보내고, 맨 아래 입력 상자만 우리가 관리하는 화면.
|
|
206
|
+
*
|
|
207
|
+
* LineScreen 을 그대로 물려받는다 — 대화가 나가는 길이 **한 글자도 다르면
|
|
208
|
+
* 안 되기** 때문이다. 다르면 검사가 읽는 화면과 사람이 보는 화면이 갈라진다.
|
|
209
|
+
* 여기서 갈아 끼우는 것은 입력 자리 세 가지뿐이다.
|
|
210
|
+
*/
|
|
211
|
+
export class BoxScreen extends LineScreen {
|
|
212
|
+
constructor(상자) {
|
|
213
|
+
super();
|
|
214
|
+
this.kind = 'box';
|
|
215
|
+
this.상자 = 상자;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/*
|
|
219
|
+
* 대화 한 줄이 나가기 전에 상자를 걷어낸다. 안 걷으면 새 글이 상자 위에
|
|
220
|
+
* 겹쳐 찍히고, 지울 때 대화까지 같이 지워진다.
|
|
221
|
+
*
|
|
222
|
+
* 일하는 중이면 찍은 **뒤에 다시 세운다.** 이게 없으면 첫 도구 결과가
|
|
223
|
+
* 찍히는 순간 상자가 사라지고, 그 뒤로는 일이 끝날 때까지 화면 아래가
|
|
224
|
+
* 텅 빈 채로 남는다 — 정확히 그게 "밋밋해 보인다" 는 상태다.
|
|
225
|
+
*/
|
|
226
|
+
줄(s = '') {
|
|
227
|
+
this.상자.지우기();
|
|
228
|
+
super.줄(s);
|
|
229
|
+
// 줄이 끝났다 — 이제 아래에 상자를 세워도 대화를 안 덮는다.
|
|
230
|
+
this.상자.줄끝남();
|
|
231
|
+
this.상자.일그리기();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/*
|
|
235
|
+
* 스트리밍으로 이어 붙이는 글은 다시 안 세운다.
|
|
236
|
+
*
|
|
237
|
+
* 토막마다 상자를 세웠다 걷으면, 글자 몇 개마다 상자가 깜빡이고 커서가
|
|
238
|
+
* 위아래로 튄다. 답이 흘러나오는 동안에는 그 자체가 살아 있다는 신호라
|
|
239
|
+
* 상자가 없어도 밋밋하지 않다. 답이 끝나면 repl.js 가 다시 세운다.
|
|
240
|
+
*/
|
|
241
|
+
붙임(s) {
|
|
242
|
+
// 상자를 걷고, **줄 중간에 멈춰 있다고 표시한다.** 이 표시가 없으면
|
|
243
|
+
// 돌아가는 표시가 90ms 뒤에 제 시계로 상자를 답 줄 위에 그려 버리고,
|
|
244
|
+
// 그 다음 지우기가 답까지 같이 지운다.
|
|
245
|
+
this.상자.줄중간표시();
|
|
246
|
+
super.붙임(s);
|
|
247
|
+
|
|
248
|
+
/*
|
|
249
|
+
* 줄이 끝나는 토막이면 그 자리에서 다시 세운다.
|
|
250
|
+
*
|
|
251
|
+
* 예전에는 답이 흐르는 내내 상자를 안 세웠다. 상자만 있을 때는 그래도
|
|
252
|
+
* 됐다 — 없어도 답이 흐르는 것 자체가 살아 있다는 신호였으니까.
|
|
253
|
+
* 그런데 **사무실을 켜면 얘기가 다르다.** 붙박아 두라고 켠 것이 정작
|
|
254
|
+
* 일할 때만 사라지니, 켠 사람 눈에는 고장으로 보인다.
|
|
255
|
+
*
|
|
256
|
+
* 그렇다고 토막마다 세우면 글자 몇 개마다 깜빡인다. 그래서 **줄이
|
|
257
|
+
* 끝날 때만** 세운다 — 줄() 이 이미 하고 있는 것과 같은 빈도이고,
|
|
258
|
+
* 그 자리에서는 답을 덮을 일이 없다.
|
|
259
|
+
*/
|
|
260
|
+
if (typeof s === 'string' && s.endsWith('\n')) {
|
|
261
|
+
this.상자.줄끝남();
|
|
262
|
+
this.상자.일그리기();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** 일하기 시작·바뀜·끝. 상자 안엣것만 바뀐다 (ui/working.js 의 문구). */
|
|
267
|
+
일시작(session, 갈래) { this.임시지움(); this.상자.일시작(session, 갈래); }
|
|
268
|
+
일바꿈(갈래, 곁정보) { this.상자.일바꿈(갈래, 곁정보); }
|
|
269
|
+
일끝() { this.상자.일끝(); }
|
|
270
|
+
/** 할 일이 바뀌었다 — 사무실 화이트보드가 이걸 본다. */
|
|
271
|
+
할일갱신(할일들) { this.상자.할일갱신(할일들); }
|
|
272
|
+
/** 도는 하위 작업 수가 바뀌었다 — 사무실 자리가 이만큼 찬다. */
|
|
273
|
+
하위갱신(n) { this.상자.하위갱신(n); }
|
|
274
|
+
|
|
275
|
+
/** 한꺼번에 도는 도구 수가 바뀌었다 — 사무실 자리가 그만큼 찬다. */
|
|
276
|
+
함께갱신(n) { this.상자.함께갱신(n); }
|
|
277
|
+
|
|
278
|
+
/** 일하는 도중에 미리 치고 있는 글 · 예약해 둔 개수. */
|
|
279
|
+
대기갱신(글, 예약수) { this.상자.대기갱신(글, 예약수); }
|
|
280
|
+
|
|
281
|
+
/** 대화가 줄 중간에 멈춰 있나 (답이 흘러나오는 중). */
|
|
282
|
+
줄중간인가() { return this.상자.줄중간; }
|
|
283
|
+
|
|
284
|
+
/*
|
|
285
|
+
* 줄화면에서 '생각 중…' 을 한 줄로 흘리던 자리.
|
|
286
|
+
*
|
|
287
|
+
* 상자를 쓸 때는 그 말이 상자 안으로 들어가야 한다. 안 그러면 같은 말이
|
|
288
|
+
* 두 군데(흘러가는 줄 + 상자)에 동시에 뜬다.
|
|
289
|
+
*/
|
|
290
|
+
기다림() { }
|
|
291
|
+
생각(s) {
|
|
292
|
+
// '생각 중… 1,234자' 에서 숫자만 떼어 곁정보로 붙인다. 진짜로 늘어나는
|
|
293
|
+
// 숫자가 하나 있어야 '살아 있다' 가 표시가 아니라 사실이 된다.
|
|
294
|
+
const m = /([\d,]+)자/.exec(String(s ?? ''));
|
|
295
|
+
this.상자.일바꿈('생각', m ? `생각 ${m[1]}자` : null);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* 입력 자리 = 상태줄 + 상자. 커서는 상자 안에 놓인다.
|
|
300
|
+
*
|
|
301
|
+
* 치던 글을 받는다. 입력 도중에 한 줄을 끼워 넣고 다시 그리는 자리
|
|
302
|
+
* (Shift+Tab·Ctrl+O·Ctrl+C)에서, 빈 칸을 그렸다가 곧바로 채우면 한 번
|
|
303
|
+
* 깜빡인다. 처음부터 채워서 그린다 — 한 번만 그리면 안 깜빡인다.
|
|
304
|
+
*/
|
|
305
|
+
입력자리(session, 글 = '', 커서 = 0, 추천 = []) {
|
|
306
|
+
this.임시지움();
|
|
307
|
+
say('');
|
|
308
|
+
this.상자.그리기(session, 글, 커서, null, 추천);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** 사람이 치는 중인 글이 바뀌었다 (repl.js 의 keypress 가 부른다). */
|
|
312
|
+
입력갱신(session, 글, 커서, 추천 = []) { this.상자.그리기(session, 글, 커서, null, 추천); }
|
|
313
|
+
|
|
314
|
+
입력지움() { this.상자.지우기(); }
|
|
315
|
+
|
|
316
|
+
close() {
|
|
317
|
+
this.상자.지우기();
|
|
318
|
+
super.close();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* 상황에 맞는 화면을 하나 고른다.
|
|
324
|
+
*
|
|
325
|
+
* 상자 쪽은 필요할 때만 읽어 들인다. 줄화면으로 돌 때 inputbox.js 를
|
|
326
|
+
* 파싱조차 안 하게 하려는 것이다 — `deel run` 이 조금이라도 빨리 시작해야 한다.
|
|
327
|
+
*/
|
|
328
|
+
export async function 화면고르기(opts = {}) {
|
|
329
|
+
if (!상자쓸까(opts)) return new LineScreen();
|
|
330
|
+
try {
|
|
331
|
+
const { InputBox } = await import('./inputbox.js');
|
|
332
|
+
return new BoxScreen(new InputBox());
|
|
333
|
+
} catch (e) {
|
|
334
|
+
// 상자를 못 세우면 조용히 줄화면으로 간다. 화면 하나 때문에
|
|
335
|
+
// 프로그램이 안 뜨는 일은 없어야 한다.
|
|
336
|
+
const s = new LineScreen();
|
|
337
|
+
s.줄(` ${c.gray(`입력 상자를 못 켰습니다 — 줄 화면으로 갑니다. (${e.message})`)}`);
|
|
338
|
+
return s;
|
|
339
|
+
}
|
|
340
|
+
}
|