@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 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("/posts/" + route.postId),
102
- replies = await get(
103
- `/posts/${route.postId}/replies?parent_id=${route.parent.id}&page=${page}`,
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
- : " · reply #" + item.id);
337
- meta = stamp(item.created) + (item.edited_at ? " · edited" : "");
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 - 5);
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
- !target?.feedback ||
1121
- target.row.user_id !== user.id ||
1122
- target.row.deleted_at
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
- invalidate("Feedback deleted; the discussion is preserved.");
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 · ↑↓ scroll/select · R reply · C code remark · Space full text · Tab navigate"
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenyokjs/devdevian-cli",
3
- "version": "0.3.0-alpha.1",
3
+ "version": "0.3.1-alpha.1",
4
4
  "description": "Devdevian in your terminal: discussions, questions and human code reviews.",
5
5
  "type": "module",
6
6
  "bin": {