@l1nsn0w/dox 0.1.0 → 1.0.0

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.
Files changed (3) hide show
  1. package/README.md +23 -33
  2. package/dox.js +1547 -944
  3. package/package.json +1 -1
package/dox.js CHANGED
@@ -2142,6 +2142,9 @@ var require_commander = __commonJS((exports) => {
2142
2142
  exports.InvalidOptionArgumentError = InvalidArgumentError;
2143
2143
  });
2144
2144
 
2145
+ // apps/cli/src/version.ts
2146
+ var VERSION = "v1.0.0", COMMIT = "8d424624ba1aa0d35c76bca365516cf3b7ec2a4e";
2147
+
2145
2148
  // node_modules/.bun/sisteransi@1.0.5/node_modules/sisteransi/src/index.js
2146
2149
  var require_src = __commonJS((exports, module) => {
2147
2150
  var ESC = "\x1B";
@@ -10750,32 +10753,32 @@ It can also happen if the client has a browser extension installed which messes
10750
10753
  var fiber = workInProgress2.child;
10751
10754
  fiber !== null && (fiber.return = workInProgress2);
10752
10755
  for (;fiber !== null; ) {
10753
- var list3 = fiber.dependencies;
10754
- if (list3 !== null) {
10756
+ var list4 = fiber.dependencies;
10757
+ if (list4 !== null) {
10755
10758
  var nextFiber = fiber.child;
10756
- list3 = list3.firstContext;
10759
+ list4 = list4.firstContext;
10757
10760
  a:
10758
- for (;list3 !== null; ) {
10759
- var dependency = list3;
10760
- list3 = fiber;
10761
+ for (;list4 !== null; ) {
10762
+ var dependency = list4;
10763
+ list4 = fiber;
10761
10764
  for (var i = 0;i < contexts.length; i++)
10762
10765
  if (dependency.context === contexts[i]) {
10763
- list3.lanes |= renderLanes2;
10764
- dependency = list3.alternate;
10766
+ list4.lanes |= renderLanes2;
10767
+ dependency = list4.alternate;
10765
10768
  dependency !== null && (dependency.lanes |= renderLanes2);
10766
- scheduleContextWorkOnParentPath(list3.return, renderLanes2, workInProgress2);
10769
+ scheduleContextWorkOnParentPath(list4.return, renderLanes2, workInProgress2);
10767
10770
  forcePropagateEntireTree || (nextFiber = null);
10768
10771
  break a;
10769
10772
  }
10770
- list3 = dependency.next;
10773
+ list4 = dependency.next;
10771
10774
  }
10772
10775
  } else if (fiber.tag === 18) {
10773
10776
  nextFiber = fiber.return;
10774
10777
  if (nextFiber === null)
10775
10778
  throw Error("We just came from a parent so we must have had a parent. This is a bug in React.");
10776
10779
  nextFiber.lanes |= renderLanes2;
10777
- list3 = nextFiber.alternate;
10778
- list3 !== null && (list3.lanes |= renderLanes2);
10780
+ list4 = nextFiber.alternate;
10781
+ list4 !== null && (list4.lanes |= renderLanes2);
10779
10782
  scheduleContextWorkOnParentPath(nextFiber, renderLanes2, workInProgress2);
10780
10783
  nextFiber = null;
10781
10784
  } else
@@ -21247,15 +21250,15 @@ var require_constants2 = __commonJS((exports, module) => {
21247
21250
  var require_buffer_util = __commonJS((exports, module) => {
21248
21251
  var { EMPTY_BUFFER } = require_constants2();
21249
21252
  var FastBuffer = Buffer[Symbol.species];
21250
- function concat2(list3, totalLength) {
21251
- if (list3.length === 0)
21253
+ function concat2(list4, totalLength) {
21254
+ if (list4.length === 0)
21252
21255
  return EMPTY_BUFFER;
21253
- if (list3.length === 1)
21254
- return list3[0];
21256
+ if (list4.length === 1)
21257
+ return list4[0];
21255
21258
  const target = Buffer.allocUnsafe(totalLength);
21256
21259
  let offset = 0;
21257
- for (let i = 0;i < list3.length; i++) {
21258
- const buf = list3[i];
21260
+ for (let i = 0;i < list4.length; i++) {
21261
+ const buf = list4[i];
21259
21262
  target.set(buf, offset);
21260
21263
  offset += buf.length;
21261
21264
  }
@@ -22516,14 +22519,14 @@ var require_sender = __commonJS((exports, module) => {
22516
22519
  this._bufferedBytes += params[3][kByteLength];
22517
22520
  this._queue.push(params);
22518
22521
  }
22519
- sendFrame(list3, cb) {
22520
- if (list3.length === 2) {
22522
+ sendFrame(list4, cb) {
22523
+ if (list4.length === 2) {
22521
22524
  this._socket.cork();
22522
- this._socket.write(list3[0]);
22523
- this._socket.write(list3[1], cb);
22525
+ this._socket.write(list4[0]);
22526
+ this._socket.write(list4[1], cb);
22524
22527
  this._socket.uncork();
22525
22528
  } else {
22526
- this._socket.write(list3[0], cb);
22529
+ this._socket.write(list4[0], cb);
22527
22530
  }
22528
22531
  }
22529
22532
  }
@@ -32049,10 +32052,33 @@ function useTerminalSize() {
32049
32052
  }, [stdout]);
32050
32053
  return size2;
32051
32054
  }
32052
- var import_react65;
32055
+ function useSpinnerFrame(active, intervalMs = 80) {
32056
+ const [i, setI] = import_react65.useState(0);
32057
+ import_react65.useEffect(() => {
32058
+ if (!active)
32059
+ return;
32060
+ const t = setInterval(() => setI((v2) => (v2 + 1) % SPINNER_FRAMES.length), intervalMs);
32061
+ return () => clearInterval(t);
32062
+ }, [active, intervalMs]);
32063
+ return SPINNER_FRAMES[i] ?? SPINNER_FRAMES[0];
32064
+ }
32065
+ function useMinHold(value, holdMs = 400) {
32066
+ const [held, setHeld] = import_react65.useState(value);
32067
+ import_react65.useEffect(() => {
32068
+ if (value) {
32069
+ setHeld(true);
32070
+ return;
32071
+ }
32072
+ const t = setTimeout(() => setHeld(false), holdMs);
32073
+ return () => clearTimeout(t);
32074
+ }, [value, holdMs]);
32075
+ return held;
32076
+ }
32077
+ var import_react65, SPINNER_FRAMES;
32053
32078
  var init_hooks = __esm(async () => {
32054
32079
  await init_build2();
32055
32080
  import_react65 = __toESM(require_react(), 1);
32081
+ SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
32056
32082
  });
32057
32083
 
32058
32084
  // apps/cli/src/tui/theme.ts
@@ -32812,111 +32838,160 @@ var init_ErrorAlert = __esm(async () => {
32812
32838
  jsx_dev_runtime6 = __toESM(require_jsx_dev_runtime(), 1);
32813
32839
  });
32814
32840
 
32815
- // apps/cli/src/tui/components/primitives/HelpOverlay.tsx
32816
- function HelpOverlay() {
32841
+ // apps/cli/src/tui/components/views/help/HelpView.tsx
32842
+ function HelpView({ onClose }) {
32843
+ const { cols, rows: totalRows } = useTerminalSize();
32844
+ use_input_default((input, key) => {
32845
+ if (input === "?" || key.escape || key.return)
32846
+ onClose();
32847
+ });
32848
+ const cardWidth = Math.min(86, Math.max(60, cols - 4));
32849
+ const cardRows = 17;
32850
+ const topPad = Math.max(1, Math.floor((totalRows - cardRows - 3) / 2));
32817
32851
  return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32818
- justifyContent: "center",
32819
- marginTop: 1,
32820
- children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32821
- flexDirection: "column",
32822
- borderStyle: "round",
32823
- borderColor: color.accent,
32824
- paddingX: 2,
32825
- paddingY: 1,
32826
- width: 64,
32827
- children: [
32828
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
32829
- bold: true,
32830
- color: color.accent,
32831
- children: [
32832
- icon.brand,
32833
- " keybindings"
32834
- ]
32835
- }, undefined, true, undefined, this),
32836
- sections.map((s) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32852
+ flexDirection: "column",
32853
+ paddingX: 1,
32854
+ children: [
32855
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32856
+ height: topPad
32857
+ }, undefined, false, undefined, this),
32858
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32859
+ justifyContent: "center",
32860
+ children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32837
32861
  flexDirection: "column",
32838
- marginTop: 1,
32862
+ borderStyle: "round",
32863
+ borderColor: color.accent,
32864
+ paddingX: 2,
32865
+ paddingY: 1,
32866
+ width: cardWidth,
32839
32867
  children: [
32840
32868
  /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
32841
- color: color.muted,
32842
- children: s.title.toUpperCase()
32843
- }, undefined, false, undefined, this),
32844
- s.rows.map(([k3, l2]) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32869
+ bold: true,
32870
+ color: color.accent,
32871
+ children: [
32872
+ icon.brand,
32873
+ " keybindings"
32874
+ ]
32875
+ }, undefined, true, undefined, this),
32876
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32877
+ marginTop: 1,
32845
32878
  children: [
32846
32879
  /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32847
- width: 16,
32848
- children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
32849
- color: color.accent,
32850
- children: [
32851
- " ",
32852
- k3
32853
- ]
32854
- }, undefined, true, undefined, this)
32880
+ flexDirection: "column",
32881
+ flexGrow: 1,
32882
+ flexBasis: 0,
32883
+ children: [
32884
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Section, {
32885
+ title: "Navigate",
32886
+ rows: navigate
32887
+ }, undefined, false, undefined, this),
32888
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Section, {
32889
+ title: "App",
32890
+ rows: app,
32891
+ marginTop: true
32892
+ }, undefined, false, undefined, this)
32893
+ ]
32894
+ }, undefined, true, undefined, this),
32895
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32896
+ width: 2
32855
32897
  }, undefined, false, undefined, this),
32856
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
32857
- children: l2
32898
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32899
+ flexDirection: "column",
32900
+ flexGrow: 1,
32901
+ flexBasis: 0,
32902
+ children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Section, {
32903
+ title: "Act",
32904
+ rows: act
32905
+ }, undefined, false, undefined, this)
32858
32906
  }, undefined, false, undefined, this)
32859
32907
  ]
32860
- }, k3, true, undefined, this))
32908
+ }, undefined, true, undefined, this)
32861
32909
  ]
32862
- }, s.title, true, undefined, this)),
32863
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32864
- marginTop: 1,
32865
- children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
32866
- color: color.muted,
32867
- children: "press ? or esc to close"
32910
+ }, undefined, true, undefined, this)
32911
+ }, undefined, false, undefined, this),
32912
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Footer, {
32913
+ mode: "help",
32914
+ version: VERSION,
32915
+ outerPadX: 1,
32916
+ hints: [
32917
+ ["?", "back"],
32918
+ ["esc", "back"],
32919
+ ["⏎", "back"]
32920
+ ]
32921
+ }, undefined, false, undefined, this)
32922
+ ]
32923
+ }, undefined, true, undefined, this);
32924
+ }
32925
+ function Section({
32926
+ title,
32927
+ rows,
32928
+ marginTop = false
32929
+ }) {
32930
+ return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32931
+ flexDirection: "column",
32932
+ marginTop: marginTop ? 1 : 0,
32933
+ children: [
32934
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
32935
+ color: color.muted,
32936
+ children: title.toUpperCase()
32937
+ }, undefined, false, undefined, this),
32938
+ rows.map(([k3, l2]) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32939
+ children: [
32940
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
32941
+ width: 14,
32942
+ children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
32943
+ color: color.accent,
32944
+ children: [
32945
+ " ",
32946
+ k3
32947
+ ]
32948
+ }, undefined, true, undefined, this)
32949
+ }, undefined, false, undefined, this),
32950
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
32951
+ wrap: "truncate",
32952
+ children: l2
32868
32953
  }, undefined, false, undefined, this)
32869
- }, undefined, false, undefined, this)
32870
- ]
32871
- }, undefined, true, undefined, this)
32872
- }, undefined, false, undefined, this);
32954
+ ]
32955
+ }, k3, true, undefined, this))
32956
+ ]
32957
+ }, undefined, true, undefined, this);
32873
32958
  }
32874
- var jsx_dev_runtime7, sections;
32875
- var init_HelpOverlay = __esm(async () => {
32959
+ var jsx_dev_runtime7, navigate, act, app;
32960
+ var init_HelpView = __esm(async () => {
32876
32961
  init_theme15();
32877
- await init_build2();
32962
+ await __promiseAll([
32963
+ init_build2(),
32964
+ init_hooks(),
32965
+ init_Footer()
32966
+ ]);
32878
32967
  jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
32879
- sections = [
32880
- {
32881
- title: "Navigate",
32882
- rows: [
32883
- ["j / ↓", "move down"],
32884
- ["k / ", "move up"],
32885
- ["h / l", "switch sidebar ↔ list"],
32886
- ["tab", "next filter"],
32887
- ["g / G", "first / last"]
32888
- ]
32889
- },
32890
- {
32891
- title: "Act",
32892
- rows: [
32893
- ["space", "toggle done"],
32894
- ["i / a", "add todo"],
32895
- ["e", "edit todo"],
32896
- ["d", "delete todo"],
32897
- ["p", "new project"],
32898
- ["m", "manage project / invite"],
32899
- ["D", "delete project"],
32900
- ["/", "search todos"],
32901
- ["r", "refresh"]
32902
- ]
32903
- },
32904
- {
32905
- title: "App",
32906
- rows: [
32907
- ["s", "open settings"],
32908
- ["A", "about dox"],
32909
- ["?", "toggle this help"],
32910
- ["q / Ctrl+C", "quit"],
32911
- ["esc", "close overlay / cancel"]
32912
- ]
32913
- }
32968
+ navigate = [
32969
+ ["j / ↓", "move down"],
32970
+ ["k / ↑", "move up"],
32971
+ ["h / l", "switch sidebar ↔ list"],
32972
+ ["tab", "next filter"],
32973
+ ["g / G", "first / last"]
32974
+ ];
32975
+ act = [
32976
+ ["space", "toggle done"],
32977
+ ["i / a", "add todo"],
32978
+ ["e", "edit todo"],
32979
+ ["d", "delete todo"],
32980
+ ["p", "new project"],
32981
+ ["m", "manage project"],
32982
+ ["D", "delete project"],
32983
+ ["/", "search todos"],
32984
+ ["r", "refresh"]
32985
+ ];
32986
+ app = [
32987
+ ["s", "open settings"],
32988
+ ["A", "about dox"],
32989
+ ["?", "toggle this help"],
32990
+ ["q / Ctrl+C", "quit"],
32991
+ ["esc", "close / cancel"]
32914
32992
  ];
32915
32993
  });
32916
32994
 
32917
- // apps/cli/src/version.ts
32918
- var VERSION2 = "v0.1.0";
32919
-
32920
32995
  // apps/cli/src/tui/components/primitives/TitledPanel.tsx
32921
32996
  function TitledPanel({
32922
32997
  title,
@@ -33002,17 +33077,34 @@ function SearchView({
33002
33077
  onOpen,
33003
33078
  onClose
33004
33079
  }) {
33005
- const { stdout } = use_stdout_default();
33006
- const cols = Math.max(80, stdout?.columns ?? 100);
33007
- const rows = Math.max(20, stdout?.rows ?? 30);
33008
- const panelWidth = cols - 2;
33009
- const panelHeight = Math.max(15, rows - 4);
33080
+ const { cols, rows } = useTerminalSize();
33081
+ const panelWidth = Math.min(96, Math.max(60, cols - 4));
33082
+ const panelHeight = Math.min(30, Math.max(18, rows - 6));
33083
+ const topPad = Math.max(1, Math.floor((rows - panelHeight - 3) / 2));
33010
33084
  const projectById = import_react67.useMemo(() => new Map(projects.map((p2) => [p2.id, p2])), [projects]);
33011
33085
  const results = import_react67.useMemo(() => searchTodos(todos, query), [todos, query]);
33012
33086
  const safeCursor = Math.min(cursor4, Math.max(0, results.length - 1));
33087
+ const onResultCountRef = import_react67.useRef(onResultCount);
33088
+ const onQueryChangeRef = import_react67.useRef(onQueryChange);
33089
+ const onOpenRef = import_react67.useRef(onOpen);
33090
+ const resultsRef = import_react67.useRef(results);
33091
+ const cursorRef = import_react67.useRef(safeCursor);
33092
+ onResultCountRef.current = onResultCount;
33093
+ onQueryChangeRef.current = onQueryChange;
33094
+ onOpenRef.current = onOpen;
33095
+ resultsRef.current = results;
33096
+ cursorRef.current = safeCursor;
33013
33097
  import_react67.useEffect(() => {
33014
- onResultCount(results.length);
33015
- }, [results.length, onResultCount]);
33098
+ onResultCountRef.current(results.length);
33099
+ }, [results.length]);
33100
+ const handleQueryChange = import_react67.useCallback((q2) => {
33101
+ onQueryChangeRef.current(q2);
33102
+ }, []);
33103
+ const handleSubmit = import_react67.useCallback(() => {
33104
+ const target = resultsRef.current[cursorRef.current];
33105
+ if (target)
33106
+ onOpenRef.current(target.id);
33107
+ }, []);
33016
33108
  use_input_default((_input, key) => {
33017
33109
  if (key.escape)
33018
33110
  return onClose();
@@ -33028,106 +33120,108 @@ function SearchView({
33028
33120
  flexDirection: "column",
33029
33121
  paddingX: 1,
33030
33122
  children: [
33031
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(TitledPanel, {
33032
- title: "Search",
33033
- width: panelWidth,
33034
- height: panelHeight,
33035
- paddingX: 2,
33036
- paddingY: 1,
33037
- focused: true,
33038
- children: [
33039
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33040
- children: [
33041
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33042
- width: 2,
33043
- children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33044
- color: color.accent,
33045
- bold: true,
33046
- children: icon.chevron
33047
- }, undefined, false, undefined, this)
33048
- }, undefined, false, undefined, this),
33049
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33050
- flexGrow: 1,
33051
- children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(TextInput, {
33052
- defaultValue: query,
33053
- placeholder: "search by title or description…",
33054
- onChange: onQueryChange,
33055
- onSubmit: () => {
33056
- const target = results[safeCursor];
33057
- if (target)
33058
- onOpen(target.id);
33059
- }
33060
- }, undefined, false, undefined, this)
33061
- }, undefined, false, undefined, this)
33062
- ]
33063
- }, undefined, true, undefined, this),
33064
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33065
- marginTop: 1,
33066
- children: [
33067
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33068
- color: color.muted,
33069
- children: matchLabel
33070
- }, undefined, false, undefined, this),
33071
- hydrating && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33072
- color: color.muted,
33073
- dimColor: true,
33074
- children: [
33075
- " ",
33076
- icon.dot,
33077
- " loading descriptions…"
33078
- ]
33079
- }, undefined, true, undefined, this)
33080
- ]
33081
- }, undefined, true, undefined, this),
33082
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33083
- marginTop: 1,
33084
- flexDirection: "column",
33085
- children: results.length === 0 ? /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33086
- color: color.muted,
33087
- dimColor: true,
33123
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33124
+ height: topPad
33125
+ }, undefined, false, undefined, this),
33126
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33127
+ justifyContent: "center",
33128
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(TitledPanel, {
33129
+ title: "Search",
33130
+ width: panelWidth,
33131
+ height: panelHeight,
33132
+ paddingX: 2,
33133
+ paddingY: 1,
33134
+ focused: true,
33135
+ children: [
33136
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33088
33137
  children: [
33089
- " ",
33090
- query.trim() === "" ? "type to search…" : "no matches — try a shorter query"
33138
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33139
+ width: 2,
33140
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33141
+ color: color.accent,
33142
+ bold: true,
33143
+ children: icon.chevron
33144
+ }, undefined, false, undefined, this)
33145
+ }, undefined, false, undefined, this),
33146
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33147
+ flexGrow: 1,
33148
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(TextInput, {
33149
+ defaultValue: query,
33150
+ placeholder: "search by title or description…",
33151
+ onChange: handleQueryChange,
33152
+ onSubmit: handleSubmit
33153
+ }, undefined, false, undefined, this)
33154
+ }, undefined, false, undefined, this)
33091
33155
  ]
33092
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(jsx_dev_runtime9.Fragment, {
33156
+ }, undefined, true, undefined, this),
33157
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33158
+ marginTop: 1,
33093
33159
  children: [
33094
- win.items.map((t, idx) => {
33095
- const projectId = t.projectId;
33096
- const project2 = projectId ? projectById.get(projectId) ?? null : null;
33097
- return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(ResultRow, {
33098
- todo: t,
33099
- project: project2,
33100
- nowMs,
33101
- highlighted: win.start + idx === safeCursor,
33102
- width: panelWidth - 6
33103
- }, t.id, false, undefined, this);
33104
- }),
33105
- (win.moreAbove > 0 || win.moreBelow > 0) && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33106
- width: panelWidth - 6,
33160
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33161
+ color: color.muted,
33162
+ children: matchLabel
33163
+ }, undefined, false, undefined, this),
33164
+ hydrating && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33165
+ color: color.muted,
33166
+ dimColor: true,
33107
33167
  children: [
33108
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33109
- color: color.muted,
33110
- dimColor: true,
33111
- children: win.moreAbove > 0 ? `↑ ${win.moreAbove} more` : ""
33112
- }, undefined, false, undefined, this),
33113
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33114
- flexGrow: 1
33115
- }, undefined, false, undefined, this),
33116
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33117
- color: color.muted,
33118
- dimColor: true,
33119
- children: win.moreBelow > 0 ? `${win.moreBelow} more ↓` : ""
33120
- }, undefined, false, undefined, this)
33168
+ " ",
33169
+ icon.dot,
33170
+ " loading descriptions…"
33121
33171
  ]
33122
33172
  }, undefined, true, undefined, this)
33123
33173
  ]
