@tenyokjs/devdevian-cli 0.7.0-alpha.1 → 0.7.1-alpha.1
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 +4 -2
- package/VERSIONS.md +8 -0
- package/bin/devdevian.mjs +1 -0
- package/lib/form-guidance.mjs +38 -0
- package/lib/tui.mjs +142 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ The default `devdevian` command and `devdevian tui` open an alternate terminal s
|
|
|
38
38
|
- **N** creates a conversation (a community on the Communities screen); **A** asks a question; **R** replies inside a conversation; **Shift+R** refreshes.
|
|
39
39
|
- **C** comments on snippet lines; **E/D** edit/delete your own ordinary replies or code feedback; **S** resolves/reopens a code remark or accepts/removes a reply as the solution (question/review author only); **!** reports selected code feedback.
|
|
40
40
|
- **Ctrl+E** opens the current multiline field in `$VISUAL` / `$EDITOR` (use `code --wait` for VS Code). Closing the editor restores the form; publishing still requires preview and confirmation. **Ctrl+L** saves an active draft and returns to sign-in.
|
|
41
|
-
- In forms, **Tab/Shift+Tab** change fields, arrows choose options or move the text cursor, **Enter** adds a line, **Ctrl+S** opens the preview. Publication requires a separate confirmation; Escape asks before discarding a draft. Drafts auto-save locally after a short pause and can be restored from **Local drafts** or `/drafts`.
|
|
41
|
+
- In forms, **Tab/Shift+Tab** change fields or focus the step controls, arrows choose options or move the text cursor, **Enter** adds a line inside multiline text, and **Ctrl+S** opens the preview. Publication requires a separate confirmation; Escape asks before discarding a draft. Drafts auto-save locally after a short pause and can be restored from **Local drafts** or `/drafts`.
|
|
42
42
|
- **Ctrl+C** exits. With an unsent draft, confirm saving and leaving or press Ctrl+C again to force exit.
|
|
43
43
|
|
|
44
44
|
`devdevian open` launches Terminal on macOS, Windows Terminal on Windows, or GNOME Terminal/Konsole/xterm on a Linux desktop. A graphical desktop and an installed supported terminal are required; over SSH and inside an editor, use `devdevian tui`. Window launchers still need platform-specific user testing; terminal rendering and input are covered by macOS/Linux pseudo-terminal checks.
|
|
@@ -63,10 +63,12 @@ Forms autosave locally. Successful upload IDs are retained if validation fails;
|
|
|
63
63
|
|
|
64
64
|
Press **Ctrl+K** outside a form or command input, or run **/actions**, to search actions, sections and creation flows. Recent views stay in memory for the current account/session; the palette offers up to eight. Type `project 7`, `event 9` or `open 142` for an explicit ID, or choose the forum-search result for a text query. Typing stays local; actions run only after Enter. Destructive actions retain their separate confirmation. **Esc** returns to the previous view without moving your selection.
|
|
65
65
|
|
|
66
|
-
Conversation, Project and Event forms show one step at a time. **Ctrl+N** validates the current step and moves forward; **Ctrl+P** returns to the previous step. **Tab**
|
|
66
|
+
Conversation, Project and Event forms show one step at a time. **Ctrl+N** validates the current step and moves forward; **Ctrl+P** returns to the previous step. **Tab / Shift+Tab** move through the current step and its **Back / Next** controls. Use arrows to choose a control and **Enter** to activate it; the last step offers **Preview**, not immediate publication. **Ctrl+S** checks every required field and opens the full publication preview; a missing value returns you to its step. Step changes preserve text, image paths, code selections and locally saved drafts. No upload or publication happens just by advancing a step.
|
|
67
67
|
|
|
68
68
|
In full-text reading (**Space** on a reply or remark), **/** opens local text search. **Enter** locates a matching rendered line; **n / Shift+N** cycles forward/backward. **Home / End** jumps to the start/end. **Esc** returns to the underlying view, and reopening the same unchanged item restores your place. This is bounded session memory, not cross-device or permanent reading history. In text fields and the command bar, **Ctrl+K** continues to delete to the end of the line.
|
|
69
69
|
|
|
70
|
+
Active fields explain their input: Space stays a space, Enter adds a line in multiline fields, and Tab leaves the field. Project feedback accepts text and URLs; dedicated website/repository/additional links are in **People & links**. Additional links use one `Label | https://example.com` per line. Context hints stay below the editor on larger terminals and use the status/hint lines on compact ones. **Ctrl+S** checks the complete form and opens a preview; drafts autosave locally and publication still requires explicit confirmation.
|
|
71
|
+
|
|
70
72
|
## Meet Devi
|
|
71
73
|
|
|
72
74
|
Devi is the Devdevian mascot: a small ghost with a laptop and an orbit. The terminal uses colored ASCII art, so it needs no image protocol or image downloads. A compact drawing is used when space is limited; authentication codes, addresses and controls take priority over decoration.
|
package/VERSIONS.md
CHANGED
|
@@ -8,6 +8,14 @@ Every published npm artifact has a unique immutable version. We record each vers
|
|
|
8
8
|
|
|
9
9
|
**0.7.0 focuses on everyday terminal use**: discoverable quick actions, staged forms and resumable reading.
|
|
10
10
|
|
|
11
|
+
## 0.7.1-alpha.1 — Clearer forms
|
|
12
|
+
|
|
13
|
+
- Added visible, keyboard-focusable Back / Next controls to staged forms. Tab reaches the controls at step boundaries, arrows choose an available action and Enter activates it. The final action is Preview; publication remains a separate confirmation. Ctrl+N/P shortcuts remain available.
|
|
14
|
+
- Pinned input guidance distinguishes Space, multiline Enter, field navigation and image/code pickers. Project help explicitly points to the dedicated People & links step. Compact layouts prioritize the caret and keep guidance in status/hint lines.
|
|
15
|
+
- Clarified additional-link syntax, preview versus autosaved drafts, and optional URL fields. Opening a new form clears stale success notices.
|
|
16
|
+
- Regression coverage publishes a project through the real terminal and isolated API, checks text spaces/newlines and multiple labeled links from a second account, exercises Back/Next/Preview, preserves Git/image selections and checks multiline editing at 60×18.
|
|
17
|
+
- No API/schema change, new dependency or production-data reset. Per-version history is recorded here; no separate GitHub release is required for this patch.
|
|
18
|
+
|
|
11
19
|
## 0.7.0-alpha.1 — Everyday flow
|
|
12
20
|
|
|
13
21
|
- Added a searchable quick-action workspace via `Ctrl+K` or `/actions`: scoped actions, navigation, creation, up to eight recent views, explicit ID opening and a regular forum-search fallback. Typing performs no requests or mutations; existing server permissions and confirmation dialogs still apply.
|
package/bin/devdevian.mjs
CHANGED
|
@@ -117,6 +117,7 @@ const help = `Devdevian CLI ${version} · alpha
|
|
|
117
117
|
Inside the workspace: /projects, /project ID, /events, /event ID
|
|
118
118
|
Ctrl+K or /actions opens quick actions; type to filter
|
|
119
119
|
Ctrl+N / Ctrl+P moves between form steps; Ctrl+S validates and previews
|
|
120
|
+
Forms: Tab fields/buttons, Enter selects Back/Next or adds a line in multiline text
|
|
120
121
|
Full reply text: / find, n/N matches, Home/End, Esc remembers your place
|
|
121
122
|
N creates · F filters · E edits your publication · X opens context actions
|
|
122
123
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function fieldGuidance(field, { wizard = false, project = false } = {}) {
|
|
2
|
+
if (!field)
|
|
3
|
+
return {
|
|
4
|
+
detail: "",
|
|
5
|
+
keys: wizard
|
|
6
|
+
? "Enter selects the highlighted action"
|
|
7
|
+
: "Enter opens the preview",
|
|
8
|
+
};
|
|
9
|
+
let detail = field.hint || "";
|
|
10
|
+
if (project && field.key === "help")
|
|
11
|
+
detail =
|
|
12
|
+
"Text and links are allowed. Dedicated links: People & links step.";
|
|
13
|
+
if (["url", "repo", "demo_url"].includes(field.key) && !detail)
|
|
14
|
+
detail = "Use a complete https:// or http:// URL.";
|
|
15
|
+
if (["logo", "banner"].includes(field.key))
|
|
16
|
+
return { detail, keys: "Ctrl+O selects an image · Tab next field" };
|
|
17
|
+
if (field.key === "file")
|
|
18
|
+
return { detail, keys: "Ctrl+O file · Ctrl+G Git diff · Tab next field" };
|
|
19
|
+
const keys = field.options
|
|
20
|
+
? "← / → choose · Tab next field"
|
|
21
|
+
: field.multiline
|
|
22
|
+
? "Space inserts a space · Enter adds a line · Tab next field"
|
|
23
|
+
: "Space inserts a space · Enter / Tab next field";
|
|
24
|
+
return { detail, keys };
|
|
25
|
+
}
|
|
26
|
+
export function stepControls(steps, index) {
|
|
27
|
+
return [
|
|
28
|
+
{ id: "back", label: "← Back", enabled: index > 0 },
|
|
29
|
+
{
|
|
30
|
+
id: "next",
|
|
31
|
+
label:
|
|
32
|
+
index === steps.length - 1
|
|
33
|
+
? "Preview →"
|
|
34
|
+
: "Next: " + steps[index + 1]?.label + " →",
|
|
35
|
+
enabled: true,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
}
|
package/lib/tui.mjs
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
appearanceFlags,
|
|
44
44
|
} from "./appearance.mjs";
|
|
45
45
|
import { liveLoop, anchoredIndex } from "./live.mjs";
|
|
46
|
+
import { fieldGuidance, stepControls } from "./form-guidance.mjs";
|
|
46
47
|
import { paletteItems } from "./palette.mjs";
|
|
47
48
|
import {
|
|
48
49
|
formSteps,
|
|
@@ -289,21 +290,29 @@ function InputText({ value, cursor, width, height = 1 }) {
|
|
|
289
290
|
}),
|
|
290
291
|
);
|
|
291
292
|
}
|
|
292
|
-
function FormView({ form, active, cursor, width, height }) {
|
|
293
|
+
function FormView({ form, active, cursor, width, height, navFocus }) {
|
|
293
294
|
const colors = useTheme();
|
|
294
295
|
const fields = fieldsFor(form),
|
|
295
296
|
rows = [];
|
|
296
297
|
const steps = formSteps(form, fields),
|
|
297
298
|
stepIndex = stepAt(steps, active, fields.length),
|
|
298
299
|
step = steps[stepIndex];
|
|
300
|
+
const guidance = fieldGuidance(navFocus ? null : fields[active], {
|
|
301
|
+
wizard: !!step,
|
|
302
|
+
project: form.kind === "catalog" && form.entity === "project",
|
|
303
|
+
});
|
|
304
|
+
const helpRows =
|
|
305
|
+
form.kind === "appearance" ? 0 : height >= 8 ? 2 : height >= 5 ? 1 : 0,
|
|
306
|
+
navRows = step && height >= 3 ? 1 : 0;
|
|
307
|
+
const editorHeight = Math.max(1, height - helpRows - navRows);
|
|
299
308
|
let selectedLine = rows.length;
|
|
300
309
|
fields.forEach((field, index) => {
|
|
301
310
|
if (step && !step.indexes.includes(index)) return;
|
|
302
311
|
if (index === active) selectedLine = rows.length;
|
|
303
312
|
rows.push({
|
|
304
|
-
text: (index === active ? "› " : " ") + field.label,
|
|
305
|
-
color: index === active ? colors.purple : colors.muted,
|
|
306
|
-
bold: index === active,
|
|
313
|
+
text: (index === active && !navFocus ? "› " : " ") + field.label,
|
|
314
|
+
color: index === active && !navFocus ? colors.purple : colors.muted,
|
|
315
|
+
bold: index === active && !navFocus,
|
|
307
316
|
});
|
|
308
317
|
if (field.options) {
|
|
309
318
|
const option = field.options.find(
|
|
@@ -312,12 +321,12 @@ function FormView({ form, active, cursor, width, height }) {
|
|
|
312
321
|
rows.push({
|
|
313
322
|
text:
|
|
314
323
|
" ‹ " + (option?.[1] || form.values[field.key] || "Choose") + " ›",
|
|
315
|
-
color: index === active ? colors.blue : colors.text,
|
|
324
|
+
color: index === active && !navFocus ? colors.blue : colors.text,
|
|
316
325
|
});
|
|
317
326
|
} else {
|
|
318
327
|
const value = form.values[field.key] || "";
|
|
319
328
|
const text =
|
|
320
|
-
index === active
|
|
329
|
+
index === active && !navFocus
|
|
321
330
|
? cursorLines(value, cursor, width - 3).lines
|
|
322
331
|
: wrap(value || "…", width - 3);
|
|
323
332
|
const caretLine =
|
|
@@ -329,7 +338,9 @@ function FormView({ form, active, cursor, width, height }) {
|
|
|
329
338
|
: 0;
|
|
330
339
|
const visible = field.multiline
|
|
331
340
|
? Math.min(
|
|
332
|
-
index === active
|
|
341
|
+
index === active
|
|
342
|
+
? Math.max(1, Math.min(12, editorHeight - 2))
|
|
343
|
+
: 3,
|
|
333
344
|
Math.max(2, text.length),
|
|
334
345
|
)
|
|
335
346
|
: 1,
|
|
@@ -337,16 +348,14 @@ function FormView({ form, active, cursor, width, height }) {
|
|
|
337
348
|
rows.push(
|
|
338
349
|
...text.slice(start, start + visible).map((line) => ({
|
|
339
350
|
text: " " + line,
|
|
340
|
-
color: index === active ? colors.text : colors.muted,
|
|
351
|
+
color: index === active && !navFocus ? colors.text : colors.muted,
|
|
341
352
|
})),
|
|
342
353
|
);
|
|
343
354
|
}
|
|
344
|
-
if (field.hint && active === index)
|
|
345
|
-
rows.push({ text: " " + field.hint, color: colors.muted });
|
|
346
355
|
rows.push("");
|
|
347
356
|
});
|
|
348
357
|
if (active === fields.length) selectedLine = rows.length;
|
|
349
|
-
if (!step
|
|
358
|
+
if (!step)
|
|
350
359
|
rows.push({
|
|
351
360
|
text:
|
|
352
361
|
(active === fields.length ? "› " : " ") +
|
|
@@ -362,9 +371,56 @@ function FormView({ form, active, cursor, width, height }) {
|
|
|
362
371
|
});
|
|
363
372
|
const offset = Math.max(
|
|
364
373
|
0,
|
|
365
|
-
Math.min(
|
|
374
|
+
Math.min(
|
|
375
|
+
selectedLine - (editorHeight >= 4 ? 1 : 0),
|
|
376
|
+
rows.length - editorHeight,
|
|
377
|
+
),
|
|
378
|
+
);
|
|
379
|
+
return h(
|
|
380
|
+
Box,
|
|
381
|
+
{ flexDirection: "column", height, overflow: "hidden" },
|
|
382
|
+
h(FrameRows, { rows, width, height: editorHeight, offset }),
|
|
383
|
+
helpRows === 2
|
|
384
|
+
? h(TextLine, {
|
|
385
|
+
text:
|
|
386
|
+
guidance.detail ||
|
|
387
|
+
(form.kind === "catalogFilter"
|
|
388
|
+
? "Ctrl+S applies these filters."
|
|
389
|
+
: form.kind === "calendar"
|
|
390
|
+
? "Ctrl+S exports a new calendar file."
|
|
391
|
+
: "Ctrl+S opens a preview; publishing requires separate confirmation."),
|
|
392
|
+
width,
|
|
393
|
+
color: colors.muted,
|
|
394
|
+
})
|
|
395
|
+
: null,
|
|
396
|
+
helpRows
|
|
397
|
+
? h(TextLine, { text: guidance.keys, width, color: colors.muted })
|
|
398
|
+
: null,
|
|
399
|
+
navRows
|
|
400
|
+
? h(
|
|
401
|
+
Text,
|
|
402
|
+
{ wrap: "truncate" },
|
|
403
|
+
...stepControls(steps, stepIndex).map((control) =>
|
|
404
|
+
h(
|
|
405
|
+
Text,
|
|
406
|
+
{
|
|
407
|
+
key: control.id,
|
|
408
|
+
color: !control.enabled
|
|
409
|
+
? colors.muted
|
|
410
|
+
: navFocus === control.id
|
|
411
|
+
? colors.blue
|
|
412
|
+
: colors.purple,
|
|
413
|
+
bold: navFocus === control.id,
|
|
414
|
+
dimColor: !control.enabled,
|
|
415
|
+
backgroundColor:
|
|
416
|
+
navFocus === control.id ? colors.selected : undefined,
|
|
417
|
+
},
|
|
418
|
+
` ${navFocus === control.id ? "›" : " "}[ ${control.label} ] `,
|
|
419
|
+
),
|
|
420
|
+
),
|
|
421
|
+
)
|
|
422
|
+
: null,
|
|
366
423
|
);
|
|
367
|
-
return h(FrameRows, { rows, width, height, offset });
|
|
368
424
|
}
|
|
369
425
|
function listRows(route, data, index, width, colors, density = "comfortable") {
|
|
370
426
|
const items = data.items || [];
|
|
@@ -591,6 +647,7 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
591
647
|
[notice, setNotice] = useState(initialAppearance.warning || "");
|
|
592
648
|
const [form, setForm] = useState(null),
|
|
593
649
|
[field, setField] = useState(0),
|
|
650
|
+
[formNav, setFormNav] = useState(null),
|
|
594
651
|
[cursor, setCursor] = useState(0),
|
|
595
652
|
[preview, setPreview] = useState(null),
|
|
596
653
|
[dialog, setDialog] = useState(null),
|
|
@@ -893,6 +950,7 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
893
950
|
setReaderFind({ query: "", cursor: 0, editing: false });
|
|
894
951
|
}
|
|
895
952
|
function moveStep(direction) {
|
|
953
|
+
setFormNav(null);
|
|
896
954
|
const fields = fieldsFor(form),
|
|
897
955
|
steps = formSteps(form, fields),
|
|
898
956
|
currentStep = stepAt(steps, field, fields.length);
|
|
@@ -1392,6 +1450,8 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
1392
1450
|
);
|
|
1393
1451
|
}
|
|
1394
1452
|
function openForm(next) {
|
|
1453
|
+
setFormNav(null);
|
|
1454
|
+
setNotice("");
|
|
1395
1455
|
setBooting(false);
|
|
1396
1456
|
setForm({
|
|
1397
1457
|
...next,
|
|
@@ -1556,6 +1616,7 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
1556
1616
|
}
|
|
1557
1617
|
async function prepare() {
|
|
1558
1618
|
if (!form) return;
|
|
1619
|
+
setFormNav(null);
|
|
1559
1620
|
if (form.kind === "catalogFilter") {
|
|
1560
1621
|
await task(async () => {
|
|
1561
1622
|
const values = validateFilter(form.values);
|
|
@@ -2128,7 +2189,19 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
2128
2189
|
}
|
|
2129
2190
|
function changeField(next) {
|
|
2130
2191
|
const fields = fieldsFor(form),
|
|
2131
|
-
|
|
2192
|
+
steps = formSteps(form, fields),
|
|
2193
|
+
stepIndex = stepAt(steps, field, fields.length),
|
|
2194
|
+
step = steps[stepIndex];
|
|
2195
|
+
if (
|
|
2196
|
+
step &&
|
|
2197
|
+
!formNav &&
|
|
2198
|
+
((next > field && field === step.indexes.at(-1)) ||
|
|
2199
|
+
(next < field && field === step.indexes[0]))
|
|
2200
|
+
) {
|
|
2201
|
+
setFormNav(next < field && stepIndex > 0 ? "back" : "next");
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
const selected = (next + fields.length + 1) % (fields.length + 1);
|
|
2132
2205
|
setField(selected);
|
|
2133
2206
|
setCursor(
|
|
2134
2207
|
selected < fields.length
|
|
@@ -2324,7 +2397,12 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
2324
2397
|
return;
|
|
2325
2398
|
}
|
|
2326
2399
|
if (form) {
|
|
2327
|
-
if (
|
|
2400
|
+
if (
|
|
2401
|
+
keyEvent.ctrl &&
|
|
2402
|
+
input === "o" &&
|
|
2403
|
+
form.kind === "catalog" &&
|
|
2404
|
+
!formNav
|
|
2405
|
+
) {
|
|
2328
2406
|
const active = fieldsFor(form)[field];
|
|
2329
2407
|
if (["logo", "banner", "screenshots"].includes(active?.key)) {
|
|
2330
2408
|
setPicker({ mediaKey: active.key });
|
|
@@ -2359,7 +2437,7 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
2359
2437
|
moveStep(input === "n" ? 1 : -1);
|
|
2360
2438
|
return;
|
|
2361
2439
|
}
|
|
2362
|
-
if (keyEvent.ctrl && input === "e") {
|
|
2440
|
+
if (keyEvent.ctrl && input === "e" && !formNav) {
|
|
2363
2441
|
externalEdit();
|
|
2364
2442
|
return;
|
|
2365
2443
|
}
|
|
@@ -2367,6 +2445,7 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
2367
2445
|
keyEvent.ctrl &&
|
|
2368
2446
|
["o", "g"].includes(input) &&
|
|
2369
2447
|
form.kind === "publication" &&
|
|
2448
|
+
!formNav &&
|
|
2370
2449
|
form.values.kind === "review"
|
|
2371
2450
|
) {
|
|
2372
2451
|
setPicker(input === "g" ? "changes" : "files");
|
|
@@ -2390,6 +2469,39 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
2390
2469
|
prepare();
|
|
2391
2470
|
return;
|
|
2392
2471
|
}
|
|
2472
|
+
if (formNav) {
|
|
2473
|
+
const fields = fieldsFor(form),
|
|
2474
|
+
steps = formSteps(form, fields),
|
|
2475
|
+
i = stepAt(steps, field, fields.length),
|
|
2476
|
+
step = steps[i];
|
|
2477
|
+
if (keyEvent.return) {
|
|
2478
|
+
moveStep(formNav === "back" ? -1 : 1);
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2481
|
+
if (keyEvent.leftArrow || keyEvent.upArrow) {
|
|
2482
|
+
setFormNav(i > 0 ? "back" : "next");
|
|
2483
|
+
return;
|
|
2484
|
+
}
|
|
2485
|
+
if (keyEvent.rightArrow || keyEvent.downArrow) {
|
|
2486
|
+
setFormNav("next");
|
|
2487
|
+
return;
|
|
2488
|
+
}
|
|
2489
|
+
if (keyEvent.tab) {
|
|
2490
|
+
if (keyEvent.shift && formNav === "next" && i > 0) {
|
|
2491
|
+
setFormNav("back");
|
|
2492
|
+
return;
|
|
2493
|
+
}
|
|
2494
|
+
if (!keyEvent.shift && formNav === "back") {
|
|
2495
|
+
setFormNav("next");
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2498
|
+
const next = keyEvent.shift ? step.indexes.at(-1) : step.indexes[0];
|
|
2499
|
+
setFormNav(null);
|
|
2500
|
+
setField(next);
|
|
2501
|
+
setCursor(graphemes(form.values[fields[next].key] || "").length);
|
|
2502
|
+
}
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2393
2505
|
if (keyEvent.tab) {
|
|
2394
2506
|
changeField(field + (keyEvent.shift ? -1 : 1));
|
|
2395
2507
|
return;
|
|
@@ -2735,8 +2847,16 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
2735
2847
|
reportSelected();
|
|
2736
2848
|
}
|
|
2737
2849
|
});
|
|
2850
|
+
const compactFieldHelp =
|
|
2851
|
+
form && !formNav && form.kind !== "appearance" && contentHeight < 8
|
|
2852
|
+
? fieldGuidance(fieldsFor(form)[field], {
|
|
2853
|
+
wizard: !!activeSteps.length,
|
|
2854
|
+
project: form.kind === "catalog" && form.entity === "project",
|
|
2855
|
+
}).detail
|
|
2856
|
+
: "";
|
|
2738
2857
|
const status =
|
|
2739
2858
|
error ||
|
|
2859
|
+
compactFieldHelp ||
|
|
2740
2860
|
liveStatus ||
|
|
2741
2861
|
notice ||
|
|
2742
2862
|
(pending
|
|
@@ -3087,6 +3207,7 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
3087
3207
|
body = h(form.kind === "appearance" ? AppearanceView : FormView, {
|
|
3088
3208
|
form,
|
|
3089
3209
|
active: field,
|
|
3210
|
+
navFocus: formNav,
|
|
3090
3211
|
cursor,
|
|
3091
3212
|
width: contentWidth,
|
|
3092
3213
|
height: contentHeight,
|
|
@@ -3432,7 +3553,11 @@ function App({ initialConfig, flags, root, initialAppearance }) {
|
|
|
3432
3553
|
: preview
|
|
3433
3554
|
? "← → choose · Enter confirm · Esc edit"
|
|
3434
3555
|
: form
|
|
3435
|
-
?
|
|
3556
|
+
? formNav
|
|
3557
|
+
? `${stepControls(activeSteps, activeStep).find((control) => control.id === formNav)?.label || "Next"} · Enter select · Tab fields · Ctrl+S preview`
|
|
3558
|
+
: fieldsFor(form)[field]?.multiline
|
|
3559
|
+
? "Enter new line · Tab next field · Ctrl+N next step · Ctrl+S preview"
|
|
3560
|
+
: "Enter/Tab next field · Ctrl+N/P steps · Ctrl+S preview"
|
|
3436
3561
|
: phase !== "ready"
|
|
3437
3562
|
? "Enter connect / open browser · Ctrl+C exit"
|
|
3438
3563
|
: route.view === "feedback"
|