@tenyokjs/devdevian-cli 0.3.0-alpha.1 → 0.3.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 +3 -1
- package/lib/tui-data.mjs +18 -4
- package/lib/tui.mjs +35 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ The default `devdevian` command and `devdevian tui` open an alternate terminal s
|
|
|
18
18
|
- **/** focuses search and commands: `/feed`, `/inbox`, `/search query`, `/open 142`, `/ask`, `/review`, `/drafts`, `/communities`, `/devices`, `/help`, `/logout`, `/quit`.
|
|
19
19
|
- **←/→** switch conversation tabs; **Space** reads the selected reply/remark in full. **[ / ]** paginate; **Page Up/Down** scroll.
|
|
20
20
|
- **N** creates a conversation (a community on the Communities screen); **A** asks a question; **R** replies inside a conversation; **Shift+R** refreshes.
|
|
21
|
-
- **C** comments on snippet lines; **E/D** edit/delete your own feedback; **S** resolves/reopens a code remark as the publication author; **!** reports selected code feedback.
|
|
21
|
+
- **C** comments on snippet lines; **E/D** edit/delete your own ordinary replies or code feedback; **S** resolves/reopens a code remark as the publication author; **!** reports selected code feedback.
|
|
22
22
|
- 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`.
|
|
23
23
|
- **Ctrl+C** exits. With an unsent draft, confirm saving and leaving or press Ctrl+C again to force exit.
|
|
24
24
|
|
|
@@ -26,6 +26,8 @@ The default `devdevian` command and `devdevian tui` open an alternate terminal s
|
|
|
26
26
|
|
|
27
27
|
Pages have a bounded 30-second in-memory cache. Refresh explicitly with Shift+R. Network errors stay inside the workspace; failed writes are never automatically retried. Existing web permissions and server validation apply to every operation.
|
|
28
28
|
|
|
29
|
+
Ordinary reply deletion replaces your text with a placeholder and preserves nested replies. Its attachments become unavailable. Deleting an accepted answer reopens the question. D/d works on a selected reply or while reading it in full; confirmation is required.
|
|
30
|
+
|
|
29
31
|
## Project files and Git changes
|
|
30
32
|
|
|
31
33
|
Start inside your project (`cd PROJECT1 && devdevian tui`) or use `devdevian tui --project /path/to/PROJECT1`. `devdevian open --project /path/to/PROJECT1` carries the same project into a new terminal window. The project root stays fixed for the session.
|
package/lib/tui-data.mjs
CHANGED
|
@@ -98,15 +98,29 @@ export async function loadPage(api, route, signal) {
|
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
100
|
if (route.view === "thread") {
|
|
101
|
-
const post = await get(
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
const post = await get(
|
|
102
|
+
"/posts/" + route.postId + "?reply_id=" + route.parent.id,
|
|
103
|
+
);
|
|
104
|
+
const target = post.reply_path?.at(-1);
|
|
105
|
+
let parent = post.replies?.find((r) => r.id === route.parent.id);
|
|
106
|
+
if (!parent && target) {
|
|
107
|
+
const siblings = await get(
|
|
108
|
+
`/posts/${route.postId}/replies?parent_id=${target.parent_id || ""}&page=${target.page}`,
|
|
104
109
|
);
|
|
110
|
+
parent = siblings.items.find((r) => r.id === route.parent.id);
|
|
111
|
+
}
|
|
112
|
+
const replies = await get(
|
|
113
|
+
`/posts/${route.postId}/replies?parent_id=${route.parent.id}&page=${page}`,
|
|
114
|
+
);
|
|
105
115
|
return {
|
|
106
116
|
post,
|
|
107
117
|
...replies,
|
|
118
|
+
items: [
|
|
119
|
+
...(parent ? [{ ...parent, entryType: "parent" }] : []),
|
|
120
|
+
...replies.items,
|
|
121
|
+
],
|
|
108
122
|
title: "Reply thread",
|
|
109
|
-
subtitle: "Replying to " + route.parent.name,
|
|
123
|
+
subtitle: "Replying to " + (parent?.name || route.parent.name),
|
|
110
124
|
};
|
|
111
125
|
}
|
|
112
126
|
if (route.view === "feedback") {
|
package/lib/tui.mjs
CHANGED
|
@@ -333,8 +333,14 @@ function listRows(route, data, index, width) {
|
|
|
333
333
|
item.name +
|
|
334
334
|
(item.entryType === "note"
|
|
335
335
|
? " · original remark"
|
|
336
|
-
:
|
|
337
|
-
|
|
336
|
+
: item.entryType === "parent"
|
|
337
|
+
? " · parent reply"
|
|
338
|
+
: " · reply #" + item.id);
|
|
339
|
+
meta =
|
|
340
|
+
stamp(item.created) +
|
|
341
|
+
((item.edited_at || item.updated_at) && !item.deleted_at
|
|
342
|
+
? " · edited"
|
|
343
|
+
: "");
|
|
338
344
|
body = item.deleted_at ? "[deleted — discussion preserved]" : item.body;
|
|
339
345
|
} else {
|
|
340
346
|
meta = [
|
|
@@ -422,7 +428,7 @@ function App({ initialConfig, flags, root }) {
|
|
|
422
428
|
sidebar = compact ? 0 : 25,
|
|
423
429
|
bodyHeight = Math.max(1, height - 9),
|
|
424
430
|
contentWidth = Math.max(1, width - sidebar - 6),
|
|
425
|
-
contentHeight = Math.max(1, bodyHeight -
|
|
431
|
+
contentHeight = Math.max(1, bodyHeight - 6);
|
|
426
432
|
const key = JSON.stringify(route),
|
|
427
433
|
current = data?.key === key ? data : null;
|
|
428
434
|
const selected = current?.items?.[index];
|
|
@@ -1035,6 +1041,11 @@ function App({ initialConfig, flags, root }) {
|
|
|
1035
1041
|
navigate({ view: "feedback", postId: route.id, id: item.id, page: 1 });
|
|
1036
1042
|
return;
|
|
1037
1043
|
}
|
|
1044
|
+
if (route.view === "thread" && item.entryType === "parent") {
|
|
1045
|
+
setReading(item);
|
|
1046
|
+
setScroll(0);
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1038
1049
|
if (
|
|
1039
1050
|
(route.view === "post" && route.tab === "replies") ||
|
|
1040
1051
|
route.view === "thread"
|
|
@@ -1116,24 +1127,30 @@ function App({ initialConfig, flags, root }) {
|
|
|
1116
1127
|
return;
|
|
1117
1128
|
}
|
|
1118
1129
|
const target = editable();
|
|
1119
|
-
if (
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
) {
|
|
1124
|
-
setError("Select your own line feedback or feedback reply to delete.");
|
|
1130
|
+
if (!target || target.row.user_id !== user.id || target.row.deleted_at) {
|
|
1131
|
+
setError(
|
|
1132
|
+
"Select your own reply or code feedback in the Replies / Code feedback tab to delete.",
|
|
1133
|
+
);
|
|
1125
1134
|
return;
|
|
1126
1135
|
}
|
|
1127
1136
|
askConfirm(
|
|
1128
|
-
"Delete your feedback?",
|
|
1129
|
-
"Your text is removed. Other members’ replies are preserved."
|
|
1137
|
+
target.feedback ? "Delete your feedback?" : "Delete your reply?",
|
|
1138
|
+
"Your text is removed. Other members’ replies are preserved." +
|
|
1139
|
+
(!target.feedback && current.post?.accepted_reply_id === target.row.id
|
|
1140
|
+
? " Its accepted-solution mark is cleared."
|
|
1141
|
+
: ""),
|
|
1130
1142
|
() =>
|
|
1131
1143
|
task(async () => {
|
|
1132
1144
|
await api(target.url, {
|
|
1133
1145
|
method: "DELETE",
|
|
1134
1146
|
body: { revision: target.row.revision },
|
|
1135
1147
|
});
|
|
1136
|
-
|
|
1148
|
+
setReading(null);
|
|
1149
|
+
invalidate(
|
|
1150
|
+
target.feedback
|
|
1151
|
+
? "Feedback deleted; the discussion is preserved."
|
|
1152
|
+
: "Reply deleted; other members’ replies are preserved.",
|
|
1153
|
+
);
|
|
1137
1154
|
}),
|
|
1138
1155
|
);
|
|
1139
1156
|
}
|
|
@@ -1298,9 +1315,11 @@ function App({ initialConfig, flags, root }) {
|
|
|
1298
1315
|
else if (input === "r") {
|
|
1299
1316
|
setReading(null);
|
|
1300
1317
|
reply();
|
|
1301
|
-
} else if (input === "e") {
|
|
1318
|
+
} else if (input === "e" || input === "E") {
|
|
1302
1319
|
setReading(null);
|
|
1303
1320
|
editSelected();
|
|
1321
|
+
} else if (input === "d" || input === "D") {
|
|
1322
|
+
deleteSelected();
|
|
1304
1323
|
}
|
|
1305
1324
|
return;
|
|
1306
1325
|
}
|
|
@@ -1565,7 +1584,7 @@ function App({ initialConfig, flags, root }) {
|
|
|
1565
1584
|
editSelected();
|
|
1566
1585
|
return;
|
|
1567
1586
|
}
|
|
1568
|
-
if (input === "d") {
|
|
1587
|
+
if (input === "d" || input === "D") {
|
|
1569
1588
|
deleteSelected();
|
|
1570
1589
|
return;
|
|
1571
1590
|
}
|
|
@@ -2001,7 +2020,7 @@ function App({ initialConfig, flags, root }) {
|
|
|
2001
2020
|
const hint = picker
|
|
2002
2021
|
? "Project files stay local until publication is confirmed"
|
|
2003
2022
|
: reading
|
|
2004
|
-
? "↑↓ scroll · R reply · E edit · Esc close"
|
|
2023
|
+
? "↑↓ scroll · R reply · E edit · D delete · Esc close"
|
|
2005
2024
|
: preview
|
|
2006
2025
|
? "← → choose · Enter confirm · Esc edit"
|
|
2007
2026
|
: form
|
|
@@ -2011,7 +2030,7 @@ function App({ initialConfig, flags, root }) {
|
|
|
2011
2030
|
: route.view === "feedback"
|
|
2012
2031
|
? "↑↓ select · R reply · E edit · D delete · S resolve · ! report"
|
|
2013
2032
|
: route.view === "post"
|
|
2014
|
-
? "←→ tabs · ↑↓
|
|
2033
|
+
? "←→ tabs · ↑↓ select · R reply · E edit · D delete · Space read"
|
|
2015
2034
|
: "↑↓ select · Enter open · Tab sections · [ ] pages · N new · / search";
|
|
2016
2035
|
return h(
|
|
2017
2036
|
Box,
|