33124
- }, undefined, true, undefined, this)
33125
- }, undefined, false, undefined, this)
33126
- ]
33127
- }, undefined, true, undefined, this),
33174
+ }, undefined, true, undefined, this),
33175
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33176
+ marginTop: 1,
33177
+ flexDirection: "column",
33178
+ children: results.length === 0 ? /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33179
+ color: color.muted,
33180
+ dimColor: true,
33181
+ children: [
33182
+ " ",
33183
+ query.trim() === "" ? "type to search…" : "no matches — try a shorter query"
33184
+ ]
33185
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(jsx_dev_runtime9.Fragment, {
33186
+ children: [
33187
+ win.items.map((t, idx) => {
33188
+ const projectId = t.projectId;
33189
+ const project2 = projectId ? projectById.get(projectId) ?? null : null;
33190
+ return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(ResultRow, {
33191
+ todo: t,
33192
+ project: project2,
33193
+ nowMs,
33194
+ highlighted: win.start + idx === safeCursor,
33195
+ width: panelWidth - 6
33196
+ }, t.id, false, undefined, this);
33197
+ }),
33198
+ (win.moreAbove > 0 || win.moreBelow > 0) && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33199
+ width: panelWidth - 6,
33200
+ children: [
33201
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33202
+ color: color.muted,
33203
+ dimColor: true,
33204
+ children: win.moreAbove > 0 ? `↑ ${win.moreAbove} more` : ""
33205
+ }, undefined, false, undefined, this),
33206
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33207
+ flexGrow: 1
33208
+ }, undefined, false, undefined, this),
33209
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
33210
+ color: color.muted,
33211
+ dimColor: true,
33212
+ children: win.moreBelow > 0 ? `${win.moreBelow} more ↓` : ""
33213
+ }, undefined, false, undefined, this)
33214
+ ]
33215
+ }, undefined, true, undefined, this)
33216
+ ]
33217
+ }, undefined, true, undefined, this)
33218
+ }, undefined, false, undefined, this)
33219
+ ]
33220
+ }, undefined, true, undefined, this)
33221
+ }, undefined, false, undefined, this),
33128
33222
  /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Footer, {
33129
33223
  mode: "search",
33130
- version: VERSION2,
33224
+ version: VERSION,
33131
33225
  outerPadX: 1,
33132
33226
  hints: [
33133
33227
  ["↑↓", "navigate"],
@@ -33235,6 +33329,7 @@ var init_SearchView = __esm(async () => {
33235
33329
  await __promiseAll([
33236
33330
  init_build2(),
33237
33331
  init_build3(),
33332
+ init_hooks(),
33238
33333
  init_Footer(),
33239
33334
  init_TitledPanel()
33240
33335
  ]);
@@ -33247,16 +33342,19 @@ function SettingsView({
33247
33342
  tabs,
33248
33343
  activeTab,
33249
33344
  cursor: cursor4,
33345
+ notice,
33250
33346
  onTabChange,
33251
33347
  onCursorChange,
33252
33348
  onClose,
33253
33349
  inputPaused = false
33254
33350
  }) {
33255
- const { stdout } = use_stdout_default();
33256
- const cols = Math.max(80, stdout?.columns ?? 100);
33257
- const rows = Math.max(20, stdout?.rows ?? 30);
33258
- const panelWidth = cols - 2;
33259
- const innerHeight = Math.max(15, rows - 4);
33351
+ const { cols, rows } = useTerminalSize();
33352
+ const panelWidth = Math.min(110, Math.max(72, cols - 4));
33353
+ const innerHeight = Math.min(28, Math.max(18, rows - 6));
33354
+ const topPad = Math.max(1, Math.floor((rows - innerHeight - 3) / 2));
33355
+ const innerW = Math.max(8, panelWidth - 4);
33356
+ const sidebarW = Math.max(16, Math.floor(panelWidth * 0.36));
33357
+ const detailW = Math.max(20, innerW - sidebarW - 2);
33260
33358
  const tabIndex = Math.max(0, tabs.findIndex((t) => t.key === activeTab));
33261
33359
  const tab2 = tabs[tabIndex] ?? tabs[0];
33262
33360
  const tabRows = tab2?.rows ?? [];
@@ -33308,47 +33406,62 @@ function SettingsView({
33308
33406
  flexDirection: "column",
33309
33407
  paddingX: 1,
33310
33408
  children: [
33311
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(TitledPanel, {
33312
- title: "Settings",
33313
- width: panelWidth,
33314
- paddingY: 1,
33315
- focused: true,
33316
- height: innerHeight,
33317
- children: [
33318
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(TabStrip, {
33319
- tabs,
33320
- activeKey: activeTab
33321
- }, undefined, false, undefined, this),
33322
- tab2?.hint && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33323
- marginTop: 1,
33324
- justifyContent: "center",
33325
- children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33326
- color: color.muted,
33327
- children: tab2.hint
33328
- }, undefined, false, undefined, this)
33329
- }, undefined, false, undefined, this),
33330
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33331
- marginTop: 1,
33332
- flexGrow: 1,
33333
- children: [
33334
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(RowList, {
33335
- rows: tabRows,
33336
- cursor: clampedCursor,
33337
- width: Math.floor(panelWidth * 0.36)
33338
- }, undefined, false, undefined, this),
33339
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33340
- width: 2
33341
- }, undefined, false, undefined, this),
33342
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Detail, {
33343
- row: current
33409
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33410
+ height: topPad
33411
+ }, undefined, false, undefined, this),
33412
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33413
+ justifyContent: "center",
33414
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(TitledPanel, {
33415
+ title: "Settings",
33416
+ width: panelWidth,
33417
+ paddingY: 1,
33418
+ focused: true,
33419
+ height: innerHeight,
33420
+ children: [
33421
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(TabStrip, {
33422
+ tabs,
33423
+ activeKey: activeTab
33424
+ }, undefined, false, undefined, this),
33425
+ notice && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33426
+ marginTop: 1,
33427
+ justifyContent: "center",
33428
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33429
+ color: color.success,
33430
+ children: `✓ ${notice}`
33344
33431
  }, undefined, false, undefined, this)
33345
- ]
33346
- }, undefined, true, undefined, this)
33347
- ]
33348
- }, undefined, true, undefined, this),
33432
+ }, undefined, false, undefined, this),
33433
+ tab2?.hint && !notice && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33434
+ marginTop: 1,
33435
+ justifyContent: "center",
33436
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33437
+ color: color.muted,
33438
+ children: tab2.hint
33439
+ }, undefined, false, undefined, this)
33440
+ }, undefined, false, undefined, this),
33441
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33442
+ marginTop: 1,
33443
+ flexGrow: 1,
33444
+ children: [
33445
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(RowList, {
33446
+ rows: tabRows,
33447
+ cursor: clampedCursor,
33448
+ width: sidebarW
33449
+ }, undefined, false, undefined, this),
33450
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33451
+ width: 2
33452
+ }, undefined, false, undefined, this),
33453
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Detail, {
33454
+ row: current,
33455
+ width: detailW
33456
+ }, undefined, false, undefined, this)
33457
+ ]
33458
+ }, undefined, true, undefined, this)
33459
+ ]
33460
+ }, undefined, true, undefined, this)
33461
+ }, undefined, false, undefined, this),
33349
33462
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Footer, {
33350
33463
  mode: "settings",
33351
- version: VERSION2,
33464
+ version: VERSION,
33352
33465
  outerPadX: 1,
33353
33466
  hints: tab2?.hints ?? [["esc", "close"]]
33354
33467
  }, undefined, false, undefined, this)
@@ -33417,19 +33530,22 @@ function RowList({
33417
33530
  wrap: "truncate",
33418
33531
  children: row.label
33419
33532
  }, undefined, false, undefined, this)
33420
- }, undefined, false, undefined, this),
33421
- row.value && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33422
- color: row.muted ? color.muted : labelColor ?? color.muted,
33423
- dimColor: row.muted,
33424
- wrap: "truncate",
33425
- children: row.value
33426
33533
  }, undefined, false, undefined, this)
33427
33534
  ]
33428
33535
  }, row.key, true, undefined, this);
33429
33536
  })
33430
33537
  }, undefined, false, undefined, this);
33431
33538
  }
33432
- function Detail({ row }) {
33539
+ function Detail({
33540
+ row,
33541
+ width
33542
+ }) {
33543
+ const sepW = Math.max(0, width - 4);
33544
+ const sep = /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33545
+ color: color.muted,
33546
+ dimColor: true,
33547
+ children: "─".repeat(sepW)
33548
+ }, undefined, false, undefined, this);
33433
33549
  if (!row) {
33434
33550
  return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33435
33551
  flexGrow: 1,
@@ -33442,6 +33558,7 @@ function Detail({ row }) {
33442
33558
  }, undefined, false, undefined, this)
33443
33559
  }, undefined, false, undefined, this);
33444
33560
  }
33561
+ const hasAction = Boolean(row.onEnter || row.secondary);
33445
33562
  return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33446
33563
  flexDirection: "column",
33447
33564
  flexGrow: 1,
@@ -33468,10 +33585,7 @@ function Detail({ row }) {
33468
33585
  }, undefined, true, undefined, this),
33469
33586
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33470
33587
  marginTop: 1,
33471
- children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33472
- color: color.muted,
33473
- children: "─".repeat(60)
33474
- }, undefined, false, undefined, this)
33588
+ children: sep
33475
33589
  }, undefined, false, undefined, this),
33476
33590
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33477
33591
  marginTop: 1,
@@ -33480,40 +33594,55 @@ function Detail({ row }) {
33480
33594
  children: typeof row.detail === "string" || !row.detail ? row.detail ?? "" : row.detail
33481
33595
  }, undefined, false, undefined, this)
33482
33596
  }, undefined, false, undefined, this),
33597
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33598
+ flexGrow: 1
33599
+ }, undefined, false, undefined, this),
33600
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33601
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33602
+ color: color.muted,
33603
+ dimColor: true,
33604
+ children: "─ tips ".padEnd(sepW, "─")
33605
+ }, undefined, false, undefined, this)
33606
+ }, undefined, false, undefined, this),
33483
33607
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
33484
33608
  marginTop: 1,
33485
- children: [
33486
- row.onEnter && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33487
- color: color.muted,
33488
- children: [
33489
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33490
- color: color.accent,
33491
- bold: true,
33492
- children: "⏎"
33493
- }, undefined, false, undefined, this),
33494
- " ",
33495
- "to activate"
33496
- ]
33497
- }, undefined, true, undefined, this),
33498
- row.onEnter && row.secondary && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33499
- color: color.muted,
33500
- children: " · "
33501
- }, undefined, false, undefined, this),
33502
- row.secondary && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33503
- color: color.muted,
33504
- children: [
33505
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33506
- color: color.accent2,
33507
- bold: true,
33508
- children: row.secondary.key
33509
- }, undefined, false, undefined, this),
33510
- " ",
33511
- "to ",
33512
- row.secondary.label
33513
- ]
33514
- }, undefined, true, undefined, this)
33515
- ]
33516
- }, undefined, true, undefined, this)
33609
+ children: hasAction ? /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(jsx_dev_runtime10.Fragment, {
33610
+ children: [
33611
+ row.onEnter && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33612
+ color: color.muted,
33613
+ children: [
33614
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33615
+ color: color.accent,
33616
+ bold: true,
33617
+ children: "⏎"
33618
+ }, undefined, false, undefined, this),
33619
+ " ",
33620
+ row.enterLabel ?? "open"
33621
+ ]
33622
+ }, undefined, true, undefined, this),
33623
+ row.onEnter && row.secondary && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33624
+ color: color.muted,
33625
+ children: " · "
33626
+ }, undefined, false, undefined, this),
33627
+ row.secondary && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33628
+ color: color.muted,
33629
+ children: [
33630
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33631
+ color: color.accent2,
33632
+ bold: true,
33633
+ children: row.secondary.key
33634
+ }, undefined, false, undefined, this),
33635
+ " ",
33636
+ row.secondary.label
33637
+ ]
33638
+ }, undefined, true, undefined, this)
33639
+ ]
33640
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
33641
+ color: color.muted,
33642
+ dimColor: true,
33643
+ children: "view only"
33644
+ }, undefined, false, undefined, this)
33645
+ }, undefined, false, undefined, this)
33517
33646
  ]
33518
33647
  }, undefined, true, undefined, this);
33519
33648
  }
@@ -33522,6 +33651,7 @@ var init_SettingsView = __esm(async () => {
33522
33651
  init_theme15();
33523
33652
  await __promiseAll([
33524
33653
  init_build2(),
33654
+ init_hooks(),
33525
33655
  init_Footer(),
33526
33656
  init_TitledPanel()
33527
33657
  ]);
@@ -33664,7 +33794,7 @@ function ConfirmDialog({
33664
33794
  }, undefined, false, undefined, this),
33665
33795
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Footer, {
33666
33796
  mode: footerMode,
33667
- version: VERSION2,
33797
+ version: VERSION,
33668
33798
  outerPadX: 1,
33669
33799
  hints: [
33670
33800
  [confirmKey, confirmLabel.toLowerCase()],
@@ -33793,7 +33923,7 @@ function ProjectEditorView({
33793
33923
  }, undefined, false, undefined, this),
33794
33924
  /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Footer, {
33795
33925
  mode: "new project",
33796
- version: VERSION2,
33926
+ version: VERSION,
33797
33927
  outerPadX: 1,
33798
33928
  hints: [
33799
33929
  ["⇥", "field"],
@@ -33946,7 +34076,7 @@ function ProjectManageView({
33946
34076
  }, undefined, false, undefined, this),
33947
34077
  /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Footer, {
33948
34078
  mode: "manage",
33949
- version: VERSION2,
34079
+ version: VERSION,
33950
34080
  outerPadX: 1,
33951
34081
  hints: [["esc", "back"]]
33952
34082
  }, undefined, false, undefined, this)
@@ -34039,7 +34169,7 @@ function ProjectManageView({
34039
34169
  }, undefined, false, undefined, this),
34040
34170
  /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Footer, {
34041
34171
  mode: "manage",
34042
- version: VERSION2,
34172
+ version: VERSION,
34043
34173
  outerPadX: 1,
34044
34174
  hints: isOwner ? [
34045
34175
  ["i", "invite"],
@@ -34201,7 +34331,7 @@ function InvitePickerModal({
34201
34331
  }, undefined, false, undefined, this),
34202
34332
  /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Footer, {
34203
34333
  mode: "invite",
34204
- version: VERSION2,
34334
+ version: VERSION,
34205
34335
  outerPadX: 1,
34206
34336
  hints: [
34207
34337
  ["e", "editor"],
@@ -34292,7 +34422,7 @@ function CodeRevealModal({
34292
34422
  }, undefined, false, undefined, this),
34293
34423
  /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Footer, {
34294
34424
  mode: "invite",
34295
- version: VERSION2,
34425
+ version: VERSION,
34296
34426
  outerPadX: 1,
34297
34427
  hints: [["any", "close"]]
34298
34428
  }, undefined, false, undefined, this)
@@ -38352,13 +38482,13 @@ https://github.com/highlightjs/highlight.js/issues/2277`);
38352
38482
  return origin.returnEnd ? 0 : lexeme.length;
38353
38483
  }
38354
38484
  function processContinuations() {
38355
- const list3 = [];
38485
+ const list4 = [];
38356
38486
  for (let current = top;current !== language; current = current.parent) {
38357
38487
  if (current.className) {
38358
- list3.unshift(current.className);
38488
+ list4.unshift(current.className);
38359
38489
  }
38360
38490
  }
38361
- list3.forEach((item) => emitter.openNode(item));
38491
+ list4.forEach((item) => emitter.openNode(item));
38362
38492
  }
38363
38493
  let lastMatch = {};
38364
38494
  function processLexeme(textBeforeMatch, match) {
@@ -42716,7 +42846,7 @@ var require_coffeescript = __commonJS((exports, module) => {
42716
42846
  "function",
42717
42847
  "static"
42718
42848
  ];
42719
- const excluding = (list3) => (kw) => !list3.includes(kw);
42849
+ const excluding = (list4) => (kw) => !list4.includes(kw);
42720
42850
  const KEYWORDS$1 = {
42721
42851
  keyword: KEYWORDS.concat(COFFEE_KEYWORDS).filter(excluding(NOT_VALID_KEYWORDS)),
42722
42852
  literal: LITERALS.concat(COFFEE_LITERALS),
@@ -65970,10 +66100,10 @@ var require_sql = __commonJS((exports, module) => {
65970
66100
  built_in: FUNCTIONS
65971
66101
  }
65972
66102
  };
65973
- function reduceRelevancy(list3, { exceptions, when } = {}) {
66103
+ function reduceRelevancy(list4, { exceptions, when } = {}) {
65974
66104
  const qualifyFn = when;
65975
66105
  exceptions = exceptions || [];
65976
- return list3.map((item) => {
66106
+ return list4.map((item) => {
65977
66107
  if (item.match(/\|\d+$/) || exceptions.includes(item)) {
65978
66108
  return item;
65979
66109
  } else if (qualifyFn(item)) {
@@ -90303,8 +90433,8 @@ function markedTerminal(options, highlightOptions) {
90303
90433
  }, { renderer: {}, useNewRenderer: true });
90304
90434
  }
90305
90435
  function reflowText(text, width, gfm) {
90306
- var splitRe = gfm ? HARD_RETURN_GFM_RE : HARD_RETURN_RE, sections2 = text.split(splitRe), reflowed = [];
90307
- sections2.forEach(function(section) {
90436
+ var splitRe = gfm ? HARD_RETURN_GFM_RE : HARD_RETURN_RE, sections = text.split(splitRe), reflowed = [];
90437
+ sections.forEach(function(section) {
90308
90438
  var fragments = section.split(/(\u001b\[(?:\d{1,3})(?:;\d{1,3})*m)/g);
90309
90439
  var column = 0;
90310
90440
  var currentLine = "";
@@ -90421,7 +90551,7 @@ function numberedLines(lines, indent) {
90421
90551
  }).join(`
90422
90552
  `);
90423
90553
  }
90424
- function list3(body, ordered, indent) {
90554
+ function list4(body, ordered, indent) {
90425
90555
  body = body.trim();
90426
90556
  body = ordered ? numberedLines(body, indent) : bulletPointLines(body, indent);
90427
90557
  return body;
@@ -90531,7 +90661,7 @@ var init_marked_terminal = __esm(() => {
90531
90661
  firstHeading: source_default.magenta.underline.bold,
90532
90662
  hr: source_default.reset,
90533
90663
  listitem: source_default.reset,
90534
- list: list3,
90664
+ list: list4,
90535
90665
  table: source_default.reset,
90536
90666
  paragraph: source_default.reset,
90537
90667
  strong: source_default.bold,
@@ -90830,11 +90960,10 @@ function TodoDetailView({
90830
90960
  onEdit,
90831
90961
  onDelete
90832
90962
  }) {
90833
- const { stdout } = use_stdout_default();
90834
- const cols = Math.max(80, stdout?.columns ?? 100);
90835
- const rows = Math.max(20, stdout?.rows ?? 30);
90836
- const panelWidth = cols - 2;
90837
- const panelHeight = Math.max(15, rows - 4);
90963
+ const { cols, rows } = useTerminalSize();
90964
+ const panelWidth = Math.min(96, Math.max(60, cols - 4));
90965
+ const panelHeight = Math.min(28, Math.max(16, rows - 6));
90966
+ const topPad = Math.max(1, Math.floor((rows - panelHeight - 3) / 2));
90838
90967
  use_input_default((input, key) => {
90839
90968
  if (key.escape)
90840
90969
  return onClose();
@@ -90852,141 +90981,147 @@ function TodoDetailView({
90852
90981
  flexDirection: "column",
90853
90982
  paddingX: 1,
90854
90983
  children: [
90855
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(TitledPanel, {
90856
- title: "Todo",
90857
- width: panelWidth,
90858
- height: panelHeight,
90859
- paddingX: 2,
90860
- paddingY: 1,
90861
- focused: true,
90862
- children: [
90863
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90864
- children: [
90865
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90866
- borderStyle: "round",
90867
- borderColor: statusColor,
90868
- paddingX: 2,
90869
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90870
- color: statusColor,
90871
- bold: true,
90872
- children: [
90873
- statusIcon,
90874
- " ",
90875
- statusLabel
90876
- ]
90877
- }, undefined, true, undefined, this)
90878
- }, undefined, false, undefined, this),
90879
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90880
- flexGrow: 1
90881
- }, undefined, false, undefined, this),
90882
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90883
- alignItems: "center",
90884
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90885
- color: color.muted,
90886
- children: todo2.id.toLowerCase()
90984
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90985
+ height: topPad
90986
+ }, undefined, false, undefined, this),
90987
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90988
+ justifyContent: "center",
90989
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(TitledPanel, {
90990
+ title: "Todo",
90991
+ width: panelWidth,
90992
+ height: panelHeight,
90993
+ paddingX: 2,
90994
+ paddingY: 1,
90995
+ focused: true,
90996
+ children: [
90997
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90998
+ children: [
90999
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
91000
+ borderStyle: "round",
91001
+ borderColor: statusColor,
91002
+ paddingX: 2,
91003
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91004
+ color: statusColor,
91005
+ bold: true,
91006
+ children: [
91007
+ statusIcon,
91008
+ " ",
91009
+ statusLabel
91010
+ ]
91011
+ }, undefined, true, undefined, this)
91012
+ }, undefined, false, undefined, this),
91013
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
91014
+ flexGrow: 1
91015
+ }, undefined, false, undefined, this),
91016
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
91017
+ alignItems: "center",
91018
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91019
+ color: color.muted,
91020
+ children: todo2.id.toLowerCase()
91021
+ }, undefined, false, undefined, this)
90887
91022
  }, undefined, false, undefined, this)
91023
+ ]
91024
+ }, undefined, true, undefined, this),
91025
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
91026
+ marginTop: 1,
91027
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91028
+ color: color.accent,
91029
+ bold: true,
91030
+ wrap: "truncate",
91031
+ children: todo2.title
90888
91032
  }, undefined, false, undefined, this)
90889
- ]
90890
- }, undefined, true, undefined, this),
90891
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90892
- marginTop: 1,
90893
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90894
- color: color.accent,
90895
- bold: true,
90896
- wrap: "truncate",
90897
- children: todo2.title
90898
- }, undefined, false, undefined, this)
90899
- }, undefined, false, undefined, this),
90900
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90901
- marginTop: 1,
90902
- children: [
90903
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90904
- flexDirection: "column",
90905
- width: Math.floor((panelWidth - 8) / 2),
90906
- children: [
90907
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MetaRow, {
90908
- label: "Project",
90909
- children: project2 ? /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90910
- children: [
90911
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90912
- color: swatchColor(project2.color),
90913
- children: "● "
90914
- }, undefined, false, undefined, this),
90915
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90916
- children: project2.name
90917
- }, undefined, false, undefined, this)
90918
- ]
90919
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90920
- color: color.muted,
90921
- dimColor: true,
90922
- children: "● inbox"
91033
+ }, undefined, false, undefined, this),
91034
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
91035
+ marginTop: 1,
91036
+ children: [
91037
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
91038
+ flexDirection: "column",
91039
+ width: Math.floor((panelWidth - 8) / 2),
91040
+ children: [
91041
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MetaRow, {
91042
+ label: "Project",
91043
+ children: project2 ? /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91044
+ children: [
91045
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91046
+ color: swatchColor(project2.color),
91047
+ children: "● "
91048
+ }, undefined, false, undefined, this),
91049
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91050
+ children: project2.name
91051
+ }, undefined, false, undefined, this)
91052
+ ]
91053
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91054
+ color: color.muted,
91055
+ dimColor: true,
91056
+ children: "● inbox"
91057
+ }, undefined, false, undefined, this)
91058
+ }, undefined, false, undefined, this),
91059
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MetaRow, {
91060
+ label: "Created by",
91061
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91062
+ color: color.accent2,
91063
+ children: ownerName ?? todo2.createdBy.toLowerCase()
91064
+ }, undefined, false, undefined, this)
90923
91065
  }, undefined, false, undefined, this)
90924
- }, undefined, false, undefined, this),
90925
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MetaRow, {
90926
- label: "Created by",
90927
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90928
- color: color.accent2,
90929
- children: ownerName ?? todo2.createdBy.toLowerCase()
91066
+ ]
91067
+ }, undefined, true, undefined, this),
91068
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
91069
+ flexDirection: "column",
91070
+ flexGrow: 1,
91071
+ children: [
91072
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MetaRow, {
91073
+ label: "Created",
91074
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91075
+ children: [
91076
+ relativeTime(nowMs, todo2.createdAt),
91077
+ " ago"
91078
+ ]
91079
+ }, undefined, true, undefined, this)
91080
+ }, undefined, false, undefined, this),
91081
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MetaRow, {
91082
+ label: "Updated",
91083
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91084
+ children: [
91085
+ relativeTime(nowMs, todo2.updatedAt),
91086
+ " ago"
91087
+ ]
91088
+ }, undefined, true, undefined, this)
90930
91089
  }, undefined, false, undefined, this)
90931
- }, undefined, false, undefined, this)
90932
- ]
90933
- }, undefined, true, undefined, this),
90934
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90935
- flexDirection: "column",
90936
- flexGrow: 1,
90937
- children: [
90938
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MetaRow, {
90939
- label: "Created",
90940
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90941
- children: [
90942
- relativeTime(nowMs, todo2.createdAt),
90943
- " ago"
90944
- ]
90945
- }, undefined, true, undefined, this)
90946
- }, undefined, false, undefined, this),
90947
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MetaRow, {
90948
- label: "Updated",
90949
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90950
- children: [
90951
- relativeTime(nowMs, todo2.updatedAt),
90952
- " ago"
90953
- ]
90954
- }, undefined, true, undefined, this)
90955
- }, undefined, false, undefined, this)
90956
- ]
90957
- }, undefined, true, undefined, this)
90958
- ]
90959
- }, undefined, true, undefined, this),
90960
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90961
- marginTop: 1,
90962
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90963
- color: color.muted,
90964
- children: "─".repeat(Math.max(10, panelWidth - 6))
90965
- }, undefined, false, undefined, this)
90966
- }, undefined, false, undefined, this),
90967
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90968
- marginTop: 1,
90969
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
90970
- bold: true,
90971
- color: color.muted,
90972
- children: "DESCRIPTION"
90973
- }, undefined, false, undefined, this)
90974
- }, undefined, false, undefined, this),
90975
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
90976
- marginTop: 1,
90977
- flexDirection: "column",
90978
- children: todo2.description ? /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Markdown, {
90979
- source: todo2.description,
90980
- width: panelWidth - 6
90981
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MarkdownEmpty, {
90982
- hint: "no description — press 'e' to add one"
91090
+ ]
91091
+ }, undefined, true, undefined, this)
91092
+ ]
91093
+ }, undefined, true, undefined, this),
91094
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
91095
+ marginTop: 1,
91096
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91097
+ color: color.muted,
91098
+ children: "".repeat(Math.max(10, panelWidth - 6))
91099
+ }, undefined, false, undefined, this)
91100
+ }, undefined, false, undefined, this),
91101
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
91102
+ marginTop: 1,
91103
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
91104
+ bold: true,
91105
+ color: color.muted,
91106
+ children: "DESCRIPTION"
91107
+ }, undefined, false, undefined, this)
91108
+ }, undefined, false, undefined, this),
91109
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
91110
+ marginTop: 1,
91111
+ flexDirection: "column",
91112
+ children: todo2.description ? /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Markdown, {
91113
+ source: todo2.description,
91114
+ width: panelWidth - 6
91115
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(MarkdownEmpty, {
91116
+ hint: "no description press 'e' to add one"
91117
+ }, undefined, false, undefined, this)
90983
91118
  }, undefined, false, undefined, this)
90984
- }, undefined, false, undefined, this)
90985
- ]
90986
- }, undefined, true, undefined, this),
91119
+ ]
91120
+ }, undefined, true, undefined, this)
91121
+ }, undefined, false, undefined, this),
90987
91122
  /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Footer, {
90988
91123
  mode: "detail",
90989
- version: VERSION2,
91124
+ version: VERSION,
90990
91125
  outerPadX: 1,
90991
91126
  hints: [
90992
91127
  ["␣", "toggle"],
@@ -91020,6 +91155,7 @@ var init_TodoDetailView = __esm(async () => {
91020
91155
  init_theme15();
91021
91156
  await __promiseAll([
91022
91157
  init_build2(),
91158
+ init_hooks(),
91023
91159
  init_Footer(),
91024
91160
  init_Markdown(),
91025
91161
  init_TitledPanel()
@@ -91027,6 +91163,240 @@ var init_TodoDetailView = __esm(async () => {
91027
91163
  jsx_dev_runtime16 = __toESM(require_jsx_dev_runtime(), 1);
91028
91164
  });
91029
91165
 
91166
+ // apps/cli/src/tui/components/primitives/MultilineInput.tsx
91167
+ function MultilineInput({
91168
+ value,
91169
+ onChange,
91170
+ placeholder,
91171
+ isDisabled = false,
91172
+ width,
91173
+ maxRows = 8
91174
+ }) {
91175
+ const lines = import_react70.useMemo(() => value.split(`
91176
+ `), [value]);
91177
+ const [cursor4, setCursor] = import_react70.useState(() => ({
91178
+ row: Math.max(0, lines.length - 1),
91179
+ col: lines[lines.length - 1]?.length ?? 0
91180
+ }));
91181
+ const desiredCol = import_react70.useRef(cursor4.col);
91182
+ const activeLineLen = lines[cursor4.row]?.length ?? 0;
91183
+ import_react70.useEffect(() => {
91184
+ setCursor((prev) => clampCursor(prev, lines));
91185
+ }, [lines.length, activeLineLen]);
91186
+ use_input_default((input, key) => {
91187
+ if (isDisabled)
91188
+ return;
91189
+ if (key.return) {
91190
+ const { next: next2, cursor: nextCursor2 } = insertText(lines, cursor4, `
91191
+ `);
91192
+ commit(next2, nextCursor2);
91193
+ return;
91194
+ }
91195
+ if (key.tab || key.escape)
91196
+ return;
91197
+ if (key.ctrl && input === "s")
91198
+ return;
91199
+ if (key.leftArrow) {
91200
+ const moved = moveLeft(cursor4, lines);
91201
+ desiredCol.current = moved.col;
91202
+ setCursor(moved);
91203
+ return;
91204
+ }
91205
+ if (key.rightArrow) {
91206
+ const moved = moveRight(cursor4, lines);
91207
+ desiredCol.current = moved.col;
91208
+ setCursor(moved);
91209
+ return;
91210
+ }
91211
+ if (key.upArrow) {
91212
+ setCursor(moveVertical(cursor4, lines, -1, desiredCol.current));
91213
+ return;
91214
+ }
91215
+ if (key.downArrow) {
91216
+ setCursor(moveVertical(cursor4, lines, 1, desiredCol.current));
91217
+ return;
91218
+ }
91219
+ if (key.home || key.ctrl && input === "a") {
91220
+ const next2 = { row: cursor4.row, col: 0 };
91221
+ desiredCol.current = 0;
91222
+ setCursor(next2);
91223
+ return;
91224
+ }
91225
+ if (key.end || key.ctrl && input === "e") {
91226
+ const col = lines[cursor4.row]?.length ?? 0;
91227
+ const next2 = { row: cursor4.row, col };
91228
+ desiredCol.current = col;
91229
+ setCursor(next2);
91230
+ return;
91231
+ }
91232
+ if (key.backspace || key.delete) {
91233
+ if (cursor4.row === 0 && cursor4.col === 0)
91234
+ return;
91235
+ const { next: next2, cursor: nextCursor2 } = deleteBackward(lines, cursor4);
91236
+ commit(next2, nextCursor2);
91237
+ return;
91238
+ }
91239
+ if (!input || key.ctrl || key.meta)
91240
+ return;
91241
+ const sanitized = input.replace(/\r/g, "");
91242
+ const { next, cursor: nextCursor } = insertText(lines, cursor4, sanitized);
91243
+ commit(next, nextCursor);
91244
+ }, { isActive: !isDisabled });
91245
+ function commit(nextLines, nextCursor) {
91246
+ onChange(nextLines.join(`
91247
+ `));
91248
+ setCursor(nextCursor);
91249
+ desiredCol.current = nextCursor.col;
91250
+ }
91251
+ const safeWidth = Math.max(1, width);
91252
+ const start = clampStart(cursor4.row, lines.length, maxRows);
91253
+ const visible = lines.slice(start, start + maxRows);
91254
+ if (isDisabled) {
91255
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
91256
+ flexDirection: "column",
91257
+ width: safeWidth,
91258
+ children: (value === "" ? [" "] : lines).map((line, idx) => /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91259
+ wrap: "wrap",
91260
+ children: line || " "
91261
+ }, idx, false, undefined, this))
91262
+ }, undefined, false, undefined, this);
91263
+ }
91264
+ const showPlaceholder = value === "" && placeholder !== undefined && placeholder.length > 0;
91265
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
91266
+ flexDirection: "column",
91267
+ width: safeWidth,
91268
+ children: showPlaceholder ? /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91269
+ children: [
91270
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91271
+ inverse: true,
91272
+ children: " "
91273
+ }, undefined, false, undefined, this),
91274
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91275
+ color: color.muted,
91276
+ dimColor: true,
91277
+ children: placeholder
91278
+ }, undefined, false, undefined, this)
91279
+ ]
91280
+ }, undefined, true, undefined, this) : visible.map((line, idx) => {
91281
+ const absoluteRow = start + idx;
91282
+ if (absoluteRow !== cursor4.row) {
91283
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91284
+ wrap: "wrap",
91285
+ children: line || " "
91286
+ }, absoluteRow, false, undefined, this);
91287
+ }
91288
+ const col = cursor4.col;
91289
+ const before2 = line.slice(0, col);
91290
+ const at2 = line[col] ?? " ";
91291
+ const after2 = line.slice(col + 1);
91292
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91293
+ wrap: "wrap",
91294
+ children: [
91295
+ before2,
91296
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91297
+ inverse: true,
91298
+ children: at2
91299
+ }, undefined, false, undefined, this),
91300
+ after2
91301
+ ]
91302
+ }, absoluteRow, true, undefined, this);
91303
+ })
91304
+ }, undefined, false, undefined, this);
91305
+ }
91306
+ function clampCursor(cursor4, lines) {
91307
+ const row = Math.max(0, Math.min(cursor4.row, lines.length - 1));
91308
+ const col = Math.max(0, Math.min(cursor4.col, lines[row]?.length ?? 0));
91309
+ if (row === cursor4.row && col === cursor4.col)
91310
+ return cursor4;
91311
+ return { row, col };
91312
+ }
91313
+ function clampStart(row, total, maxRows) {
91314
+ if (total <= maxRows)
91315
+ return 0;
91316
+ const maxStart = total - maxRows;
91317
+ return Math.max(0, Math.min(row - (maxRows - 1), maxStart));
91318
+ }
91319
+ function moveLeft(cursor4, lines) {
91320
+ if (cursor4.col > 0)
91321
+ return { row: cursor4.row, col: cursor4.col - 1 };
91322
+ if (cursor4.row > 0) {
91323
+ const prevLen = lines[cursor4.row - 1]?.length ?? 0;
91324
+ return { row: cursor4.row - 1, col: prevLen };
91325
+ }
91326
+ return cursor4;
91327
+ }
91328
+ function moveRight(cursor4, lines) {
91329
+ const len = lines[cursor4.row]?.length ?? 0;
91330
+ if (cursor4.col < len)
91331
+ return { row: cursor4.row, col: cursor4.col + 1 };
91332
+ if (cursor4.row < lines.length - 1)
91333
+ return { row: cursor4.row + 1, col: 0 };
91334
+ return cursor4;
91335
+ }
91336
+ function moveVertical(cursor4, lines, direction, desiredCol) {
91337
+ const nextRow = cursor4.row + direction;
91338
+ if (nextRow < 0 || nextRow >= lines.length)
91339
+ return cursor4;
91340
+ const target = lines[nextRow]?.length ?? 0;
91341
+ return { row: nextRow, col: Math.min(desiredCol, target) };
91342
+ }
91343
+ function insertText(lines, cursor4, text) {
91344
+ const segments = text.split(`
91345
+ `);
91346
+ const current = lines[cursor4.row] ?? "";
91347
+ const before2 = current.slice(0, cursor4.col);
91348
+ const after2 = current.slice(cursor4.col);
91349
+ if (segments.length === 1) {
91350
+ const nextLine = before2 + segments[0] + after2;
91351
+ const next2 = lines.slice();
91352
+ next2[cursor4.row] = nextLine;
91353
+ return {
91354
+ next: next2,
91355
+ cursor: { row: cursor4.row, col: before2.length + segments[0].length }
91356
+ };
91357
+ }
91358
+ const first = before2 + segments[0];
91359
+ const last2 = segments[segments.length - 1] + after2;
91360
+ const middle = segments.slice(1, -1);
91361
+ const next = [
91362
+ ...lines.slice(0, cursor4.row),
91363
+ first,
91364
+ ...middle,
91365
+ last2,
91366
+ ...lines.slice(cursor4.row + 1)
91367
+ ];
91368
+ const newRow = cursor4.row + segments.length - 1;
91369
+ return {
91370
+ next,
91371
+ cursor: { row: newRow, col: segments[segments.length - 1].length }
91372
+ };
91373
+ }
91374
+ function deleteBackward(lines, cursor4) {
91375
+ if (cursor4.col > 0) {
91376
+ const current2 = lines[cursor4.row] ?? "";
91377
+ const nextLine = current2.slice(0, cursor4.col - 1) + current2.slice(cursor4.col);
91378
+ const next2 = lines.slice();
91379
+ next2[cursor4.row] = nextLine;
91380
+ return { next: next2, cursor: { row: cursor4.row, col: cursor4.col - 1 } };
91381
+ }
91382
+ const prev = lines[cursor4.row - 1] ?? "";
91383
+ const current = lines[cursor4.row] ?? "";
91384
+ const merged = prev + current;
91385
+ const next = [
91386
+ ...lines.slice(0, cursor4.row - 1),
91387
+ merged,
91388
+ ...lines.slice(cursor4.row + 1)
91389
+ ];
91390
+ return { next, cursor: { row: cursor4.row - 1, col: prev.length } };
91391
+ }
91392
+ var import_react70, jsx_dev_runtime17;
91393
+ var init_MultilineInput = __esm(async () => {
91394
+ init_theme15();
91395
+ await init_build2();
91396
+ import_react70 = __toESM(require_react(), 1);
91397
+ jsx_dev_runtime17 = __toESM(require_jsx_dev_runtime(), 1);
91398
+ });
91399
+
91030
91400
  // apps/cli/src/tui/components/views/todo/TodoEditorView.tsx
91031
91401
  function TodoEditorView({
91032
91402
  mode,
@@ -91038,88 +91408,93 @@ function TodoEditorView({
91038
91408
  const { stdout } = use_stdout_default();
91039
91409
  const cols = Math.max(60, stdout?.columns ?? 100);
91040
91410
  const rows = Math.max(15, stdout?.rows ?? 30);
91041
- const [title, setTitle] = import_react70.useState(defaultTitle ?? "");
91042
- const [description, setDescription] = import_react70.useState(defaultDescription ?? "");
91411
+ const [title, setTitle] = import_react71.useState(defaultTitle ?? "");
91412
+ const [description, setDescription] = import_react71.useState(defaultDescription ?? "");
91043
91413
  const { focus } = use_focus_manager_default();
91044
- use_input_default((_input, key) => {
91045
- if (key.escape)
91414
+ use_input_default((input, key) => {
91415
+ if (key.escape) {
91046
91416
  onCancel();
91417
+ return;
91418
+ }
91419
+ if (key.ctrl && input === "s") {
91420
+ const trimmed = title.trim();
91421
+ if (trimmed === "") {
91422
+ onCancel();
91423
+ return;
91424
+ }
91425
+ onSubmit({ title: trimmed, description });
91426
+ }
91047
91427
  });
91048
91428
  const headerTitle = mode === "add" ? "New todo" : "Edit todo";
91049
- const subtitle = mode === "add" ? "Capture a new task. Blank title + Enter cancels." : "Update the title and optional markdown description.";
91429
+ const subtitle = mode === "add" ? "Capture a new task. Ctrl+S to save · Esc cancels." : "Update the title and optional markdown description.";
91050
91430
  const cardWidth = Math.min(80, cols - 8);
91051
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
91431
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91052
91432
  flexDirection: "column",
91053
91433
  paddingX: 1,
91054
91434
  height: rows - 1,
91055
91435
  children: [
91056
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
91436
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91057
91437
  flexGrow: 1,
91058
91438
  flexDirection: "column",
91059
91439
  alignItems: "center",
91060
91440
  justifyContent: "center",
91061
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(TitledPanel, {
91441
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(TitledPanel, {
91062
91442
  title: headerTitle,
91063
91443
  width: cardWidth,
91064
91444
  paddingX: 2,
91065
91445
  paddingY: 1,
91066
91446
  focused: true,
91067
91447
  children: [
91068
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91448
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91069
91449
  color: color.muted,
91070
91450
  children: subtitle
91071
91451
  }, undefined, false, undefined, this),
91072
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Field, {
91452
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Field, {
91073
91453
  id: TITLE_FIELD,
91074
91454
  label: "title",
91075
91455
  autoFocus: true,
91076
91456
  value: title,
91077
- children: (focused) => /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(TextInput, {
91457
+ children: (focused) => /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(TextInput, {
91078
91458
  isDisabled: !focused,
91079
91459
  defaultValue: defaultTitle,
91080
91460
  placeholder: mode === "add" ? "new todo title…" : undefined,
91081
91461
  onChange: setTitle,
91082
91462
  onSubmit: (value) => {
91083
91463
  setTitle(value);
91084
- if (value.trim() === "" && description === "") {
91085
- onSubmit({ title: "", description: "" });
91086
- } else {
91087
- focus(DESC_FIELD);
91088
- }
91464
+ focus(DESC_FIELD);
91089
91465
  }
91090
91466
  }, undefined, false, undefined, this)
91091
91467
  }, undefined, false, undefined, this),
91092
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Field, {
91468
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Field, {
91093
91469
  id: DESC_FIELD,
91094
91470
  label: "description",
91095
91471
  value: description,
91096
- children: (focused) => /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(TextInput, {
91472
+ children: (focused) => /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(MultilineInput, {
91097
91473
  isDisabled: !focused,
91098
- defaultValue: defaultDescription,
91099
- placeholder: "optional · markdown (**bold**, `code`, [link](url), #, -, ```)",
91474
+ value: description,
91100
91475
  onChange: setDescription,
91101
- onSubmit: (value) => {
91102
- onSubmit({ title: title.trim(), description: value });
91103
- }
91476
+ placeholder: "optional · markdown (**bold**, `code`, [link](url), #, -, ```)",
91477
+ width: cardWidth - 6,
91478
+ maxRows: 8
91104
91479
  }, undefined, false, undefined, this)
91105
91480
  }, undefined, false, undefined, this),
91106
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
91481
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91107
91482
  marginTop: 1,
91108
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91483
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91109
91484
  color: color.muted,
91110
- children: "Tab to switch · Enter on description to save · Esc to cancel"
91485
+ children: "Tab to switch fields · Enter inserts newline · Ctrl+S to save · Esc cancels"
91111
91486
  }, undefined, false, undefined, this)
91112
91487
  }, undefined, false, undefined, this)
91113
91488
  ]
91114
91489
  }, undefined, true, undefined, this)
91115
91490
  }, undefined, false, undefined, this),
91116
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Footer, {
91491
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Footer, {
91117
91492
  mode: mode === "add" ? "insert" : "edit",
91118
- version: VERSION2,
91493
+ version: VERSION,
91119
91494
  outerPadX: 1,
91120
91495
  hints: [
91121
91496
  ["tab", "next field"],
91122
- ["", "save"],
91497
+ ["^S", "save"],
91123
91498
  ["esc", "cancel"]
91124
91499
  ]
91125
91500
  }, undefined, false, undefined, this)
@@ -91128,45 +91503,46 @@ function TodoEditorView({
91128
91503
  }
91129
91504
  function Field({ id, label, value, autoFocus, children }) {
91130
91505
  const { isFocused } = use_focus_default({ id, autoFocus });
91131
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
91506
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91132
91507
  marginTop: 1,
91133
91508
  flexDirection: "column",
91134
91509
  children: [
91135
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
91510
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91136
91511
  children: [
91137
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
91512
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91138
91513
  width: 14,
91139
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91514
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91140
91515
  color: isFocused ? color.accent : color.muted,
91141
91516
  bold: isFocused,
91142
91517
  children: `${isFocused ? icon.chevron : " "} ${label}`
91143
91518
  }, undefined, false, undefined, this)
91144
91519
  }, undefined, false, undefined, this),
91145
- value.length === 0 && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
91520
+ value.length === 0 && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91146
91521
  color: color.muted,
91147
91522
  dimColor: true,
91148
91523
  children: "(empty)"
91149
91524
  }, undefined, false, undefined, this)
91150
91525
  ]
91151
91526
  }, undefined, true, undefined, this),
91152
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
91527
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91153
91528
  marginLeft: 4,
91154
91529
  children: children(isFocused)
91155
91530
  }, undefined, false, undefined, this)
91156
91531
  ]
91157
91532
  }, undefined, true, undefined, this);
91158
91533
  }
91159
- var import_react70, jsx_dev_runtime17, TITLE_FIELD = "todo-editor:title", DESC_FIELD = "todo-editor:description";
91534
+ var import_react71, jsx_dev_runtime18, TITLE_FIELD = "todo-editor:title", DESC_FIELD = "todo-editor:description";
91160
91535
  var init_TodoEditorView = __esm(async () => {
91161
91536
  init_theme15();
91162
91537
  await __promiseAll([
91163
91538
  init_build2(),
91164
91539
  init_build3(),
91165
91540
  init_Footer(),
91541
+ init_MultilineInput(),
91166
91542
  init_TitledPanel()
91167
91543
  ]);
91168
- import_react70 = __toESM(require_react(), 1);
91169
- jsx_dev_runtime17 = __toESM(require_jsx_dev_runtime(), 1);
91544
+ import_react71 = __toESM(require_react(), 1);
91545
+ jsx_dev_runtime18 = __toESM(require_jsx_dev_runtime(), 1);
91170
91546
  });
91171
91547
 
91172
91548
  // apps/cli/src/tui/components/views/todo/TodoInfo.tsx
@@ -91179,11 +91555,11 @@ function TodoInfo({
91179
91555
  panelHeight
91180
91556
  }) {
91181
91557
  if (!todo2) {
91182
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91558
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91183
91559
  flexGrow: 1,
91184
91560
  alignItems: "center",
91185
91561
  justifyContent: "center",
91186
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91562
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91187
91563
  color: color.muted,
91188
91564
  dimColor: true,
91189
91565
  children: "no todo selected"
@@ -91199,12 +91575,12 @@ function TodoInfo({
91199
91575
  const rawDesc = todo2.description?.trim() ?? "";
91200
91576
  const preview = rawDesc && descBudget > 0 ? previewDescription(rawDesc, contentW, descBudget) : null;
91201
91577
  const overflowed = Boolean(preview?.truncated) || rawDesc.length > 0 && descBudget === 0;
91202
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91578
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91203
91579
  flexDirection: "column",
91204
91580
  children: [
91205
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91581
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91206
91582
  children: [
91207
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91583
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91208
91584
  color: statusColor,
91209
91585
  bold: true,
91210
91586
  children: [
@@ -91213,10 +91589,10 @@ function TodoInfo({
91213
91589
  statusLabel
91214
91590
  ]
91215
91591
  }, undefined, true, undefined, this),
91216
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91592
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91217
91593
  flexGrow: 1
91218
91594
  }, undefined, false, undefined, this),
91219
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91595
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91220
91596
  color: color.muted,
91221
91597
  dimColor: true,
91222
91598
  children: [
@@ -91226,56 +91602,56 @@ function TodoInfo({
91226
91602
  }, undefined, true, undefined, this)
91227
91603
  ]
91228
91604
  }, undefined, true, undefined, this),
91229
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91605
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91230
91606
  marginTop: 1,
91231
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91607
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91232
91608
  color: color.accent,
91233
91609
  bold: true,
91234
91610
  wrap: "truncate",
91235
91611
  children: todo2.title
91236
91612
  }, undefined, false, undefined, this)
91237
91613
  }, undefined, false, undefined, this),
91238
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91614
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91239
91615
  flexDirection: "column",
91240
91616
  marginTop: 1,
91241
91617
  children: [
91242
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(InfoRow, {
91618
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(InfoRow, {
91243
91619
  label: "Project",
91244
- children: project2 ? /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91620
+ children: project2 ? /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91245
91621
  children: [
91246
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91622
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91247
91623
  color: swatchColor(project2.color),
91248
91624
  children: "● "
91249
91625
  }, undefined, false, undefined, this),
91250
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91626
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91251
91627
  children: project2.name
91252
91628
  }, undefined, false, undefined, this)
91253
91629
  ]
91254
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91630
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91255
91631
  color: color.muted,
91256
91632
  dimColor: true,
91257
91633
  children: "● inbox"
91258
91634
  }, undefined, false, undefined, this)
91259
91635
  }, undefined, false, undefined, this),
91260
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(InfoRow, {
91636
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(InfoRow, {
91261
91637
  label: "By",
91262
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91638
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91263
91639
  color: color.accent2,
91264
91640
  children: ownerName ?? todo2.createdBy.slice(0, 8).toLowerCase() + "…"
91265
91641
  }, undefined, false, undefined, this)
91266
91642
  }, undefined, false, undefined, this),
91267
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(InfoRow, {
91643
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(InfoRow, {
91268
91644
  label: "Created",
91269
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91645
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91270
91646
  children: [
91271
91647
  relativeTime(nowMs, todo2.createdAt),
91272
91648
  " ago"
91273
91649
  ]
91274
91650
  }, undefined, true, undefined, this)
91275
91651
  }, undefined, false, undefined, this),
91276
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(InfoRow, {
91652
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(InfoRow, {
91277
91653
  label: "Updated",
91278
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91654
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91279
91655
  children: [
91280
91656
  relativeTime(nowMs, todo2.updatedAt),
91281
91657
  " ago"
@@ -91284,30 +91660,30 @@ function TodoInfo({
91284
91660
  }, undefined, false, undefined, this)
91285
91661
  ]
91286
91662
  }, undefined, true, undefined, this),
91287
- preview && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91663
+ preview && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91288
91664
  flexDirection: "column",
91289
91665
  marginTop: 1,
91290
91666
  children: [
91291
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91667
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91292
91668
  color: color.muted,
91293
91669
  dimColor: true,
91294
91670
  children: "─ Description ".padEnd(Math.max(0, contentW), "─")
91295
91671
  }, undefined, false, undefined, this),
91296
- preview.lines.map((line, idx) => /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91672
+ preview.lines.map((line, idx) => /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91297
91673
  color: color.muted,
91298
91674
  children: line || " "
91299
91675
  }, idx, false, undefined, this))
91300
91676
  ]
91301
91677
  }, undefined, true, undefined, this),
91302
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91678
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91303
91679
  marginTop: 1,
91304
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91680
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91305
91681
  color: color.muted,
91306
91682
  dimColor: true,
91307
91683
  children: [
91308
91684
  "press",
91309
91685
  " ",
91310
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91686
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91311
91687
  color: color.accent,
91312
91688
  bold: true,
91313
91689
  children: "⏎"
@@ -91324,11 +91700,11 @@ function InfoRow({
91324
91700
  label,
91325
91701
  children
91326
91702
  }) {
91327
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91703
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91328
91704
  children: [
91329
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
91705
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91330
91706
  width: 9,
91331
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
91707
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91332
91708
  color: color.muted,
91333
91709
  children: label
91334
91710
  }, undefined, false, undefined, this)
@@ -91388,11 +91764,11 @@ function previewDescription(text, width, maxLines) {
91388
91764
  }
91389
91765
  return { lines, truncated: overflowed };
91390
91766
  }
91391
- var jsx_dev_runtime18, FIXED_ROWS = 10, DESC_HEADER = 2, PANEL_CHROME = 4;
91767
+ var jsx_dev_runtime19, FIXED_ROWS = 10, DESC_HEADER = 2, PANEL_CHROME = 4;
91392
91768
  var init_TodoInfo = __esm(async () => {
91393
91769
  init_theme15();
91394
91770
  await init_build2();
91395
- jsx_dev_runtime18 = __toESM(require_jsx_dev_runtime(), 1);
91771
+ jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
91396
91772
  });
91397
91773
 
91398
91774
  // apps/cli/src/tui/components/views/settings/SettingsFormModal.tsx
@@ -91410,13 +91786,13 @@ function SettingsFormModal({
91410
91786
  const cols = Math.max(60, stdout?.columns ?? 100);
91411
91787
  const rows = Math.max(15, stdout?.rows ?? 30);
91412
91788
  const cardWidth = Math.min(72, cols - 8);
91413
- const valuesRef = import_react71.useRef((() => {
91789
+ const valuesRef = import_react72.useRef((() => {
91414
91790
  const init = {};
91415
91791
  for (const f of fields)
91416
91792
  init[f.key] = f.initial ?? "";
91417
91793
  return init;
91418
91794
  })());
91419
- const [field, setField] = import_react71.useState(0);
91795
+ const [field, setField] = import_react72.useState(0);
91420
91796
  const handleFieldSubmit = (idx, value) => {
91421
91797
  if (busy)
91422
91798
  return;
@@ -91435,49 +91811,49 @@ function SettingsFormModal({
91435
91811
  onCancel();
91436
91812
  }
91437
91813
  });
91438
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91814
+ return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
91439
91815
  flexDirection: "column",
91440
91816
  paddingX: 1,
91441
91817
  height: rows - 1,
91442
91818
  children: [
91443
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91819
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
91444
91820
  flexGrow: 1,
91445
91821
  flexDirection: "column",
91446
91822
  alignItems: "center",
91447
91823
  justifyContent: "center",
91448
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(TitledPanel, {
91824
+ children: /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(TitledPanel, {
91449
91825
  title,
91450
91826
  width: cardWidth,
91451
91827
  paddingX: 2,
91452
91828
  paddingY: 1,
91453
91829
  focused: true,
91454
91830
  children: [
91455
- help && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91831
+ help && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
91456
91832
  color: color.muted,
91457
91833
  wrap: "wrap",
91458
91834
  children: help
91459
91835
  }, undefined, false, undefined, this),
91460
- fields.map((f, idx) => /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(FieldRow2, {
91836
+ fields.map((f, idx) => /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(FieldRow2, {
91461
91837
  label: f.label,
91462
91838
  active: field === idx,
91463
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(TextInput, {
91839
+ children: /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(TextInput, {
91464
91840
  isDisabled: field !== idx,
91465
91841
  placeholder: f.placeholder ?? "",
91466
91842
  defaultValue: f.initial ?? "",
91467
91843
  onSubmit: (value) => handleFieldSubmit(idx, value)
91468
91844
  }, undefined, false, undefined, this)
91469
91845
  }, f.key, false, undefined, this)),
91470
- error && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91846
+ error && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
91471
91847
  marginTop: 1,
91472
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91848
+ children: /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
91473
91849
  color: color.danger,
91474
91850
  wrap: "wrap",
91475
91851
  children: error
91476
91852
  }, undefined, false, undefined, this)
91477
91853
  }, undefined, false, undefined, this),
91478
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91854
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
91479
91855
  marginTop: 1,
91480
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91856
+ children: /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
91481
91857
  color: color.muted,
91482
91858
  children: [
91483
91859
  fields.length > 1 ? "⏎ on each field to advance · " : "⏎ to ",
@@ -91491,9 +91867,9 @@ function SettingsFormModal({
91491
91867
  ]
91492
91868
  }, undefined, true, undefined, this)
91493
91869
  }, undefined, false, undefined, this),
91494
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Footer, {
91870
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Footer, {
91495
91871
  mode: title.toLowerCase(),
91496
- version: VERSION2,
91872
+ version: VERSION,
91497
91873
  outerPadX: 1,
91498
91874
  hints: [
91499
91875
  ["⏎", fields.length > 1 ? "next / submit" : submitLabel],
@@ -91509,31 +91885,31 @@ function FieldRow2({
91509
91885
  children
91510
91886
  }) {
91511
91887
  const tint = active ? color.accent : color.muted;
91512
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91888
+ return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
91513
91889
  marginTop: 1,
91514
91890
  children: [
91515
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91891
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
91516
91892
  width: 16,
91517
91893
  children: [
91518
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91894
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
91519
91895
  color: tint,
91520
91896
  children: active ? `${icon.chevron} ` : " "
91521
91897
  }, undefined, false, undefined, this),
91522
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
91898
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
91523
91899
  color: tint,
91524
91900
  bold: active,
91525
91901
  children: label
91526
91902
  }, undefined, false, undefined, this)
91527
91903
  ]
91528
91904
  }, undefined, true, undefined, this),
91529
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
91905
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
91530
91906
  flexGrow: 1,
91531
91907
  children
91532
91908
  }, undefined, false, undefined, this)
91533
91909
  ]
91534
91910
  }, undefined, true, undefined, this);
91535
91911
  }
91536
- var import_react71, jsx_dev_runtime19;
91912
+ var import_react72, jsx_dev_runtime20;
91537
91913
  var init_SettingsFormModal = __esm(async () => {
91538
91914
  init_theme15();
91539
91915
  await __promiseAll([
@@ -91542,8 +91918,8 @@ var init_SettingsFormModal = __esm(async () => {
91542
91918
  init_Footer(),
91543
91919
  init_TitledPanel()
91544
91920
  ]);
91545
- import_react71 = __toESM(require_react(), 1);
91546
- jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
91921
+ import_react72 = __toESM(require_react(), 1);
91922
+ jsx_dev_runtime20 = __toESM(require_jsx_dev_runtime(), 1);
91547
91923
  });
91548
91924
 
91549
91925
  // apps/cli/src/tui/components/views/settings/SettingsModal.tsx
@@ -91553,11 +91929,13 @@ function SettingsModal({
91553
91929
  dispatch,
91554
91930
  users,
91555
91931
  invites,
91932
+ projects,
91933
+ onRefresh,
91556
91934
  onSignedOut
91557
91935
  }) {
91558
91936
  const cancel = () => dispatch({ type: "SETTINGS_EDIT", editing: null });
91559
91937
  if (editing.kind === "serverName") {
91560
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(SettingsFormModal, {
91938
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(SettingsFormModal, {
91561
91939
  title: "Edit Server Name",
91562
91940
  help: "Up to 64 chars. Shown on the login screen and dashboard header.",
91563
91941
  fields: [
@@ -91588,7 +91966,7 @@ function SettingsModal({
91588
91966
  }, undefined, false, undefined, this);
91589
91967
  }
91590
91968
  if (editing.kind === "serverDescription") {
91591
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(SettingsFormModal, {
91969
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(SettingsFormModal, {
91592
91970
  title: "Edit Description",
91593
91971
  help: "Up to 256 chars. Shown to anyone hitting /v1/auth/server-info.",
91594
91972
  fields: [
@@ -91621,7 +91999,7 @@ function SettingsModal({
91621
91999
  }, undefined, false, undefined, this);
91622
92000
  }
91623
92001
  if (editing.kind === "changePassword") {
91624
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(SettingsFormModal, {
92002
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(SettingsFormModal, {
91625
92003
  title: "Change Password",
91626
92004
  help: "Enter your current password, then a new one (min 8 chars). Shown in cleartext — make sure nobody's looking.",
91627
92005
  fields: [
@@ -91653,7 +92031,7 @@ function SettingsModal({
91653
92031
  }, undefined, false, undefined, this);
91654
92032
  }
91655
92033
  if (editing.kind === "redeemCode") {
91656
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(SettingsFormModal, {
92034
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(SettingsFormModal, {
91657
92035
  title: "Redeem Invite Code",
91658
92036
  help: "Paste a project invite code someone shared with you.",
91659
92037
  submitLabel: "redeem",
@@ -91665,9 +92043,20 @@ function SettingsModal({
91665
92043
  return cancel();
91666
92044
  dispatch({ type: "SETTINGS_BUSY", busy: true });
91667
92045
  try {
91668
- await invites.accept(vals.code ?? "");
92046
+ const res = await invites.accept(vals.code ?? "");
92047
+ let name = "";
92048
+ if (res.projectId && projects) {
92049
+ try {
92050
+ const list5 = await projects.list();
92051
+ name = list5.find((p2) => p2.id === res.projectId)?.name ?? "";
92052
+ } catch {}
92053
+ }
92054
+ await onRefresh?.();
91669
92055
  dispatch({ type: "SETTINGS_BUSY", busy: false });
91670
92056
  dispatch({ type: "SETTINGS_EDIT", editing: null });
92057
+ const role = res.role || "member";
92058
+ const notice = name ? `Joined "${name}" as ${role}.` : res.projectId ? `Joined project as ${role}.` : "Invite accepted.";
92059
+ dispatch({ type: "SETTINGS_NOTICE", notice });
91671
92060
  } catch (err) {
91672
92061
  dispatch({ type: "SETTINGS_ERROR", error: err.message });
91673
92062
  }
@@ -91677,7 +92066,7 @@ function SettingsModal({
91677
92066
  }
91678
92067
  if (editing.kind === "registrationToggle") {
91679
92068
  const next = editing.next;
91680
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ConfirmAction, {
92069
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ConfirmAction, {
91681
92070
  title: next ? "Open Registration" : "Close Registration",
91682
92071
  message: next ? "Anyone with the server URL will be able to register a new account. Continue?" : "Registration will require a server invite issued by you. Existing users keep access. Continue?",
91683
92072
  tone: next ? color.accent2 : color.accent,
@@ -91701,7 +92090,7 @@ function SettingsModal({
91701
92090
  }, undefined, false, undefined, this);
91702
92091
  }
91703
92092
  if (editing.kind === "signOut") {
91704
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ConfirmAction, {
92093
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ConfirmAction, {
91705
92094
  title: "Sign Out",
91706
92095
  message: "Clears the local token. Server URL stays. Re-login required to use this client.",
91707
92096
  tone: color.danger,
@@ -91733,7 +92122,7 @@ function SettingsModal({
91733
92122
  if (editing.kind === "revokeInvite") {
91734
92123
  const inv = state.settingsOutgoing.find((i) => i.codeHash === editing.codeHash);
91735
92124
  const label = inv ? inv.projectId ? `${inv.projectName || "(deleted project)"} · ${inv.role || "editor"}` : "Server invite" : "this invite";
91736
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ConfirmAction, {
92125
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ConfirmAction, {
91737
92126
  title: "Revoke Invite",
91738
92127
  message: `Revoke "${label}"? The code becomes invalid immediately.`,
91739
92128
  tone: color.danger,
@@ -91745,9 +92134,9 @@ function SettingsModal({
91745
92134
  dispatch({ type: "SETTINGS_BUSY", busy: true });
91746
92135
  try {
91747
92136
  await invites.revoke(editing.codeHash);
91748
- const list4 = await invites.listOutgoing();
92137
+ const list5 = await invites.listOutgoing();
91749
92138
  dispatch({ type: "SETTINGS_BUSY", busy: false });
91750
- dispatch({ type: "SETTINGS_OUTGOING_SET", invites: list4 });
92139
+ dispatch({ type: "SETTINGS_OUTGOING_SET", invites: list5 });
91751
92140
  dispatch({ type: "SETTINGS_EDIT", editing: null });
91752
92141
  } catch (err) {
91753
92142
  dispatch({ type: "SETTINGS_ERROR", error: err.message });
@@ -91778,37 +92167,37 @@ function ConfirmAction({
91778
92167
  onConfirm();
91779
92168
  }
91780
92169
  });
91781
- const body = /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(jsx_dev_runtime20.Fragment, {
92170
+ const body = /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(jsx_dev_runtime21.Fragment, {
91782
92171
  children: [
91783
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
92172
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
91784
92173
  wrap: "wrap",
91785
92174
  children: message
91786
92175
  }, undefined, false, undefined, this),
91787
- error && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
92176
+ error && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
91788
92177
  marginTop: 1,
91789
- children: /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
92178
+ children: /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
91790
92179
  color: color.danger,
91791
92180
  wrap: "wrap",
91792
92181
  children: error
91793
92182
  }, undefined, false, undefined, this)
91794
92183
  }, undefined, false, undefined, this),
91795
- busy && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
92184
+ busy && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
91796
92185
  marginTop: 1,
91797
- children: /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
92186
+ children: /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
91798
92187
  color: color.muted,
91799
92188
  children: "working…"
91800
92189
  }, undefined, false, undefined, this)
91801
92190
  }, undefined, false, undefined, this)
91802
92191
  ]
91803
92192
  }, undefined, true, undefined, this);
91804
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ConfirmDialog, {
92193
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ConfirmDialog, {
91805
92194
  title,
91806
92195
  tone,
91807
92196
  message: body,
91808
92197
  footerMode: title.toLowerCase()
91809
92198
  }, undefined, false, undefined, this);
91810
92199
  }
91811
- var jsx_dev_runtime20;
92200
+ var jsx_dev_runtime21;
91812
92201
  var init_SettingsModal = __esm(async () => {
91813
92202
  init_src();
91814
92203
  init_theme15();
@@ -91817,7 +92206,7 @@ var init_SettingsModal = __esm(async () => {
91817
92206
  init_ConfirmDialog(),
91818
92207
  init_SettingsFormModal()
91819
92208
  ]);
91820
- jsx_dev_runtime20 = __toESM(require_jsx_dev_runtime(), 1);
92209
+ jsx_dev_runtime21 = __toESM(require_jsx_dev_runtime(), 1);
91821
92210
  });
91822
92211
 
91823
92212
  // apps/cli/src/tui/settings.ts
@@ -91850,16 +92239,18 @@ function buildServerTab(args) {
91850
92239
  key: "name",
91851
92240
  label: "Server Name",
91852
92241
  value: s?.serverName || (args.serverLoaded ? "(unset)" : "loading…"),
91853
- detail: "Display name shown on the login screen and in the dashboard header.",
92242
+ detail: "A friendly name for this server. Shows up on the welcome screen.",
91854
92243
  onEnter: owner ? args.on.editServerName : undefined,
92244
+ enterLabel: "edit",
91855
92245
  muted: !s?.serverName
91856
92246
  },
91857
92247
  {
91858
92248
  key: "description",
91859
92249
  label: "Description",
91860
92250
  value: s?.serverDescription || (args.serverLoaded ? "(unset)" : "loading…"),
91861
- detail: "Short blurb shown to anyone hitting /v1/auth/server-info describes what this instance is for.",
92251
+ detail: "A short blurb about this server, so new members know what they're joining.",
91862
92252
  onEnter: owner ? args.on.editServerDescription : undefined,
92253
+ enterLabel: "edit",
91863
92254
  muted: !s?.serverDescription
91864
92255
  }
91865
92256
  ];
@@ -91868,14 +92259,15 @@ function buildServerTab(args) {
91868
92259
  key: "registration",
91869
92260
  label: "Open Registration",
91870
92261
  value: s ? s.registrationOpen ? "true" : "false" : "loading…",
91871
- detail: "When on, anyone with the server URL can register an account. When off, registration requires a server invite issued by you.",
91872
- onEnter: s ? () => args.on.toggleRegistration(!s.registrationOpen) : undefined
92262
+ detail: "When on, anyone with the server address can sign up. When off, new people need an invite from you.",
92263
+ onEnter: s ? () => args.on.toggleRegistration(!s.registrationOpen) : undefined,
92264
+ enterLabel: "toggle"
91873
92265
  });
91874
92266
  }
91875
92267
  return {
91876
92268
  key: "server",
91877
92269
  label: "Server",
91878
- hint: owner ? "Instance metadata. You're the owner." : "Read-only — only the server owner can change these.",
92270
+ hint: owner ? "Server info and settings. You're the owner." : "View only — only the owner can change these.",
91879
92271
  rows,
91880
92272
  hints: owner ? [
91881
92273
  ["⏎", "edit"],
@@ -91893,31 +92285,33 @@ function buildAccountTab(args) {
91893
92285
  key: "user",
91894
92286
  label: "Logged-in User",
91895
92287
  value: args.identity.userName || "—",
91896
- detail: "The identity bound to the JWT in your local config. Cannot be changed in place sign out and log in as someone else."
92288
+ detail: "The account you're signed in as. To use a different one, sign out and log in again."
91897
92289
  },
91898
92290
  {
91899
92291
  key: "server-url",
91900
92292
  label: "Server URL",
91901
92293
  value: args.identity.server || "—",
91902
- detail: "Base URL of the dox server this client talks to. Set via `dox login --server …`."
92294
+ detail: "The dox server this app is connected to."
91903
92295
  },
91904
92296
  {
91905
92297
  key: "role",
91906
92298
  label: "Role",
91907
92299
  value: args.identity.role || "—",
91908
- detail: "Server-level role. `owner` can edit server settings and issue server invites; `member` cannot."
92300
+ detail: "Owners can change server settings and invite new people. Members can't."
91909
92301
  },
91910
92302
  {
91911
92303
  key: "change-password",
91912
92304
  label: "Change Password…",
91913
- detail: "Replace your password. You'll be prompted for the current one and a new one (min 8 chars).",
91914
- onEnter: args.on.changePassword
92305
+ detail: "Set a new password. You'll need your current one and a new one (at least 8 characters).",
92306
+ onEnter: args.on.changePassword,
92307
+ enterLabel: "change"
91915
92308
  },
91916
92309
  {
91917
92310
  key: "sign-out",
91918
92311
  label: "Sign Out",
91919
- detail: "Clears the token in ~/.config/dox/config.toml and exits. Re-run `dox` to log in again.",
91920
- onEnter: args.on.signOut
92312
+ detail: "Sign out on this device. Your account stays log back in any time to keep using dox.",
92313
+ onEnter: args.on.signOut,
92314
+ enterLabel: "sign out"
91921
92315
  }
91922
92316
  ];
91923
92317
  return {
@@ -91925,7 +92319,7 @@ function buildAccountTab(args) {
91925
92319
  label: "Account",
91926
92320
  rows,
91927
92321
  hints: [
91928
- ["⏎", "activate"],
92322
+ ["⏎", "open"],
91929
92323
  ["1/2/3", "tab"],
91930
92324
  ["esc", "close"]
91931
92325
  ]
@@ -91936,7 +92330,7 @@ function buildInvitesTab(args) {
91936
92330
  key: `inv-${inv.codeHash}`,
91937
92331
  label: inviteSummary(inv),
91938
92332
  value: fmtExpiry(inv.expiresAt),
91939
- detail: inv.projectId ? `Project invite for ${inv.projectName || "(deleted project)"} role ${inv.role || "editor"}. ${fmtExpiry(inv.expiresAt)}. Press r to revoke.` : `Server invite — anyone with the code can register a new account. ${fmtExpiry(inv.expiresAt)}. Press r to revoke.`,
92333
+ detail: inv.projectId ? `Invite to join ${inv.projectName || "(deleted project)"} as ${inv.role || "editor"}. ${fmtExpiry(inv.expiresAt)}.` : `Sign-up invite — anyone with this code can create an account on this server. ${fmtExpiry(inv.expiresAt)}.`,
91940
92334
  secondary: {
91941
92335
  key: "r",
91942
92336
  label: "revoke",
@@ -91948,14 +92342,15 @@ function buildInvitesTab(args) {
91948
92342
  {
91949
92343
  key: "redeem",
91950
92344
  label: "Redeem code…",
91951
- detail: "Paste a project invite code someone shared with you to join their project.",
91952
- onEnter: args.on.redeemCode
92345
+ detail: "Paste an invite code someone shared to join their project.",
92346
+ onEnter: args.on.redeemCode,
92347
+ enterLabel: "redeem"
91953
92348
  }
91954
92349
  ];
91955
92350
  return {
91956
92351
  key: "invites",
91957
92352
  label: "Invites",
91958
- hint: args.outgoingLoaded ? args.outgoing.length === 0 ? "No outgoing invites. Issue one from a project's manage view (Phase 2)." : `${args.outgoing.length} outgoing invite${args.outgoing.length === 1 ? "" : "s"}.` : "Loading invites…",
92353
+ hint: args.outgoingLoaded ? args.outgoing.length === 0 ? "You haven't shared any invites yet. Create one from a project's page." : `${args.outgoing.length} active invite${args.outgoing.length === 1 ? "" : "s"}.` : "Loading invites…",
91959
92354
  rows,
91960
92355
  hints: [
91961
92356
  ["⏎", "redeem"],
@@ -91972,31 +92367,36 @@ var isOwner = (role) => role === "owner";
91972
92367
 
91973
92368
  // apps/cli/src/tui/components/layout/Sidebar.tsx
91974
92369
  function filterKey(f) {
91975
- if (f === "inbox")
92370
+ if (typeof f === "string")
91976
92371
  return f;
91977
92372
  return `p:${f.id}`;
91978
92373
  }
91979
- var jsx_dev_runtime21;
92374
+ var jsx_dev_runtime22;
91980
92375
  var init_Sidebar = __esm(async () => {
91981
92376
  init_theme15();
91982
92377
  await init_build2();
91983
- jsx_dev_runtime21 = __toESM(require_jsx_dev_runtime(), 1);
92378
+ jsx_dev_runtime22 = __toESM(require_jsx_dev_runtime(), 1);
91984
92379
  });
91985
92380
 
91986
92381
  // apps/cli/src/tui/state.ts
91987
92382
  function visibleTodos(state) {
91988
92383
  const f = state.filter;
92384
+ if (f === "done") {
92385
+ return state.todos.filter((t) => t.done).slice().sort((a, b4) => Number(b4.updatedAt ?? 0) - Number(a.updatedAt ?? 0));
92386
+ }
92387
+ const base2 = state.todos.filter((t) => !t.done);
91989
92388
  if (f === "inbox")
91990
- return state.todos.filter((t) => !t.projectId);
91991
- return state.todos.filter((t) => t.projectId === f.id);
92389
+ return base2.filter((t) => !t.projectId);
92390
+ return base2.filter((t) => t.projectId === f.id);
91992
92391
  }
91993
92392
  function filterList(projects) {
91994
92393
  return [
91995
92394
  "inbox",
92395
+ "done",
91996
92396
  ...projects.map((p2) => ({ type: "project", id: p2.id }))
91997
92397
  ];
91998
92398
  }
91999
- function clampCursor(cursor4, length) {
92399
+ function clampCursor2(cursor4, length) {
92000
92400
  if (length === 0)
92001
92401
  return 0;
92002
92402
  return Math.max(0, Math.min(cursor4, length - 1));
@@ -92012,7 +92412,7 @@ function reducer2(state, action) {
92012
92412
  };
92013
92413
  return {
92014
92414
  ...next,
92015
- cursor: clampCursor(state.cursor, visibleTodos(next).length)
92415
+ cursor: clampCursor2(state.cursor, visibleTodos(next).length)
92016
92416
  };
92017
92417
  }
92018
92418
  case "PROJECTS_LOADED":
@@ -92060,23 +92460,23 @@ function reducer2(state, action) {
92060
92460
  const nextFocus = state.focus === "list" ? "sidebar" : "list";
92061
92461
  if (nextFocus !== "sidebar")
92062
92462
  return { ...state, focus: nextFocus };
92063
- const list4 = filterList(state.projects);
92463
+ const list5 = filterList(state.projects);
92064
92464
  const currentKey = filterKey(state.filter);
92065
- const idx = list4.findIndex((f) => filterKey(f) === currentKey);
92465
+ const idx = list5.findIndex((f) => filterKey(f) === currentKey);
92066
92466
  return { ...state, focus: nextFocus, sidebarCursor: idx >= 0 ? idx : 0 };
92067
92467
  }
92068
92468
  case "FILTER_SET": {
92069
92469
  const next = { ...state, filter: action.filter, focus: "list" };
92070
- return { ...next, cursor: clampCursor(0, visibleTodos(next).length) };
92470
+ return { ...next, cursor: clampCursor2(0, visibleTodos(next).length) };
92071
92471
  }
92072
92472
  case "FILTER_CYCLE": {
92073
- const list4 = filterList(state.projects);
92473
+ const list5 = filterList(state.projects);
92074
92474
  const currentKey = filterKey(state.filter);
92075
- const idx = list4.findIndex((f) => filterKey(f) === currentKey);
92076
- const nextIdx = (idx + action.direction + list4.length) % list4.length;
92077
- const nextFilter = list4[nextIdx] ?? state.filter;
92475
+ const idx = list5.findIndex((f) => filterKey(f) === currentKey);
92476
+ const nextIdx = (idx + action.direction + list5.length) % list5.length;
92477
+ const nextFilter = list5[nextIdx] ?? state.filter;
92078
92478
  const next = { ...state, filter: nextFilter, sidebarCursor: nextIdx };
92079
- return { ...next, cursor: clampCursor(0, visibleTodos(next).length) };
92479
+ return { ...next, cursor: clampCursor2(0, visibleTodos(next).length) };
92080
92480
  }
92081
92481
  case "ENTER_ADD":
92082
92482
  return {
@@ -92103,7 +92503,7 @@ function reducer2(state, action) {
92103
92503
  const projects = [...state.projects, action.project];
92104
92504
  const filter2 = { type: "project", id: action.project.id };
92105
92505
  const next = { ...state, projects, filter: filter2, mode: "list" };
92106
- return { ...next, cursor: clampCursor(0, visibleTodos(next).length) };
92506
+ return { ...next, cursor: clampCursor2(0, visibleTodos(next).length) };
92107
92507
  }
92108
92508
  case "ENTER_PROJECT_DELETE_CONFIRM":
92109
92509
  return {
@@ -92126,7 +92526,7 @@ function reducer2(state, action) {
92126
92526
  mode: "list",
92127
92527
  deletingProjectId: null
92128
92528
  };
92129
- return { ...next, cursor: clampCursor(0, visibleTodos(next).length) };
92529
+ return { ...next, cursor: clampCursor2(0, visibleTodos(next).length) };
92130
92530
  }
92131
92531
  case "EXIT_MODE":
92132
92532
  return {
@@ -92150,6 +92550,7 @@ function reducer2(state, action) {
92150
92550
  settingsOutgoingLoaded: false,
92151
92551
  settingsEditing: null,
92152
92552
  settingsError: null,
92553
+ settingsNotice: null,
92153
92554
  settingsBusy: false
92154
92555
  };
92155
92556
  case "CLOSE_SETTINGS":
@@ -92157,14 +92558,16 @@ function reducer2(state, action) {
92157
92558
  ...state,
92158
92559
  mode: "list",
92159
92560
  settingsEditing: null,
92160
- settingsError: null
92561
+ settingsError: null,
92562
+ settingsNotice: null
92161
92563
  };
92162
92564
  case "SETTINGS_TAB":
92163
92565
  return {
92164
92566
  ...state,
92165
92567
  settingsTab: action.tab,
92166
92568
  settingsCursor: 0,
92167
- settingsError: null
92569
+ settingsError: null,
92570
+ settingsNotice: null
92168
92571
  };
92169
92572
  case "SETTINGS_CURSOR":
92170
92573
  return { ...state, settingsCursor: action.index };
@@ -92186,6 +92589,8 @@ function reducer2(state, action) {
92186
92589
  return { ...state, settingsBusy: action.busy };
92187
92590
  case "SETTINGS_ERROR":
92188
92591
  return { ...state, settingsError: action.error, settingsBusy: false };
92592
+ case "SETTINGS_NOTICE":
92593
+ return { ...state, settingsNotice: action.notice };
92189
92594
  case "ENTER_PROJECT_MANAGE":
92190
92595
  return {
92191
92596
  ...state,
@@ -92266,23 +92671,32 @@ function reducer2(state, action) {
92266
92671
  };
92267
92672
  return { ...next, cursor: 0 };
92268
92673
  }
92269
- case "TODO_UPDATED":
92270
- return {
92674
+ case "TODO_UPDATED": {
92675
+ const next = {
92271
92676
  ...state,
92272
92677
  todos: state.todos.map((t) => t.id === action.todo.id ? { ...t, ...action.todo } : t)
92273
92678
  };
92679
+ return {
92680
+ ...next,
92681
+ cursor: clampCursor2(state.cursor, visibleTodos(next).length)
92682
+ };
92683
+ }
92274
92684
  case "TODO_DELETED": {
92275
92685
  const todos = state.todos.filter((t) => t.id !== action.id);
92276
92686
  const next = { ...state, todos };
92277
92687
  return {
92278
92688
  ...next,
92279
- cursor: clampCursor(state.cursor, visibleTodos(next).length)
92689
+ cursor: clampCursor2(state.cursor, visibleTodos(next).length)
92280
92690
  };
92281
92691
  }
92282
92692
  case "OPEN_ABOUT":
92283
92693
  return { ...state, mode: "about", helpOpen: false, error: null };
92284
92694
  case "CLOSE_ABOUT":
92285
92695
  return { ...state, mode: "list" };
92696
+ case "TOAST_SET":
92697
+ return { ...state, toast: action.toast };
92698
+ case "TOAST_CLEAR":
92699
+ return { ...state, toast: null };
92286
92700
  default:
92287
92701
  return state;
92288
92702
  }
@@ -92315,6 +92729,7 @@ var init_state = __esm(async () => {
92315
92729
  settingsEditing: null,
92316
92730
  settingsBusy: false,
92317
92731
  settingsError: null,
92732
+ settingsNotice: null,
92318
92733
  manageProjectId: null,
92319
92734
  manageMembers: [],
92320
92735
  manageMembersLoaded: false,
@@ -92324,7 +92739,8 @@ var init_state = __esm(async () => {
92324
92739
  deletingProjectId: null,
92325
92740
  searchQuery: "",
92326
92741
  searchCursor: 0,
92327
- searchDetailTodoId: null
92742
+ searchDetailTodoId: null,
92743
+ toast: null
92328
92744
  };
92329
92745
  });
92330
92746
 
@@ -92339,11 +92755,11 @@ function App2({
92339
92755
  identity: identity3,
92340
92756
  onSignedOut
92341
92757
  }) {
92342
- const [state, dispatch] = import_react72.useReducer(reducer2, initialState);
92343
- const [serverInfo, setServerInfo] = import_react72.useState(null);
92758
+ const [state, dispatch] = import_react73.useReducer(reducer2, initialState);
92759
+ const [serverInfo, setServerInfo] = import_react73.useState(null);
92344
92760
  const { exit } = use_app_default();
92345
92761
  const { cols: totalCols, rows: totalRows } = useTerminalSize();
92346
- const refresh = import_react72.useCallback(async () => {
92762
+ const refresh = import_react73.useCallback(async () => {
92347
92763
  dispatch({ type: "SYNC_START" });
92348
92764
  try {
92349
92765
  const [todos, projectList] = await Promise.all([
@@ -92359,27 +92775,27 @@ function App2({
92359
92775
  dispatch({ type: "SYNC_END" });
92360
92776
  }
92361
92777
  }, [api, projects]);
92362
- import_react72.useEffect(() => {
92778
+ import_react73.useEffect(() => {
92363
92779
  refresh();
92364
92780
  const timer = setInterval(() => void refresh(), POLL_INTERVAL_MS);
92365
92781
  return () => clearInterval(timer);
92366
92782
  }, [refresh]);
92367
- const refreshEvents = import_react72.useCallback(async () => {
92783
+ const refreshEvents = import_react73.useCallback(async () => {
92368
92784
  if (!events)
92369
92785
  return;
92370
92786
  try {
92371
- const list4 = await events.list();
92372
- dispatch({ type: "EVENTS_LOADED", events: list4 });
92787
+ const list5 = await events.list();
92788
+ dispatch({ type: "EVENTS_LOADED", events: list5 });
92373
92789
  } catch {}
92374
92790
  }, [events]);
92375
- import_react72.useEffect(() => {
92791
+ import_react73.useEffect(() => {
92376
92792
  if (!events)
92377
92793
  return;
92378
92794
  refreshEvents();
92379
92795
  const timer = setInterval(() => void refreshEvents(), EVENTS_POLL_INTERVAL_MS);
92380
92796
  return () => clearInterval(timer);
92381
92797
  }, [events, refreshEvents]);
92382
- import_react72.useEffect(() => {
92798
+ import_react73.useEffect(() => {
92383
92799
  if (state.mode !== "projectManage")
92384
92800
  return;
92385
92801
  if (state.manageMembersLoaded)
@@ -92399,7 +92815,7 @@ function App2({
92399
92815
  }
92400
92816
  })();
92401
92817
  }, [state.mode, state.manageProjectId, state.manageMembersLoaded, projects]);
92402
- import_react72.useEffect(() => {
92818
+ import_react73.useEffect(() => {
92403
92819
  if (state.mode !== "settings")
92404
92820
  return;
92405
92821
  if (!state.settingsServerLoaded && users && identity3?.role === "owner") {
@@ -92425,8 +92841,8 @@ function App2({
92425
92841
  if (!state.settingsOutgoingLoaded && invites) {
92426
92842
  (async () => {
92427
92843
  try {
92428
- const list4 = await invites.listOutgoing();
92429
- dispatch({ type: "SETTINGS_OUTGOING_SET", invites: list4 });
92844
+ const list5 = await invites.listOutgoing();
92845
+ dispatch({ type: "SETTINGS_OUTGOING_SET", invites: list5 });
92430
92846
  } catch (err) {
92431
92847
  dispatch({ type: "SETTINGS_ERROR", error: err.message });
92432
92848
  dispatch({ type: "SETTINGS_OUTGOING_SET", invites: [] });
@@ -92442,7 +92858,7 @@ function App2({
92442
92858
  identity3?.role,
92443
92859
  serverInfo
92444
92860
  ]);
92445
- import_react72.useEffect(() => {
92861
+ import_react73.useEffect(() => {
92446
92862
  const url = identity3?.server;
92447
92863
  if (!url)
92448
92864
  return;
@@ -92458,7 +92874,13 @@ function App2({
92458
92874
  cancelled = true;
92459
92875
  };
92460
92876
  }, [identity3?.server]);
92461
- const hydrateTodoDescription = import_react72.useCallback(async (id) => {
92877
+ import_react73.useEffect(() => {
92878
+ if (!state.settingsNotice)
92879
+ return;
92880
+ const timer = setTimeout(() => dispatch({ type: "SETTINGS_NOTICE", notice: null }), 4000);
92881
+ return () => clearTimeout(timer);
92882
+ }, [state.settingsNotice]);
92883
+ const hydrateTodoDescription = import_react73.useCallback(async (id) => {
92462
92884
  try {
92463
92885
  const full = await api.getTodo(id);
92464
92886
  dispatch({ type: "TODO_UPDATED", todo: full });
@@ -92466,7 +92888,7 @@ function App2({
92466
92888
  dispatch({ type: "LOAD_ERROR", error: err.message });
92467
92889
  }
92468
92890
  }, [api]);
92469
- const enterEditWithFullTodo = import_react72.useCallback(async (id, fallbackTitle, cachedDescription) => {
92891
+ const enterEditWithFullTodo = import_react73.useCallback(async (id, fallbackTitle, cachedDescription) => {
92470
92892
  dispatch({
92471
92893
  type: "ENTER_EDIT",
92472
92894
  id,
@@ -92487,8 +92909,8 @@ function App2({
92487
92909
  dispatch({ type: "LOAD_ERROR", error: err.message });
92488
92910
  }
92489
92911
  }, [api]);
92490
- const [hydratingSearch, setHydratingSearch] = import_react72.useState(false);
92491
- import_react72.useEffect(() => {
92912
+ const [hydratingSearch, setHydratingSearch] = import_react73.useState(false);
92913
+ import_react73.useEffect(() => {
92492
92914
  if (state.mode !== "search")
92493
92915
  return;
92494
92916
  const missing = state.todos.filter((t) => t.description === undefined);
@@ -92511,11 +92933,11 @@ function App2({
92511
92933
  cancelled = true;
92512
92934
  };
92513
92935
  }, [state.mode, api]);
92514
- const visible = import_react72.useMemo(() => visibleTodos(state), [state]);
92936
+ const visible = import_react73.useMemo(() => visibleTodos(state), [state]);
92515
92937
  const cursoredTodo = visible[state.cursor];
92516
92938
  const cursoredId = cursoredTodo?.id;
92517
92939
  const cursoredDescriptionLoaded = cursoredTodo?.description !== undefined;
92518
- import_react72.useEffect(() => {
92940
+ import_react73.useEffect(() => {
92519
92941
  if (state.mode !== "list")
92520
92942
  return;
92521
92943
  if (!cursoredId || cursoredDescriptionLoaded)
@@ -92532,12 +92954,35 @@ function App2({
92532
92954
  cancelled = true;
92533
92955
  };
92534
92956
  }, [state.mode, cursoredId, cursoredDescriptionLoaded, api]);
92535
- const createdSeries = import_react72.useMemo(() => activityByDay(state.todos, ACTIVITY_DAYS, "created"), [state.todos]);
92536
- const doneSeries = import_react72.useMemo(() => activityByDay(state.todos, ACTIVITY_DAYS, "done"), [state.todos]);
92537
- use_input_default((input, key) => {
92538
- if (input === "?" || key.escape)
92539
- dispatch({ type: "CLOSE_HELP" });
92540
- }, { isActive: state.helpOpen });
92957
+ const createdSeries = import_react73.useMemo(() => activityByDay(state.todos, ACTIVITY_DAYS, "created"), [state.todos]);
92958
+ const doneSeries = import_react73.useMemo(() => activityByDay(state.todos, ACTIVITY_DAYS, "done"), [state.todos]);
92959
+ const toggleDone = import_react73.useCallback(async (todo2) => {
92960
+ const prevDone = todo2.done;
92961
+ try {
92962
+ const updated = await api.updateTodo(todo2.id, { done: !prevDone });
92963
+ dispatch({ type: "TODO_UPDATED", todo: updated });
92964
+ dispatch({
92965
+ type: "TOAST_SET",
92966
+ toast: {
92967
+ kind: "doneToggled",
92968
+ todoId: todo2.id,
92969
+ title: todo2.title,
92970
+ prevDone,
92971
+ expiresAt: Date.now() + 5000
92972
+ }
92973
+ });
92974
+ } catch (err) {
92975
+ dispatch({ type: "LOAD_ERROR", error: err.message });
92976
+ }
92977
+ }, [api]);
92978
+ import_react73.useEffect(() => {
92979
+ const t = state.toast;
92980
+ if (!t)
92981
+ return;
92982
+ const ms = Math.max(0, t.expiresAt - Date.now());
92983
+ const id = setTimeout(() => dispatch({ type: "TOAST_CLEAR" }), ms);
92984
+ return () => clearTimeout(id);
92985
+ }, [state.toast]);
92541
92986
  use_input_default((input, key) => {
92542
92987
  if (state.helpOpen)
92543
92988
  return;
@@ -92561,6 +93006,21 @@ function App2({
92561
93006
  dispatch({ type: "OPEN_SEARCH" });
92562
93007
  return;
92563
93008
  }
93009
+ if (input === "z" && state.toast?.kind === "doneToggled") {
93010
+ const t = state.toast;
93011
+ (async () => {
93012
+ try {
93013
+ const updated = await api.updateTodo(t.todoId, {
93014
+ done: t.prevDone
93015
+ });
93016
+ dispatch({ type: "TODO_UPDATED", todo: updated });
93017
+ dispatch({ type: "TOAST_CLEAR" });
93018
+ } catch (err) {
93019
+ dispatch({ type: "LOAD_ERROR", error: err.message });
93020
+ }
93021
+ })();
93022
+ return;
93023
+ }
92564
93024
  if (state.error)
92565
93025
  dispatch({ type: "CLEAR_ERROR" });
92566
93026
  if (input === "j" || key.downArrow)
@@ -92602,16 +93062,7 @@ function App2({
92602
93062
  return;
92603
93063
  }
92604
93064
  if (input === " ") {
92605
- (async () => {
92606
- try {
92607
- const updated = await api.updateTodo(current.id, {
92608
- done: !current.done
92609
- });
92610
- dispatch({ type: "TODO_UPDATED", todo: updated });
92611
- } catch (err) {
92612
- dispatch({ type: "LOAD_ERROR", error: err.message });
92613
- }
92614
- })();
93065
+ toggleDone(current);
92615
93066
  } else if (input === "d") {
92616
93067
  (async () => {
92617
93068
  try {
@@ -92698,13 +93149,19 @@ function App2({
92698
93149
  const activityH = Math.max(17, Math.min(22, Math.floor(innerH * 0.46)));
92699
93150
  const todoInfoH = Math.max(11, innerH - activityH - rowGap);
92700
93151
  const activeTab = filterToTabKey(state.filter);
92701
- const projectById = import_react72.useMemo(() => new Map(state.projects.map((p2) => [p2.id, p2])), [state.projects]);
92702
- const totalCount = state.todos.length;
92703
- const doneCount = state.todos.filter((t) => t.done).length;
92704
- const openCount = totalCount - doneCount;
93152
+ const projectById = import_react73.useMemo(() => new Map(state.projects.map((p2) => [p2.id, p2])), [state.projects]);
93153
+ const openCount = state.todos.filter((t) => !t.done).length;
92705
93154
  const nowMs = Date.now();
93155
+ const todayMs = startOfDay(nowMs);
93156
+ const doneTodayCount = state.todos.filter((t) => t.done && Number(t.updatedAt ?? 0) >= todayMs).length;
92706
93157
  const showSpinner = state.loading && state.todos.length === 0;
92707
- const LIST_VIEWPORT_H = Math.max(5, todosH - 8);
93158
+ if (state.helpOpen) {
93159
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(HelpView, {
93160
+ onClose: () => dispatch({ type: "CLOSE_HELP" })
93161
+ }, undefined, false, undefined, this);
93162
+ }
93163
+ const toastChromeH = state.toast?.kind === "doneToggled" ? 2 : 0;
93164
+ const LIST_VIEWPORT_H = Math.max(5, todosH - 8 - toastChromeH);
92708
93165
  const listWindow = sliceWindow2(visible, state.cursor, LIST_VIEWPORT_H);
92709
93166
  if (state.mode === "settings") {
92710
93167
  const settingsTabs = buildSettingsTabs({
@@ -92741,19 +93198,22 @@ function App2({
92741
93198
  });
92742
93199
  const editing = state.settingsEditing;
92743
93200
  if (editing) {
92744
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(SettingsModal, {
93201
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(SettingsModal, {
92745
93202
  editing,
92746
93203
  state,
92747
93204
  dispatch,
92748
93205
  users,
92749
93206
  invites,
93207
+ projects,
93208
+ onRefresh: refresh,
92750
93209
  onSignedOut
92751
93210
  }, undefined, false, undefined, this);
92752
93211
  }
92753
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(SettingsView, {
93212
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(SettingsView, {
92754
93213
  tabs: settingsTabs,
92755
93214
  activeTab: state.settingsTab,
92756
93215
  cursor: state.settingsCursor,
93216
+ notice: state.settingsNotice,
92757
93217
  onTabChange: (tab2) => dispatch({ type: "SETTINGS_TAB", tab: tab2 }),
92758
93218
  onCursorChange: (i) => dispatch({ type: "SETTINGS_CURSOR", index: i }),
92759
93219
  onClose: () => dispatch({ type: "CLOSE_SETTINGS" })
@@ -92761,7 +93221,7 @@ function App2({
92761
93221
  }
92762
93222
  if (state.mode === "add" || state.mode === "edit") {
92763
93223
  const editingTodo = state.mode === "edit" && state.editingId ? state.todos.find((t) => t.id === state.editingId) ?? null : null;
92764
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TodoEditorView, {
93224
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TodoEditorView, {
92765
93225
  mode: state.mode,
92766
93226
  defaultTitle: state.mode === "edit" ? state.editingTitle : undefined,
92767
93227
  defaultDescription: state.mode === "edit" ? state.editingDescription || editingTodo?.description || "" : "",
@@ -92770,7 +93230,7 @@ function App2({
92770
93230
  }, undefined, false, undefined, this);
92771
93231
  }
92772
93232
  if (state.mode === "projectAdd") {
92773
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ProjectEditorView, {
93233
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(ProjectEditorView, {
92774
93234
  onSubmit: (input) => {
92775
93235
  if (!projects) {
92776
93236
  dispatch({ type: "EXIT_MODE" });
@@ -92791,7 +93251,7 @@ function App2({
92791
93251
  if (state.mode === "projectManage") {
92792
93252
  const proj = state.manageProjectId ? projectById.get(state.manageProjectId) ?? null : null;
92793
93253
  const isOwner2 = !!(proj && identity3?.userId && proj.ownerId === identity3.userId);
92794
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ProjectManageView, {
93254
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(ProjectManageView, {
92795
93255
  project: proj,
92796
93256
  members: state.manageMembers,
92797
93257
  membersLoaded: state.manageMembersLoaded,
@@ -92828,7 +93288,7 @@ function App2({
92828
93288
  }, undefined, false, undefined, this);
92829
93289
  }
92830
93290
  if (state.mode === "search") {
92831
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(SearchView, {
93291
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(SearchView, {
92832
93292
  todos: state.todos,
92833
93293
  projects: state.projects,
92834
93294
  query: state.searchQuery,
@@ -92853,24 +93313,13 @@ function App2({
92853
93313
  return null;
92854
93314
  }
92855
93315
  const proj = current.projectId ? projectById.get(current.projectId) ?? null : null;
92856
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TodoDetailView, {
93316
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TodoDetailView, {
92857
93317
  todo: current,
92858
93318
  project: proj,
92859
93319
  ownerName: identity3?.userName,
92860
93320
  nowMs,
92861
93321
  onClose: () => dispatch({ type: "SEARCH_CLOSE_DETAIL" }),
92862
- onToggleDone: () => {
92863
- (async () => {
92864
- try {
92865
- const updated = await api.updateTodo(current.id, {
92866
- done: !current.done
92867
- });
92868
- dispatch({ type: "TODO_UPDATED", todo: updated });
92869
- } catch (err) {
92870
- dispatch({ type: "LOAD_ERROR", error: err.message });
92871
- }
92872
- })();
92873
- },
93322
+ onToggleDone: () => void toggleDone(current),
92874
93323
  onEdit: () => void enterEditWithFullTodo(current.id, current.title, current.description),
92875
93324
  onDelete: () => {
92876
93325
  (async () => {
@@ -92892,24 +93341,13 @@ function App2({
92892
93341
  return null;
92893
93342
  }
92894
93343
  const proj = current.projectId ? projectById.get(current.projectId) ?? null : null;
92895
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TodoDetailView, {
93344
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TodoDetailView, {
92896
93345
  todo: current,
92897
93346
  project: proj,
92898
93347
  ownerName: identity3?.userName,
92899
93348
  nowMs,
92900
93349
  onClose: () => dispatch({ type: "CLOSE_TODO_DETAIL" }),
92901
- onToggleDone: () => {
92902
- (async () => {
92903
- try {
92904
- const updated = await api.updateTodo(current.id, {
92905
- done: !current.done
92906
- });
92907
- dispatch({ type: "TODO_UPDATED", todo: updated });
92908
- } catch (err) {
92909
- dispatch({ type: "LOAD_ERROR", error: err.message });
92910
- }
92911
- })();
92912
- },
93350
+ onToggleDone: () => void toggleDone(current),
92913
93351
  onEdit: () => void enterEditWithFullTodo(current.id, current.title, current.description),
92914
93352
  onDelete: () => {
92915
93353
  (async () => {
@@ -92925,8 +93363,8 @@ function App2({
92925
93363
  }, undefined, false, undefined, this);
92926
93364
  }
92927
93365
  if (state.mode === "about") {
92928
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(AboutView, {
92929
- version: VERSION2,
93366
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(AboutView, {
93367
+ version: VERSION,
92930
93368
  onClose: () => dispatch({ type: "CLOSE_ABOUT" })
92931
93369
  }, undefined, false, undefined, this);
92932
93370
  }
@@ -92934,18 +93372,18 @@ function App2({
92934
93372
  const target = state.deletingProjectId ? state.projects.find((p2) => p2.id === state.deletingProjectId) : null;
92935
93373
  const name = target?.name ?? "this project";
92936
93374
  const todoCount = state.deletingProjectId ? state.todos.filter((t) => t.projectId === state.deletingProjectId).length : 0;
92937
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ConfirmDialog, {
93375
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(ConfirmDialog, {
92938
93376
  title: "Delete project",
92939
93377
  tone: color.danger,
92940
93378
  footerMode: "confirm delete",
92941
- message: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(jsx_dev_runtime22.Fragment, {
93379
+ message: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(jsx_dev_runtime23.Fragment, {
92942
93380
  children: [
92943
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93381
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
92944
93382
  wrap: "wrap",
92945
93383
  children: [
92946
93384
  "Delete project",
92947
93385
  " ",
92948
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93386
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
92949
93387
  color: color.accent,
92950
93388
  bold: true,
92951
93389
  children: name
@@ -92953,9 +93391,9 @@ function App2({
92953
93391
  "?"
92954
93392
  ]
92955
93393
  }, undefined, true, undefined, this),
92956
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93394
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
92957
93395
  marginTop: 1,
92958
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93396
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
92959
93397
  color: color.muted,
92960
93398
  wrap: "wrap",
92961
93399
  children: todoCount > 0 ? `This also removes ${todoCount} todo${todoCount === 1 ? "" : "s"} in this project. Cannot be undone.` : "Cannot be undone."
@@ -92965,135 +93403,123 @@ function App2({
92965
93403
  }, undefined, true, undefined, this)
92966
93404
  }, undefined, false, undefined, this);
92967
93405
  }
92968
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93406
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
92969
93407
  flexDirection: "column",
92970
93408
  paddingX: 1,
92971
93409
  children: [
92972
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93410
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
92973
93411
  children: [
92974
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93412
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
92975
93413
  flexDirection: "column",
92976
93414
  width: leftColW,
92977
93415
  children: [
92978
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93416
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
92979
93417
  children: [
92980
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TitledPanel, {
93418
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TitledPanel, {
92981
93419
  title: "Server",
92982
93420
  width: serverW,
92983
93421
  paddingY: 1,
92984
93422
  height: topRowH,
92985
- children: [
92986
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Logo, {}, undefined, false, undefined, this),
92987
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
92988
- marginTop: 1,
92989
- children: [
92990
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
92991
- color: color.muted,
92992
- children: "serving at "
92993
- }, undefined, false, undefined, this),
92994
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
92995
- color: color.accent2,
92996
- wrap: "truncate",
92997
- children: identity3?.server ?? "local"
92998
- }, undefined, false, undefined, this)
92999
- ]
93000
- }, undefined, true, undefined, this)
93001
- ]
93002
- }, undefined, true, undefined, this),
93003
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93423
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93424
+ flexDirection: "column",
93425
+ flexGrow: 1,
93426
+ justifyContent: "center",
93427
+ alignItems: "center",
93428
+ children: [
93429
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Logo, {}, undefined, false, undefined, this),
93430
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93431
+ marginTop: 1,
93432
+ children: [
93433
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93434
+ color: color.muted,
93435
+ children: "serving at "
93436
+ }, undefined, false, undefined, this),
93437
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93438
+ color: color.accent2,
93439
+ wrap: "truncate",
93440
+ children: identity3?.server ?? "local"
93441
+ }, undefined, false, undefined, this)
93442
+ ]
93443
+ }, undefined, true, undefined, this)
93444
+ ]
93445
+ }, undefined, true, undefined, this)
93446
+ }, undefined, false, undefined, this),
93447
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93004
93448
  width: topGap
93005
93449
  }, undefined, false, undefined, this),
93006
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TitledPanel, {
93450
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TitledPanel, {
93007
93451
  title: "Status",
93008
93452
  width: statusW,
93009
93453
  paddingY: 1,
93010
93454
  height: topRowH,
93011
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(StatusPanel, {
93455
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(StatusPanel, {
93012
93456
  userName: identity3?.userName,
93013
93457
  server: identity3?.server,
93014
93458
  configPath: identity3?.configPath,
93015
93459
  serverInfo,
93016
- clientVersion: VERSION2,
93460
+ clientVersion: VERSION,
93017
93461
  syncing: state.syncing
93018
93462
  }, undefined, false, undefined, this)
93019
93463
  }, undefined, false, undefined, this)
93020
93464
  ]
93021
93465
  }, undefined, true, undefined, this),
93022
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93466
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93023
93467
  height: rowGap
93024
93468
  }, undefined, false, undefined, this),
93025
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TitledPanel, {
93469
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TitledPanel, {
93026
93470
  title: "Todos",
93027
93471
  width: leftColW,
93028
93472
  paddingY: 1,
93029
93473
  height: todosH,
93030
93474
  focused: true,
93031
93475
  children: [
93032
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93476
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93033
93477
  children: [
93034
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93035
- color: color.accent,
93478
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93479
+ color: color.accent2,
93036
93480
  bold: true,
93037
- children: totalCount
93481
+ children: openCount
93038
93482
  }, undefined, false, undefined, this),
93039
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93483
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93040
93484
  color: color.muted,
93041
- children: " todos "
93485
+ children: " open "
93042
93486
  }, undefined, false, undefined, this),
93043
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93487
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93044
93488
  color: color.muted,
93045
93489
  dimColor: true,
93046
93490
  children: icon.dot
93047
93491
  }, undefined, false, undefined, this),
93048
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93492
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93049
93493
  color: color.muted,
93050
93494
  children: " "
93051
93495
  }, undefined, false, undefined, this),
93052
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93496
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93053
93497
  color: color.success,
93054
93498
  bold: true,
93055
- children: doneCount
93056
- }, undefined, false, undefined, this),
93057
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93058
- color: color.muted,
93059
- children: " done "
93499
+ children: doneTodayCount
93060
93500
  }, undefined, false, undefined, this),
93061
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93501
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93062
93502
  color: color.muted,
93063
- dimColor: true,
93064
- children: icon.dot
93065
- }, undefined, false, undefined, this),
93066
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93067
- color: color.muted,
93068
- children: " "
93069
- }, undefined, false, undefined, this),
93070
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93071
- color: color.accent2,
93072
- bold: true,
93073
- children: openCount
93503
+ children: " done today"
93074
93504
  }, undefined, false, undefined, this),
93075
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93076
- color: color.muted,
93077
- children: " open"
93078
- }, undefined, false, undefined, this),
93079
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93505
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93080
93506
  flexGrow: 1
93081
93507
  }, undefined, false, undefined, this),
93082
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93508
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93083
93509
  color: color.muted,
93084
93510
  dimColor: true,
93085
93511
  children: "sorted by updated"
93086
93512
  }, undefined, false, undefined, this)
93087
93513
  ]
93088
93514
  }, undefined, true, undefined, this),
93089
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93515
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93090
93516
  marginTop: 1,
93091
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Tabs, {
93517
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Tabs, {
93092
93518
  tabs: [
93093
93519
  {
93094
93520
  key: "inbox",
93095
93521
  label: "Private",
93096
- count: state.todos.filter((t) => !t.projectId).length,
93522
+ count: state.todos.filter((t) => !t.projectId && !t.done).length,
93097
93523
  kind: "system",
93098
93524
  prefixIcon: icon.brand
93099
93525
  },
@@ -93107,7 +93533,7 @@ function App2({
93107
93533
  ...state.projects.map((p2) => ({
93108
93534
  key: `p:${p2.id}`,
93109
93535
  label: p2.name,
93110
- count: state.todos.filter((t) => t.projectId === p2.id).length,
93536
+ count: state.todos.filter((t) => t.projectId === p2.id && !t.done).length,
93111
93537
  kind: "project",
93112
93538
  prefixIcon: icon.on,
93113
93539
  prefixColor: swatchColor(p2.color)
@@ -93116,44 +93542,94 @@ function App2({
93116
93542
  activeKey: activeTab
93117
93543
  }, undefined, false, undefined, this)
93118
93544
  }, undefined, false, undefined, this),
93119
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93545
+ state.toast?.kind === "doneToggled" && (() => {
93546
+ const reopened = state.toast.prevDone;
93547
+ const glyph = reopened ? icon.open : icon.done;
93548
+ const tint = reopened ? color.accent2 : color.success;
93549
+ const verb = reopened ? "reopened" : "completed";
93550
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93551
+ marginTop: 1,
93552
+ children: [
93553
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93554
+ color: tint,
93555
+ children: glyph
93556
+ }, undefined, false, undefined, this),
93557
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93558
+ color: color.muted,
93559
+ children: ` ${verb} "`
93560
+ }, undefined, false, undefined, this),
93561
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93562
+ color: color.accent,
93563
+ children: state.toast.title
93564
+ }, undefined, false, undefined, this),
93565
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93566
+ color: color.muted,
93567
+ children: '"'
93568
+ }, undefined, false, undefined, this),
93569
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93570
+ flexGrow: 1
93571
+ }, undefined, false, undefined, this),
93572
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93573
+ color: color.muted,
93574
+ children: "press "
93575
+ }, undefined, false, undefined, this),
93576
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93577
+ color: color.accent,
93578
+ bold: true,
93579
+ children: "z"
93580
+ }, undefined, false, undefined, this),
93581
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93582
+ color: color.muted,
93583
+ children: " to undo"
93584
+ }, undefined, false, undefined, this)
93585
+ ]
93586
+ }, undefined, true, undefined, this);
93587
+ })(),
93588
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93120
93589
  marginTop: 1,
93121
93590
  flexDirection: "column",
93122
- children: showSpinner ? /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Spinner, {
93591
+ children: showSpinner ? /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Spinner, {
93123
93592
  label: "Loading todos…"
93124
- }, undefined, false, undefined, this) : visible.length === 0 ? /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93593
+ }, undefined, false, undefined, this) : visible.length === 0 ? state.filter === "done" ? /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93125
93594
  color: color.muted,
93126
93595
  dimColor: true,
93127
93596
  children: [
93128
93597
  " ",
93129
- "nothing herepress ",
93130
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93598
+ "no completions yet finished todos land here"
93599
+ ]
93600
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93601
+ color: color.muted,
93602
+ dimColor: true,
93603
+ children: [
93604
+ " ",
93605
+ "nothing here — press",
93606
+ " ",
93607
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93131
93608
  color: color.accent,
93132
93609
  children: "i"
93133
93610
  }, undefined, false, undefined, this),
93134
- " ",
93135
- "to add a todo"
93611
+ " to add a todo"
93136
93612
  ]
93137
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(jsx_dev_runtime22.Fragment, {
93613
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(jsx_dev_runtime23.Fragment, {
93138
93614
  children: [
93139
- listWindow.items.map((t, idx) => /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TodoRow, {
93615
+ listWindow.items.map((t, idx) => /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TodoRow, {
93140
93616
  todo: t,
93141
93617
  nowMs,
93142
93618
  highlighted: listWindow.start + idx === state.cursor,
93143
93619
  width: leftColW - 4
93144
93620
  }, t.id, false, undefined, this)),
93145
- (listWindow.moreAbove > 0 || listWindow.moreBelow > 0) && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93621
+ (listWindow.moreAbove > 0 || listWindow.moreBelow > 0) && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93146
93622
  width: leftColW - 4,
93147
93623
  children: [
93148
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93624
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93149
93625
  color: color.muted,
93150
93626
  dimColor: true,
93151
93627
  children: listWindow.moreAbove > 0 ? `↑ ${listWindow.moreAbove} more` : ""
93152
93628
  }, undefined, false, undefined, this),
93153
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93629
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93154
93630
  flexGrow: 1
93155
93631
  }, undefined, false, undefined, this),
93156
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93632
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93157
93633
  color: color.muted,
93158
93634
  dimColor: true,
93159
93635
  children: listWindow.moreBelow > 0 ? `${listWindow.moreBelow} more ↓` : ""
@@ -93167,97 +93643,97 @@ function App2({
93167
93643
  }, undefined, true, undefined, this)
93168
93644
  ]
93169
93645
  }, undefined, true, undefined, this),
93170
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93646
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93171
93647
  width: colGap
93172
93648
  }, undefined, false, undefined, this),
93173
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93649
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93174
93650
  flexDirection: "column",
93175
93651
  width: rightColW,
93176
93652
  children: [
93177
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TitledPanel, {
93653
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TitledPanel, {
93178
93654
  title: "Activity",
93179
93655
  width: rightColW,
93180
93656
  paddingY: 1,
93181
93657
  height: activityH,
93182
93658
  borderTint: color.accent2,
93183
93659
  children: [
93184
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93660
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93185
93661
  children: [
93186
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93662
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93187
93663
  color: color.muted,
93188
93664
  dimColor: true,
93189
93665
  children: "▒"
93190
93666
  }, undefined, false, undefined, this),
93191
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93667
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93192
93668
  color: color.muted,
93193
93669
  children: " Created "
93194
93670
  }, undefined, false, undefined, this),
93195
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93671
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93196
93672
  color: color.accent,
93197
93673
  children: sum2(createdSeries)
93198
93674
  }, undefined, false, undefined, this),
93199
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93675
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93200
93676
  color: color.muted,
93201
93677
  children: " "
93202
93678
  }, undefined, false, undefined, this),
93203
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93679
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93204
93680
  color: color.accent,
93205
93681
  children: "█"
93206
93682
  }, undefined, false, undefined, this),
93207
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93683
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93208
93684
  color: color.muted,
93209
93685
  children: " Done "
93210
93686
  }, undefined, false, undefined, this),
93211
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93687
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93212
93688
  color: color.success,
93213
93689
  children: sum2(doneSeries)
93214
93690
  }, undefined, false, undefined, this),
93215
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93691
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93216
93692
  color: color.muted,
93217
93693
  children: ` · ${ACTIVITY_DAYS} days`
93218
93694
  }, undefined, false, undefined, this)
93219
93695
  ]
93220
93696
  }, undefined, true, undefined, this),
93221
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93697
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93222
93698
  marginTop: 1,
93223
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(DualBarChart, {
93699
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(DualBarChart, {
93224
93700
  primary: doneSeries,
93225
93701
  secondary: createdSeries,
93226
93702
  rows: 5,
93227
93703
  ySteps: 2
93228
93704
  }, undefined, false, undefined, this)
93229
93705
  }, undefined, false, undefined, this),
93230
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(XAxis, {
93706
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(XAxis, {
93231
93707
  days: ACTIVITY_DAYS
93232
93708
  }, undefined, false, undefined, this),
93233
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93709
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93234
93710
  marginTop: 1,
93235
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93711
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93236
93712
  color: color.muted,
93237
93713
  dimColor: true,
93238
93714
  children: "─ Recent ".padEnd(Math.max(0, rightColW - 4), "─")
93239
93715
  }, undefined, false, undefined, this)
93240
93716
  }, undefined, false, undefined, this),
93241
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93717
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93242
93718
  marginTop: 1,
93243
93719
  flexGrow: 1,
93244
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ActivityFeed, {
93720
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(ActivityFeed, {
93245
93721
  events: state.events,
93246
93722
  nowMs
93247
93723
  }, undefined, false, undefined, this)
93248
93724
  }, undefined, false, undefined, this)
93249
93725
  ]
93250
93726
  }, undefined, true, undefined, this),
93251
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93727
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93252
93728
  height: rowGap
93253
93729
  }, undefined, false, undefined, this),
93254
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TitledPanel, {
93730
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TitledPanel, {
93255
93731
  title: "Todo Info",
93256
93732
  width: rightColW,
93257
93733
  height: todoInfoH,
93258
93734
  paddingY: 1,
93259
93735
  borderTint: color.accent3,
93260
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TodoInfo, {
93736
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TodoInfo, {
93261
93737
  todo: visible[state.cursor] ?? null,
93262
93738
  project: visible[state.cursor]?.projectId ? projectById.get(visible[state.cursor].projectId) ?? null : null,
93263
93739
  ownerName: identity3?.userName,
@@ -93270,13 +93746,12 @@ function App2({
93270
93746
  }, undefined, true, undefined, this)
93271
93747
  ]
93272
93748
  }, undefined, true, undefined, this),
93273
- state.error && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ErrorAlert, {
93749
+ state.error && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(ErrorAlert, {
93274
93750
  message: state.error
93275
93751
  }, undefined, false, undefined, this),
93276
- state.helpOpen && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(HelpOverlay, {}, undefined, false, undefined, this),
93277
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Footer, {
93752
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Footer, {
93278
93753
  mode: "normal",
93279
- version: VERSION2,
93754
+ version: VERSION,
93280
93755
  hints: listHints,
93281
93756
  outerPadX: 1
93282
93757
  }, undefined, false, undefined, this)
@@ -93299,48 +93774,54 @@ function StatusPanel({
93299
93774
  serverInfo?.version || (serverInfo ? "unknown" : "—"),
93300
93775
  color.accent
93301
93776
  ],
93302
- ["sha", serverInfo?.commit || (serverInfo ? "unknown" : "—"), color.accent],
93777
+ [
93778
+ "sha",
93779
+ serverInfo?.commit ? shortSha(serverInfo.commit) : serverInfo ? "unknown" : "—",
93780
+ color.accent
93781
+ ],
93303
93782
  ["cli", clientVersion, color.accent],
93304
93783
  ["cfg", configPath2 ? tildifyPath(configPath2) : "—", color.muted]
93305
93784
  ];
93306
93785
  const connTint = serverInfo ? color.success : server ? color.warn : color.muted;
93307
93786
  const connLabel = serverInfo ? "connected" : server ? "probing…" : "local";
93308
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93787
+ const showSyncing = useMinHold(syncing, 600);
93788
+ const spinnerFrame = useSpinnerFrame(showSyncing);
93789
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93309
93790
  flexDirection: "column",
93310
93791
  children: [
93311
- rows.map(([label, value, tint]) => /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93792
+ rows.map(([label, value, tint]) => /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93312
93793
  children: [
93313
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93794
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93314
93795
  width: 5,
93315
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93796
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93316
93797
  color: color.muted,
93317
93798
  children: label
93318
93799
  }, undefined, false, undefined, this)
93319
93800
  }, undefined, false, undefined, this),
93320
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93801
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93321
93802
  color: tint,
93322
93803
  wrap: "truncate",
93323
93804
  children: value
93324
93805
  }, undefined, false, undefined, this)
93325
93806
  ]
93326
93807
  }, label, true, undefined, this)),
93327
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93808
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93328
93809
  marginTop: 1,
93329
93810
  children: [
93330
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93811
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93331
93812
  color: connTint,
93332
93813
  children: [
93333
93814
  icon.on,
93334
93815
  " "
93335
93816
  ]
93336
93817
  }, undefined, true, undefined, this),
93337
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93818
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93338
93819
  color: connTint,
93339
93820
  children: connLabel
93340
93821
  }, undefined, false, undefined, this),
93341
- syncing && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93822
+ showSyncing && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93342
93823
  color: color.muted,
93343
- children: ` ${icon.dot} syncing`
93824
+ children: ` ${spinnerFrame} syncing`
93344
93825
  }, undefined, false, undefined, this)
93345
93826
  ]
93346
93827
  }, undefined, true, undefined, this)
@@ -93350,6 +93831,9 @@ function StatusPanel({
93350
93831
  function stripScheme(url) {
93351
93832
  return url.replace(/^https?:\/\//, "");
93352
93833
  }
93834
+ function shortSha(sha) {
93835
+ return /^[0-9a-f]{7,}$/i.test(sha) ? sha.slice(0, 7) : sha;
93836
+ }
93353
93837
  function tildifyPath(path) {
93354
93838
  const home = homedir2();
93355
93839
  if (home && path.startsWith(home)) {
@@ -93368,28 +93852,28 @@ function TodoRow({
93368
93852
  const bar = highlighted ? icon.selectBar : " ";
93369
93853
  const age = relativeTime(nowMs, todo2.updatedAt);
93370
93854
  const ageWidth = 4;
93371
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93855
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93372
93856
  width,
93373
93857
  children: [
93374
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93858
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93375
93859
  color: color.accent,
93376
93860
  children: bar
93377
93861
  }, undefined, false, undefined, this),
93378
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93862
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93379
93863
  children: " "
93380
93864
  }, undefined, false, undefined, this),
93381
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93865
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93382
93866
  color: markColor,
93383
93867
  children: mark
93384
93868
  }, undefined, false, undefined, this),
93385
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93869
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93386
93870
  children: " "
93387
93871
  }, undefined, false, undefined, this),
93388
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93872
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93389
93873
  flexGrow: 1,
93390
93874
  flexShrink: 1,
93391
93875
  overflow: "hidden",
93392
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93876
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93393
93877
  color: highlighted ? color.accent : undefined,
93394
93878
  bold: highlighted,
93395
93879
  dimColor: todo2.done,
@@ -93398,10 +93882,10 @@ function TodoRow({
93398
93882
  children: todo2.title
93399
93883
  }, undefined, false, undefined, this)
93400
93884
  }, undefined, false, undefined, this),
93401
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93885
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93402
93886
  width: ageWidth,
93403
93887
  justifyContent: "flex-end",
93404
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93888
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93405
93889
  color: color.muted,
93406
93890
  children: age
93407
93891
  }, undefined, false, undefined, this)
@@ -93410,7 +93894,7 @@ function TodoRow({
93410
93894
  }, undefined, true, undefined, this);
93411
93895
  }
93412
93896
  function activeProjectId(filter2) {
93413
- if (filter2 === "inbox")
93897
+ if (typeof filter2 === "string")
93414
93898
  return;
93415
93899
  return filter2.id;
93416
93900
  }
@@ -93442,38 +93926,38 @@ function XAxis({ days }) {
93442
93926
  const gutter = 4;
93443
93927
  const chartWidth = days * 2;
93444
93928
  const totalW = gutter + chartWidth;
93445
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93929
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93446
93930
  flexDirection: "column",
93447
93931
  width: totalW,
93448
93932
  children: [
93449
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93933
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93450
93934
  width: totalW,
93451
93935
  children: [
93452
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93936
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93453
93937
  color: color.muted,
93454
93938
  children: " ".repeat(gutter)
93455
93939
  }, undefined, false, undefined, this),
93456
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93940
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93457
93941
  color: color.muted,
93458
93942
  children: "─".repeat(chartWidth)
93459
93943
  }, undefined, false, undefined, this)
93460
93944
  ]
93461
93945
  }, undefined, true, undefined, this),
93462
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93946
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93463
93947
  width: totalW,
93464
93948
  children: [
93465
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93949
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93466
93950
  color: color.muted,
93467
93951
  children: " ".repeat(gutter)
93468
93952
  }, undefined, false, undefined, this),
93469
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93953
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93470
93954
  color: color.muted,
93471
93955
  children: `${days}d ago`
93472
93956
  }, undefined, false, undefined, this),
93473
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
93957
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
93474
93958
  flexGrow: 1
93475
93959
  }, undefined, false, undefined, this),
93476
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
93960
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
93477
93961
  color: color.muted,
93478
93962
  children: "today"
93479
93963
  }, undefined, false, undefined, this)
@@ -93504,7 +93988,7 @@ function startOfDay(ms) {
93504
93988
  d4.setUTCHours(0, 0, 0, 0);
93505
93989
  return d4.getTime();
93506
93990
  }
93507
- var import_react72, jsx_dev_runtime22, POLL_INTERVAL_MS = 30000, EVENTS_POLL_INTERVAL_MS = 60000, ACTIVITY_DAYS = 14, listHints;
93991
+ var import_react73, jsx_dev_runtime23, POLL_INTERVAL_MS = 30000, EVENTS_POLL_INTERVAL_MS = 60000, ACTIVITY_DAYS = 14, listHints;
93508
93992
  var init_App2 = __esm(async () => {
93509
93993
  init_src();
93510
93994
  init_theme15();
@@ -93516,7 +94000,7 @@ var init_App2 = __esm(async () => {
93516
94000
  init_DualBarChart(),
93517
94001
  init_ErrorAlert(),
93518
94002
  init_Footer(),
93519
- init_HelpOverlay(),
94003
+ init_HelpView(),
93520
94004
  init_Logo(),
93521
94005
  init_SearchView(),
93522
94006
  init_SettingsView(),
@@ -93533,8 +94017,8 @@ var init_App2 = __esm(async () => {
93533
94017
  init_state(),
93534
94018
  init_Sidebar()
93535
94019
  ]);
93536
- import_react72 = __toESM(require_react(), 1);
93537
- jsx_dev_runtime22 = __toESM(require_jsx_dev_runtime(), 1);
94020
+ import_react73 = __toESM(require_react(), 1);
94021
+ jsx_dev_runtime23 = __toESM(require_jsx_dev_runtime(), 1);
93538
94022
  listHints = [
93539
94023
  ["␣", "toggle"],
93540
94024
  ["i", "todo"],
@@ -93559,7 +94043,7 @@ function Panel({
93559
94043
  children
93560
94044
  }) {
93561
94045
  const borderColor = focused ? color.accent : borderTint ?? color.muted;
93562
- return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
94046
+ return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
93563
94047
  flexDirection: "column",
93564
94048
  borderStyle: "round",
93565
94049
  borderColor,
@@ -93571,11 +94055,11 @@ function Panel({
93571
94055
  children
93572
94056
  }, undefined, false, undefined, this);
93573
94057
  }
93574
- var jsx_dev_runtime23;
94058
+ var jsx_dev_runtime24;
93575
94059
  var init_Panel = __esm(async () => {
93576
94060
  init_theme15();
93577
94061
  await init_build2();
93578
- jsx_dev_runtime23 = __toESM(require_jsx_dev_runtime(), 1);
94062
+ jsx_dev_runtime24 = __toESM(require_jsx_dev_runtime(), 1);
93579
94063
  });
93580
94064
 
93581
94065
  // apps/cli/src/tui/components/primitives/SectionHeader.tsx
@@ -93585,27 +94069,27 @@ function SectionHeader({
93585
94069
  leadWidth = 3
93586
94070
  }) {
93587
94071
  const lead = "─".repeat(leadWidth);
93588
- return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
93589
- children: /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text, {
94072
+ return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Box_default, {
94073
+ children: /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Text, {
93590
94074
  wrap: "truncate",
93591
94075
  children: [
93592
- /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text, {
94076
+ /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Text, {
93593
94077
  color: color.muted,
93594
94078
  children: [
93595
94079
  lead,
93596
94080
  " "
93597
94081
  ]
93598
94082
  }, undefined, true, undefined, this),
93599
- /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text, {
94083
+ /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Text, {
93600
94084
  bold: true,
93601
94085
  color: color.accent,
93602
94086
  children: title
93603
94087
  }, undefined, false, undefined, this),
93604
- hint && /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text, {
94088
+ hint && /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Text, {
93605
94089
  color: color.muted,
93606
94090
  children: ` · ${hint}`
93607
94091
  }, undefined, false, undefined, this),
93608
- /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text, {
94092
+ /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Text, {
93609
94093
  color: color.muted,
93610
94094
  children: ` ${"─".repeat(200)}`
93611
94095
  }, undefined, false, undefined, this)
@@ -93613,32 +94097,32 @@ function SectionHeader({
93613
94097
  }, undefined, true, undefined, this)
93614
94098
  }, undefined, false, undefined, this);
93615
94099
  }
93616
- var jsx_dev_runtime24;
94100
+ var jsx_dev_runtime25;
93617
94101
  var init_SectionHeader = __esm(async () => {
93618
94102
  init_theme15();
93619
94103
  await init_build2();
93620
- jsx_dev_runtime24 = __toESM(require_jsx_dev_runtime(), 1);
94104
+ jsx_dev_runtime25 = __toESM(require_jsx_dev_runtime(), 1);
93621
94105
  });
93622
94106
 
93623
94107
  // apps/cli/src/tui/components/primitives/Stepper.tsx
93624
94108
  function Stepper({ steps, activeIndex }) {
93625
- return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Box_default, {
94109
+ return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
93626
94110
  flexDirection: "column",
93627
94111
  children: [
93628
- /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Box_default, {
94112
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
93629
94113
  children: steps.map((_4, idx) => {
93630
94114
  const state = idx < activeIndex ? "done" : idx === activeIndex ? "active" : "pending";
93631
94115
  const glyph = state === "done" ? icon.stepDone : state === "active" ? icon.stepActive : icon.stepPending;
93632
94116
  const dotColor = state === "done" ? color.success : state === "active" ? color.accent : color.muted;
93633
94117
  const linkColor = idx < activeIndex ? color.success : color.muted;
93634
- return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Box_default, {
94118
+ return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
93635
94119
  width: CELL_WIDTH,
93636
94120
  children: [
93637
- /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Text, {
94121
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
93638
94122
  color: dotColor,
93639
94123
  children: glyph
93640
94124
  }, undefined, false, undefined, this),
93641
- idx < steps.length - 1 && /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Text, {
94125
+ idx < steps.length - 1 && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
93642
94126
  color: linkColor,
93643
94127
  children: [
93644
94128
  " ",
@@ -93652,13 +94136,13 @@ function Stepper({ steps, activeIndex }) {
93652
94136
  }, `d-${idx}`, true, undefined, this);
93653
94137
  })
93654
94138
  }, undefined, false, undefined, this),
93655
- /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Box_default, {
94139
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
93656
94140
  children: steps.map((label, idx) => {
93657
94141
  const state = idx < activeIndex ? "done" : idx === activeIndex ? "active" : "pending";
93658
94142
  const labelColor = state === "active" ? color.accent : color.muted;
93659
- return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Box_default, {
94143
+ return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
93660
94144
  width: CELL_WIDTH,
93661
- children: /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Text, {
94145
+ children: /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
93662
94146
  color: labelColor,
93663
94147
  bold: state === "active",
93664
94148
  dimColor: state === "pending",
@@ -93670,27 +94154,27 @@ function Stepper({ steps, activeIndex }) {
93670
94154
  ]
93671
94155
  }, undefined, true, undefined, this);
93672
94156
  }
93673
- var jsx_dev_runtime25, CELL_WIDTH = 11;
94157
+ var jsx_dev_runtime26, CELL_WIDTH = 11;
93674
94158
  var init_Stepper = __esm(async () => {
93675
94159
  init_theme15();
93676
94160
  await init_build2();
93677
- jsx_dev_runtime25 = __toESM(require_jsx_dev_runtime(), 1);
94161
+ jsx_dev_runtime26 = __toESM(require_jsx_dev_runtime(), 1);
93678
94162
  });
93679
94163
 
93680
94164
  // apps/cli/src/tui/components/views/onboarding/Onboarding.tsx
93681
94165
  import os3 from "node:os";
93682
94166
  function Onboarding({ reason = "fresh", onDone }) {
93683
- const [stage, setStage] = import_react73.useState("server");
93684
- const [server, setServer] = import_react73.useState(DEFAULT_SERVER);
93685
- const [info, setInfo] = import_react73.useState(null);
93686
- const [userName, setUserName] = import_react73.useState(os3.userInfo().username || "");
93687
- const [password, setPassword] = import_react73.useState("");
93688
- const [inviteCode, setInviteCode] = import_react73.useState("");
93689
- const [serverName, setServerName2] = import_react73.useState("");
93690
- const [serverDesc, setServerDesc] = import_react73.useState("");
93691
- const [intent, setIntent] = import_react73.useState("login");
93692
- const [error, setError] = import_react73.useState(null);
93693
- import_react73.useEffect(() => {
94167
+ const [stage, setStage] = import_react74.useState("server");
94168
+ const [server, setServer] = import_react74.useState(DEFAULT_SERVER);
94169
+ const [info, setInfo] = import_react74.useState(null);
94170
+ const [userName, setUserName] = import_react74.useState(os3.userInfo().username || "");
94171
+ const [password, setPassword] = import_react74.useState("");
94172
+ const [inviteCode, setInviteCode] = import_react74.useState("");
94173
+ const [serverName, setServerName2] = import_react74.useState("");
94174
+ const [serverDesc, setServerDesc] = import_react74.useState("");
94175
+ const [intent, setIntent] = import_react74.useState("login");
94176
+ const [error, setError] = import_react74.useState(null);
94177
+ import_react74.useEffect(() => {
93694
94178
  if (stage !== "probing")
93695
94179
  return;
93696
94180
  let cancelled = false;
@@ -93717,7 +94201,7 @@ function Onboarding({ reason = "fresh", onDone }) {
93717
94201
  cancelled = true;
93718
94202
  };
93719
94203
  }, [stage, server]);
93720
- import_react73.useEffect(() => {
94204
+ import_react74.useEffect(() => {
93721
94205
  if (stage !== "submitting")
93722
94206
  return;
93723
94207
  let cancelled = false;
@@ -93839,13 +94323,13 @@ function Onboarding({ reason = "fresh", onDone }) {
93839
94323
  setStage("submitting");
93840
94324
  };
93841
94325
  const stepIndex = stepIndexFor(stage);
93842
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94326
+ return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93843
94327
  flexDirection: "column",
93844
94328
  padding: 1,
93845
94329
  children: [
93846
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94330
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93847
94331
  children: [
93848
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94332
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93849
94333
  bold: true,
93850
94334
  color: color.brand,
93851
94335
  children: [
@@ -93853,7 +94337,7 @@ function Onboarding({ reason = "fresh", onDone }) {
93853
94337
  " dox"
93854
94338
  ]
93855
94339
  }, undefined, true, undefined, this),
93856
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94340
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93857
94341
  color: color.muted,
93858
94342
  children: [
93859
94343
  " ",
@@ -93862,48 +94346,48 @@ function Onboarding({ reason = "fresh", onDone }) {
93862
94346
  }, undefined, true, undefined, this)
93863
94347
  ]
93864
94348
  }, undefined, true, undefined, this),
93865
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94349
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93866
94350
  marginTop: 1,
93867
94351
  marginBottom: 1,
93868
- children: /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Stepper, {
94352
+ children: /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Stepper, {
93869
94353
  steps: STEPS,
93870
94354
  activeIndex: stepIndex
93871
94355
  }, undefined, false, undefined, this)
93872
94356
  }, undefined, false, undefined, this),
93873
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(ContextStrip, {
94357
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(ContextStrip, {
93874
94358
  server: stage !== "server" ? server : undefined,
93875
94359
  info: stage !== "server" && stage !== "probing" ? info : null,
93876
94360
  inviteCode: intent === "register" && inviteCode ? inviteCode : undefined,
93877
94361
  userName: stage === "enter-password" || stage === "confirm-password" || stage === "enter-server-name" || stage === "enter-server-description" || stage === "submitting" ? userName : undefined
93878
94362
  }, undefined, false, undefined, this),
93879
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94363
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93880
94364
  flexDirection: "column",
93881
94365
  marginTop: 1,
93882
94366
  width: 64,
93883
94367
  children: [
93884
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(SectionHeader, {
94368
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(SectionHeader, {
93885
94369
  title: panelTitle(stage, intent)
93886
94370
  }, undefined, false, undefined, this),
93887
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Panel, {
94371
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Panel, {
93888
94372
  focused: true,
93889
94373
  paddingY: 1,
93890
94374
  width: 64,
93891
94375
  children: [
93892
- stage === "server" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94376
+ stage === "server" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93893
94377
  flexDirection: "column",
93894
94378
  children: [
93895
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94379
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93896
94380
  color: color.muted,
93897
94381
  children: "where does your dox server live?"
93898
94382
  }, undefined, false, undefined, this),
93899
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94383
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93900
94384
  marginTop: 1,
93901
94385
  children: [
93902
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94386
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93903
94387
  color: color.muted,
93904
94388
  children: "> "
93905
94389
  }, undefined, false, undefined, this),
93906
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(TextInput, {
94390
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(TextInput, {
93907
94391
  defaultValue: server,
93908
94392
  placeholder: DEFAULT_SERVER,
93909
94393
  onSubmit: submitServer
@@ -93912,43 +94396,43 @@ function Onboarding({ reason = "fresh", onDone }) {
93912
94396
  }, undefined, true, undefined, this)
93913
94397
  ]
93914
94398
  }, undefined, true, undefined, this),
93915
- stage === "probing" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Spinner, {
94399
+ stage === "probing" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Spinner, {
93916
94400
  label: `connecting to ${server}…`
93917
94401
  }, undefined, false, undefined, this),
93918
- stage === "choose-branch" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94402
+ stage === "choose-branch" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93919
94403
  flexDirection: "column",
93920
94404
  children: [
93921
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94405
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93922
94406
  color: color.muted,
93923
94407
  children: "how would you like to connect?"
93924
94408
  }, undefined, false, undefined, this),
93925
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94409
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93926
94410
  flexDirection: "column",
93927
94411
  marginTop: 1,
93928
94412
  children: [
93929
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94413
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93930
94414
  children: [
93931
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94415
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93932
94416
  color: color.accent,
93933
94417
  bold: true,
93934
94418
  children: " 1 "
93935
94419
  }, undefined, false, undefined, this),
93936
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94420
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93937
94421
  children: "Log in to an existing account on this server"
93938
94422
  }, undefined, false, undefined, this)
93939
94423
  ]
93940
94424
  }, undefined, true, undefined, this),
93941
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94425
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93942
94426
  children: [
93943
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94427
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93944
94428
  color: color.accent,
93945
94429
  bold: true,
93946
94430
  children: " 2 "
93947
94431
  }, undefined, false, undefined, this),
93948
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94432
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93949
94433
  children: "Create a new account "
93950
94434
  }, undefined, false, undefined, this),
93951
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94435
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93952
94436
  color: color.muted,
93953
94437
  children: info && info.registrationOpen ? "(open — no invite needed)" : "(invite code required)"
93954
94438
  }, undefined, false, undefined, this)
@@ -93956,30 +94440,30 @@ function Onboarding({ reason = "fresh", onDone }) {
93956
94440
  }, undefined, true, undefined, this)
93957
94441
  ]
93958
94442
  }, undefined, true, undefined, this),
93959
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94443
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93960
94444
  marginTop: 1,
93961
- children: /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94445
+ children: /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93962
94446
  color: color.muted,
93963
94447
  children: "press 1 or 2"
93964
94448
  }, undefined, false, undefined, this)
93965
94449
  }, undefined, false, undefined, this)
93966
94450
  ]
93967
94451
  }, undefined, true, undefined, this),
93968
- stage === "enter-invite" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94452
+ stage === "enter-invite" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93969
94453
  flexDirection: "column",
93970
94454
  children: [
93971
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94455
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93972
94456
  color: color.muted,
93973
94457
  children: "paste your invite code"
93974
94458
  }, undefined, false, undefined, this),
93975
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94459
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93976
94460
  marginTop: 1,
93977
94461
  children: [
93978
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94462
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93979
94463
  color: color.muted,
93980
94464
  children: "> "
93981
94465
  }, undefined, false, undefined, this),
93982
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(TextInput, {
94466
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(TextInput, {
93983
94467
  placeholder: "ABCD-EFGH",
93984
94468
  onSubmit: submitInvite
93985
94469
  }, undefined, false, undefined, this)
@@ -93987,21 +94471,21 @@ function Onboarding({ reason = "fresh", onDone }) {
93987
94471
  }, undefined, true, undefined, this)
93988
94472
  ]
93989
94473
  }, undefined, true, undefined, this),
93990
- stage === "enter-username" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94474
+ stage === "enter-username" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93991
94475
  flexDirection: "column",
93992
94476
  children: [
93993
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94477
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
93994
94478
  color: color.muted,
93995
94479
  children: intent === "login" ? "your username" : "pick a username"
93996
94480
  }, undefined, false, undefined, this),
93997
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94481
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
93998
94482
  marginTop: 1,
93999
94483
  children: [
94000
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94484
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94001
94485
  color: color.muted,
94002
94486
  children: "> "
94003
94487
  }, undefined, false, undefined, this),
94004
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(TextInput, {
94488
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(TextInput, {
94005
94489
  defaultValue: userName,
94006
94490
  onSubmit: submitUserName
94007
94491
  }, undefined, false, undefined, this)
@@ -94009,21 +94493,21 @@ function Onboarding({ reason = "fresh", onDone }) {
94009
94493
  }, undefined, true, undefined, this)
94010
94494
  ]
94011
94495
  }, undefined, true, undefined, this),
94012
- stage === "enter-password" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94496
+ stage === "enter-password" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94013
94497
  flexDirection: "column",
94014
94498
  children: [
94015
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94499
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94016
94500
  color: color.muted,
94017
94501
  children: intent === "login" ? "your password" : `pick a password (min ${MIN_PASSWORD_LEN} chars)`
94018
94502
  }, undefined, false, undefined, this),
94019
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94503
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94020
94504
  marginTop: 1,
94021
94505
  children: [
94022
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94506
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94023
94507
  color: color.muted,
94024
94508
  children: "> "
94025
94509
  }, undefined, false, undefined, this),
94026
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(TextInput, {
94510
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(TextInput, {
94027
94511
  placeholder: "••••••••",
94028
94512
  onSubmit: submitPassword
94029
94513
  }, undefined, false, undefined, this)
@@ -94031,21 +94515,21 @@ function Onboarding({ reason = "fresh", onDone }) {
94031
94515
  }, undefined, true, undefined, this)
94032
94516
  ]
94033
94517
  }, undefined, true, undefined, this),
94034
- stage === "confirm-password" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94518
+ stage === "confirm-password" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94035
94519
  flexDirection: "column",
94036
94520
  children: [
94037
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94521
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94038
94522
  color: color.muted,
94039
94523
  children: "confirm password"
94040
94524
  }, undefined, false, undefined, this),
94041
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94525
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94042
94526
  marginTop: 1,
94043
94527
  children: [
94044
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94528
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94045
94529
  color: color.muted,
94046
94530
  children: "> "
94047
94531
  }, undefined, false, undefined, this),
94048
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(TextInput, {
94532
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(TextInput, {
94049
94533
  placeholder: "••••••••",
94050
94534
  onSubmit: submitConfirmPassword
94051
94535
  }, undefined, false, undefined, this)
@@ -94053,28 +94537,28 @@ function Onboarding({ reason = "fresh", onDone }) {
94053
94537
  }, undefined, true, undefined, this)
94054
94538
  ]
94055
94539
  }, undefined, true, undefined, this),
94056
- stage === "enter-server-name" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94540
+ stage === "enter-server-name" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94057
94541
  flexDirection: "column",
94058
94542
  children: [
94059
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94543
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94060
94544
  color: color.muted,
94061
94545
  children: [
94062
94546
  "give this server a display name",
94063
94547
  " ",
94064
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94548
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94065
94549
  dimColor: true,
94066
94550
  children: "(leave blank to skip)"
94067
94551
  }, undefined, false, undefined, this)
94068
94552
  ]
94069
94553
  }, undefined, true, undefined, this),
94070
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94554
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94071
94555
  marginTop: 1,
94072
94556
  children: [
94073
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94557
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94074
94558
  color: color.muted,
94075
94559
  children: "> "
94076
94560
  }, undefined, false, undefined, this),
94077
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(TextInput, {
94561
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(TextInput, {
94078
94562
  placeholder: "Alice's Dox",
94079
94563
  onSubmit: submitServerName
94080
94564
  }, undefined, false, undefined, this)
@@ -94082,27 +94566,27 @@ function Onboarding({ reason = "fresh", onDone }) {
94082
94566
  }, undefined, true, undefined, this)
94083
94567
  ]
94084
94568
  }, undefined, true, undefined, this),
94085
- stage === "enter-server-description" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94569
+ stage === "enter-server-description" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94086
94570
  flexDirection: "column",
94087
94571
  children: [
94088
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94572
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94089
94573
  color: color.muted,
94090
94574
  children: [
94091
94575
  "one-line description ",
94092
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94576
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94093
94577
  dimColor: true,
94094
94578
  children: "(leave blank to skip)"
94095
94579
  }, undefined, false, undefined, this)
94096
94580
  ]
94097
94581
  }, undefined, true, undefined, this),
94098
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94582
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94099
94583
  marginTop: 1,
94100
94584
  children: [
94101
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94585
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94102
94586
  color: color.muted,
94103
94587
  children: "> "
94104
94588
  }, undefined, false, undefined, this),
94105
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(TextInput, {
94589
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(TextInput, {
94106
94590
  placeholder: "family todos",
94107
94591
  onSubmit: submitServerDesc
94108
94592
  }, undefined, false, undefined, this)
@@ -94110,23 +94594,23 @@ function Onboarding({ reason = "fresh", onDone }) {
94110
94594
  }, undefined, true, undefined, this)
94111
94595
  ]
94112
94596
  }, undefined, true, undefined, this),
94113
- stage === "submitting" && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Spinner, {
94597
+ stage === "submitting" && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Spinner, {
94114
94598
  label: intent === "login" ? "logging in…" : "creating account…"
94115
94599
  }, undefined, false, undefined, this)
94116
94600
  ]
94117
94601
  }, undefined, true, undefined, this)
94118
94602
  ]
94119
94603
  }, undefined, true, undefined, this),
94120
- error && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(ErrorAlert, {
94604
+ error && /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(ErrorAlert, {
94121
94605
  message: error
94122
94606
  }, undefined, false, undefined, this),
94123
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94607
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94124
94608
  marginTop: 1,
94125
94609
  paddingX: 1,
94126
- children: /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94610
+ children: /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94127
94611
  color: color.muted,
94128
94612
  children: [
94129
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94613
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94130
94614
  color: color.accent,
94131
94615
  children: "Ctrl+C"
94132
94616
  }, undefined, false, undefined, this),
@@ -94209,11 +94693,11 @@ function ContextStrip({
94209
94693
  chips.push({ label: "user", value: userName });
94210
94694
  if (chips.length === 0)
94211
94695
  return null;
94212
- return /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94696
+ return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94213
94697
  flexDirection: "column",
94214
- children: chips.map((c) => /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
94698
+ children: chips.map((c) => /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
94215
94699
  children: [
94216
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94700
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94217
94701
  color: color.success,
94218
94702
  children: [
94219
94703
  " ",
@@ -94221,14 +94705,14 @@ function ContextStrip({
94221
94705
  " "
94222
94706
  ]
94223
94707
  }, undefined, true, undefined, this),
94224
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94708
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94225
94709
  color: color.muted,
94226
94710
  children: [
94227
94711
  c.label,
94228
94712
  ": "
94229
94713
  ]
94230
94714
  }, undefined, true, undefined, this),
94231
- /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text, {
94715
+ /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text, {
94232
94716
  color: c.tone ?? undefined,
94233
94717
  children: c.value
94234
94718
  }, undefined, false, undefined, this)
@@ -94236,7 +94720,7 @@ function ContextStrip({
94236
94720
  }, c.label, true, undefined, this))
94237
94721
  }, undefined, false, undefined, this);
94238
94722
  }
94239
- var import_react73, jsx_dev_runtime26, DEFAULT_SERVER = "http://localhost:8080", STEPS, MIN_PASSWORD_LEN = 8;
94723
+ var import_react74, jsx_dev_runtime27, DEFAULT_SERVER = "http://localhost:6278", STEPS, MIN_PASSWORD_LEN = 8;
94240
94724
  var init_Onboarding = __esm(async () => {
94241
94725
  init_src();
94242
94726
  init_theme15();
@@ -94248,8 +94732,8 @@ var init_Onboarding = __esm(async () => {
94248
94732
  init_SectionHeader(),
94249
94733
  init_Stepper()
94250
94734
  ]);
94251
- import_react73 = __toESM(require_react(), 1);
94252
- jsx_dev_runtime26 = __toESM(require_jsx_dev_runtime(), 1);
94735
+ import_react74 = __toESM(require_react(), 1);
94736
+ jsx_dev_runtime27 = __toESM(require_jsx_dev_runtime(), 1);
94253
94737
  STEPS = ["Server", "Method", "Account", "Password"];
94254
94738
  });
94255
94739
 
@@ -94259,9 +94743,9 @@ __export(exports_tui, {
94259
94743
  runTui: () => runTui
94260
94744
  });
94261
94745
  function Root({ initialConfig, initialReason }) {
94262
- const [config2, setConfig] = import_react74.useState(initialConfig);
94746
+ const [config2, setConfig] = import_react75.useState(initialConfig);
94263
94747
  if (!config2) {
94264
- return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Onboarding, {
94748
+ return /* @__PURE__ */ jsx_dev_runtime28.jsxDEV(Onboarding, {
94265
94749
  reason: initialReason,
94266
94750
  onDone: setConfig
94267
94751
  }, undefined, false, undefined, this);
@@ -94272,7 +94756,7 @@ function Root({ initialConfig, initialReason }) {
94272
94756
  const events = new EventClient(fetcher2, config2.server);
94273
94757
  const users = new UserClient(fetcher2, config2.server);
94274
94758
  const invites = new InviteClient(fetcher2, config2.server);
94275
- return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(App2, {
94759
+ return /* @__PURE__ */ jsx_dev_runtime28.jsxDEV(App2, {
94276
94760
  api,
94277
94761
  projects,
94278
94762
  events,
@@ -94299,13 +94783,13 @@ async function runTui() {
94299
94783
  initialReason = "reauth";
94300
94784
  }
94301
94785
  }
94302
- const app = render_default(/* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Root, {
94786
+ const app2 = render_default(/* @__PURE__ */ jsx_dev_runtime28.jsxDEV(Root, {
94303
94787
  initialConfig,
94304
94788
  initialReason
94305
94789
  }, undefined, false, undefined, this));
94306
- await app.waitUntilExit();
94790
+ await app2.waitUntilExit();
94307
94791
  }
94308
- var import_react74, jsx_dev_runtime27;
94792
+ var import_react75, jsx_dev_runtime28;
94309
94793
  var init_tui = __esm(async () => {
94310
94794
  init_src();
94311
94795
  await __promiseAll([
@@ -94313,8 +94797,8 @@ var init_tui = __esm(async () => {
94313
94797
  init_App2(),
94314
94798
  init_Onboarding()
94315
94799
  ]);
94316
- import_react74 = __toESM(require_react(), 1);
94317
- jsx_dev_runtime27 = __toESM(require_jsx_dev_runtime(), 1);
94800
+ import_react75 = __toESM(require_react(), 1);
94801
+ jsx_dev_runtime28 = __toESM(require_jsx_dev_runtime(), 1);
94318
94802
  });
94319
94803
 
94320
94804
  // node_modules/.bun/commander@14.0.3/node_modules/commander/esm.mjs
@@ -94333,6 +94817,29 @@ var {
94333
94817
  Help
94334
94818
  } = import__.default;
94335
94819
 
94820
+ // apps/cli/src/banner.ts
94821
+ var LOGO_ROWS = [
94822
+ " __ ",
94823
+ " ____/ /___ _ __",
94824
+ " / __ / __ \\| |/_/",
94825
+ "/ /_/ / /_/ /> < ",
94826
+ "\\__,_/\\____/_/|_| "
94827
+ ];
94828
+ var BRAND_ON = "\x1B[1;95m";
94829
+ var ANSI_OFF = "\x1B[0m";
94830
+ function renderVersionBanner() {
94831
+ const color = process.stdout.isTTY;
94832
+ const lines = [""];
94833
+ for (const row of LOGO_ROWS) {
94834
+ lines.push(color ? `${BRAND_ON}${row}${ANSI_OFF}` : row);
94835
+ }
94836
+ lines.push("");
94837
+ lines.push(` dox ${VERSION} (${COMMIT})`);
94838
+ lines.push("");
94839
+ return lines.join(`
94840
+ `);
94841
+ }
94842
+
94336
94843
  // node_modules/.bun/@clack+core@0.4.2/node_modules/@clack/core/dist/index.mjs
94337
94844
  var import_sisteransi = __toESM(require_src(), 1);
94338
94845
  import { stdin as j, stdout as M } from "node:process";
@@ -94911,7 +95418,7 @@ async function acceptInviteCmd(codeArg, opts) {
94911
95418
  }
94912
95419
  return;
94913
95420
  }
94914
- const server = opts.server ?? await promptText("Server URL:", "http://localhost:8080");
95421
+ const server = opts.server ?? await promptText("Server URL:", "http://localhost:6278");
94915
95422
  try {
94916
95423
  await fetchServerInfo(server);
94917
95424
  } catch (err) {
@@ -94947,6 +95454,29 @@ async function passwdCmd(opts) {
94947
95454
  console.log("password changed.");
94948
95455
  }
94949
95456
 
95457
+ // apps/cli/src/cli/config.ts
95458
+ init_src();
95459
+ function loadOrExit() {
95460
+ return loadConfig().then((cfg) => {
95461
+ if (!cfg) {
95462
+ console.error("dox: not logged in. Run 'dox register --server <url>' first.");
95463
+ process.exit(1);
95464
+ }
95465
+ return cfg;
95466
+ });
95467
+ }
95468
+ async function setDefaultProject(value, _opts) {
95469
+ const cfg = await loadOrExit();
95470
+ const clear = value === "" || value === "none" || value === "clear";
95471
+ cfg.defaultProject = clear ? undefined : value;
95472
+ await saveConfig(cfg);
95473
+ if (clear) {
95474
+ console.log(`default project cleared (${getConfigPath()}).`);
95475
+ } else {
95476
+ console.log(`default project = ${value} (${getConfigPath()}).`);
95477
+ }
95478
+ }
95479
+
94950
95480
  // apps/cli/src/cli/context.ts
94951
95481
  init_src();
94952
95482
  async function buildContext(opts) {
@@ -94980,8 +95510,29 @@ async function withContext(opts, fn) {
94980
95510
  }
94981
95511
  }
94982
95512
 
95513
+ // apps/cli/src/cli/invite.ts
95514
+ var list = (opts) => withContext(opts, async ({ invites, output: output2 }) => {
95515
+ const rows = await invites.listOutgoing();
95516
+ if (rows.length === 0) {
95517
+ output2.ok("(no outgoing invites)", { invites: rows });
95518
+ return;
95519
+ }
95520
+ const lines = rows.map((r) => {
95521
+ const hash = r.codeHash.slice(0, 12);
95522
+ const expires = new Date(Number(r.expiresAt)).toISOString();
95523
+ const target = r.projectId ? `project=${r.projectName}` : "server-level";
95524
+ return `${hash}… ${r.role.padEnd(7)} ${target.padEnd(28)} expires ${expires}`;
95525
+ });
95526
+ output2.ok(lines.join(`
95527
+ `), { invites: rows });
95528
+ });
95529
+ var revoke = (codeHash, opts) => withContext(opts, async ({ invites, output: output2 }) => {
95530
+ await invites.revoke(codeHash);
95531
+ output2.ok(`revoked ${codeHash}`, { revoked: codeHash });
95532
+ });
95533
+
94983
95534
  // apps/cli/src/cli/project.ts
94984
- var list = (opts) => withContext(opts, async ({ projects, output: output2 }) => {
95535
+ var list2 = (opts) => withContext(opts, async ({ projects, output: output2 }) => {
94985
95536
  const rows = await projects.list();
94986
95537
  output2.ok(rows.map((p2) => `${p2.id.slice(0, 8)} ${p2.archived ? "[archived] " : ""}${p2.name}`).join(`
94987
95538
  `) || "(no projects)", { projects: rows });
@@ -95074,6 +95625,28 @@ Temp password: ${tempPassword}
95074
95625
  Relay this out-of-band; the user should run \`dox passwd\` after first login.`, { userName, tempPassword });
95075
95626
  });
95076
95627
 
95628
+ // apps/cli/src/cli/helpers.ts
95629
+ import { readFile as readFile2 } from "node:fs/promises";
95630
+ async function readDescription(opts) {
95631
+ if (opts.description !== undefined && opts.descriptionFile !== undefined) {
95632
+ throw new Error("use either --description or --description-file, not both");
95633
+ }
95634
+ if (opts.description !== undefined)
95635
+ return opts.description;
95636
+ if (opts.descriptionFile === undefined)
95637
+ return;
95638
+ if (opts.descriptionFile === "-")
95639
+ return readStdin();
95640
+ return readFile2(opts.descriptionFile, "utf-8");
95641
+ }
95642
+ async function readStdin() {
95643
+ let data = "";
95644
+ process.stdin.setEncoding("utf-8");
95645
+ for await (const chunk of process.stdin)
95646
+ data += chunk;
95647
+ return data;
95648
+ }
95649
+
95077
95650
  // apps/cli/src/cli/todo.ts
95078
95651
  function resolveFilter(opts, defaultProject) {
95079
95652
  const raw = opts.project ?? defaultProject;
@@ -95081,7 +95654,7 @@ function resolveFilter(opts, defaultProject) {
95081
95654
  return;
95082
95655
  return raw;
95083
95656
  }
95084
- var list2 = (opts) => withContext(opts, async ({ api, output: output2, defaultProject }) => {
95657
+ var list3 = (opts) => withContext(opts, async ({ api, output: output2, defaultProject }) => {
95085
95658
  const todos = await api.listTodos(resolveFilter(opts, defaultProject));
95086
95659
  output2.todos(todos);
95087
95660
  });
@@ -95091,8 +95664,10 @@ var get = (id, opts) => withContext(opts, async ({ api, output: output2 }) => {
95091
95664
  });
95092
95665
  var add = (title, opts) => withContext(opts, async ({ api, output: output2, defaultProject }) => {
95093
95666
  const project2 = resolveFilter(opts, defaultProject);
95667
+ const description = await readDescription(opts);
95094
95668
  const todo2 = await api.createTodo(title, {
95095
- projectId: project2 === "inbox" ? undefined : project2
95669
+ projectId: project2 === "inbox" ? undefined : project2,
95670
+ description
95096
95671
  });
95097
95672
  output2.todo(todo2);
95098
95673
  });
@@ -95104,8 +95679,20 @@ var undone = (id, opts) => withContext(opts, async ({ api, output: output2 }) =>
95104
95679
  const todo2 = await api.updateTodo(id, { done: false });
95105
95680
  output2.todo(todo2);
95106
95681
  });
95107
- var edit = (id, title, opts) => withContext(opts, async ({ api, output: output2 }) => {
95108
- const todo2 = await api.updateTodo(id, { title });
95682
+ var edit = (id, opts) => withContext(opts, async ({ api, output: output2 }) => {
95683
+ if (opts.clearDescription) {
95684
+ if (opts.description !== undefined || opts.descriptionFile !== undefined) {
95685
+ throw new Error("--clear-description cannot be combined with --description / --description-file");
95686
+ }
95687
+ }
95688
+ const description = opts.clearDescription ? "" : await readDescription(opts);
95689
+ if (opts.title === undefined && description === undefined) {
95690
+ throw new Error("nothing to update — pass --title, --description, --description-file, or --clear-description");
95691
+ }
95692
+ const todo2 = await api.updateTodo(id, {
95693
+ title: opts.title,
95694
+ description
95695
+ });
95109
95696
  output2.todo(todo2);
95110
95697
  });
95111
95698
  var rm2 = (id, opts) => withContext(opts, async ({ api, output: output2 }) => {
@@ -95114,7 +95701,7 @@ var rm2 = (id, opts) => withContext(opts, async ({ api, output: output2 }) => {
95114
95701
  });
95115
95702
 
95116
95703
  // apps/cli/src/version.ts
95117
- var VERSION = "v0.1.0";
95704
+ var VERSION2 = "v1.0.0";
95118
95705
 
95119
95706
  // apps/cli/src/index.ts
95120
95707
  var args = process.argv.slice(2);
@@ -95123,19 +95710,24 @@ if (args.length === 0 && process.stdout.isTTY) {
95123
95710
  await runTui2();
95124
95711
  process.exit(0);
95125
95712
  }
95713
+ if (args[0] === "--version" || args[0] === "-V" || args[0] === "version") {
95714
+ process.stdout.write(renderVersionBanner() + `
95715
+ `);
95716
+ process.exit(0);
95717
+ }
95126
95718
  var program2 = new Command;
95127
- program2.name("dox").description("Self-hosted personal todo \u2014 thin client").version(VERSION).option("--json", "output JSON for machine consumption");
95128
- program2.command("register").description("Create an account on a dox server (first user becomes the owner)").requiredOption("--server <url>", "server URL, e.g. http://localhost:8080").option("--name <username>", "username").option("--password <password>", "password (min 8 chars)").option("--invite <code>", "invite code (required if registration is closed)").action(registerCmd);
95129
- program2.command("login").description("Log in to an existing account on a dox server").requiredOption("--server <url>", "server URL, e.g. http://localhost:8080").option("--name <username>", "username").option("--password <password>", "password").action(loginCmd);
95719
+ program2.name("dox").description("Self-hosted personal todo \u2014 thin client").version(VERSION2).option("--json", "output JSON for machine consumption");
95720
+ program2.command("register").description("Create an account on a dox server (first user becomes the owner)").requiredOption("--server <url>", "server URL, e.g. http://localhost:6278").option("--name <username>", "username").option("--password <password>", "password (min 8 chars)").option("--invite <code>", "invite code (required if registration is closed)").action(registerCmd);
95721
+ program2.command("login").description("Log in to an existing account on a dox server").requiredOption("--server <url>", "server URL, e.g. http://localhost:6278").option("--name <username>", "username").option("--password <password>", "password").action(loginCmd);
95130
95722
  program2.command("logout").description("Remove local credentials. Server-side token expires on its own.").action(logoutCmd);
95131
95723
  program2.command("passwd").description("Change your password").option("--old <password>", "current password").option("--new <password>", "new password (min 8 chars)").action(function() {
95132
95724
  return passwdCmd(this.optsWithGlobals());
95133
95725
  });
95134
95726
  program2.command("accept <code>").description("Accept an invite (joins a project, or registers + joins if not logged in)").option("--server <url>", "server URL (only needed if not logged in)").action(acceptInviteCmd);
95135
95727
  program2.command("list").alias("ls").description("List visible todos (Inbox + all projects you can see)").option("--project <id>", "filter to a project, 'inbox', or 'all'").action(function() {
95136
- return list2(this.optsWithGlobals());
95728
+ return list3(this.optsWithGlobals());
95137
95729
  });
95138
- program2.command("add <title>").description("Create a new todo").option("--project <id>", "place in a specific project (default: Inbox)").action(function(title) {
95730
+ program2.command("add <title>").description("Create a new todo").option("--project <id>", "place in a specific project (default: Inbox)").option("--description <text>", "markdown body").option("--description-file <path>", "read markdown body from file ('-' for stdin)").action(function(title) {
95139
95731
  return add(title, this.optsWithGlobals());
95140
95732
  });
95141
95733
  program2.command("get <id>").description("Show a single todo by id (or unique prefix)").action(function(id) {
@@ -95147,17 +95739,17 @@ program2.command("done <id>").description("Mark a todo as done").action(function
95147
95739
  program2.command("undone <id>").description("Mark a todo as not done").action(function(id) {
95148
95740
  return undone(id, this.optsWithGlobals());
95149
95741
  });
95150
- program2.command("edit <id>").description("Edit a todo's title").requiredOption("--title <text>", "new title").action(function(id, opts) {
95151
- return edit(id, opts.title, this.optsWithGlobals());
95742
+ program2.command("edit <id>").description("Edit a todo. For rich multi-line markdown editing, run `dox` for the TUI.").option("--title <text>", "new title").option("--description <text>", "new markdown body").option("--description-file <path>", "read new markdown body from file ('-' for stdin)").option("--clear-description", "remove the markdown body").action(function(id) {
95743
+ return edit(id, this.optsWithGlobals());
95152
95744
  });
95153
95745
  program2.command("rm <id>").alias("del").description("Delete a todo permanently").action(function(id) {
95154
95746
  return rm2(id, this.optsWithGlobals());
95155
95747
  });
95156
95748
  var proj = program2.command("project").alias("p").description("Manage projects");
95157
95749
  proj.command("list").alias("ls").description("List projects you can see").action(function() {
95158
- return list(this.optsWithGlobals());
95750
+ return list2(this.optsWithGlobals());
95159
95751
  });
95160
- proj.command("create <name>").description("Create a project (you become its owner)").option("--description <text>").option("--color <code>").action(function(name) {
95752
+ proj.command("create <name>").description("Create a project (you become its owner). description/color can only be edited later from the TUI \u2014 run `dox`.").option("--description <text>").option("--color <code>").action(function(name) {
95161
95753
  return create(name, this.optsWithGlobals());
95162
95754
  });
95163
95755
  proj.command("rename <id> <name>").description("Rename a project (owner only)").action(function(id, name) {
@@ -95203,4 +95795,15 @@ srv.command("set-description <desc>").description("Set the server's one-line des
95203
95795
  srv.command("reset-password <user-name>").description("Reset a user's password and print a one-time temp password").action(function(name) {
95204
95796
  return resetUserPassword(name, this.optsWithGlobals());
95205
95797
  });
95798
+ var inv = program2.command("invite").description("Manage outgoing invite codes");
95799
+ inv.command("list").alias("ls").description("List invite codes you've issued (not yet redeemed/expired)").action(function() {
95800
+ return list(this.optsWithGlobals());
95801
+ });
95802
+ inv.command("revoke <codeHash>").description("Revoke an outgoing invite by its codeHash (from `invite list`)").action(function(hash) {
95803
+ return revoke(hash, this.optsWithGlobals());
95804
+ });
95805
+ var cfg = program2.command("config").description("Read / write ~/.config/dox/config.toml");
95806
+ cfg.command("default-project <value>").description("Set default project filter. value = project id, 'inbox', or 'none' to clear.").action(function(value) {
95807
+ return setDefaultProject(value, this.optsWithGlobals());
95808
+ });
95206
95809
  await program2.parseAsync(process.argv);