brainfile 0.21.2 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/__tests__/helpers/mcp-wire.d.ts +2 -0
- package/dist/__tests__/helpers/mcp-wire.d.ts.map +1 -1
- package/dist/__tests__/helpers/mcp-wire.js +4 -2
- package/dist/__tests__/helpers/mcp-wire.js.map +1 -1
- package/dist/chunk-CWUTU5VV.mjs +5585 -0
- package/dist/chunk-L4WSDMK3.mjs +1973 -0
- package/dist/{chunk-7KU2R7QC.mjs → chunk-XFDTTLSW.mjs} +3230 -4322
- package/dist/cli.js +7 -1
- package/dist/cli.mjs +8078 -5357
- package/dist/commands/add.d.ts +4 -0
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +68 -72
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/boards.d.ts +18 -0
- package/dist/commands/boards.d.ts.map +1 -0
- package/dist/commands/boards.js +166 -0
- package/dist/commands/boards.js.map +1 -0
- package/dist/commands/brief.d.ts +17 -11
- package/dist/commands/brief.d.ts.map +1 -1
- package/dist/commands/brief.js +38 -35
- package/dist/commands/brief.js.map +1 -1
- package/dist/commands/check.d.ts +47 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +134 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/columns.d.ts +48 -0
- package/dist/commands/columns.d.ts.map +1 -0
- package/dist/commands/columns.js +132 -0
- package/dist/commands/columns.js.map +1 -0
- package/dist/commands/complete.js +9 -4
- package/dist/commands/complete.js.map +1 -1
- package/dist/commands/decide.d.ts +72 -0
- package/dist/commands/decide.d.ts.map +1 -0
- package/dist/commands/decide.js +264 -0
- package/dist/commands/decide.js.map +1 -0
- package/dist/commands/hooks.d.ts +5 -1
- package/dist/commands/hooks.d.ts.map +1 -1
- package/dist/commands/hooks.js +10 -11
- package/dist/commands/hooks.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +8 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +27 -0
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +127 -20
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +216 -7
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/log.d.ts +51 -0
- package/dist/commands/log.d.ts.map +1 -1
- package/dist/commands/log.js +113 -84
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/mcp.d.ts +5 -2
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +44 -9
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/memory-index.d.ts +34 -0
- package/dist/commands/memory-index.d.ts.map +1 -0
- package/dist/commands/memory-index.js +135 -0
- package/dist/commands/memory-index.js.map +1 -0
- package/dist/commands/merge-driver.d.ts +1 -15
- package/dist/commands/merge-driver.d.ts.map +1 -1
- package/dist/commands/merge-driver.js +8 -134
- package/dist/commands/merge-driver.js.map +1 -1
- package/dist/commands/migrate-tracked.d.ts +1 -1
- package/dist/commands/migrate-tracked.d.ts.map +1 -1
- package/dist/commands/migrate-tracked.js +21 -10
- package/dist/commands/migrate-tracked.js.map +1 -1
- package/dist/commands/patch.d.ts +23 -4
- package/dist/commands/patch.d.ts.map +1 -1
- package/dist/commands/patch.js +149 -91
- package/dist/commands/patch.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +11 -5
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/schema.d.ts +12 -0
- package/dist/commands/schema.d.ts.map +1 -1
- package/dist/commands/schema.js +28 -10
- package/dist/commands/schema.js.map +1 -1
- package/dist/commands/search.d.ts +46 -12
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +89 -44
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +175 -10
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/template.d.ts +2 -0
- package/dist/commands/template.d.ts.map +1 -1
- package/dist/commands/template.js +2 -1
- package/dist/commands/template.js.map +1 -1
- package/dist/commands/types.d.ts +31 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js +122 -22
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/where.d.ts +20 -1
- package/dist/commands/where.d.ts.map +1 -1
- package/dist/commands/where.js +56 -6
- package/dist/commands/where.js.map +1 -1
- package/dist/hook.mjs +15 -0
- package/dist/hooks/session-start.d.ts +39 -0
- package/dist/hooks/session-start.d.ts.map +1 -0
- package/dist/hooks/session-start.js +181 -0
- package/dist/hooks/session-start.js.map +1 -0
- package/dist/lib/contractRunner.d.ts.map +1 -1
- package/dist/lib/contractRunner.js +8 -0
- package/dist/lib/contractRunner.js.map +1 -1
- package/dist/mcp/helpers.d.ts +29 -1
- package/dist/mcp/helpers.d.ts.map +1 -1
- package/dist/mcp/helpers.js +57 -2
- package/dist/mcp/helpers.js.map +1 -1
- package/dist/mcp/instructions.d.ts +5 -0
- package/dist/mcp/instructions.d.ts.map +1 -0
- package/dist/mcp/instructions.js +57 -0
- package/dist/mcp/instructions.js.map +1 -0
- package/dist/mcp/resources.d.ts +9 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +29 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/schemas.d.ts +600 -9
- package/dist/mcp/schemas.d.ts.map +1 -1
- package/dist/mcp/schemas.js +374 -12
- package/dist/mcp/schemas.js.map +1 -1
- package/dist/mcp/tools/adr_promote_tool.d.ts +7 -0
- package/dist/mcp/tools/adr_promote_tool.d.ts.map +1 -0
- package/dist/mcp/tools/adr_promote_tool.js +37 -0
- package/dist/mcp/tools/adr_promote_tool.js.map +1 -0
- package/dist/mcp/tools/board_tool.d.ts +7 -0
- package/dist/mcp/tools/board_tool.d.ts.map +1 -0
- package/dist/mcp/tools/board_tool.js +61 -0
- package/dist/mcp/tools/board_tool.js.map +1 -0
- package/dist/mcp/tools/brief_tool.d.ts.map +1 -1
- package/dist/mcp/tools/brief_tool.js +13 -5
- package/dist/mcp/tools/brief_tool.js.map +1 -1
- package/dist/mcp/tools/check_tool.d.ts +6 -0
- package/dist/mcp/tools/check_tool.d.ts.map +1 -0
- package/dist/mcp/tools/check_tool.js +54 -0
- package/dist/mcp/tools/check_tool.js.map +1 -0
- package/dist/mcp/tools/columns_tool.d.ts +7 -0
- package/dist/mcp/tools/columns_tool.d.ts.map +1 -0
- package/dist/mcp/tools/columns_tool.js +94 -0
- package/dist/mcp/tools/columns_tool.js.map +1 -0
- package/dist/mcp/tools/decide_tool.d.ts +91 -0
- package/dist/mcp/tools/decide_tool.d.ts.map +1 -0
- package/dist/mcp/tools/decide_tool.js +141 -0
- package/dist/mcp/tools/decide_tool.js.map +1 -0
- package/dist/mcp/tools/get_task_tool.d.ts.map +1 -1
- package/dist/mcp/tools/get_task_tool.js +62 -4
- package/dist/mcp/tools/get_task_tool.js.map +1 -1
- package/dist/mcp/tools/lint_tool.d.ts +7 -0
- package/dist/mcp/tools/lint_tool.d.ts.map +1 -0
- package/dist/mcp/tools/lint_tool.js +48 -0
- package/dist/mcp/tools/lint_tool.js.map +1 -0
- package/dist/mcp/tools/list_tasks_tool.d.ts.map +1 -1
- package/dist/mcp/tools/list_tasks_tool.js +132 -15
- package/dist/mcp/tools/list_tasks_tool.js.map +1 -1
- package/dist/mcp/tools/log_tool.d.ts +4 -0
- package/dist/mcp/tools/log_tool.d.ts.map +1 -0
- package/dist/mcp/tools/log_tool.js +52 -0
- package/dist/mcp/tools/log_tool.js.map +1 -0
- package/dist/mcp/tools/note_tool.d.ts +7 -0
- package/dist/mcp/tools/note_tool.d.ts.map +1 -0
- package/dist/mcp/tools/note_tool.js +39 -0
- package/dist/mcp/tools/note_tool.js.map +1 -0
- package/dist/mcp/tools/retire_tool.d.ts +4 -0
- package/dist/mcp/tools/retire_tool.d.ts.map +1 -0
- package/dist/mcp/tools/retire_tool.js +81 -0
- package/dist/mcp/tools/retire_tool.js.map +1 -0
- package/dist/mcp/tools/search_tool.d.ts.map +1 -1
- package/dist/mcp/tools/search_tool.js +36 -19
- package/dist/mcp/tools/search_tool.js.map +1 -1
- package/dist/mcp/tools/supersede_tool.d.ts +4 -0
- package/dist/mcp/tools/supersede_tool.d.ts.map +1 -0
- package/dist/mcp/tools/supersede_tool.js +72 -0
- package/dist/mcp/tools/supersede_tool.js.map +1 -0
- package/dist/mcp/tools/sync_tool.d.ts +9 -0
- package/dist/mcp/tools/sync_tool.d.ts.map +1 -0
- package/dist/mcp/tools/sync_tool.js +87 -0
- package/dist/mcp/tools/sync_tool.js.map +1 -0
- package/dist/mcp/tools/task_add_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_add_tool.js +39 -17
- package/dist/mcp/tools/task_add_tool.js.map +1 -1
- package/dist/mcp/tools/task_complete_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_complete_tool.js +3 -2
- package/dist/mcp/tools/task_complete_tool.js.map +1 -1
- package/dist/mcp/tools/task_patch_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_patch_tool.js +30 -5
- package/dist/mcp/tools/task_patch_tool.js.map +1 -1
- package/dist/mcp/tools/template_tool.d.ts +4 -0
- package/dist/mcp/tools/template_tool.d.ts.map +1 -0
- package/dist/mcp/tools/template_tool.js +70 -0
- package/dist/mcp/tools/template_tool.js.map +1 -0
- package/dist/mcp/tools/types_tool.d.ts +4 -0
- package/dist/mcp/tools/types_tool.d.ts.map +1 -0
- package/dist/mcp/tools/types_tool.js +61 -0
- package/dist/mcp/tools/types_tool.js.map +1 -0
- package/dist/mcp/tools/where_tool.d.ts +4 -0
- package/dist/mcp/tools/where_tool.d.ts.map +1 -0
- package/dist/mcp/tools/where_tool.js +73 -0
- package/dist/mcp/tools/where_tool.js.map +1 -0
- package/dist/schemas/board.json +29 -8
- package/dist/{tui-3Z7ZJV7M.mjs → tui-D6RMFL6X.mjs} +994 -315
- package/dist/utils/board-autosync.d.ts +1 -10
- package/dist/utils/board-autosync.d.ts.map +1 -1
- package/dist/utils/board-autosync.js +11 -29
- package/dist/utils/board-autosync.js.map +1 -1
- package/dist/utils/board-commit-hooks.d.ts +8 -0
- package/dist/utils/board-commit-hooks.d.ts.map +1 -1
- package/dist/utils/board-commit-hooks.js +45 -1
- package/dist/utils/board-commit-hooks.js.map +1 -1
- package/dist/utils/board-repo.d.ts +1 -119
- package/dist/utils/board-repo.d.ts.map +1 -1
- package/dist/utils/board-repo.js +43 -571
- package/dist/utils/board-repo.js.map +1 -1
- package/dist/utils/board-sync.d.ts +3 -123
- package/dist/utils/board-sync.d.ts.map +1 -1
- package/dist/utils/board-sync.js +34 -551
- package/dist/utils/board-sync.js.map +1 -1
- package/dist/utils/graph.d.ts +20 -0
- package/dist/utils/graph.d.ts.map +1 -0
- package/dist/utils/graph.js +61 -0
- package/dist/utils/graph.js.map +1 -0
- package/dist/utils/related.d.ts +21 -0
- package/dist/utils/related.d.ts.map +1 -0
- package/dist/utils/related.js +36 -0
- package/dist/utils/related.js.map +1 -0
- package/package.json +2 -2
- package/dist/tui/theme.d.ts +0 -98
- package/dist/tui/theme.d.ts.map +0 -1
- package/dist/tui/theme.js +0 -141
- package/dist/tui/theme.js.map +0 -1
|
@@ -5,30 +5,26 @@ const require = __brainfileCreateRequire(import.meta.url);
|
|
|
5
5
|
const __filename = __brainfileFileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = __brainfileDirname(__filename);
|
|
7
7
|
import {
|
|
8
|
-
GLYPHS,
|
|
9
|
-
PALETTE,
|
|
10
|
-
RULE,
|
|
11
8
|
createGitHubIssue,
|
|
12
9
|
createLinearIssue,
|
|
13
|
-
fetchBeforeRead,
|
|
14
10
|
getArchiveConfig,
|
|
15
|
-
getContractStateColor,
|
|
16
11
|
getEffectiveDestination,
|
|
17
12
|
getLinearTeams,
|
|
18
|
-
getTypeGlyph,
|
|
19
13
|
import_core,
|
|
14
|
+
import_repo,
|
|
15
|
+
import_repo2,
|
|
16
|
+
import_repo3,
|
|
20
17
|
installAutosync,
|
|
21
18
|
isGitHubAuthenticated,
|
|
22
19
|
isLinearAuthenticated,
|
|
23
20
|
markV2MigrationHintShown,
|
|
24
|
-
require_dist,
|
|
25
21
|
require_source,
|
|
26
22
|
resolveCliBrainfilePath,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
} from "./chunk-
|
|
23
|
+
shouldSuggestV2Migration
|
|
24
|
+
} from "./chunk-CWUTU5VV.mjs";
|
|
25
|
+
import {
|
|
26
|
+
require_dist
|
|
27
|
+
} from "./chunk-XFDTTLSW.mjs";
|
|
32
28
|
import {
|
|
33
29
|
__commonJS,
|
|
34
30
|
__export,
|
|
@@ -8388,14 +8384,14 @@ var require_react_reconciler_production = __commonJS({
|
|
|
8388
8384
|
}
|
|
8389
8385
|
var exports2 = {};
|
|
8390
8386
|
"use strict";
|
|
8391
|
-
var
|
|
8387
|
+
var React21 = require_react(), Scheduler2 = require_scheduler(), assign = Object.assign, REACT_LEGACY_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.element"), REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = /* @__PURE__ */ Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = /* @__PURE__ */ Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = /* @__PURE__ */ Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = /* @__PURE__ */ Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = /* @__PURE__ */ Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = /* @__PURE__ */ Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo"), REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
|
|
8392
8388
|
/* @__PURE__ */ Symbol.for("react.scope");
|
|
8393
8389
|
var REACT_ACTIVITY_TYPE = /* @__PURE__ */ Symbol.for("react.activity");
|
|
8394
8390
|
/* @__PURE__ */ Symbol.for("react.legacy_hidden");
|
|
8395
8391
|
/* @__PURE__ */ Symbol.for("react.tracing_marker");
|
|
8396
8392
|
var REACT_MEMO_CACHE_SENTINEL = /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel");
|
|
8397
8393
|
/* @__PURE__ */ Symbol.for("react.view_transition");
|
|
8398
|
-
var MAYBE_ITERATOR_SYMBOL = Symbol.iterator, REACT_CLIENT_REFERENCE = /* @__PURE__ */ Symbol.for("react.client.reference"), isArrayImpl = Array.isArray, ReactSharedInternals =
|
|
8394
|
+
var MAYBE_ITERATOR_SYMBOL = Symbol.iterator, REACT_CLIENT_REFERENCE = /* @__PURE__ */ Symbol.for("react.client.reference"), isArrayImpl = Array.isArray, ReactSharedInternals = React21.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, rendererVersion = $$$config.rendererVersion, rendererPackageName = $$$config.rendererPackageName, extraDevToolsConfig = $$$config.extraDevToolsConfig, getPublicInstance = $$$config.getPublicInstance, getRootHostContext = $$$config.getRootHostContext, getChildHostContext = $$$config.getChildHostContext, prepareForCommit = $$$config.prepareForCommit, resetAfterCommit = $$$config.resetAfterCommit, createInstance = $$$config.createInstance;
|
|
8399
8395
|
$$$config.cloneMutableInstance;
|
|
8400
8396
|
var appendInitialChild = $$$config.appendInitialChild, finalizeInitialChildren = $$$config.finalizeInitialChildren, shouldSetTextContent = $$$config.shouldSetTextContent, createTextInstance = $$$config.createTextInstance;
|
|
8401
8397
|
$$$config.cloneMutableTextInstance;
|
|
@@ -9508,7 +9504,7 @@ var require_stack_utils = __commonJS({
|
|
|
9508
9504
|
});
|
|
9509
9505
|
|
|
9510
9506
|
// src/commands/tui.tsx
|
|
9511
|
-
var
|
|
9507
|
+
var import_react46 = __toESM(require_react());
|
|
9512
9508
|
|
|
9513
9509
|
// node_modules/ink/build/render.js
|
|
9514
9510
|
import { Stream } from "node:stream";
|
|
@@ -19197,8 +19193,99 @@ var import_chalk4 = __toESM(require_source());
|
|
|
19197
19193
|
import * as fs6 from "fs";
|
|
19198
19194
|
|
|
19199
19195
|
// src/tui/BrainfileTUI.tsx
|
|
19200
|
-
var
|
|
19201
|
-
var
|
|
19196
|
+
var import_react45 = __toESM(require_react());
|
|
19197
|
+
var import_core10 = __toESM(require_dist());
|
|
19198
|
+
|
|
19199
|
+
// src/tui/theme.ts
|
|
19200
|
+
var import_core2 = __toESM(require_dist());
|
|
19201
|
+
var RAW_PALETTE = {
|
|
19202
|
+
// Text hierarchy
|
|
19203
|
+
text: "#ffffff",
|
|
19204
|
+
textSecondary: "#a0a0a0",
|
|
19205
|
+
textMuted: "#606060",
|
|
19206
|
+
textDim: "#404040",
|
|
19207
|
+
// Priority signal
|
|
19208
|
+
critical: "#ef4444",
|
|
19209
|
+
high: "#f59e0b",
|
|
19210
|
+
medium: "#3b82f6",
|
|
19211
|
+
low: "#6b7280",
|
|
19212
|
+
// Contract-state signal (design §3: draft dim · ready cyan · in_progress
|
|
19213
|
+
// yellow · delivered magenta · done green · failed/blocked red)
|
|
19214
|
+
contractDraft: "#606060",
|
|
19215
|
+
contractReady: "#06b6d4",
|
|
19216
|
+
contractInProgress: "#eab308",
|
|
19217
|
+
contractDelivered: "#c026d3",
|
|
19218
|
+
contractDone: "#22c55e",
|
|
19219
|
+
contractFailed: "#ef4444",
|
|
19220
|
+
// Feedback
|
|
19221
|
+
success: "#22c55e",
|
|
19222
|
+
warning: "#eab308",
|
|
19223
|
+
error: "#ef4444",
|
|
19224
|
+
info: "#3b82f6",
|
|
19225
|
+
accent: "#7c3aed"
|
|
19226
|
+
};
|
|
19227
|
+
function isNoColor(env3 = process.env) {
|
|
19228
|
+
return Boolean(env3.NO_COLOR);
|
|
19229
|
+
}
|
|
19230
|
+
function makePalette(noColor) {
|
|
19231
|
+
if (!noColor) return RAW_PALETTE;
|
|
19232
|
+
return new Proxy(RAW_PALETTE, { get: () => void 0 });
|
|
19233
|
+
}
|
|
19234
|
+
var NO_COLOR = isNoColor();
|
|
19235
|
+
var PALETTE = makePalette(NO_COLOR);
|
|
19236
|
+
var RULE = "\u2500";
|
|
19237
|
+
var TYPE_GLYPHS = {
|
|
19238
|
+
...import_core2.TYPE_GLYPHS,
|
|
19239
|
+
adr: import_core2.TYPE_GLYPHS.decision
|
|
19240
|
+
};
|
|
19241
|
+
function getTypeGlyph(type) {
|
|
19242
|
+
if (!type) return "";
|
|
19243
|
+
return TYPE_GLYPHS[(0, import_core2.canonicalTypeName)(type)] ?? "";
|
|
19244
|
+
}
|
|
19245
|
+
var GLYPHS = {
|
|
19246
|
+
pointer: "\u25B8",
|
|
19247
|
+
cursor: "\u258C",
|
|
19248
|
+
collapsed: "\u25B6",
|
|
19249
|
+
success: "\u2713",
|
|
19250
|
+
error: "\u2717",
|
|
19251
|
+
warning: "\u26A0",
|
|
19252
|
+
live: "\u25CF",
|
|
19253
|
+
subtaskOpen: "\u25FB",
|
|
19254
|
+
subtaskDone: "\u2611",
|
|
19255
|
+
orphanParent: "\u2190",
|
|
19256
|
+
/** Board-list row glyph for a parent-with-children that is expanded (§A1). */
|
|
19257
|
+
parentExpanded: "\u25BE",
|
|
19258
|
+
/** Board-list row glyph for a parent-with-children that is collapsed (§A1). */
|
|
19259
|
+
parentCollapsed: "\u25B8",
|
|
19260
|
+
/** Detail body scroll indicator (§B2), e.g. `↕ 2/9`. */
|
|
19261
|
+
scroll: "\u2195",
|
|
19262
|
+
/**
|
|
19263
|
+
* A decision that no longer stands (superseded, rejected, stale): the
|
|
19264
|
+
* hollow of the decision glyph `●`, as the desktop draws it (task-19).
|
|
19265
|
+
*/
|
|
19266
|
+
retiredDecision: "\u25CB",
|
|
19267
|
+
/** Fold glyphs for the decisions view's history heading. */
|
|
19268
|
+
foldOpen: "\u25BE",
|
|
19269
|
+
foldClosed: "\u25B8"
|
|
19270
|
+
};
|
|
19271
|
+
function getContractStateColor(state) {
|
|
19272
|
+
switch (state?.toLowerCase()) {
|
|
19273
|
+
case "ready":
|
|
19274
|
+
return PALETTE.contractReady;
|
|
19275
|
+
case "in_progress":
|
|
19276
|
+
return PALETTE.contractInProgress;
|
|
19277
|
+
case "delivered":
|
|
19278
|
+
return PALETTE.contractDelivered;
|
|
19279
|
+
case "done":
|
|
19280
|
+
return PALETTE.contractDone;
|
|
19281
|
+
case "failed":
|
|
19282
|
+
case "blocked":
|
|
19283
|
+
return PALETTE.contractFailed;
|
|
19284
|
+
case "draft":
|
|
19285
|
+
default:
|
|
19286
|
+
return PALETTE.contractDraft;
|
|
19287
|
+
}
|
|
19288
|
+
}
|
|
19202
19289
|
|
|
19203
19290
|
// src/tui/copy.ts
|
|
19204
19291
|
var STATUS = {
|
|
@@ -19231,14 +19318,19 @@ var STATUS = {
|
|
|
19231
19318
|
priorityUpdated: (priority) => `Priority: ${priority}`,
|
|
19232
19319
|
archived: (id) => `Archived ${id}`,
|
|
19233
19320
|
archiving: "Archiving\u2026",
|
|
19234
|
-
archiveFailed: "Could not archive"
|
|
19321
|
+
archiveFailed: "Could not archive",
|
|
19322
|
+
decisionsReadOnly: "Decisions change with brainfile decide, supersede or retire",
|
|
19323
|
+
decisionNoColumn: "Decisions have no column",
|
|
19324
|
+
cannotOpen: (id, why) => `${id} is ${why}`,
|
|
19325
|
+
noDecisionHistory: "No retired decisions"
|
|
19235
19326
|
};
|
|
19236
19327
|
var EMPTY = {
|
|
19237
19328
|
noMatches: "No matches \xB7 esc to clear",
|
|
19238
19329
|
nothingCompleted: "Nothing completed yet \xB7 L board",
|
|
19239
19330
|
emptyColumn: "No documents in this column \xB7 a add",
|
|
19240
19331
|
noType: (type) => `No ${type} documents`,
|
|
19241
|
-
noDocuments: "No documents"
|
|
19332
|
+
noDocuments: "No documents",
|
|
19333
|
+
noDecisions: "No decisions yet \xB7 brainfile decide records one"
|
|
19242
19334
|
};
|
|
19243
19335
|
var CHROME = {
|
|
19244
19336
|
filterAffordance: "/ filter ? help",
|
|
@@ -19274,49 +19366,80 @@ var HEADER_ROWS = 2;
|
|
|
19274
19366
|
var FOOTER_ROWS = 3;
|
|
19275
19367
|
|
|
19276
19368
|
// src/tui/rows.ts
|
|
19277
|
-
function
|
|
19278
|
-
|
|
19279
|
-
|
|
19280
|
-
|
|
19369
|
+
function markDecisionChanges(rows, changed) {
|
|
19370
|
+
if (changed.size === 0) return rows;
|
|
19371
|
+
return rows.map((row) => changed.has(row.task.id) ? { ...row, decisionChanged: true } : row);
|
|
19372
|
+
}
|
|
19373
|
+
function buildOutline({ columns, logs = [] }) {
|
|
19374
|
+
const columnOf = /* @__PURE__ */ new Map();
|
|
19375
|
+
for (const column of columns) for (const task of column.tasks ?? []) if (!columnOf.has(task.id)) columnOf.set(task.id, column.id);
|
|
19376
|
+
const done = logs.filter((task) => !columnOf.has(task.id));
|
|
19377
|
+
const shown = /* @__PURE__ */ new Set([...columnOf.keys(), ...done.map((task) => task.id)]);
|
|
19281
19378
|
const childrenOf = /* @__PURE__ */ new Map();
|
|
19282
|
-
|
|
19379
|
+
const hang = (task, archived, under) => {
|
|
19283
19380
|
const parentId = task.parentId;
|
|
19284
|
-
if (!parentId || parentId === task.id || !
|
|
19381
|
+
if (!parentId || parentId === task.id || !under(parentId)) return;
|
|
19285
19382
|
const bucket = childrenOf.get(parentId);
|
|
19286
|
-
if (bucket) bucket.push(task);
|
|
19287
|
-
else childrenOf.set(parentId, [task]);
|
|
19383
|
+
if (bucket) bucket.push({ task, archived });
|
|
19384
|
+
else childrenOf.set(parentId, [{ task, archived }]);
|
|
19385
|
+
};
|
|
19386
|
+
for (const column of columns) {
|
|
19387
|
+
for (const task of column.tasks ?? []) if (columnOf.get(task.id) === column.id) hang(task, false, (id) => columnOf.has(id));
|
|
19288
19388
|
}
|
|
19289
|
-
for (const task of
|
|
19290
|
-
|
|
19291
|
-
|
|
19292
|
-
|
|
19293
|
-
if (parentVisible) continue;
|
|
19389
|
+
for (const task of done) hang(task, true, (id) => shown.has(id));
|
|
19390
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
19391
|
+
const lists = new Map(columns.map((column) => [column.id, []]));
|
|
19392
|
+
const emit = (task, archived, depth, listColumn, rows) => {
|
|
19294
19393
|
emitted.add(task.id);
|
|
19295
|
-
const children = childrenOf.get(task.id) ?? [];
|
|
19296
|
-
const
|
|
19394
|
+
const children = (childrenOf.get(task.id) ?? []).filter((child) => !emitted.has(child.task.id));
|
|
19395
|
+
const own = columnOf.get(task.id);
|
|
19396
|
+
const parentId = task.parentId && task.parentId !== task.id ? task.parentId : void 0;
|
|
19297
19397
|
rows.push({
|
|
19298
19398
|
task,
|
|
19299
|
-
depth
|
|
19300
|
-
orphanParentId: parentId && !
|
|
19399
|
+
depth,
|
|
19400
|
+
orphanParentId: depth === 0 && parentId && !columnOf.has(parentId) ? parentId : void 0,
|
|
19301
19401
|
childCount: children.length > 0 ? children.length : void 0,
|
|
19302
|
-
|
|
19402
|
+
...archived ? { archived: true } : own && own !== listColumn ? { column: own } : {}
|
|
19303
19403
|
});
|
|
19304
|
-
if (isCollapsed) {
|
|
19305
|
-
for (const child of children) emitted.add(child.id);
|
|
19306
|
-
continue;
|
|
19307
|
-
}
|
|
19308
19404
|
for (const child of children) {
|
|
19309
|
-
if (emitted.has(child.id))
|
|
19310
|
-
|
|
19311
|
-
|
|
19405
|
+
if (!emitted.has(child.task.id)) emit(child.task, child.archived, depth + 1, listColumn, rows);
|
|
19406
|
+
}
|
|
19407
|
+
};
|
|
19408
|
+
for (const column of columns) {
|
|
19409
|
+
const rows = lists.get(column.id);
|
|
19410
|
+
for (const task of column.tasks ?? []) {
|
|
19411
|
+
if (emitted.has(task.id) || columnOf.get(task.id) !== column.id) continue;
|
|
19412
|
+
const parentId = task.parentId;
|
|
19413
|
+
if (parentId && parentId !== task.id && columnOf.has(parentId)) continue;
|
|
19414
|
+
emit(task, false, 0, column.id, rows);
|
|
19312
19415
|
}
|
|
19313
19416
|
}
|
|
19314
|
-
for (const
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19417
|
+
for (const column of columns) {
|
|
19418
|
+
const rows = lists.get(column.id);
|
|
19419
|
+
for (const task of column.tasks ?? []) {
|
|
19420
|
+
if (emitted.has(task.id) || columnOf.get(task.id) !== column.id) continue;
|
|
19421
|
+
const before = rows.length;
|
|
19422
|
+
emit(task, false, 0, column.id, rows);
|
|
19423
|
+
rows[before] = { ...rows[before], orphanParentId: task.parentId };
|
|
19424
|
+
}
|
|
19318
19425
|
}
|
|
19319
|
-
return
|
|
19426
|
+
return lists;
|
|
19427
|
+
}
|
|
19428
|
+
function collapseRows(rows, collapsedIds) {
|
|
19429
|
+
const out = [];
|
|
19430
|
+
let hiddenBelow = Infinity;
|
|
19431
|
+
for (const row of rows) {
|
|
19432
|
+
if (row.depth > hiddenBelow) continue;
|
|
19433
|
+
hiddenBelow = Infinity;
|
|
19434
|
+
if (!row.childCount) {
|
|
19435
|
+
out.push(row);
|
|
19436
|
+
continue;
|
|
19437
|
+
}
|
|
19438
|
+
const collapsed = Boolean(collapsedIds?.has(row.task.id));
|
|
19439
|
+
if (collapsed) hiddenBelow = row.depth;
|
|
19440
|
+
out.push({ ...row, collapsed });
|
|
19441
|
+
}
|
|
19442
|
+
return out;
|
|
19320
19443
|
}
|
|
19321
19444
|
function buildFlatRows(tasks) {
|
|
19322
19445
|
return tasks.map((task) => ({
|
|
@@ -19333,83 +19456,136 @@ function windowRows(rows, selectedIndex, viewportHeight) {
|
|
|
19333
19456
|
return { visible: rows.slice(start, start + viewportHeight), start };
|
|
19334
19457
|
}
|
|
19335
19458
|
|
|
19336
|
-
// src/tui/
|
|
19337
|
-
var
|
|
19338
|
-
|
|
19339
|
-
|
|
19340
|
-
function truncate(text, maxWidth) {
|
|
19341
|
-
if (!text) return " ";
|
|
19342
|
-
if (maxWidth <= 0) return " ";
|
|
19343
|
-
if (maxWidth <= 3) return text.slice(0, maxWidth) || " ";
|
|
19344
|
-
if (text.length <= maxWidth) return text;
|
|
19345
|
-
return `${text.slice(0, maxWidth - 1)}\u2026`;
|
|
19459
|
+
// src/tui/decisions.ts
|
|
19460
|
+
var import_core3 = __toESM(require_dist());
|
|
19461
|
+
function boardTypes(board) {
|
|
19462
|
+
return board?.types;
|
|
19346
19463
|
}
|
|
19347
|
-
function
|
|
19348
|
-
|
|
19349
|
-
if (maxWidth <= 0) return " ";
|
|
19350
|
-
if (maxWidth <= 1) return text.slice(-1) || " ";
|
|
19351
|
-
if (text.length <= maxWidth) return text;
|
|
19352
|
-
return `\u2026${text.slice(text.length - (maxWidth - 1))}`;
|
|
19464
|
+
function isDecisionTask(task, types) {
|
|
19465
|
+
return Boolean(task) && (0, import_core3.resolveDocType)(task.type, types).role === "decision";
|
|
19353
19466
|
}
|
|
19354
|
-
function
|
|
19355
|
-
if (!
|
|
19356
|
-
|
|
19357
|
-
|
|
19358
|
-
|
|
19359
|
-
|
|
19360
|
-
|
|
19361
|
-
|
|
19362
|
-
|
|
19363
|
-
|
|
19364
|
-
|
|
19365
|
-
|
|
19366
|
-
|
|
19367
|
-
|
|
19368
|
-
|
|
19369
|
-
|
|
19370
|
-
|
|
19371
|
-
|
|
19372
|
-
|
|
19467
|
+
function isDecisionStop(type, types) {
|
|
19468
|
+
if (!type || type === "all") return false;
|
|
19469
|
+
return (0, import_core3.resolveDocType)(type, types).role === "decision";
|
|
19470
|
+
}
|
|
19471
|
+
function splitDecisions(board, logs) {
|
|
19472
|
+
const types = boardTypes(board);
|
|
19473
|
+
const decisions = [];
|
|
19474
|
+
const columns = board.columns.map((column) => {
|
|
19475
|
+
const tasks = column.tasks ?? [];
|
|
19476
|
+
const work2 = tasks.filter((task) => {
|
|
19477
|
+
if (!isDecisionTask(task, types)) return true;
|
|
19478
|
+
decisions.push(task);
|
|
19479
|
+
return false;
|
|
19480
|
+
});
|
|
19481
|
+
return work2.length === tasks.length ? column : { ...column, tasks: work2 };
|
|
19482
|
+
});
|
|
19483
|
+
const seen = new Set(decisions.map((task) => task.id));
|
|
19484
|
+
const work = logs.filter((task) => {
|
|
19485
|
+
if (!isDecisionTask(task, types)) return true;
|
|
19486
|
+
if (!seen.has(task.id)) {
|
|
19487
|
+
seen.add(task.id);
|
|
19488
|
+
decisions.push(task);
|
|
19373
19489
|
}
|
|
19374
|
-
|
|
19375
|
-
|
|
19376
|
-
return
|
|
19490
|
+
return false;
|
|
19491
|
+
});
|
|
19492
|
+
return { board: { ...board, columns }, logs: work, decisions };
|
|
19377
19493
|
}
|
|
19378
|
-
function
|
|
19379
|
-
|
|
19494
|
+
function filterDecisions(decisions, query) {
|
|
19495
|
+
const q = query.trim();
|
|
19496
|
+
if (!q) return decisions;
|
|
19497
|
+
const docs = decisions.map((task) => ({ task, body: task.description ?? "" }));
|
|
19498
|
+
return (0, import_core3.searchTasksRanked)(docs, q).map((match) => match.doc.task);
|
|
19380
19499
|
}
|
|
19381
|
-
|
|
19382
|
-
|
|
19383
|
-
function getPriorityColor(priority) {
|
|
19384
|
-
switch (priority?.toLowerCase()) {
|
|
19385
|
-
case "critical":
|
|
19386
|
-
return PALETTE.critical;
|
|
19387
|
-
case "high":
|
|
19388
|
-
return PALETTE.high;
|
|
19389
|
-
case "medium":
|
|
19390
|
-
return PALETTE.medium;
|
|
19391
|
-
case "low":
|
|
19392
|
-
return PALETTE.low;
|
|
19393
|
-
default:
|
|
19394
|
-
return PALETTE.textDim;
|
|
19395
|
-
}
|
|
19500
|
+
function today() {
|
|
19501
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
19396
19502
|
}
|
|
19397
|
-
function
|
|
19398
|
-
return (
|
|
19503
|
+
function isPastValidUntil(summary, on = today()) {
|
|
19504
|
+
return Boolean(summary.validUntil) && summary.validUntil.slice(0, 10) < on;
|
|
19399
19505
|
}
|
|
19400
|
-
function
|
|
19401
|
-
if (!
|
|
19402
|
-
|
|
19506
|
+
function claimKeyTail(claimKey) {
|
|
19507
|
+
if (!claimKey) return "";
|
|
19508
|
+
const parts = claimKey.split(".");
|
|
19509
|
+
return parts.length > 1 ? parts.slice(1).join(".") : claimKey;
|
|
19403
19510
|
}
|
|
19404
|
-
function
|
|
19405
|
-
|
|
19406
|
-
if (subtasks.length === 0) return void 0;
|
|
19407
|
-
const done = subtasks.filter((s) => s.completed).length;
|
|
19408
|
-
return `${done}/${subtasks.length}`;
|
|
19511
|
+
function retiredLabel(summary) {
|
|
19512
|
+
return summary.supersededBy ? `${summary.status} \u2192 ${summary.supersededBy}` : summary.status;
|
|
19409
19513
|
}
|
|
19410
|
-
|
|
19411
|
-
|
|
19412
|
-
|
|
19514
|
+
var UNFILED = "unfiled";
|
|
19515
|
+
function buildDecisionView(decisions, { historyOpen, types }) {
|
|
19516
|
+
const lines = [];
|
|
19517
|
+
const selectable = [];
|
|
19518
|
+
const history = [];
|
|
19519
|
+
const pushRow = (task, retired) => {
|
|
19520
|
+
lines.push({ kind: "row", task, summary: (0, import_core3.summarizeDecision)(task), retired, index: selectable.length });
|
|
19521
|
+
selectable.push(task);
|
|
19522
|
+
};
|
|
19523
|
+
let standingCount = 0;
|
|
19524
|
+
for (const group of (0, import_core3.groupDecisionsByPrefix)(decisions, types)) {
|
|
19525
|
+
const standing = group.families.flatMap((family) => [...family.current, ...family.other]);
|
|
19526
|
+
history.push(...group.families.flatMap((family) => family.history));
|
|
19527
|
+
if (standing.length === 0) continue;
|
|
19528
|
+
if (lines.length > 0) lines.push({ kind: "gap" });
|
|
19529
|
+
lines.push({ kind: "heading", label: group.prefix || UNFILED, count: standing.length });
|
|
19530
|
+
for (const task of standing) pushRow(task, false);
|
|
19531
|
+
standingCount += standing.length;
|
|
19532
|
+
}
|
|
19533
|
+
if (history.length > 0) {
|
|
19534
|
+
if (standingCount === 0) lines.push({ kind: "note", text: "No standing decisions" });
|
|
19535
|
+
lines.push({ kind: "gap" });
|
|
19536
|
+
lines.push({ kind: "history", count: history.length, open: historyOpen });
|
|
19537
|
+
if (historyOpen) {
|
|
19538
|
+
for (const task of [...history].sort(import_core3.compareDecisionsNewestFirst)) pushRow(task, true);
|
|
19539
|
+
}
|
|
19540
|
+
}
|
|
19541
|
+
return { lines, selectable, standingCount, historyCount: history.length };
|
|
19542
|
+
}
|
|
19543
|
+
function selectedLineIndex(lines, selectedIndex) {
|
|
19544
|
+
const at = lines.findIndex((line) => line.kind === "row" && line.index === selectedIndex);
|
|
19545
|
+
return at < 0 ? 0 : at;
|
|
19546
|
+
}
|
|
19547
|
+
function windowLines(lines, selectedIndex, viewportHeight) {
|
|
19548
|
+
if (lines.length <= viewportHeight) return { visible: lines, start: 0 };
|
|
19549
|
+
const selected = selectedLineIndex(lines, selectedIndex);
|
|
19550
|
+
const half = Math.floor(viewportHeight / 2);
|
|
19551
|
+
const start = Math.max(0, Math.min(selected - half, lines.length - viewportHeight));
|
|
19552
|
+
return { visible: lines.slice(start, start + viewportHeight), start };
|
|
19553
|
+
}
|
|
19554
|
+
var NO_RELATIONS = { refs: [], resting: [] };
|
|
19555
|
+
function refFor(relation, id, graph) {
|
|
19556
|
+
if (id.includes(":")) return { relation, id, missing: "on a related board" };
|
|
19557
|
+
const task = graph.get(id);
|
|
19558
|
+
if (!task) return { relation, id, missing: "not on this board" };
|
|
19559
|
+
if (graph.typeOf(task).role !== "decision") return { relation, id, task, missing: "not a decision" };
|
|
19560
|
+
return { relation, id, task, summary: (0, import_core3.summarizeDecision)(task) };
|
|
19561
|
+
}
|
|
19562
|
+
function relationsFor(task, graph) {
|
|
19563
|
+
if (!task) return NO_RELATIONS;
|
|
19564
|
+
if (graph.typeOf(task).role === "decision") {
|
|
19565
|
+
const decision = (0, import_core3.summarizeDecision)(task);
|
|
19566
|
+
const refs2 = [
|
|
19567
|
+
...decision.supersedes.map((id) => refFor("replaces", id, graph)),
|
|
19568
|
+
...decision.supersededBy ? [refFor("replaced-by", decision.supersededBy, graph)] : []
|
|
19569
|
+
];
|
|
19570
|
+
const resting = graph.restingOn(task.id).filter((t) => graph.isOpenWork(t));
|
|
19571
|
+
return { decision, refs: refs2, resting };
|
|
19572
|
+
}
|
|
19573
|
+
const refs = graph.decisionsOf(task.id).map((link2) => refFor("rests-on", link2.id, graph));
|
|
19574
|
+
return { refs, resting: [] };
|
|
19575
|
+
}
|
|
19576
|
+
function isRetiredRef(ref) {
|
|
19577
|
+
return Boolean(ref.summary && (0, import_core3.isHistoricalStatus)(ref.summary.status));
|
|
19578
|
+
}
|
|
19579
|
+
function workOnRetiredDecisions(graph) {
|
|
19580
|
+
const out = /* @__PURE__ */ new Set();
|
|
19581
|
+
for (const task of graph.tasks) {
|
|
19582
|
+
if (!graph.isOpenWork(task)) continue;
|
|
19583
|
+
const links = graph.decisionsOf(task.id);
|
|
19584
|
+
if (links.some((link2) => link2.isDecision && link2.status && (0, import_core3.isHistoricalStatus)(link2.status))) {
|
|
19585
|
+
out.add(task.id);
|
|
19586
|
+
}
|
|
19587
|
+
}
|
|
19588
|
+
return out;
|
|
19413
19589
|
}
|
|
19414
19590
|
|
|
19415
19591
|
// src/tui/hooks/useBrainfileLoader.ts
|
|
@@ -21108,17 +21284,17 @@ function watch(paths, options = {}) {
|
|
|
21108
21284
|
}
|
|
21109
21285
|
|
|
21110
21286
|
// src/tui/hooks/useBrainfileLoader.ts
|
|
21111
|
-
var
|
|
21287
|
+
var import_core5 = __toESM(require_dist());
|
|
21112
21288
|
|
|
21113
21289
|
// src/tui/actions.ts
|
|
21114
21290
|
import * as fs3 from "fs";
|
|
21115
21291
|
import * as path from "path";
|
|
21116
21292
|
import { spawnSync } from "child_process";
|
|
21117
|
-
var
|
|
21293
|
+
var import_core4 = __toESM(require_dist());
|
|
21118
21294
|
function readBrainfile(filePath) {
|
|
21119
21295
|
try {
|
|
21120
21296
|
const content = fs3.readFileSync(filePath, "utf-8");
|
|
21121
|
-
const result =
|
|
21297
|
+
const result = import_core4.Brainfile.parseWithErrors(content);
|
|
21122
21298
|
if (!result.board) {
|
|
21123
21299
|
return { board: null, content, error: result.error || "Failed to parse brainfile" };
|
|
21124
21300
|
}
|
|
@@ -21129,7 +21305,7 @@ function readBrainfile(filePath) {
|
|
|
21129
21305
|
}
|
|
21130
21306
|
function writeBrainfile(filePath, board) {
|
|
21131
21307
|
try {
|
|
21132
|
-
const content =
|
|
21308
|
+
const content = import_core4.Brainfile.serialize(board);
|
|
21133
21309
|
fs3.writeFileSync(filePath, content, "utf-8");
|
|
21134
21310
|
return { success: true };
|
|
21135
21311
|
} catch (err) {
|
|
@@ -21150,61 +21326,61 @@ function readBoardState(filePath) {
|
|
|
21150
21326
|
}
|
|
21151
21327
|
function resolveV2TaskPath(filePath, taskId) {
|
|
21152
21328
|
const dirs = (0, import_core.getV2Dirs)(filePath);
|
|
21153
|
-
const doc = (0,
|
|
21329
|
+
const doc = (0, import_core4.findTask)(dirs.boardDir, taskId);
|
|
21154
21330
|
if (!doc || !doc.filePath) return null;
|
|
21155
21331
|
return { taskPath: doc.filePath, task: doc.task };
|
|
21156
21332
|
}
|
|
21157
21333
|
function moveTaskAction(filePath, taskId, targetColumnId) {
|
|
21158
21334
|
const { board, error } = readBoardState(filePath);
|
|
21159
21335
|
if (!board) return { success: false, error };
|
|
21160
|
-
const taskInfo = (0,
|
|
21336
|
+
const taskInfo = (0, import_core4.findTaskById)(board, taskId);
|
|
21161
21337
|
if (!taskInfo) return { success: false, error: `Task ${taskId} not found` };
|
|
21162
|
-
const targetColumn = (0,
|
|
21338
|
+
const targetColumn = (0, import_core4.findColumnById)(board, targetColumnId) || (0, import_core4.findColumnByName)(board, targetColumnId);
|
|
21163
21339
|
if (!targetColumn) return { success: false, error: `Column ${targetColumnId} not found` };
|
|
21164
21340
|
if (taskInfo.column.id === targetColumn.id) {
|
|
21165
21341
|
return { success: true, message: "Task already in this column" };
|
|
21166
21342
|
}
|
|
21167
21343
|
if ((0, import_core.isV2)(filePath)) {
|
|
21168
|
-
const boardFile = (0,
|
|
21344
|
+
const boardFile = (0, import_core4.readBoardConfig)(filePath);
|
|
21169
21345
|
if (!boardFile) return { success: false, error: `Failed to parse brainfile: ${filePath}` };
|
|
21170
|
-
const moveResult = (0,
|
|
21346
|
+
const moveResult = (0, import_core4.moveTaskFileToColumn)(
|
|
21171
21347
|
(0, import_core.getV2Dirs)(filePath),
|
|
21172
21348
|
boardFile.config,
|
|
21173
21349
|
taskId,
|
|
21174
21350
|
targetColumn.id
|
|
21175
21351
|
);
|
|
21176
21352
|
if (!moveResult.success) return { success: false, error: moveResult.error };
|
|
21177
|
-
scheduleBoardCommit(filePath, `move ${taskId}: ${targetColumn.id}`);
|
|
21353
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `move ${taskId}: ${targetColumn.id}`);
|
|
21178
21354
|
return {
|
|
21179
21355
|
success: true,
|
|
21180
21356
|
message: moveResult.autoCompleted ? `Completed via ${targetColumn.title}` : `Moved to ${targetColumn.title}`
|
|
21181
21357
|
};
|
|
21182
21358
|
}
|
|
21183
|
-
const result = (0,
|
|
21359
|
+
const result = (0, import_core4.moveTask)(board, taskId, taskInfo.column.id, targetColumn.id, targetColumn.tasks.length);
|
|
21184
21360
|
if (!result.success) return { success: false, error: result.error };
|
|
21185
21361
|
const writeResult = writeBrainfile(filePath, result.board);
|
|
21186
21362
|
if (!writeResult.success) return writeResult;
|
|
21187
|
-
scheduleBoardCommit(filePath, `move ${taskId}: ${targetColumn.id}`);
|
|
21363
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `move ${taskId}: ${targetColumn.id}`);
|
|
21188
21364
|
return { success: true, message: `Moved to ${targetColumn.title}` };
|
|
21189
21365
|
}
|
|
21190
21366
|
function deleteTaskAction(filePath, taskId) {
|
|
21191
21367
|
const { board, error } = readBoardState(filePath);
|
|
21192
21368
|
if (!board) return { success: false, error };
|
|
21193
|
-
const taskInfo = (0,
|
|
21369
|
+
const taskInfo = (0, import_core4.findTaskById)(board, taskId);
|
|
21194
21370
|
if (!taskInfo) return { success: false, error: `Task ${taskId} not found` };
|
|
21195
21371
|
if ((0, import_core.isV2)(filePath)) {
|
|
21196
21372
|
const located = resolveV2TaskPath(filePath, taskId);
|
|
21197
21373
|
if (!located) return { success: false, error: `Task ${taskId} not found` };
|
|
21198
|
-
const result2 = (0,
|
|
21374
|
+
const result2 = (0, import_core4.deleteTaskFile)(located.taskPath);
|
|
21199
21375
|
if (!result2.success) return { success: false, error: result2.error };
|
|
21200
|
-
scheduleBoardCommit(filePath, `delete ${taskId}`);
|
|
21376
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `delete ${taskId}`);
|
|
21201
21377
|
return { success: true, message: `Deleted ${taskId}` };
|
|
21202
21378
|
}
|
|
21203
|
-
const result = (0,
|
|
21379
|
+
const result = (0, import_core4.deleteTask)(board, taskInfo.column.id, taskId);
|
|
21204
21380
|
if (!result.success) return { success: false, error: result.error };
|
|
21205
21381
|
const writeResult = writeBrainfile(filePath, result.board);
|
|
21206
21382
|
if (!writeResult.success) return writeResult;
|
|
21207
|
-
scheduleBoardCommit(filePath, `delete ${taskId}`);
|
|
21383
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `delete ${taskId}`);
|
|
21208
21384
|
return { success: true, message: `Deleted ${taskId}` };
|
|
21209
21385
|
}
|
|
21210
21386
|
function getArchivePath(filePath) {
|
|
@@ -21225,7 +21401,7 @@ function archiveTaskAction(filePath, taskId, options) {
|
|
|
21225
21401
|
const dirs = (0, import_core.getV2Dirs)(filePath);
|
|
21226
21402
|
const located = resolveV2TaskPath(filePath, taskId);
|
|
21227
21403
|
if (!located) return { success: false, error: `Task ${taskId} not found` };
|
|
21228
|
-
const result2 = (0,
|
|
21404
|
+
const result2 = (0, import_core4.completeTaskFile)(located.taskPath, dirs.logsDir, { force: options?.force });
|
|
21229
21405
|
if (!result2.success) {
|
|
21230
21406
|
return {
|
|
21231
21407
|
success: false,
|
|
@@ -21233,16 +21409,16 @@ function archiveTaskAction(filePath, taskId, options) {
|
|
|
21233
21409
|
incompleteChildren: result2.incompleteChildren
|
|
21234
21410
|
};
|
|
21235
21411
|
}
|
|
21236
|
-
scheduleBoardCommit(filePath, `complete ${taskId}`);
|
|
21412
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `complete ${taskId}`);
|
|
21237
21413
|
return { success: true, message: `Moved ${taskId} to logs` };
|
|
21238
21414
|
}
|
|
21239
21415
|
const { board, error } = readBrainfile(filePath);
|
|
21240
21416
|
if (!board) return { success: false, error };
|
|
21241
|
-
const taskInfo = (0,
|
|
21417
|
+
const taskInfo = (0, import_core4.findTaskById)(board, taskId);
|
|
21242
21418
|
if (!taskInfo) return { success: false, error: `Task ${taskId} not found` };
|
|
21243
21419
|
const task = taskInfo.task;
|
|
21244
21420
|
const columnId = taskInfo.column.id;
|
|
21245
|
-
const result = (0,
|
|
21421
|
+
const result = (0, import_core4.deleteTask)(board, columnId, taskId);
|
|
21246
21422
|
if (!result.success) return { success: false, error: result.error };
|
|
21247
21423
|
const writeResult = writeBrainfile(filePath, result.board);
|
|
21248
21424
|
if (!writeResult.success) return writeResult;
|
|
@@ -21251,7 +21427,7 @@ function archiveTaskAction(filePath, taskId, options) {
|
|
|
21251
21427
|
if (fs3.existsSync(archivePath)) {
|
|
21252
21428
|
try {
|
|
21253
21429
|
const archiveContent = fs3.readFileSync(archivePath, "utf-8");
|
|
21254
|
-
const parseResult =
|
|
21430
|
+
const parseResult = import_core4.Brainfile.parseWithErrors(archiveContent);
|
|
21255
21431
|
if (parseResult.board) {
|
|
21256
21432
|
archiveBoard = parseResult.board;
|
|
21257
21433
|
} else {
|
|
@@ -21268,7 +21444,7 @@ function archiveTaskAction(filePath, taskId, options) {
|
|
|
21268
21444
|
}
|
|
21269
21445
|
archiveBoard.archive.unshift(task);
|
|
21270
21446
|
try {
|
|
21271
|
-
const archiveContent =
|
|
21447
|
+
const archiveContent = import_core4.Brainfile.serialize(archiveBoard);
|
|
21272
21448
|
fs3.writeFileSync(archivePath, archiveContent, "utf-8");
|
|
21273
21449
|
} catch (err) {
|
|
21274
21450
|
return {
|
|
@@ -21276,13 +21452,13 @@ function archiveTaskAction(filePath, taskId, options) {
|
|
|
21276
21452
|
error: `Task removed from board but failed to write logs: ${err instanceof Error ? err.message : String(err)}`
|
|
21277
21453
|
};
|
|
21278
21454
|
}
|
|
21279
|
-
scheduleBoardCommit(filePath, `complete ${taskId}`);
|
|
21455
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `complete ${taskId}`);
|
|
21280
21456
|
return { success: true, message: `Moved ${taskId} to logs` };
|
|
21281
21457
|
}
|
|
21282
21458
|
async function archiveTaskActionAsync(filePath, taskId, options) {
|
|
21283
21459
|
const { board, error } = readBoardState(filePath);
|
|
21284
21460
|
if (!board) return { success: false, error };
|
|
21285
|
-
const taskInfo = (0,
|
|
21461
|
+
const taskInfo = (0, import_core4.findTaskById)(board, taskId);
|
|
21286
21462
|
if (!taskInfo) return { success: false, error: `Task ${taskId} not found` };
|
|
21287
21463
|
const task = taskInfo.task;
|
|
21288
21464
|
const columnId = taskInfo.column.id;
|
|
@@ -21303,7 +21479,7 @@ async function archiveTaskActionAsync(filePath, taskId, options) {
|
|
|
21303
21479
|
if (!owner || !repo) {
|
|
21304
21480
|
return { success: false, error: "GitHub owner/repo not configured. Set archive.destination: github:owner/repo" };
|
|
21305
21481
|
}
|
|
21306
|
-
const payload = (0,
|
|
21482
|
+
const payload = (0, import_core4.formatTaskForGitHub)(task, {
|
|
21307
21483
|
includeMeta: true,
|
|
21308
21484
|
includeSubtasks: true,
|
|
21309
21485
|
includeRelatedFiles: true,
|
|
@@ -21325,10 +21501,10 @@ async function archiveTaskActionAsync(filePath, taskId, options) {
|
|
|
21325
21501
|
if ((0, import_core.isV2)(filePath)) {
|
|
21326
21502
|
const located = resolveV2TaskPath(filePath, taskId);
|
|
21327
21503
|
if (located) {
|
|
21328
|
-
(0,
|
|
21504
|
+
(0, import_core4.deleteTaskFile)(located.taskPath);
|
|
21329
21505
|
}
|
|
21330
21506
|
} else {
|
|
21331
|
-
const deleteResult = (0,
|
|
21507
|
+
const deleteResult = (0, import_core4.deleteTask)(board, columnId, taskId);
|
|
21332
21508
|
if (deleteResult.success) {
|
|
21333
21509
|
writeBrainfile(filePath, deleteResult.board);
|
|
21334
21510
|
}
|
|
@@ -21362,7 +21538,7 @@ async function archiveTaskActionAsync(filePath, taskId, options) {
|
|
|
21362
21538
|
return { success: false, error: "No Linear teams found" };
|
|
21363
21539
|
}
|
|
21364
21540
|
}
|
|
21365
|
-
const payload = (0,
|
|
21541
|
+
const payload = (0, import_core4.formatTaskForLinear)(task, {
|
|
21366
21542
|
includeMeta: true,
|
|
21367
21543
|
includeSubtasks: true,
|
|
21368
21544
|
includeRelatedFiles: true,
|
|
@@ -21384,10 +21560,10 @@ async function archiveTaskActionAsync(filePath, taskId, options) {
|
|
|
21384
21560
|
if ((0, import_core.isV2)(filePath)) {
|
|
21385
21561
|
const located = resolveV2TaskPath(filePath, taskId);
|
|
21386
21562
|
if (located) {
|
|
21387
|
-
(0,
|
|
21563
|
+
(0, import_core4.deleteTaskFile)(located.taskPath);
|
|
21388
21564
|
}
|
|
21389
21565
|
} else {
|
|
21390
|
-
const deleteResult = (0,
|
|
21566
|
+
const deleteResult = (0, import_core4.deleteTask)(board, columnId, taskId);
|
|
21391
21567
|
if (deleteResult.success) {
|
|
21392
21568
|
writeBrainfile(filePath, deleteResult.board);
|
|
21393
21569
|
}
|
|
@@ -21401,62 +21577,62 @@ function cyclePriorityAction(filePath, taskId) {
|
|
|
21401
21577
|
if ((0, import_core.isV2)(filePath)) {
|
|
21402
21578
|
const located = resolveV2TaskPath(filePath, taskId);
|
|
21403
21579
|
if (!located) return { success: false, error: `Task ${taskId} not found` };
|
|
21404
|
-
const doc = (0,
|
|
21580
|
+
const doc = (0, import_core4.readTaskFile)(located.taskPath);
|
|
21405
21581
|
if (!doc) return { success: false, error: `Failed to read task ${taskId}` };
|
|
21406
21582
|
const currentIndex2 = priorities.indexOf(doc.task.priority);
|
|
21407
21583
|
const nextIndex2 = (currentIndex2 + 1) % priorities.length;
|
|
21408
21584
|
const nextPriority2 = priorities[nextIndex2];
|
|
21409
|
-
const result2 = (0,
|
|
21585
|
+
const result2 = (0, import_core4.patchTaskFile)(located.taskPath, { priority: nextPriority2 ?? null });
|
|
21410
21586
|
if (!result2.success) return { success: false, error: result2.error };
|
|
21411
|
-
scheduleBoardCommit(filePath, `patch ${taskId}: priority ${nextPriority2 || "none"}`);
|
|
21587
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `patch ${taskId}: priority ${nextPriority2 || "none"}`);
|
|
21412
21588
|
return { success: true, message: `Priority: ${nextPriority2 || "none"}` };
|
|
21413
21589
|
}
|
|
21414
21590
|
const { board, error } = readBrainfile(filePath);
|
|
21415
21591
|
if (!board) return { success: false, error };
|
|
21416
|
-
const taskInfo = (0,
|
|
21592
|
+
const taskInfo = (0, import_core4.findTaskById)(board, taskId);
|
|
21417
21593
|
if (!taskInfo) return { success: false, error: `Task ${taskId} not found` };
|
|
21418
21594
|
const currentIndex = priorities.indexOf(taskInfo.task.priority);
|
|
21419
21595
|
const nextIndex = (currentIndex + 1) % priorities.length;
|
|
21420
21596
|
const nextPriority = priorities[nextIndex];
|
|
21421
21597
|
const patch = { priority: nextPriority || null };
|
|
21422
|
-
const result = (0,
|
|
21598
|
+
const result = (0, import_core4.patchTask)(board, taskId, patch);
|
|
21423
21599
|
if (!result.success) return { success: false, error: result.error };
|
|
21424
21600
|
const writeResult = writeBrainfile(filePath, result.board);
|
|
21425
21601
|
if (!writeResult.success) return writeResult;
|
|
21426
|
-
scheduleBoardCommit(filePath, `patch ${taskId}: priority ${nextPriority || "none"}`);
|
|
21602
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `patch ${taskId}: priority ${nextPriority || "none"}`);
|
|
21427
21603
|
return { success: true, message: `Priority: ${nextPriority || "none"}` };
|
|
21428
21604
|
}
|
|
21429
21605
|
function toggleSubtaskAction(filePath, taskId, subtaskId) {
|
|
21430
21606
|
if ((0, import_core.isV2)(filePath)) {
|
|
21431
21607
|
const located = resolveV2TaskPath(filePath, taskId);
|
|
21432
21608
|
if (!located) return { success: false, error: `Task ${taskId} not found` };
|
|
21433
|
-
const result2 = (0,
|
|
21609
|
+
const result2 = (0, import_core4.toggleSubtasksInFile)(located.taskPath, [subtaskId]);
|
|
21434
21610
|
if (!result2.success) {
|
|
21435
21611
|
return {
|
|
21436
21612
|
success: false,
|
|
21437
21613
|
error: result2.missing?.length ? `Subtask ${subtaskId} not found` : result2.error
|
|
21438
21614
|
};
|
|
21439
21615
|
}
|
|
21440
|
-
scheduleBoardCommit(filePath, `subtask toggle ${subtaskId}`);
|
|
21616
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `subtask toggle ${subtaskId}`);
|
|
21441
21617
|
return { success: true, message: `Toggled ${subtaskId}` };
|
|
21442
21618
|
}
|
|
21443
21619
|
const { board, error } = readBrainfile(filePath);
|
|
21444
21620
|
if (!board) return { success: false, error };
|
|
21445
|
-
const result = (0,
|
|
21621
|
+
const result = (0, import_core4.toggleSubtask)(board, taskId, subtaskId);
|
|
21446
21622
|
if (!result.success) return { success: false, error: result.error };
|
|
21447
21623
|
const writeResult = writeBrainfile(filePath, result.board);
|
|
21448
21624
|
if (!writeResult.success) return writeResult;
|
|
21449
|
-
scheduleBoardCommit(filePath, `subtask toggle ${subtaskId}`);
|
|
21625
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `subtask toggle ${subtaskId}`);
|
|
21450
21626
|
return { success: true, message: `Toggled ${subtaskId}` };
|
|
21451
21627
|
}
|
|
21452
21628
|
function addTaskAction(filePath, columnId, taskInput) {
|
|
21453
21629
|
const { board, error } = readBoardState(filePath);
|
|
21454
21630
|
if (!board) return { success: false, error };
|
|
21455
|
-
const column = (0,
|
|
21631
|
+
const column = (0, import_core4.findColumnById)(board, columnId) || (0, import_core4.findColumnByName)(board, columnId);
|
|
21456
21632
|
if (!column) return { success: false, error: `Column ${columnId} not found` };
|
|
21457
21633
|
if ((0, import_core.isV2)(filePath)) {
|
|
21458
21634
|
const dirs = (0, import_core.getV2Dirs)(filePath);
|
|
21459
|
-
const result2 = (0,
|
|
21635
|
+
const result2 = (0, import_core4.addTaskFile)(
|
|
21460
21636
|
dirs.boardDir,
|
|
21461
21637
|
{
|
|
21462
21638
|
title: taskInput.title,
|
|
@@ -21474,16 +21650,16 @@ ${taskInput.description.trim()}
|
|
|
21474
21650
|
dirs.logsDir
|
|
21475
21651
|
);
|
|
21476
21652
|
if (!result2.success) return { success: false, error: result2.error };
|
|
21477
|
-
scheduleBoardCommit(filePath, `add ${result2.task?.id || "task"}`);
|
|
21653
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `add ${result2.task?.id || "task"}`);
|
|
21478
21654
|
return { success: true, message: `Added ${result2.task?.id || "task"}` };
|
|
21479
21655
|
}
|
|
21480
|
-
const result = (0,
|
|
21656
|
+
const result = (0, import_core4.addTask)(board, column.id, taskInput);
|
|
21481
21657
|
if (!result.success) return { success: false, error: result.error };
|
|
21482
21658
|
const writeResult = writeBrainfile(filePath, result.board);
|
|
21483
21659
|
if (!writeResult.success) return writeResult;
|
|
21484
21660
|
const newColumn = result.board.columns.find((c) => c.id === column.id);
|
|
21485
21661
|
const newTask = newColumn?.tasks[newColumn.tasks.length - 1];
|
|
21486
|
-
scheduleBoardCommit(filePath, `add ${newTask?.id || "task"}`);
|
|
21662
|
+
(0, import_repo.scheduleBoardCommit)(filePath, `add ${newTask?.id || "task"}`);
|
|
21487
21663
|
return { success: true, message: `Added ${newTask?.id || "task"}` };
|
|
21488
21664
|
}
|
|
21489
21665
|
function resolveTaskFilePath(filePath, taskId) {
|
|
@@ -21534,10 +21710,10 @@ function getTaskActivity(filePath, taskId, limit = 5) {
|
|
|
21534
21710
|
if (!(0, import_core.isV2)(filePath)) return [];
|
|
21535
21711
|
try {
|
|
21536
21712
|
const dirs = (0, import_core.getV2Dirs)(filePath);
|
|
21537
|
-
const doc = (0,
|
|
21713
|
+
const doc = (0, import_core4.findTask)(dirs.boardDir, taskId) ?? (0, import_core4.findTask)(dirs.logsDir, taskId);
|
|
21538
21714
|
const entries = [];
|
|
21539
21715
|
if (doc) {
|
|
21540
|
-
for (const note of (0,
|
|
21716
|
+
for (const note of (0, import_core4.parseNotes)(doc.body)) {
|
|
21541
21717
|
entries.push({
|
|
21542
21718
|
at: note.at,
|
|
21543
21719
|
text: note.agent ? `[${note.agent}] ${note.text}` : note.text
|
|
@@ -21545,7 +21721,7 @@ function getTaskActivity(filePath, taskId, limit = 5) {
|
|
|
21545
21721
|
}
|
|
21546
21722
|
}
|
|
21547
21723
|
try {
|
|
21548
|
-
for (const record of (0,
|
|
21724
|
+
for (const record of (0, import_core4.readLedger)(dirs.logsDir)) {
|
|
21549
21725
|
if (record.id !== taskId) continue;
|
|
21550
21726
|
entries.push({ at: record.completedAt, text: `completed \xB7 ${record.summary}` });
|
|
21551
21727
|
}
|
|
@@ -21561,8 +21737,15 @@ function loadLogs(filePath) {
|
|
|
21561
21737
|
if ((0, import_core.isV2)(filePath)) {
|
|
21562
21738
|
try {
|
|
21563
21739
|
const dirs = (0, import_core.getV2Dirs)(filePath);
|
|
21564
|
-
const docs = (0,
|
|
21565
|
-
const logs = docs.map((doc) =>
|
|
21740
|
+
const docs = (0, import_core4.readTasksDir)(dirs.logsDir);
|
|
21741
|
+
const logs = docs.map((doc) => {
|
|
21742
|
+
const task = { ...doc.task };
|
|
21743
|
+
if (!task.description) {
|
|
21744
|
+
const description = (0, import_core4.extractDescription)(doc.body);
|
|
21745
|
+
if (description) task.description = description;
|
|
21746
|
+
}
|
|
21747
|
+
return task;
|
|
21748
|
+
}).sort((a, b) => {
|
|
21566
21749
|
const aTime = a.completedAt ? Date.parse(a.completedAt) : 0;
|
|
21567
21750
|
const bTime = b.completedAt ? Date.parse(b.completedAt) : 0;
|
|
21568
21751
|
return bTime - aTime;
|
|
@@ -21578,7 +21761,7 @@ function loadLogs(filePath) {
|
|
|
21578
21761
|
}
|
|
21579
21762
|
try {
|
|
21580
21763
|
const archiveContent = fs3.readFileSync(archivePath, "utf-8");
|
|
21581
|
-
const parseResult =
|
|
21764
|
+
const parseResult = import_core4.Brainfile.parseWithErrors(archiveContent);
|
|
21582
21765
|
if (parseResult.board && parseResult.board.archive) {
|
|
21583
21766
|
return { logs: parseResult.board.archive };
|
|
21584
21767
|
}
|
|
@@ -21624,30 +21807,53 @@ function useBrainfileLoader(filePath, state, setState) {
|
|
|
21624
21807
|
try {
|
|
21625
21808
|
const content = fs4.readFileSync(filePath, "utf-8");
|
|
21626
21809
|
const v2 = (0, import_core.isV2)(filePath);
|
|
21627
|
-
let
|
|
21810
|
+
let parsed = null;
|
|
21628
21811
|
let parseError;
|
|
21629
21812
|
if (v2) {
|
|
21630
21813
|
try {
|
|
21631
|
-
|
|
21814
|
+
parsed = (0, import_core.buildBoardFromV2)(filePath);
|
|
21632
21815
|
} catch (err) {
|
|
21633
21816
|
parseError = err instanceof Error ? err.message : String(err);
|
|
21634
21817
|
}
|
|
21635
21818
|
} else {
|
|
21636
|
-
const result =
|
|
21819
|
+
const result = import_core5.Brainfile.parseWithErrors(content);
|
|
21637
21820
|
if (result.board) {
|
|
21638
|
-
|
|
21821
|
+
parsed = result.board;
|
|
21639
21822
|
} else {
|
|
21640
21823
|
parseError = result.error || "Not a valid brainfile";
|
|
21641
21824
|
}
|
|
21642
21825
|
}
|
|
21643
|
-
const contentHash = v2 ? (0,
|
|
21644
|
-
${getV2ChangeSignature(filePath)}`) : (0,
|
|
21645
|
-
const
|
|
21826
|
+
const contentHash = v2 ? (0, import_core5.hashBoardContent)(`${content}
|
|
21827
|
+
${getV2ChangeSignature(filePath)}`) : (0, import_core5.hashBoardContent)(content);
|
|
21828
|
+
const split = parsed ? splitDecisions(parsed, loadLogs(filePath).logs) : null;
|
|
21829
|
+
const board = split?.board ?? null;
|
|
21830
|
+
const logs = split?.logs ?? [];
|
|
21831
|
+
const decisions = split?.decisions ?? [];
|
|
21646
21832
|
setState((prev) => {
|
|
21647
21833
|
if (!forceRefresh && prev.lastContentHash === contentHash) {
|
|
21648
21834
|
return prev;
|
|
21649
21835
|
}
|
|
21650
21836
|
if (board) {
|
|
21837
|
+
if (isDecisionStop(prev.activeTypeFilter, boardTypes(board))) {
|
|
21838
|
+
const order = (list) => buildDecisionView(filterDecisions(list, prev.filterQuery), {
|
|
21839
|
+
historyOpen: prev.decisionHistory,
|
|
21840
|
+
types: boardTypes(board)
|
|
21841
|
+
}).selectable;
|
|
21842
|
+
const prevId = order(prev.decisions)[prev.selectedTaskIndex]?.id;
|
|
21843
|
+
const next = order(decisions);
|
|
21844
|
+
const foundIdx = prevId ? next.findIndex((t) => t.id === prevId) : -1;
|
|
21845
|
+
return {
|
|
21846
|
+
...prev,
|
|
21847
|
+
board,
|
|
21848
|
+
logs,
|
|
21849
|
+
decisions,
|
|
21850
|
+
error: null,
|
|
21851
|
+
lastUpdated: /* @__PURE__ */ new Date(),
|
|
21852
|
+
selectedTaskIndex: foundIdx >= 0 ? foundIdx : Math.max(0, Math.min(prev.selectedTaskIndex, next.length - 1)),
|
|
21853
|
+
reloadFlash: true,
|
|
21854
|
+
lastContentHash: contentHash
|
|
21855
|
+
};
|
|
21856
|
+
}
|
|
21651
21857
|
if (prev.doneView) {
|
|
21652
21858
|
const prevLogId = prev.logs[prev.selectedTaskIndex]?.id;
|
|
21653
21859
|
const foundIdx = prevLogId ? logs.findIndex((t) => t.id === prevLogId) : -1;
|
|
@@ -21656,6 +21862,7 @@ ${getV2ChangeSignature(filePath)}`) : (0, import_core4.hashBoardContent)(content
|
|
|
21656
21862
|
...prev,
|
|
21657
21863
|
board,
|
|
21658
21864
|
logs,
|
|
21865
|
+
decisions,
|
|
21659
21866
|
error: null,
|
|
21660
21867
|
lastUpdated: /* @__PURE__ */ new Date(),
|
|
21661
21868
|
selectedTaskIndex: nextIndex,
|
|
@@ -21684,6 +21891,7 @@ ${getV2ChangeSignature(filePath)}`) : (0, import_core4.hashBoardContent)(content
|
|
|
21684
21891
|
...prev,
|
|
21685
21892
|
board,
|
|
21686
21893
|
logs,
|
|
21894
|
+
decisions,
|
|
21687
21895
|
error: null,
|
|
21688
21896
|
lastUpdated: /* @__PURE__ */ new Date(),
|
|
21689
21897
|
activeColumnIndex: newColumnIndex,
|
|
@@ -21770,9 +21978,9 @@ import * as path3 from "path";
|
|
|
21770
21978
|
var POLL_MS = 5e3;
|
|
21771
21979
|
function useSyncStatus(filePath, lastUpdated) {
|
|
21772
21980
|
const dotDir = path3.dirname(path3.resolve(filePath));
|
|
21773
|
-
const [label, setLabel] = (0, import_react35.useState)(() => syncStatusLabel(dotDir) ?? void 0);
|
|
21981
|
+
const [label, setLabel] = (0, import_react35.useState)(() => (0, import_repo2.syncStatusLabel)(dotDir) ?? void 0);
|
|
21774
21982
|
(0, import_react35.useEffect)(() => {
|
|
21775
|
-
const refresh = () => setLabel(syncStatusLabel(dotDir) ?? void 0);
|
|
21983
|
+
const refresh = () => setLabel((0, import_repo2.syncStatusLabel)(dotDir) ?? void 0);
|
|
21776
21984
|
refresh();
|
|
21777
21985
|
const timer = setInterval(refresh, POLL_MS);
|
|
21778
21986
|
timer.unref?.();
|
|
@@ -21785,9 +21993,93 @@ function useSyncStatus(filePath, lastUpdated) {
|
|
|
21785
21993
|
var import_react37 = __toESM(require_react());
|
|
21786
21994
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
21787
21995
|
|
|
21996
|
+
// src/tui/utils.ts
|
|
21997
|
+
var import_core6 = __toESM(require_dist());
|
|
21998
|
+
var import_core7 = __toESM(require_dist());
|
|
21999
|
+
|
|
22000
|
+
// src/tui/text.ts
|
|
22001
|
+
function truncate(text, maxWidth) {
|
|
22002
|
+
if (!text) return " ";
|
|
22003
|
+
if (maxWidth <= 0) return " ";
|
|
22004
|
+
if (maxWidth <= 3) return text.slice(0, maxWidth) || " ";
|
|
22005
|
+
if (text.length <= maxWidth) return text;
|
|
22006
|
+
return `${text.slice(0, maxWidth - 1)}\u2026`;
|
|
22007
|
+
}
|
|
22008
|
+
function truncateStart(text, maxWidth) {
|
|
22009
|
+
if (!text) return " ";
|
|
22010
|
+
if (maxWidth <= 0) return " ";
|
|
22011
|
+
if (maxWidth <= 1) return text.slice(-1) || " ";
|
|
22012
|
+
if (text.length <= maxWidth) return text;
|
|
22013
|
+
return `\u2026${text.slice(text.length - (maxWidth - 1))}`;
|
|
22014
|
+
}
|
|
22015
|
+
function wrapText2(text, maxWidth) {
|
|
22016
|
+
if (!text || maxWidth <= 0) return [];
|
|
22017
|
+
const words = text.split(/\s+/);
|
|
22018
|
+
const lines = [];
|
|
22019
|
+
let currentLine = "";
|
|
22020
|
+
for (const word of words) {
|
|
22021
|
+
if (word.length > maxWidth) {
|
|
22022
|
+
if (currentLine) {
|
|
22023
|
+
lines.push(currentLine);
|
|
22024
|
+
currentLine = "";
|
|
22025
|
+
}
|
|
22026
|
+
for (let i = 0; i < word.length; i += maxWidth) {
|
|
22027
|
+
lines.push(word.slice(i, i + maxWidth));
|
|
22028
|
+
}
|
|
22029
|
+
} else if (currentLine.length + 1 + word.length <= maxWidth) {
|
|
22030
|
+
currentLine = currentLine ? `${currentLine} ${word}` : word;
|
|
22031
|
+
} else {
|
|
22032
|
+
if (currentLine) lines.push(currentLine);
|
|
22033
|
+
currentLine = word;
|
|
22034
|
+
}
|
|
22035
|
+
}
|
|
22036
|
+
if (currentLine) lines.push(currentLine);
|
|
22037
|
+
return lines.length > 0 ? lines : [""];
|
|
22038
|
+
}
|
|
22039
|
+
function pad(n) {
|
|
22040
|
+
return " ".repeat(Math.max(0, n));
|
|
22041
|
+
}
|
|
22042
|
+
|
|
22043
|
+
// src/tui/utils.ts
|
|
22044
|
+
function getPriorityColor(priority) {
|
|
22045
|
+
switch (priority?.toLowerCase()) {
|
|
22046
|
+
case "critical":
|
|
22047
|
+
return PALETTE.critical;
|
|
22048
|
+
case "high":
|
|
22049
|
+
return PALETTE.high;
|
|
22050
|
+
case "medium":
|
|
22051
|
+
return PALETTE.medium;
|
|
22052
|
+
case "low":
|
|
22053
|
+
return PALETTE.low;
|
|
22054
|
+
default:
|
|
22055
|
+
return PALETTE.textDim;
|
|
22056
|
+
}
|
|
22057
|
+
}
|
|
22058
|
+
function getDocType(task) {
|
|
22059
|
+
return (task.type || "task").toLowerCase();
|
|
22060
|
+
}
|
|
22061
|
+
function isCompletable(task) {
|
|
22062
|
+
if (!task) return false;
|
|
22063
|
+
return (0, import_core6.docRole)(task.type) !== "decision";
|
|
22064
|
+
}
|
|
22065
|
+
function getSubtaskProgress(task) {
|
|
22066
|
+
const subtasks = task.subtasks ?? [];
|
|
22067
|
+
if (subtasks.length === 0) return void 0;
|
|
22068
|
+
const done = subtasks.filter((s) => s.completed).length;
|
|
22069
|
+
return `${done}/${subtasks.length}`;
|
|
22070
|
+
}
|
|
22071
|
+
function getContractState(task) {
|
|
22072
|
+
const contract = task.contract;
|
|
22073
|
+
return contract?.status;
|
|
22074
|
+
}
|
|
22075
|
+
|
|
21788
22076
|
// src/tui/detailStops.ts
|
|
21789
|
-
function buildDetailStops(task, children) {
|
|
21790
|
-
const stops =
|
|
22077
|
+
function buildDetailStops(task, children, relations) {
|
|
22078
|
+
const stops = [
|
|
22079
|
+
...(relations?.refs ?? []).map((ref) => ({ kind: "ref", ref })),
|
|
22080
|
+
...(relations?.resting ?? []).map((resting) => ({ kind: "resting", task: resting })),
|
|
22081
|
+
...children.map((child) => ({ kind: "child", task: child }))
|
|
22082
|
+
];
|
|
21791
22083
|
(task.subtasks ?? []).forEach((subtask, index) => {
|
|
21792
22084
|
stops.push({ kind: "subtask", subtask, index });
|
|
21793
22085
|
});
|
|
@@ -21802,14 +22094,17 @@ function clampDetailCursor(cursor, stopCount) {
|
|
|
21802
22094
|
var import_react36 = __toESM(require_react());
|
|
21803
22095
|
var MAX_DELIVERABLES = 3;
|
|
21804
22096
|
var MAX_VALIDATION_COMMANDS = 2;
|
|
21805
|
-
function computeDetailLayout(task, width, height, children, activity) {
|
|
22097
|
+
function computeDetailLayout(task, width, height, children, activity, relations = NO_RELATIONS) {
|
|
21806
22098
|
const contentWidth = Math.max(20, width - 2);
|
|
21807
22099
|
const stacked = contentWidth < 60;
|
|
21808
22100
|
const subtasks = task.subtasks ?? [];
|
|
21809
22101
|
const files = task.relatedFiles ?? [];
|
|
21810
22102
|
const contract = task.contract;
|
|
22103
|
+
const { decision, refs, resting } = relations;
|
|
21811
22104
|
const bodyLines = buildBodyLines(task.description, contentWidth);
|
|
21812
|
-
const metaLines = stacked ? 4 : 1;
|
|
22105
|
+
const metaLines = decision ? decisionFieldLines(decision, refs, contentWidth).length : stacked ? 4 : 1;
|
|
22106
|
+
const restsOnLines = !decision && refs.length > 0 ? 1 + refs.length : 0;
|
|
22107
|
+
const restingLines = resting.length > 0 ? 1 + resting.length : 0;
|
|
21813
22108
|
const childrenLines = children.length > 0 ? 1 + children.length : 0;
|
|
21814
22109
|
const subtasksLines = subtasks.length > 0 ? 1 + subtasks.length : 0;
|
|
21815
22110
|
const contractLines = contract ? 1 + Math.min(contract.deliverables?.length ?? 0, MAX_DELIVERABLES) + Math.min(contract.validation?.commands?.length ?? 0, MAX_VALIDATION_COMMANDS) + (contract.feedback ? 1 : 0) : 0;
|
|
@@ -21817,6 +22112,8 @@ function computeDetailLayout(task, width, height, children, activity) {
|
|
|
21817
22112
|
const filesLines = files.length > 0 ? 1 : 0;
|
|
21818
22113
|
const sectionsWithMargin = [
|
|
21819
22114
|
bodyLines.length > 0,
|
|
22115
|
+
restsOnLines > 0,
|
|
22116
|
+
restingLines > 0,
|
|
21820
22117
|
childrenLines > 0,
|
|
21821
22118
|
subtasksLines > 0,
|
|
21822
22119
|
contractLines > 0,
|
|
@@ -21825,7 +22122,7 @@ function computeDetailLayout(task, width, height, children, activity) {
|
|
|
21825
22122
|
].filter(Boolean).length;
|
|
21826
22123
|
const bodyBudget = Math.max(
|
|
21827
22124
|
2,
|
|
21828
|
-
height - 2 - metaLines - 1 - childrenLines - subtasksLines - contractLines - activityLines - filesLines - sectionsWithMargin
|
|
22125
|
+
height - 2 - metaLines - 1 - restsOnLines - restingLines - childrenLines - subtasksLines - contractLines - activityLines - filesLines - sectionsWithMargin
|
|
21829
22126
|
);
|
|
21830
22127
|
return { bodyLines, bodyBudget, bodyOverflows: bodyLines.length > bodyBudget };
|
|
21831
22128
|
}
|
|
@@ -21839,20 +22136,26 @@ function DetailView({
|
|
|
21839
22136
|
children,
|
|
21840
22137
|
activity,
|
|
21841
22138
|
cursor,
|
|
21842
|
-
scrollOffset
|
|
22139
|
+
scrollOffset,
|
|
22140
|
+
relations = NO_RELATIONS
|
|
21843
22141
|
}) {
|
|
21844
22142
|
const contentWidth = Math.max(20, width - 2);
|
|
21845
22143
|
const stacked = contentWidth < 60;
|
|
21846
|
-
const
|
|
22144
|
+
const { decision, refs, resting } = relations;
|
|
22145
|
+
const priority = decision ? void 0 : task.priority;
|
|
21847
22146
|
const subtasks = task.subtasks ?? [];
|
|
21848
22147
|
const files = task.relatedFiles ?? [];
|
|
21849
22148
|
const contract = task.contract;
|
|
22149
|
+
const restingBase = refs.length;
|
|
22150
|
+
const childBase = restingBase + resting.length;
|
|
22151
|
+
const subtaskBase = childBase + children.length;
|
|
21850
22152
|
const { bodyLines, bodyBudget, bodyOverflows } = computeDetailLayout(
|
|
21851
22153
|
task,
|
|
21852
22154
|
width,
|
|
21853
22155
|
height,
|
|
21854
22156
|
children,
|
|
21855
|
-
activity
|
|
22157
|
+
activity,
|
|
22158
|
+
relations
|
|
21856
22159
|
);
|
|
21857
22160
|
const clampedScroll = Math.max(0, Math.min(scrollOffset, Math.max(0, bodyLines.length - bodyBudget)));
|
|
21858
22161
|
const visibleBody = bodyLines.slice(clampedScroll, clampedScroll + bodyBudget);
|
|
@@ -21861,7 +22164,16 @@ function DetailView({
|
|
|
21861
22164
|
const breadcrumbStr = truncateStart(breadcrumb.join(" \u25B8 ") || task.id, breadcrumbBudget);
|
|
21862
22165
|
const headWidth = Math.max(1, contentWidth - stateText.length - 2);
|
|
21863
22166
|
const head = truncate(`${breadcrumbStr} ${task.title}`, headWidth);
|
|
21864
|
-
return /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", width, flexShrink: 0 }, /* @__PURE__ */ import_react36.default.createElement(Box_default, { paddingLeft: 1, width }, /* @__PURE__ */ import_react36.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.text, bold: true }, head), /* @__PURE__ */ import_react36.default.createElement(Text, null, pad(Math.max(1, contentWidth - head.length - stateText.length))), /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textSecondary }, columnLabel), priority ? /* @__PURE__ */ import_react36.default.createElement(Text, null, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textDim }, " \xB7 "), /* @__PURE__ */ import_react36.default.createElement(Text, { color: getPriorityColor(priority) }, priority)) : null)), /* @__PURE__ */ import_react36.default.createElement(Box_default, { paddingLeft: 1, width }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textDim }, RULE.repeat(contentWidth))),
|
|
22167
|
+
return /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", width, flexShrink: 0 }, /* @__PURE__ */ import_react36.default.createElement(Box_default, { paddingLeft: 1, width }, /* @__PURE__ */ import_react36.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.text, bold: true }, head), /* @__PURE__ */ import_react36.default.createElement(Text, null, pad(Math.max(1, contentWidth - head.length - stateText.length))), /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textSecondary }, columnLabel), priority ? /* @__PURE__ */ import_react36.default.createElement(Text, null, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textDim }, " \xB7 "), /* @__PURE__ */ import_react36.default.createElement(Text, { color: getPriorityColor(priority) }, priority)) : null)), /* @__PURE__ */ import_react36.default.createElement(Box_default, { paddingLeft: 1, width }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textDim }, RULE.repeat(contentWidth))), decision ? /* @__PURE__ */ import_react36.default.createElement(
|
|
22168
|
+
DecisionFields,
|
|
22169
|
+
{
|
|
22170
|
+
decision,
|
|
22171
|
+
refs,
|
|
22172
|
+
cursor,
|
|
22173
|
+
contentWidth,
|
|
22174
|
+
width
|
|
22175
|
+
}
|
|
22176
|
+
) : /* @__PURE__ */ import_react36.default.createElement(MetadataBlock, { task, parent, stacked, width }), bodyLines.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(
|
|
21865
22177
|
DescriptionHeading,
|
|
21866
22178
|
{
|
|
21867
22179
|
bodyOverflows,
|
|
@@ -21878,18 +22190,42 @@ function DetailView({
|
|
|
21878
22190
|
color: line.kind === "code" ? PALETTE.textMuted : line.kind === "heading" ? PALETTE.text : PALETTE.textSecondary
|
|
21879
22191
|
},
|
|
21880
22192
|
line.text || " "
|
|
22193
|
+
))) : null, !decision && refs.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, `rests on (${refs.length})`), refs.map((ref, index) => {
|
|
22194
|
+
const retired = isRetiredRef(ref);
|
|
22195
|
+
return /* @__PURE__ */ import_react36.default.createElement(
|
|
22196
|
+
StopRow,
|
|
22197
|
+
{
|
|
22198
|
+
key: `ref-${ref.id}`,
|
|
22199
|
+
selected: index === cursor,
|
|
22200
|
+
left: ` ${refGlyph(ref)} ${ref.id} ${ref.task?.title ?? ref.missing ?? ""}`,
|
|
22201
|
+
leftColor: ref.task && !ref.missing ? PALETTE.textSecondary : PALETTE.textMuted,
|
|
22202
|
+
right: retired && ref.summary ? retiredLabel(ref.summary) : ref.missing === "not a decision" ? ref.missing : void 0,
|
|
22203
|
+
rightColor: retired ? PALETTE.warning : PALETTE.textMuted,
|
|
22204
|
+
contentWidth
|
|
22205
|
+
}
|
|
22206
|
+
);
|
|
22207
|
+
})) : null, resting.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, `work resting on it (${resting.length})`), resting.map((work, index) => /* @__PURE__ */ import_react36.default.createElement(
|
|
22208
|
+
StopRow,
|
|
22209
|
+
{
|
|
22210
|
+
key: `resting-${work.id}`,
|
|
22211
|
+
selected: restingBase + index === cursor,
|
|
22212
|
+
left: ` ${work.id} ${work.title}`,
|
|
22213
|
+
right: work.column ?? "",
|
|
22214
|
+
rightColor: PALETTE.textSecondary,
|
|
22215
|
+
contentWidth
|
|
22216
|
+
}
|
|
21881
22217
|
))) : null, children.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, `children (${children.length})`), children.map((child, index) => /* @__PURE__ */ import_react36.default.createElement(
|
|
21882
22218
|
StopRow,
|
|
21883
22219
|
{
|
|
21884
22220
|
key: child.id,
|
|
21885
|
-
selected: index === cursor,
|
|
22221
|
+
selected: childBase + index === cursor,
|
|
21886
22222
|
left: ` ${child.id} ${child.title}`,
|
|
21887
22223
|
right: child.column ?? "",
|
|
21888
22224
|
rightColor: PALETTE.textSecondary,
|
|
21889
22225
|
contentWidth
|
|
21890
22226
|
}
|
|
21891
22227
|
))) : null, subtasks.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, `subtasks (${subtasks.filter((s) => s.completed).length}/${subtasks.length})`), subtasks.map((subtask, index) => {
|
|
21892
|
-
const stopIndex =
|
|
22228
|
+
const stopIndex = subtaskBase + index;
|
|
21893
22229
|
const glyph = subtask.completed ? GLYPHS.subtaskDone : GLYPHS.subtaskOpen;
|
|
21894
22230
|
return /* @__PURE__ */ import_react36.default.createElement(
|
|
21895
22231
|
StopRow,
|
|
@@ -21942,7 +22278,8 @@ function StopRow({
|
|
|
21942
22278
|
contentWidth
|
|
21943
22279
|
}) {
|
|
21944
22280
|
const rightText = right ?? "";
|
|
21945
|
-
const
|
|
22281
|
+
const marked = selected && left.startsWith(" ") ? ` ${GLYPHS.cursor}${left.slice(2)}` : left;
|
|
22282
|
+
const leftTruncated = truncate(marked, Math.max(1, contentWidth - rightText.length - 2));
|
|
21946
22283
|
const gap = Math.max(1, contentWidth - leftTruncated.length - rightText.length);
|
|
21947
22284
|
return /* @__PURE__ */ import_react36.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: selected ? void 0 : leftColor, inverse: selected }, leftTruncated), /* @__PURE__ */ import_react36.default.createElement(Text, null, pad(gap)), rightText ? /* @__PURE__ */ import_react36.default.createElement(Text, { color: rightColor }, rightText) : null);
|
|
21948
22285
|
}
|
|
@@ -21955,6 +22292,74 @@ function LabeledList({
|
|
|
21955
22292
|
const labelWidth = 16;
|
|
21956
22293
|
return /* @__PURE__ */ import_react36.default.createElement(import_react36.default.Fragment, null, items.map((item, index) => /* @__PURE__ */ import_react36.default.createElement(Text, { key: `${label}-${index}`, wrap: "truncate" }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, ` ${index === 0 ? label.padEnd(labelWidth - 2) : " ".repeat(labelWidth - 2)}`), /* @__PURE__ */ import_react36.default.createElement(Text, { color }, truncate(item, Math.max(1, contentWidth - labelWidth))))));
|
|
21957
22294
|
}
|
|
22295
|
+
var FIELD_LABEL_WIDTH = 13;
|
|
22296
|
+
var MAX_FIELD_LINES = 3;
|
|
22297
|
+
function decisionFieldLines(decision, refs, contentWidth) {
|
|
22298
|
+
const valueWidth = Math.max(8, contentWidth - FIELD_LABEL_WIDTH);
|
|
22299
|
+
const lines = [];
|
|
22300
|
+
const add = (label, value, wrap = false) => {
|
|
22301
|
+
if (!value) return;
|
|
22302
|
+
let parts = wrap ? wrapText2(value, valueWidth) : [value];
|
|
22303
|
+
if (parts.length > MAX_FIELD_LINES) {
|
|
22304
|
+
const rest = parts.slice(MAX_FIELD_LINES - 1).join(" ");
|
|
22305
|
+
parts = [...parts.slice(0, MAX_FIELD_LINES - 1), truncate(rest, valueWidth)];
|
|
22306
|
+
}
|
|
22307
|
+
parts.forEach((text, index) => lines.push({ label: index === 0 ? label : "", value: text }));
|
|
22308
|
+
};
|
|
22309
|
+
add("claim key", decision.claimKey);
|
|
22310
|
+
add("kind", decision.kind);
|
|
22311
|
+
if (decision.stance) {
|
|
22312
|
+
add("stance", decision.subject ? `${decision.stance} ${decision.subject}` : decision.stance, true);
|
|
22313
|
+
} else {
|
|
22314
|
+
add("subject", decision.subject, true);
|
|
22315
|
+
}
|
|
22316
|
+
add("holds while", decision.holdsWhile, true);
|
|
22317
|
+
add("recheck", decision.recheck, true);
|
|
22318
|
+
if (decision.validUntil) {
|
|
22319
|
+
lines.push({
|
|
22320
|
+
label: "valid until",
|
|
22321
|
+
value: decision.validUntil.slice(0, 10),
|
|
22322
|
+
...isPastValidUntil(decision) ? { signal: "review" } : {}
|
|
22323
|
+
});
|
|
22324
|
+
}
|
|
22325
|
+
refs.forEach((ref, index) => {
|
|
22326
|
+
lines.push({
|
|
22327
|
+
label: ref.relation === "replaced-by" ? "replaced by" : "replaces",
|
|
22328
|
+
value: `${ref.id} ${ref.task?.title ?? ref.missing ?? ""}`,
|
|
22329
|
+
...ref.missing ? { muted: true } : {},
|
|
22330
|
+
stop: index
|
|
22331
|
+
});
|
|
22332
|
+
});
|
|
22333
|
+
add("created", decision.createdAt?.slice(0, 10));
|
|
22334
|
+
return lines;
|
|
22335
|
+
}
|
|
22336
|
+
function DecisionFields({
|
|
22337
|
+
decision,
|
|
22338
|
+
refs,
|
|
22339
|
+
cursor,
|
|
22340
|
+
contentWidth,
|
|
22341
|
+
width
|
|
22342
|
+
}) {
|
|
22343
|
+
const lines = decisionFieldLines(decision, refs, contentWidth);
|
|
22344
|
+
if (lines.length === 0) return null;
|
|
22345
|
+
return /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, width }, lines.map((line, index) => {
|
|
22346
|
+
const selected = line.stop !== void 0 && line.stop === cursor;
|
|
22347
|
+
const signal = line.signal ?? "";
|
|
22348
|
+
const valueWidth = Math.max(1, contentWidth - FIELD_LABEL_WIDTH - (signal ? signal.length + 2 : 0));
|
|
22349
|
+
return /* @__PURE__ */ import_react36.default.createElement(Text, { key: `field-${index}`, wrap: "truncate" }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, line.label.padEnd(FIELD_LABEL_WIDTH - 1)), /* @__PURE__ */ import_react36.default.createElement(Text, null, selected ? GLYPHS.cursor : " "), /* @__PURE__ */ import_react36.default.createElement(
|
|
22350
|
+
Text,
|
|
22351
|
+
{
|
|
22352
|
+
color: selected ? void 0 : line.muted ? PALETTE.textMuted : PALETTE.textSecondary,
|
|
22353
|
+
inverse: selected
|
|
22354
|
+
},
|
|
22355
|
+
truncate(line.value, valueWidth)
|
|
22356
|
+
), signal ? /* @__PURE__ */ import_react36.default.createElement(Text, null, pad(2)) : null, signal ? /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.warning }, signal) : null);
|
|
22357
|
+
}));
|
|
22358
|
+
}
|
|
22359
|
+
function refGlyph(ref) {
|
|
22360
|
+
if (!ref.summary) return " ";
|
|
22361
|
+
return isRetiredRef(ref) ? GLYPHS.retiredDecision : getTypeGlyph("decision");
|
|
22362
|
+
}
|
|
21958
22363
|
function MetadataBlock({
|
|
21959
22364
|
task,
|
|
21960
22365
|
parent,
|
|
@@ -22019,11 +22424,11 @@ function buildBodyLines(description, width) {
|
|
|
22019
22424
|
}
|
|
22020
22425
|
|
|
22021
22426
|
// src/tui/tuiState.ts
|
|
22022
|
-
var
|
|
22427
|
+
var import_core8 = __toESM(require_dist());
|
|
22023
22428
|
import * as fs5 from "fs";
|
|
22024
22429
|
import * as path4 from "path";
|
|
22025
22430
|
function getTuiStatePath(brainfilePath) {
|
|
22026
|
-
return path4.join((0,
|
|
22431
|
+
return path4.join((0, import_core8.getBrainfileStateDir)(brainfilePath), import_core8.DOT_BRAINFILE_STATE_DIRNAME, "tui.json");
|
|
22027
22432
|
}
|
|
22028
22433
|
function readTuiState(brainfilePath) {
|
|
22029
22434
|
const statePath = getTuiStatePath(brainfilePath);
|
|
@@ -22045,8 +22450,8 @@ function patchTuiState(brainfilePath, patch) {
|
|
|
22045
22450
|
writeTuiState(brainfilePath, { ...current, ...patch });
|
|
22046
22451
|
}
|
|
22047
22452
|
function writeTuiState(brainfilePath, state) {
|
|
22048
|
-
(0,
|
|
22049
|
-
(0,
|
|
22453
|
+
(0, import_core8.ensureDotBrainfileGitignore)(brainfilePath);
|
|
22454
|
+
(0, import_core8.atomicWriteFileSync)(getTuiStatePath(brainfilePath), `${JSON.stringify(state, null, 2)}
|
|
22050
22455
|
`);
|
|
22051
22456
|
}
|
|
22052
22457
|
|
|
@@ -22069,7 +22474,12 @@ function useKeyboardNavigation({
|
|
|
22069
22474
|
detailWidth,
|
|
22070
22475
|
detailHeight,
|
|
22071
22476
|
doneView,
|
|
22072
|
-
detailArchived
|
|
22477
|
+
detailArchived,
|
|
22478
|
+
decisionsView = false,
|
|
22479
|
+
decisionStop = "decision",
|
|
22480
|
+
decisionHistoryCount = 0,
|
|
22481
|
+
detailRelations = NO_RELATIONS,
|
|
22482
|
+
detailIsDecision = false
|
|
22073
22483
|
}) {
|
|
22074
22484
|
const { exit, suspendTerminal } = use_app_default();
|
|
22075
22485
|
const { isRawModeSupported } = use_stdin_default();
|
|
@@ -22129,6 +22539,11 @@ function useKeyboardNavigation({
|
|
|
22129
22539
|
);
|
|
22130
22540
|
const currentTask = rows[state.selectedTaskIndex]?.task;
|
|
22131
22541
|
const maxRowIndex = Math.max(0, rows.length - 1);
|
|
22542
|
+
const rowArchived = doneView || Boolean(rows[state.selectedTaskIndex]?.archived);
|
|
22543
|
+
const ownColumnIndex = (task, fallback) => {
|
|
22544
|
+
const at = task ? allColumns.findIndex((column) => column.tasks?.some((t) => t.id === task.id)) : -1;
|
|
22545
|
+
return at >= 0 ? at : fallback;
|
|
22546
|
+
};
|
|
22132
22547
|
const overlayTarget = state.detailPath.length > 0 ? detailTask : currentTask;
|
|
22133
22548
|
const setMode = (mode, extra = {}) => setState((prev) => ({ ...prev, mode, ...extra }));
|
|
22134
22549
|
const closeOverlay = (opts = {}) => {
|
|
@@ -22331,10 +22746,17 @@ function useKeyboardNavigation({
|
|
|
22331
22746
|
return;
|
|
22332
22747
|
}
|
|
22333
22748
|
if (state.mode === "detail") {
|
|
22334
|
-
const stops = detailTask ? buildDetailStops(detailTask, detailChildren) : [];
|
|
22749
|
+
const stops = detailTask ? buildDetailStops(detailTask, detailChildren, detailRelations) : [];
|
|
22335
22750
|
const cursor = clampDetailCursor(state.detailCursor, stops.length);
|
|
22336
22751
|
const stop = stops[cursor];
|
|
22337
|
-
const layout = detailTask ? computeDetailLayout(
|
|
22752
|
+
const layout = detailTask ? computeDetailLayout(
|
|
22753
|
+
detailTask,
|
|
22754
|
+
detailWidth,
|
|
22755
|
+
detailHeight,
|
|
22756
|
+
detailChildren,
|
|
22757
|
+
detailActivity,
|
|
22758
|
+
detailRelations
|
|
22759
|
+
) : null;
|
|
22338
22760
|
if (input === "q") {
|
|
22339
22761
|
exit();
|
|
22340
22762
|
return;
|
|
@@ -22389,17 +22811,24 @@ function useKeyboardNavigation({
|
|
|
22389
22811
|
return;
|
|
22390
22812
|
}
|
|
22391
22813
|
if (key.return) {
|
|
22392
|
-
|
|
22814
|
+
const target = stop?.kind === "child" || stop?.kind === "resting" ? stop.task.id : stop?.kind === "ref" && stop.ref.task ? stop.ref.id : void 0;
|
|
22815
|
+
if (target) {
|
|
22393
22816
|
setState((prev) => ({
|
|
22394
22817
|
...prev,
|
|
22395
|
-
detailPath: [...prev.detailPath,
|
|
22818
|
+
detailPath: [...prev.detailPath, target],
|
|
22396
22819
|
detailCursor: 0,
|
|
22397
22820
|
detailScroll: 0
|
|
22398
22821
|
}));
|
|
22822
|
+
} else if (stop?.kind === "ref" && stop.ref.missing) {
|
|
22823
|
+
showStatus(STATUS.cannotOpen(stop.ref.id, stop.ref.missing), "info");
|
|
22399
22824
|
}
|
|
22400
22825
|
return;
|
|
22401
22826
|
}
|
|
22402
22827
|
if (input === " ") {
|
|
22828
|
+
if (detailIsDecision) {
|
|
22829
|
+
showStatus(STATUS.decisionsReadOnly, "info");
|
|
22830
|
+
return;
|
|
22831
|
+
}
|
|
22403
22832
|
if (detailArchived) {
|
|
22404
22833
|
showStatus(STATUS.readOnly, "info");
|
|
22405
22834
|
return;
|
|
@@ -22430,8 +22859,12 @@ function useKeyboardNavigation({
|
|
|
22430
22859
|
});
|
|
22431
22860
|
return;
|
|
22432
22861
|
}
|
|
22862
|
+
if (detailIsDecision && detailTask && (input === "m" || input === "c")) {
|
|
22863
|
+
showStatus(input === "m" ? STATUS.decisionNoColumn : STATUS.cannotComplete(detailTask.id), "info");
|
|
22864
|
+
return;
|
|
22865
|
+
}
|
|
22433
22866
|
if (input === "m" && detailTask && !detailArchived) {
|
|
22434
|
-
setState((prev) => ({ ...prev, mode: "move", moveTargetIndex: prev.activeColumnIndex }));
|
|
22867
|
+
setState((prev) => ({ ...prev, mode: "move", moveTargetIndex: ownColumnIndex(detailTask, prev.activeColumnIndex) }));
|
|
22435
22868
|
return;
|
|
22436
22869
|
}
|
|
22437
22870
|
if (input === "c" && detailTask && !detailArchived && isCompletable(detailTask)) {
|
|
@@ -22456,21 +22889,40 @@ function useKeyboardNavigation({
|
|
|
22456
22889
|
setMode("filter", { filterQuery: "", selectedTaskIndex: 0 });
|
|
22457
22890
|
return;
|
|
22458
22891
|
}
|
|
22892
|
+
if (input === "D") {
|
|
22893
|
+
const next = decisionsView ? state.decisionsReturn ?? "all" : decisionStop;
|
|
22894
|
+
patchTuiState(filePath, { lastTypeFilter: next });
|
|
22895
|
+
setState((prev) => ({
|
|
22896
|
+
...prev,
|
|
22897
|
+
activeTypeFilter: next,
|
|
22898
|
+
decisionsReturn: decisionsView ? null : prev.activeTypeFilter,
|
|
22899
|
+
selectedTaskIndex: 0
|
|
22900
|
+
}));
|
|
22901
|
+
return;
|
|
22902
|
+
}
|
|
22459
22903
|
if (input === "L") {
|
|
22904
|
+
if (decisionsView) {
|
|
22905
|
+
if (decisionHistoryCount === 0) {
|
|
22906
|
+
showStatus(STATUS.noDecisionHistory, "info");
|
|
22907
|
+
return;
|
|
22908
|
+
}
|
|
22909
|
+
setState((prev) => ({ ...prev, decisionHistory: !prev.decisionHistory }));
|
|
22910
|
+
return;
|
|
22911
|
+
}
|
|
22460
22912
|
setState((prev) => ({ ...prev, doneView: !prev.doneView, selectedTaskIndex: 0 }));
|
|
22461
22913
|
return;
|
|
22462
22914
|
}
|
|
22463
22915
|
if (input === "t") {
|
|
22464
22916
|
if (typeCycleOptions.length > 1) {
|
|
22465
|
-
const idx = typeCycleOptions.indexOf(state.activeTypeFilter);
|
|
22917
|
+
const idx = typeCycleOptions.indexOf(decisionsView ? decisionStop : state.activeTypeFilter);
|
|
22466
22918
|
const next = typeCycleOptions[(idx < 0 ? 0 : idx + 1) % typeCycleOptions.length];
|
|
22467
22919
|
patchTuiState(filePath, { lastTypeFilter: next });
|
|
22468
|
-
setState((prev) => ({ ...prev, activeTypeFilter: next, selectedTaskIndex: 0 }));
|
|
22920
|
+
setState((prev) => ({ ...prev, activeTypeFilter: next, decisionsReturn: null, selectedTaskIndex: 0 }));
|
|
22469
22921
|
}
|
|
22470
22922
|
return;
|
|
22471
22923
|
}
|
|
22472
22924
|
if (input === " ") {
|
|
22473
|
-
if (doneView) return;
|
|
22925
|
+
if (doneView || decisionsView) return;
|
|
22474
22926
|
const row = rows[state.selectedTaskIndex];
|
|
22475
22927
|
if (row && (row.childCount ?? 0) > 0) {
|
|
22476
22928
|
const id = row.task.id;
|
|
@@ -22497,6 +22949,12 @@ function useKeyboardNavigation({
|
|
|
22497
22949
|
}
|
|
22498
22950
|
return;
|
|
22499
22951
|
}
|
|
22952
|
+
if (decisionsView && ["a", "n", "N", "m", "c", "d", "p", "A"].includes(input) && !key.ctrl) {
|
|
22953
|
+
if (input === "c" && currentTask) showStatus(STATUS.cannotComplete(currentTask.id), "info");
|
|
22954
|
+
else if (input === "m") showStatus(STATUS.decisionNoColumn, "info");
|
|
22955
|
+
else showStatus(STATUS.decisionsReadOnly, "info");
|
|
22956
|
+
return;
|
|
22957
|
+
}
|
|
22500
22958
|
if (input === "a" || input === "n") {
|
|
22501
22959
|
if (doneView) {
|
|
22502
22960
|
showStatus(STATUS.readOnly, "info");
|
|
@@ -22506,7 +22964,7 @@ function useKeyboardNavigation({
|
|
|
22506
22964
|
return;
|
|
22507
22965
|
}
|
|
22508
22966
|
if (input === "m") {
|
|
22509
|
-
if (
|
|
22967
|
+
if (rowArchived) {
|
|
22510
22968
|
showStatus(STATUS.readOnly, "info");
|
|
22511
22969
|
return;
|
|
22512
22970
|
}
|
|
@@ -22514,11 +22972,11 @@ function useKeyboardNavigation({
|
|
|
22514
22972
|
showStatus(STATUS.noDocument, "error");
|
|
22515
22973
|
return;
|
|
22516
22974
|
}
|
|
22517
|
-
setState((prev) => ({ ...prev, mode: "move", moveTargetIndex: prev.activeColumnIndex }));
|
|
22975
|
+
setState((prev) => ({ ...prev, mode: "move", moveTargetIndex: ownColumnIndex(currentTask, prev.activeColumnIndex) }));
|
|
22518
22976
|
return;
|
|
22519
22977
|
}
|
|
22520
22978
|
if (input === "c") {
|
|
22521
|
-
if (
|
|
22979
|
+
if (rowArchived) {
|
|
22522
22980
|
showStatus(STATUS.alreadyCompleted, "info");
|
|
22523
22981
|
return;
|
|
22524
22982
|
}
|
|
@@ -22534,7 +22992,7 @@ function useKeyboardNavigation({
|
|
|
22534
22992
|
return;
|
|
22535
22993
|
}
|
|
22536
22994
|
if (input === "d") {
|
|
22537
|
-
if (
|
|
22995
|
+
if (rowArchived) {
|
|
22538
22996
|
showStatus(STATUS.readOnly, "info");
|
|
22539
22997
|
return;
|
|
22540
22998
|
}
|
|
@@ -22555,7 +23013,7 @@ function useKeyboardNavigation({
|
|
|
22555
23013
|
}
|
|
22556
23014
|
if (input === "p") {
|
|
22557
23015
|
if (!currentTask) return;
|
|
22558
|
-
if (
|
|
23016
|
+
if (rowArchived) {
|
|
22559
23017
|
showStatus(STATUS.readOnly, "info");
|
|
22560
23018
|
return;
|
|
22561
23019
|
}
|
|
@@ -22587,7 +23045,7 @@ function useKeyboardNavigation({
|
|
|
22587
23045
|
}
|
|
22588
23046
|
if (input === "A") {
|
|
22589
23047
|
if (!currentTask) return;
|
|
22590
|
-
if (
|
|
23048
|
+
if (rowArchived) {
|
|
22591
23049
|
showStatus(STATUS.readOnly, "info");
|
|
22592
23050
|
return;
|
|
22593
23051
|
}
|
|
@@ -22641,7 +23099,7 @@ function useKeyboardNavigation({
|
|
|
22641
23099
|
return;
|
|
22642
23100
|
}
|
|
22643
23101
|
if (key.tab || key.rightArrow || input === "l") {
|
|
22644
|
-
if (!doneView && filteredColumnsLength > 0) {
|
|
23102
|
+
if (!doneView && !decisionsView && filteredColumnsLength > 0) {
|
|
22645
23103
|
setState((prev) => {
|
|
22646
23104
|
const nextIndex = (prev.activeColumnIndex + 1) % filteredColumnsLength;
|
|
22647
23105
|
rememberColumn(nextIndex);
|
|
@@ -22651,7 +23109,7 @@ function useKeyboardNavigation({
|
|
|
22651
23109
|
return;
|
|
22652
23110
|
}
|
|
22653
23111
|
if (key.shift && key.tab || key.leftArrow || input === "h") {
|
|
22654
|
-
if (!doneView && filteredColumnsLength > 0) {
|
|
23112
|
+
if (!doneView && !decisionsView && filteredColumnsLength > 0) {
|
|
22655
23113
|
setState((prev) => {
|
|
22656
23114
|
const nextIndex = prev.activeColumnIndex === 0 ? filteredColumnsLength - 1 : prev.activeColumnIndex - 1;
|
|
22657
23115
|
rememberColumn(nextIndex);
|
|
@@ -22668,14 +23126,25 @@ function useKeyboardNavigation({
|
|
|
22668
23126
|
}
|
|
22669
23127
|
|
|
22670
23128
|
// src/tui/typeCycle.ts
|
|
22671
|
-
var
|
|
22672
|
-
|
|
23129
|
+
var import_core9 = __toESM(require_dist());
|
|
23130
|
+
var FIXED_TYPE_ORDER = ["task", "epic", "spec", "plan", "adr", "decision"];
|
|
23131
|
+
function getTypeCycleOptions(board, decisions = []) {
|
|
22673
23132
|
const configured = Object.keys(
|
|
22674
23133
|
board?.types ?? {}
|
|
22675
23134
|
);
|
|
22676
23135
|
const configuredSet = new Set(configured);
|
|
22677
|
-
const
|
|
22678
|
-
|
|
23136
|
+
const present = new Set(
|
|
23137
|
+
(board?.columns ?? []).flatMap((column) => (column.tasks ?? []).map((task) => (task.type || "task").toLowerCase()))
|
|
23138
|
+
);
|
|
23139
|
+
const types = boardTypes(board);
|
|
23140
|
+
const isDecisionType = (type) => (0, import_core9.resolveDocType)(type, types).role === "decision";
|
|
23141
|
+
const custom = configured.filter((type) => !FIXED_TYPE_ORDER.includes(type) && !(0, import_core9.isTypeAlias)(type));
|
|
23142
|
+
const holdsDecisions = decisions.length > 0 || [...present].some(isDecisionType) || custom.some(isDecisionType);
|
|
23143
|
+
let known = FIXED_TYPE_ORDER.filter(
|
|
23144
|
+
(type) => type === "task" || configuredSet.has(type) || present.has(type) || type === "decision" && holdsDecisions
|
|
23145
|
+
);
|
|
23146
|
+
if (known.includes("adr") && known.includes("decision")) known = known.filter((type) => type !== "adr");
|
|
23147
|
+
const extra = custom.filter((type) => !isDecisionType(type)).sort();
|
|
22679
23148
|
return ["all", ...known, ...extra];
|
|
22680
23149
|
}
|
|
22681
23150
|
|
|
@@ -22684,6 +23153,7 @@ var import_react38 = __toESM(require_react());
|
|
|
22684
23153
|
function HeaderBar({
|
|
22685
23154
|
title,
|
|
22686
23155
|
columns,
|
|
23156
|
+
counts,
|
|
22687
23157
|
activeColumnIndex,
|
|
22688
23158
|
width,
|
|
22689
23159
|
filterQuery = "",
|
|
@@ -22698,9 +23168,10 @@ function HeaderBar({
|
|
|
22698
23168
|
const boardTitle = truncate(title, 24);
|
|
22699
23169
|
const separator = ` ${GLYPHS.pointer} `;
|
|
22700
23170
|
const typeLabel = activeType && activeType !== "all" ? ` \xB7 ${activeType}` : "";
|
|
23171
|
+
const countOf = (column, index) => counts?.[index] ?? column.tasks?.length ?? 0;
|
|
22701
23172
|
const tabsWidth = (panelLabel ? panelLabel.length : columns.reduce((sum, column, index) => {
|
|
22702
23173
|
const active = index === activeColumnIndex;
|
|
22703
|
-
return sum + (index > 0 ? 3 : 0) + `${column.title} ${column
|
|
23174
|
+
return sum + (index > 0 ? 3 : 0) + `${column.title} ${countOf(column, index)}`.length + (active ? 1 : 0);
|
|
22704
23175
|
}, 0)) + typeLabel.length;
|
|
22705
23176
|
const status = statusLabel ? `${statusLabel} ` : "";
|
|
22706
23177
|
const used = boardTitle.length + separator.length + tabsWidth;
|
|
@@ -22712,7 +23183,7 @@ function HeaderBar({
|
|
|
22712
23183
|
// header silently loses rows rather than the frame simply growing.
|
|
22713
23184
|
/* @__PURE__ */ import_react38.default.createElement(Box_default, { flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ import_react38.default.createElement(Box_default, { paddingLeft: 1, width }, /* @__PURE__ */ import_react38.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.text, bold: true }, boardTitle), /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.textDim }, separator), panelLabel ? /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.text }, panelLabel) : columns.map((column, index) => {
|
|
22714
23185
|
const active = index === activeColumnIndex;
|
|
22715
|
-
const count = column
|
|
23186
|
+
const count = countOf(column, index);
|
|
22716
23187
|
return /* @__PURE__ */ import_react38.default.createElement(Text, { key: column.id }, index > 0 ? /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.textDim }, " \u2502 ") : null, /* @__PURE__ */ import_react38.default.createElement(Text, { color: active ? PALETTE.text : PALETTE.textMuted, bold: active }, `${column.title} ${count}`), active ? /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.text, bold: true }, "*") : null);
|
|
22717
23188
|
}), typeLabel ? /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.textDim }, typeLabel) : null, /* @__PURE__ */ import_react38.default.createElement(Text, null, pad(gap)), status ? /* @__PURE__ */ import_react38.default.createElement(Text, { color: statusLabel?.startsWith("not synced \xB7") ? PALETTE.warning : PALETTE.textDim }, status) : null, /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.textMuted }, affordances))), filterActive ? /* @__PURE__ */ import_react38.default.createElement(
|
|
22718
23189
|
FilterLine,
|
|
@@ -22739,21 +23210,37 @@ function FilterLine({
|
|
|
22739
23210
|
|
|
22740
23211
|
// src/tui/components/FooterBar.tsx
|
|
22741
23212
|
var import_react39 = __toESM(require_react());
|
|
22742
|
-
function browseActions(selected, archived = false) {
|
|
23213
|
+
function browseActions(selected, archived = false, hasDecisions = false, selectedDone = false) {
|
|
22743
23214
|
if (archived) return ["\u21B5 detail", "e edit", "t type", "L board", "q quit"];
|
|
22744
|
-
const actions = ["\u21B5 detail"
|
|
22745
|
-
if (
|
|
22746
|
-
|
|
23215
|
+
const actions = ["\u21B5 detail"];
|
|
23216
|
+
if (selectedDone) actions.push("e edit");
|
|
23217
|
+
else {
|
|
23218
|
+
actions.push("m move");
|
|
23219
|
+
if (isCompletable(selected)) actions.push("c complete");
|
|
23220
|
+
}
|
|
23221
|
+
actions.push("a add", "t type", "L done");
|
|
23222
|
+
if (hasDecisions) actions.push("D decisions");
|
|
23223
|
+
actions.push("tab column", "q quit");
|
|
23224
|
+
return actions;
|
|
23225
|
+
}
|
|
23226
|
+
function decisionsActions({
|
|
23227
|
+
historyCount,
|
|
23228
|
+
historyOpen
|
|
23229
|
+
}) {
|
|
23230
|
+
const actions = ["\u21B5 detail", "e edit"];
|
|
23231
|
+
if (historyCount > 0) actions.push(historyOpen ? "L hide history" : "L history");
|
|
23232
|
+
actions.push("t type", "D board", "q quit");
|
|
22747
23233
|
return actions;
|
|
22748
23234
|
}
|
|
22749
23235
|
function detailActions(ctx) {
|
|
22750
23236
|
const actions = [];
|
|
22751
|
-
if (ctx.
|
|
22752
|
-
if (ctx.
|
|
23237
|
+
if (ctx.hasLinks) actions.push("\u21B5 open");
|
|
23238
|
+
else if (ctx.hasChildren) actions.push("\u21B5 open child");
|
|
23239
|
+
if (ctx.hasSubtasks && !ctx.decision) actions.push("space toggle");
|
|
22753
23240
|
if (ctx.bodyOverflows) actions.push("u/d scroll");
|
|
22754
23241
|
if (ctx.hasParent) actions.push("p parent");
|
|
22755
23242
|
actions.push("e edit");
|
|
22756
|
-
if (!ctx.archived) {
|
|
23243
|
+
if (!ctx.archived && !ctx.decision) {
|
|
22757
23244
|
actions.push("m move");
|
|
22758
23245
|
if (isCompletable(ctx.task)) actions.push("c complete");
|
|
22759
23246
|
}
|
|
@@ -22785,12 +23272,16 @@ function FooterBar({
|
|
|
22785
23272
|
// src/tui/components/DocumentRow.tsx
|
|
22786
23273
|
var import_react40 = __toESM(require_react());
|
|
22787
23274
|
var MAX_CHIPS = 3;
|
|
23275
|
+
var DECISION_CHANGED = "decision changed";
|
|
22788
23276
|
function buildChips(row) {
|
|
22789
23277
|
const chips = [];
|
|
22790
23278
|
const { task } = row;
|
|
22791
23279
|
if (row.collapsed && row.childCount) {
|
|
22792
23280
|
chips.push({ text: `${row.childCount} hidden`, color: PALETTE.textMuted });
|
|
22793
23281
|
}
|
|
23282
|
+
if (row.decisionChanged) {
|
|
23283
|
+
chips.push({ text: DECISION_CHANGED, color: PALETTE.warning });
|
|
23284
|
+
}
|
|
22794
23285
|
const contractState = getContractState(task);
|
|
22795
23286
|
if (contractState) {
|
|
22796
23287
|
chips.push({ text: contractState, color: getContractStateColor(contractState) });
|
|
@@ -22805,6 +23296,11 @@ function buildChips(row) {
|
|
|
22805
23296
|
}
|
|
22806
23297
|
return chips.slice(0, MAX_CHIPS);
|
|
22807
23298
|
}
|
|
23299
|
+
function buildStatus(row) {
|
|
23300
|
+
if (row.archived) return { text: `${GLYPHS.success} done`, color: PALETTE.success };
|
|
23301
|
+
if (row.column) return { text: row.column, color: PALETTE.textSecondary };
|
|
23302
|
+
return void 0;
|
|
23303
|
+
}
|
|
22808
23304
|
function buildPriorityMarker(row) {
|
|
22809
23305
|
const priority = row.task.priority;
|
|
22810
23306
|
if (priority !== "high" && priority !== "critical") return void 0;
|
|
@@ -22821,16 +23317,19 @@ function DocumentRow({ row, selected, width, idWidth, archived = false }) {
|
|
|
22821
23317
|
const marker = buildPriorityMarker(row);
|
|
22822
23318
|
const chips = buildChips(row);
|
|
22823
23319
|
const orphan = orphanParentId ? `${GLYPHS.orphanParent} ${orphanParentId}` : void 0;
|
|
23320
|
+
const status = buildStatus(row);
|
|
23321
|
+
const muted = archived || Boolean(row.archived);
|
|
22824
23322
|
const rightSegments = [];
|
|
22825
23323
|
if (orphan) rightSegments.push({ text: orphan, color: PALETTE.textDim });
|
|
22826
23324
|
if (marker) rightSegments.push(marker);
|
|
22827
23325
|
rightSegments.push(...chips);
|
|
23326
|
+
if (status) rightSegments.push(status);
|
|
22828
23327
|
const rightWidth = rightSegments.reduce((sum, seg) => sum + seg.text.length, 0) + Math.max(0, rightSegments.length - 1) * 2;
|
|
22829
23328
|
const titleWidth = Math.max(1, width - leftWidth - rightWidth - 2);
|
|
22830
23329
|
const title = truncate(task.title, titleWidth);
|
|
22831
23330
|
const gap = Math.max(1, width - leftWidth - title.length - rightWidth - 1);
|
|
22832
23331
|
const dim = selected ? void 0 : PALETTE.textMuted;
|
|
22833
|
-
return /* @__PURE__ */ import_react40.default.createElement(Text, { wrap: "truncate", inverse: selected }, /* @__PURE__ */ import_react40.default.createElement(Text, null, indent), /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 : PALETTE.textSecondary }, glyphCell), /* @__PURE__ */ import_react40.default.createElement(Text, { color: dim }, idCell), /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 :
|
|
23332
|
+
return /* @__PURE__ */ import_react40.default.createElement(Text, { wrap: "truncate", inverse: selected }, /* @__PURE__ */ import_react40.default.createElement(Text, null, indent), /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 : PALETTE.textSecondary }, glyphCell), /* @__PURE__ */ import_react40.default.createElement(Text, { color: dim }, idCell), /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 : muted ? PALETTE.textMuted : PALETTE.text }, title), /* @__PURE__ */ import_react40.default.createElement(Text, null, pad(gap)), rightSegments.map((seg, index) => /* @__PURE__ */ import_react40.default.createElement(Text, { key: `${seg.text}-${index}` }, index > 0 ? /* @__PURE__ */ import_react40.default.createElement(Text, null, pad(2)) : null, /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 : seg.color }, seg.text))), /* @__PURE__ */ import_react40.default.createElement(Text, null, pad(1)));
|
|
22834
23333
|
}
|
|
22835
23334
|
|
|
22836
23335
|
// src/tui/components/DocumentList.tsx
|
|
@@ -22861,8 +23360,91 @@ function DocumentList({
|
|
|
22861
23360
|
)));
|
|
22862
23361
|
}
|
|
22863
23362
|
|
|
22864
|
-
// src/tui/components/
|
|
23363
|
+
// src/tui/components/DecisionList.tsx
|
|
22865
23364
|
var import_react42 = __toESM(require_react());
|
|
23365
|
+
function decisionSignals(line) {
|
|
23366
|
+
const { summary, retired } = line;
|
|
23367
|
+
const out = [];
|
|
23368
|
+
if (retired) {
|
|
23369
|
+
out.push({ text: retiredLabel(summary), color: PALETTE.textMuted });
|
|
23370
|
+
return out;
|
|
23371
|
+
}
|
|
23372
|
+
if (summary.status === "proposed") out.push({ text: "proposed", color: PALETTE.textMuted });
|
|
23373
|
+
if (summary.kind && summary.kind !== "decision") out.push({ text: summary.kind, color: PALETTE.textMuted });
|
|
23374
|
+
if (isPastValidUntil(summary)) out.push({ text: "review", color: PALETTE.warning });
|
|
23375
|
+
return out;
|
|
23376
|
+
}
|
|
23377
|
+
function revealedClaimKey(line) {
|
|
23378
|
+
const key = line.summary.claimKey;
|
|
23379
|
+
if (!key) return "";
|
|
23380
|
+
return line.retired ? key : claimKeyTail(key);
|
|
23381
|
+
}
|
|
23382
|
+
var widthOf = (signals) => signals.reduce((sum, signal) => sum + signal.text.length, 0) + Math.max(0, signals.length - 1) * 2;
|
|
23383
|
+
function DecisionRow({ line, selected, width, idWidth }) {
|
|
23384
|
+
const { task, retired } = line;
|
|
23385
|
+
const indent = selected ? GLYPHS.cursor : " ";
|
|
23386
|
+
const glyphCell = `${retired ? GLYPHS.retiredDecision : getTypeGlyph("decision")} `;
|
|
23387
|
+
const idCell = `${task.id.padEnd(idWidth)} `;
|
|
23388
|
+
const leftWidth = indent.length + glyphCell.length + idCell.length;
|
|
23389
|
+
const atRest = decisionSignals(line);
|
|
23390
|
+
const key = selected ? revealedClaimKey(line) : "";
|
|
23391
|
+
const withKey = key ? [{ text: key, color: PALETTE.textMuted }, ...atRest] : atRest;
|
|
23392
|
+
const fits = width - leftWidth - widthOf(withKey) - 2 >= task.title.length;
|
|
23393
|
+
const signals = key && fits ? withKey : atRest;
|
|
23394
|
+
const rightWidth = widthOf(signals);
|
|
23395
|
+
const titleWidth = Math.max(1, width - leftWidth - rightWidth - 2);
|
|
23396
|
+
const title = truncate(task.title, titleWidth);
|
|
23397
|
+
const gap = Math.max(1, width - leftWidth - title.length - rightWidth - 1);
|
|
23398
|
+
return /* @__PURE__ */ import_react42.default.createElement(Text, { wrap: "truncate", inverse: selected }, /* @__PURE__ */ import_react42.default.createElement(Text, null, indent), /* @__PURE__ */ import_react42.default.createElement(Text, { color: selected ? void 0 : retired ? PALETTE.textMuted : PALETTE.textSecondary }, glyphCell), /* @__PURE__ */ import_react42.default.createElement(Text, { color: selected ? void 0 : PALETTE.textMuted }, idCell), /* @__PURE__ */ import_react42.default.createElement(Text, { color: selected ? void 0 : retired ? PALETTE.textMuted : PALETTE.text }, title), /* @__PURE__ */ import_react42.default.createElement(Text, null, pad(gap)), signals.map((signal, index) => /* @__PURE__ */ import_react42.default.createElement(Text, { key: `${signal.text}-${index}` }, index > 0 ? /* @__PURE__ */ import_react42.default.createElement(Text, null, pad(2)) : null, /* @__PURE__ */ import_react42.default.createElement(Text, { color: selected ? void 0 : signal.color }, signal.text))), /* @__PURE__ */ import_react42.default.createElement(Text, null, pad(1)));
|
|
23399
|
+
}
|
|
23400
|
+
function DecisionList({
|
|
23401
|
+
lines,
|
|
23402
|
+
selectedIndex,
|
|
23403
|
+
viewportHeight,
|
|
23404
|
+
width,
|
|
23405
|
+
emptyMessage = EMPTY.noDecisions
|
|
23406
|
+
}) {
|
|
23407
|
+
if (lines.length === 0) {
|
|
23408
|
+
return /* @__PURE__ */ import_react42.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textMuted }, emptyMessage));
|
|
23409
|
+
}
|
|
23410
|
+
const { visible, start } = windowLines(lines, selectedIndex, viewportHeight);
|
|
23411
|
+
const idWidth = Math.max(
|
|
23412
|
+
6,
|
|
23413
|
+
...visible.flatMap((line) => line.kind === "row" ? [line.task.id.length] : [])
|
|
23414
|
+
);
|
|
23415
|
+
return /* @__PURE__ */ import_react42.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1 }, visible.map((line, offset) => {
|
|
23416
|
+
const key = `${line.kind}-${start + offset}`;
|
|
23417
|
+
switch (line.kind) {
|
|
23418
|
+
case "row":
|
|
23419
|
+
return /* @__PURE__ */ import_react42.default.createElement(
|
|
23420
|
+
DecisionRow,
|
|
23421
|
+
{
|
|
23422
|
+
key: line.task.id,
|
|
23423
|
+
line,
|
|
23424
|
+
selected: line.index === selectedIndex,
|
|
23425
|
+
width,
|
|
23426
|
+
idWidth
|
|
23427
|
+
}
|
|
23428
|
+
);
|
|
23429
|
+
case "heading":
|
|
23430
|
+
return (
|
|
23431
|
+
// Headings sit in the glyph column, so a family's name lines up
|
|
23432
|
+
// with the marks of the decisions under it.
|
|
23433
|
+
/* @__PURE__ */ import_react42.default.createElement(Text, { key, wrap: "truncate" }, /* @__PURE__ */ import_react42.default.createElement(Text, null, " "), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textSecondary }, truncate(line.label, Math.max(1, width - 10))), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textMuted }, ` (${line.count})`))
|
|
23434
|
+
);
|
|
23435
|
+
case "history":
|
|
23436
|
+
return /* @__PURE__ */ import_react42.default.createElement(Text, { key, wrap: "truncate" }, /* @__PURE__ */ import_react42.default.createElement(Text, null, " "), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textMuted }, `${line.open ? GLYPHS.foldOpen : GLYPHS.foldClosed} `), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textSecondary }, "history"), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textMuted }, ` (${line.count})`));
|
|
23437
|
+
case "note":
|
|
23438
|
+
return /* @__PURE__ */ import_react42.default.createElement(Text, { key, wrap: "truncate", color: PALETTE.textMuted }, ` ${line.text}`);
|
|
23439
|
+
case "gap":
|
|
23440
|
+
default:
|
|
23441
|
+
return /* @__PURE__ */ import_react42.default.createElement(Text, { key }, " ");
|
|
23442
|
+
}
|
|
23443
|
+
}));
|
|
23444
|
+
}
|
|
23445
|
+
|
|
23446
|
+
// src/tui/components/HelpOverlay.tsx
|
|
23447
|
+
var import_react43 = __toESM(require_react());
|
|
22866
23448
|
var LIST = {
|
|
22867
23449
|
heading: "list",
|
|
22868
23450
|
rows: [
|
|
@@ -22872,14 +23454,15 @@ var LIST = {
|
|
|
22872
23454
|
["ctrl-d/u", "half page"],
|
|
22873
23455
|
["\u21B5", "open"],
|
|
22874
23456
|
["t", "type"],
|
|
22875
|
-
["L", "done"],
|
|
23457
|
+
["L", "done \xB7 history"],
|
|
23458
|
+
["D", "decisions"],
|
|
22876
23459
|
["/", "filter"]
|
|
22877
23460
|
]
|
|
22878
23461
|
};
|
|
22879
23462
|
var DETAIL = {
|
|
22880
23463
|
heading: "detail",
|
|
22881
23464
|
rows: [
|
|
22882
|
-
["\u21B5", "open child"],
|
|
23465
|
+
["\u21B5", "open child or link"],
|
|
22883
23466
|
["space", "toggle subtask"],
|
|
22884
23467
|
["u d", "scroll body"],
|
|
22885
23468
|
["p", "parent"],
|
|
@@ -22917,29 +23500,34 @@ var COLUMNS = [
|
|
|
22917
23500
|
[LIST, DETAIL],
|
|
22918
23501
|
[ACTIONS, FILTER]
|
|
22919
23502
|
];
|
|
22920
|
-
var
|
|
23503
|
+
var KEYS_WIDTHS = COLUMNS.map(
|
|
23504
|
+
(column) => Math.max(...column.flatMap((group) => group.rows.map(([keys]) => keys.length))) + 2
|
|
23505
|
+
);
|
|
22921
23506
|
function HelpOverlay({ termWidth, termHeight }) {
|
|
22922
23507
|
const columnWidth = Math.max(24, Math.floor((termWidth - 4) / COLUMNS.length));
|
|
22923
|
-
|
|
22924
|
-
|
|
22925
|
-
|
|
22926
|
-
{
|
|
22927
|
-
|
|
22928
|
-
|
|
22929
|
-
|
|
22930
|
-
|
|
22931
|
-
|
|
22932
|
-
|
|
22933
|
-
|
|
23508
|
+
return /* @__PURE__ */ import_react43.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight, paddingLeft: 1 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.text, bold: true }, CHROME.helpTitle), /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textDim }, RULE.repeat(Math.max(1, termWidth - 2))), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1 }, COLUMNS.map((column, columnIndex) => {
|
|
23509
|
+
const keysWidth = KEYS_WIDTHS[columnIndex];
|
|
23510
|
+
const descriptionWidth = Math.max(6, columnWidth - keysWidth - 2);
|
|
23511
|
+
return /* @__PURE__ */ import_react43.default.createElement(Box_default, { key: columnIndex, flexDirection: "column", width: columnWidth }, column.map((group, groupIndex) => /* @__PURE__ */ import_react43.default.createElement(
|
|
23512
|
+
Box_default,
|
|
23513
|
+
{
|
|
23514
|
+
key: group.heading,
|
|
23515
|
+
flexDirection: "column",
|
|
23516
|
+
marginTop: groupIndex === 0 ? 0 : 1
|
|
23517
|
+
},
|
|
23518
|
+
/* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textSecondary, bold: true }, group.heading),
|
|
23519
|
+
group.rows.map(([keys, description]) => /* @__PURE__ */ import_react43.default.createElement(Text, { key: keys, wrap: "truncate" }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.text }, keys.padEnd(keysWidth)), /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, truncate(description, descriptionWidth))))
|
|
23520
|
+
)));
|
|
23521
|
+
})), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textDim }, CHROME.helpDismiss)));
|
|
22934
23522
|
}
|
|
22935
23523
|
|
|
22936
23524
|
// src/tui/components/Overlays.tsx
|
|
22937
|
-
var
|
|
23525
|
+
var import_react44 = __toESM(require_react());
|
|
22938
23526
|
function StatusMessageDisplay({ message }) {
|
|
22939
23527
|
if (!message) return null;
|
|
22940
23528
|
const color = message.type === "success" ? PALETTE.success : message.type === "error" ? PALETTE.error : PALETTE.textSecondary;
|
|
22941
23529
|
const glyph = message.type === "success" ? GLYPHS.success : message.type === "error" ? GLYPHS.error : GLYPHS.live;
|
|
22942
|
-
return /* @__PURE__ */
|
|
23530
|
+
return /* @__PURE__ */ import_react44.default.createElement(Box_default, { paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color, wrap: "truncate" }, `${glyph} ${message.text}`));
|
|
22943
23531
|
}
|
|
22944
23532
|
function MoveOverlay({
|
|
22945
23533
|
columns,
|
|
@@ -22948,16 +23536,16 @@ function MoveOverlay({
|
|
|
22948
23536
|
taskTitle,
|
|
22949
23537
|
width
|
|
22950
23538
|
}) {
|
|
22951
|
-
return /* @__PURE__ */
|
|
23539
|
+
return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "move"), /* @__PURE__ */ import_react44.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, ` ${taskId} `), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.text }, truncate(taskTitle, Math.max(1, width - taskId.length - 6)))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", marginTop: 1, paddingLeft: 2 }, columns.map((column, index) => /* @__PURE__ */ import_react44.default.createElement(Text, { key: column.id, inverse: index === selectedIndex, wrap: "truncate" }, `${index + 1} ${column.title}`))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "\u2191\u2193 select \xB7 1-", columns.length, " jump \xB7 \u21B5 confirm \xB7 esc cancel")));
|
|
22952
23540
|
}
|
|
22953
23541
|
function DeleteConfirmOverlay({ taskId, taskTitle, width }) {
|
|
22954
|
-
return /* @__PURE__ */
|
|
23542
|
+
return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.error }, "delete"), /* @__PURE__ */ import_react44.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, ` ${taskId} `), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.text }, truncate(taskTitle, Math.max(1, width - taskId.length - 6)))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "Deletes the file. This cannot be undone.")), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "y delete \xB7 n cancel")));
|
|
22955
23543
|
}
|
|
22956
23544
|
function CompleteConfirmOverlay({ target, width }) {
|
|
22957
|
-
return /* @__PURE__ */
|
|
23545
|
+
return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.warning }, "complete"), /* @__PURE__ */ import_react44.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, ` ${target.id} `), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.text }, truncate(target.title, Math.max(1, width - target.id.length - 6)))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, incompleteChildrenLabel(target.incompleteChildren.length))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 4 }, target.incompleteChildren.map((child) => /* @__PURE__ */ import_react44.default.createElement(Text, { key: child.id, wrap: "truncate" }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, `${child.id} `), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textSecondary }, truncate(child.title, Math.max(1, width - child.id.length - 8)))))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "y complete anyway \xB7 n cancel")));
|
|
22958
23546
|
}
|
|
22959
23547
|
function AddOverlay({ title, columnName, thenEdit = false }) {
|
|
22960
|
-
return /* @__PURE__ */
|
|
23548
|
+
return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, thenEdit ? `add to ${columnName}, then edit` : `add to ${columnName}`), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: title ? PALETTE.text : PALETTE.textMuted }, title || "title\u2026"), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.accent }, GLYPHS.cursor))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, thenEdit ? "\u21B5 create and edit \xB7 esc cancel" : "\u21B5 create \xB7 esc cancel")));
|
|
22961
23549
|
}
|
|
22962
23550
|
|
|
22963
23551
|
// src/tui/BrainfileTUI.tsx
|
|
@@ -22982,15 +23570,18 @@ var baseInitialState = {
|
|
|
22982
23570
|
statusMessage: null,
|
|
22983
23571
|
lastContentHash: null,
|
|
22984
23572
|
reloadFlash: false,
|
|
22985
|
-
logs: []
|
|
23573
|
+
logs: [],
|
|
23574
|
+
decisions: [],
|
|
23575
|
+
decisionHistory: false,
|
|
23576
|
+
decisionsReturn: null
|
|
22986
23577
|
};
|
|
22987
23578
|
function BrainfileTUI({ filePath, width, height }) {
|
|
22988
23579
|
const { stdout } = use_stdout_default();
|
|
22989
|
-
const [dimensions, setDimensions] = (0,
|
|
23580
|
+
const [dimensions, setDimensions] = (0, import_react45.useState)({
|
|
22990
23581
|
width: width ?? stdout?.columns ?? process.stdout.columns ?? 80,
|
|
22991
23582
|
height: height ?? stdout?.rows ?? process.stdout.rows ?? 24
|
|
22992
23583
|
});
|
|
22993
|
-
(0,
|
|
23584
|
+
(0, import_react45.useEffect)(() => {
|
|
22994
23585
|
if (width !== void 0 && height !== void 0) return void 0;
|
|
22995
23586
|
const handleResize = () => {
|
|
22996
23587
|
setDimensions({
|
|
@@ -23007,8 +23598,8 @@ function BrainfileTUI({ filePath, width, height }) {
|
|
|
23007
23598
|
const termHeight = height ?? dimensions.height;
|
|
23008
23599
|
const isTooSmall = termWidth < LAYOUT.MIN_WIDTH || termHeight < LAYOUT.MIN_HEIGHT;
|
|
23009
23600
|
const layoutMode = termWidth >= LAYOUT.DETAIL_PANE_MIN_WIDTH ? "wide" : "narrow";
|
|
23010
|
-
const resumeColumn = (0,
|
|
23011
|
-
const [state, setState] = (0,
|
|
23601
|
+
const resumeColumn = (0, import_react45.useRef)(void 0);
|
|
23602
|
+
const [state, setState] = (0, import_react45.useState)(() => {
|
|
23012
23603
|
const persisted = readTuiState(filePath);
|
|
23013
23604
|
resumeColumn.current = persisted.lastColumn;
|
|
23014
23605
|
return {
|
|
@@ -23021,7 +23612,7 @@ function BrainfileTUI({ filePath, width, height }) {
|
|
|
23021
23612
|
const viewportHeight = Math.max(termHeight - HEADER_ROWS - FOOTER_ROWS, 3);
|
|
23022
23613
|
const { loadBrainfile } = useBrainfileLoader(filePath, state, setState);
|
|
23023
23614
|
const syncStatus = useSyncStatus(filePath, state.lastUpdated);
|
|
23024
|
-
const orderedColumns = (0,
|
|
23615
|
+
const orderedColumns = (0, import_react45.useMemo)(() => {
|
|
23025
23616
|
if (!state.board) return [];
|
|
23026
23617
|
return [...state.board.columns].sort((a, b) => {
|
|
23027
23618
|
const orderA = a.order ?? Number.MAX_SAFE_INTEGER;
|
|
@@ -23029,79 +23620,137 @@ function BrainfileTUI({ filePath, width, height }) {
|
|
|
23029
23620
|
return orderA - orderB;
|
|
23030
23621
|
});
|
|
23031
23622
|
}, [state.board]);
|
|
23032
|
-
const
|
|
23033
|
-
const
|
|
23034
|
-
|
|
23035
|
-
|
|
23036
|
-
|
|
23623
|
+
const types = boardTypes(state.board);
|
|
23624
|
+
const typeCycleOptions = (0, import_react45.useMemo)(
|
|
23625
|
+
() => getTypeCycleOptions(state.board, state.decisions),
|
|
23626
|
+
[state.board, state.decisions]
|
|
23627
|
+
);
|
|
23628
|
+
const activeTypeFilter = typeCycleOptions.includes(state.activeTypeFilter) || isDecisionStop(state.activeTypeFilter, types) ? state.activeTypeFilter : "all";
|
|
23629
|
+
const decisionsView = isDecisionStop(activeTypeFilter, types);
|
|
23630
|
+
const decisionStop = typeCycleOptions.find((type) => isDecisionStop(type, types)) ?? "decision";
|
|
23631
|
+
const typeFilterActive = activeTypeFilter !== "all" && !decisionsView;
|
|
23632
|
+
const doneView = state.doneView && !decisionsView;
|
|
23633
|
+
const typeFilteredColumns = (0, import_react45.useMemo)(() => {
|
|
23037
23634
|
if (!typeFilterActive) return orderedColumns;
|
|
23038
23635
|
return orderedColumns.map((column) => ({
|
|
23039
23636
|
...column,
|
|
23040
|
-
tasks: (column.tasks ?? []).filter((task) =>
|
|
23637
|
+
tasks: (column.tasks ?? []).filter((task) => (0, import_core10.typesMatch)(task.type, activeTypeFilter))
|
|
23041
23638
|
}));
|
|
23042
23639
|
}, [orderedColumns, typeFilterActive, doneView, activeTypeFilter]);
|
|
23043
|
-
const totalCount = (0,
|
|
23044
|
-
|
|
23045
|
-
|
|
23046
|
-
|
|
23640
|
+
const totalCount = (0, import_react45.useMemo)(() => {
|
|
23641
|
+
if (decisionsView) return state.decisions.length;
|
|
23642
|
+
if (doneView) {
|
|
23643
|
+
return typeFilterActive ? state.logs.filter((task) => (0, import_core10.typesMatch)(task.type, activeTypeFilter)).length : state.logs.length;
|
|
23644
|
+
}
|
|
23645
|
+
return typeFilteredColumns.reduce((sum, column) => sum + (column.tasks?.length ?? 0), 0);
|
|
23646
|
+
}, [typeFilteredColumns, doneView, decisionsView, state.logs, state.decisions]);
|
|
23047
23647
|
const filterQuery = state.filterQuery.trim();
|
|
23048
23648
|
const hasFilter = filterQuery.length > 0;
|
|
23049
|
-
const filteredColumns = (0,
|
|
23649
|
+
const filteredColumns = (0, import_react45.useMemo)(() => {
|
|
23050
23650
|
if (!hasFilter) return typeFilteredColumns;
|
|
23051
23651
|
return typeFilteredColumns.map((column) => {
|
|
23052
23652
|
const docs = (column.tasks ?? []).map((task) => ({ task, body: task.description ?? "" }));
|
|
23053
|
-
const matches = (0,
|
|
23653
|
+
const matches = (0, import_core10.searchTasksRanked)(docs, filterQuery);
|
|
23054
23654
|
return { ...column, tasks: matches.map((match) => match.doc.task) };
|
|
23055
23655
|
});
|
|
23056
23656
|
}, [typeFilteredColumns, hasFilter, filterQuery]);
|
|
23057
|
-
const filteredLogs = (0,
|
|
23657
|
+
const filteredLogs = (0, import_react45.useMemo)(() => {
|
|
23058
23658
|
if (!doneView) return [];
|
|
23059
|
-
const typed = typeFilterActive ? state.logs.filter((task) =>
|
|
23659
|
+
const typed = typeFilterActive ? state.logs.filter((task) => (0, import_core10.typesMatch)(task.type, activeTypeFilter)) : state.logs;
|
|
23060
23660
|
if (!hasFilter) return typed;
|
|
23061
23661
|
const docs = typed.map((task) => ({ task, body: task.description ?? "" }));
|
|
23062
|
-
return (0,
|
|
23662
|
+
return (0, import_core10.searchTasksRanked)(docs, filterQuery).map((match) => match.doc.task);
|
|
23063
23663
|
}, [doneView, state.logs, typeFilterActive, activeTypeFilter, hasFilter, filterQuery]);
|
|
23064
|
-
const
|
|
23065
|
-
() =>
|
|
23066
|
-
[
|
|
23664
|
+
const filteredDecisions = (0, import_react45.useMemo)(
|
|
23665
|
+
() => decisionsView ? filterDecisions(state.decisions, filterQuery) : [],
|
|
23666
|
+
[decisionsView, state.decisions, filterQuery]
|
|
23667
|
+
);
|
|
23668
|
+
const decisionModel = (0, import_react45.useMemo)(
|
|
23669
|
+
() => buildDecisionView(filteredDecisions, { historyOpen: state.decisionHistory, types }),
|
|
23670
|
+
[filteredDecisions, state.decisionHistory, types]
|
|
23671
|
+
);
|
|
23672
|
+
const matchCount = (0, import_react45.useMemo)(() => {
|
|
23673
|
+
if (decisionsView) return filteredDecisions.length;
|
|
23674
|
+
if (doneView) return filteredLogs.length;
|
|
23675
|
+
return filteredColumns.reduce((sum, column) => sum + (column.tasks?.length ?? 0), 0);
|
|
23676
|
+
}, [filteredColumns, doneView, filteredLogs, decisionsView, filteredDecisions]);
|
|
23677
|
+
const allBoardTasks = (0, import_react45.useMemo)(
|
|
23678
|
+
() => orderedColumns.flatMap((column) => column.tasks ?? []),
|
|
23679
|
+
[orderedColumns]
|
|
23680
|
+
);
|
|
23681
|
+
const graph = (0, import_react45.useMemo)(
|
|
23682
|
+
() => (0, import_core10.buildDocGraph)([...allBoardTasks, ...state.decisions, ...state.logs], { types }),
|
|
23683
|
+
[allBoardTasks, state.decisions, state.logs, types]
|
|
23067
23684
|
);
|
|
23685
|
+
const decisionChanged = (0, import_react45.useMemo)(() => workOnRetiredDecisions(graph), [graph]);
|
|
23068
23686
|
const activeColumnIndex = Math.min(
|
|
23069
23687
|
state.activeColumnIndex,
|
|
23070
23688
|
Math.max(0, filteredColumns.length - 1)
|
|
23071
23689
|
);
|
|
23072
23690
|
const currentColumn = filteredColumns[activeColumnIndex];
|
|
23073
|
-
const
|
|
23074
|
-
|
|
23075
|
-
|
|
23691
|
+
const outline = (0, import_react45.useMemo)(() => {
|
|
23692
|
+
if (typeFilterActive || doneView || decisionsView) return null;
|
|
23693
|
+
const logs = hasFilter ? (0, import_core10.searchTasksRanked)(state.logs.map((task) => ({ task, body: task.description ?? "" })), filterQuery).map(
|
|
23694
|
+
(match) => match.doc.task
|
|
23695
|
+
) : state.logs;
|
|
23696
|
+
return buildOutline({ columns: filteredColumns, logs });
|
|
23697
|
+
}, [typeFilterActive, doneView, decisionsView, hasFilter, filterQuery, state.logs, filteredColumns]);
|
|
23698
|
+
const tabCounts = (0, import_react45.useMemo)(
|
|
23699
|
+
() => outline ? filteredColumns.map((column) => outline.get(column.id)?.length ?? 0) : void 0,
|
|
23700
|
+
[outline, filteredColumns]
|
|
23076
23701
|
);
|
|
23702
|
+
const rows = (0, import_react45.useMemo)(() => {
|
|
23703
|
+
if (decisionsView) return decisionModel.selectable.map((task) => ({ task, depth: 0 }));
|
|
23704
|
+
if (doneView) return buildFlatRows(filteredLogs);
|
|
23705
|
+
const tasks = currentColumn?.tasks ?? [];
|
|
23706
|
+
return markDecisionChanges(
|
|
23707
|
+
outline && currentColumn ? collapseRows(outline.get(currentColumn.id) ?? [], state.collapsedIds) : buildFlatRows(tasks),
|
|
23708
|
+
decisionChanged
|
|
23709
|
+
);
|
|
23710
|
+
}, [currentColumn, doneView, filteredLogs, outline, state.collapsedIds, decisionsView, decisionModel, decisionChanged]);
|
|
23077
23711
|
const maxRowIndex = Math.max(0, rows.length - 1);
|
|
23078
23712
|
const selectedIndex = Math.min(state.selectedTaskIndex, maxRowIndex);
|
|
23079
23713
|
const selectedTask = rows[selectedIndex]?.task;
|
|
23080
|
-
const
|
|
23081
|
-
|
|
23082
|
-
[
|
|
23083
|
-
);
|
|
23084
|
-
|
|
23085
|
-
|
|
23086
|
-
|
|
23714
|
+
const lastSelection = (0, import_react45.useRef)({
|
|
23715
|
+
board: null,
|
|
23716
|
+
logs: []
|
|
23717
|
+
});
|
|
23718
|
+
(0, import_react45.useEffect)(() => {
|
|
23719
|
+
const was = lastSelection.current;
|
|
23720
|
+
const reloaded = was.board !== null && (was.board !== state.board || was.logs !== state.logs);
|
|
23721
|
+
if (reloaded && was.id && rows[selectedIndex]?.task.id !== was.id) {
|
|
23722
|
+
const found = rows.findIndex((row) => row.task.id === was.id);
|
|
23723
|
+
if (found >= 0) setState((prev) => ({ ...prev, selectedTaskIndex: found }));
|
|
23724
|
+
}
|
|
23725
|
+
lastSelection.current = { board: state.board, logs: state.logs, id: rows[selectedIndex]?.task.id };
|
|
23726
|
+
});
|
|
23727
|
+
const allDocs = (0, import_react45.useMemo)(
|
|
23728
|
+
() => [...allBoardTasks, ...state.decisions, ...state.logs],
|
|
23729
|
+
[allBoardTasks, state.decisions, state.logs]
|
|
23087
23730
|
);
|
|
23088
|
-
const archivedIds = (0,
|
|
23731
|
+
const archivedIds = (0, import_react45.useMemo)(() => new Set(state.logs.map((t) => t.id)), [state.logs]);
|
|
23089
23732
|
const detailTaskId = state.detailPath[state.detailPath.length - 1];
|
|
23090
23733
|
const detailTask = state.detailPath.length > 0 ? allDocs.find((t) => t.id === detailTaskId) : void 0;
|
|
23091
|
-
const detailChildren = (0,
|
|
23734
|
+
const detailChildren = (0, import_react45.useMemo)(
|
|
23092
23735
|
() => detailTask ? allDocs.filter((t) => t.parentId === detailTask.id) : [],
|
|
23093
23736
|
[allDocs, detailTask]
|
|
23094
23737
|
);
|
|
23095
23738
|
const detailParent = detailTask?.parentId ? allDocs.find((t) => t.id === detailTask.parentId) : void 0;
|
|
23096
|
-
const
|
|
23739
|
+
const detailRelations = (0, import_react45.useMemo)(
|
|
23740
|
+
() => detailTask ? relationsFor(detailTask, graph) : NO_RELATIONS,
|
|
23741
|
+
[detailTask, graph]
|
|
23742
|
+
);
|
|
23743
|
+
const detailIsDecision = Boolean(detailRelations.decision);
|
|
23744
|
+
const detailColumnLabel = (0, import_react45.useMemo)(() => {
|
|
23097
23745
|
if (!detailTask) return "";
|
|
23746
|
+
if (detailRelations.decision) return detailRelations.decision.status;
|
|
23098
23747
|
if (archivedIds.has(detailTask.id)) {
|
|
23099
23748
|
const at = detailTask.completedAt;
|
|
23100
23749
|
return at ? `completed ${at.slice(5, 10)}` : "completed";
|
|
23101
23750
|
}
|
|
23102
23751
|
return orderedColumns.find((c) => c.tasks?.some((t) => t.id === detailTask.id))?.id ?? "";
|
|
23103
|
-
}, [orderedColumns, detailTask, archivedIds]);
|
|
23104
|
-
const detailActivity = (0,
|
|
23752
|
+
}, [orderedColumns, detailTask, archivedIds, detailRelations]);
|
|
23753
|
+
const detailActivity = (0, import_react45.useMemo)(
|
|
23105
23754
|
() => detailTask ? getTaskActivity(filePath, detailTask.id) : [],
|
|
23106
23755
|
[detailTask, filePath, state.lastUpdated]
|
|
23107
23756
|
);
|
|
@@ -23110,15 +23759,22 @@ function BrainfileTUI({ filePath, width, height }) {
|
|
|
23110
23759
|
const detailPaneWidth = Math.floor(termWidth * LAYOUT.DETAIL_PANE_FRACTION);
|
|
23111
23760
|
const detailViewWidth = fullscreenDetailMode ? termWidth : detailPaneWidth;
|
|
23112
23761
|
const detailViewHeight = fullscreenDetailMode ? termHeight - FOOTER_ROWS : viewportHeight;
|
|
23113
|
-
const detailLayout = detailTask ? computeDetailLayout(
|
|
23114
|
-
|
|
23762
|
+
const detailLayout = detailTask ? computeDetailLayout(
|
|
23763
|
+
detailTask,
|
|
23764
|
+
detailViewWidth,
|
|
23765
|
+
detailViewHeight,
|
|
23766
|
+
detailChildren,
|
|
23767
|
+
detailActivity,
|
|
23768
|
+
detailRelations
|
|
23769
|
+
) : null;
|
|
23770
|
+
(0, import_react45.useEffect)(() => {
|
|
23115
23771
|
const wanted = resumeColumn.current;
|
|
23116
23772
|
if (!wanted || orderedColumns.length === 0) return;
|
|
23117
23773
|
resumeColumn.current = void 0;
|
|
23118
23774
|
const idx = orderedColumns.findIndex((c) => c.id === wanted);
|
|
23119
23775
|
if (idx > 0) setState((prev) => ({ ...prev, activeColumnIndex: idx }));
|
|
23120
23776
|
}, [orderedColumns]);
|
|
23121
|
-
(0,
|
|
23777
|
+
(0, import_react45.useEffect)(() => {
|
|
23122
23778
|
setState((prev) => ({
|
|
23123
23779
|
...prev,
|
|
23124
23780
|
activeColumnIndex: Math.min(prev.activeColumnIndex, Math.max(0, filteredColumns.length - 1)),
|
|
@@ -23142,22 +23798,27 @@ function BrainfileTUI({ filePath, width, height }) {
|
|
|
23142
23798
|
detailWidth: detailViewWidth,
|
|
23143
23799
|
detailHeight: detailViewHeight,
|
|
23144
23800
|
doneView,
|
|
23145
|
-
detailArchived: Boolean(detailTask && archivedIds.has(detailTask.id))
|
|
23801
|
+
detailArchived: Boolean(detailTask && archivedIds.has(detailTask.id)),
|
|
23802
|
+
decisionsView,
|
|
23803
|
+
decisionStop,
|
|
23804
|
+
decisionHistoryCount: decisionModel.historyCount,
|
|
23805
|
+
detailRelations,
|
|
23806
|
+
detailIsDecision
|
|
23146
23807
|
});
|
|
23147
23808
|
if (isTooSmall) {
|
|
23148
|
-
return /* @__PURE__ */
|
|
23809
|
+
return /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight, paddingLeft: 1 }, /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.error }, CHROME.tooSmall), /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.textMuted }, "Minimum ", LAYOUT.MIN_WIDTH, "\xD7", LAYOUT.MIN_HEIGHT, " \xB7 current ", termWidth, "\xD7", termHeight));
|
|
23149
23810
|
}
|
|
23150
23811
|
if (state.error) {
|
|
23151
|
-
return /* @__PURE__ */
|
|
23812
|
+
return /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1 }, /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.error }, CHROME.errorTitle), /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.textSecondary }, state.error), /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.textMuted }, CHROME.errorHint));
|
|
23152
23813
|
}
|
|
23153
23814
|
if (!state.board) {
|
|
23154
|
-
return /* @__PURE__ */
|
|
23815
|
+
return /* @__PURE__ */ import_react45.default.createElement(Box_default, { paddingLeft: 1 }, /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.textMuted }, CHROME.loading));
|
|
23155
23816
|
}
|
|
23156
23817
|
if (state.mode === "help") {
|
|
23157
|
-
return /* @__PURE__ */
|
|
23818
|
+
return /* @__PURE__ */ import_react45.default.createElement(HelpOverlay, { termWidth, termHeight });
|
|
23158
23819
|
}
|
|
23159
23820
|
const boardTitle = state.board.title || "brainfile";
|
|
23160
|
-
const columnLabel = doneView ? "done" : currentColumn?.id ?? "";
|
|
23821
|
+
const columnLabel = decisionsView ? "decisions" : doneView ? "done" : currentColumn?.id ?? "";
|
|
23161
23822
|
const columnName = currentColumn?.title ?? "";
|
|
23162
23823
|
const fullscreenDetail = detailOpen && layoutMode === "narrow";
|
|
23163
23824
|
const detailArchived = Boolean(detailTask && archivedIds.has(detailTask.id));
|
|
@@ -23167,10 +23828,12 @@ function BrainfileTUI({ filePath, width, height }) {
|
|
|
23167
23828
|
hasChildren: detailChildren.length > 0,
|
|
23168
23829
|
hasSubtasks: (detailTask?.subtasks?.length ?? 0) > 0,
|
|
23169
23830
|
bodyOverflows: detailLayout?.bodyOverflows ?? false,
|
|
23170
|
-
archived: detailArchived
|
|
23831
|
+
archived: detailArchived,
|
|
23832
|
+
decision: detailIsDecision,
|
|
23833
|
+
hasLinks: detailRelations.refs.length + detailRelations.resting.length > 0
|
|
23171
23834
|
};
|
|
23172
23835
|
if (fullscreenDetail && detailTask) {
|
|
23173
|
-
return /* @__PURE__ */
|
|
23836
|
+
return /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight }, /* @__PURE__ */ import_react45.default.createElement(
|
|
23174
23837
|
DetailView,
|
|
23175
23838
|
{
|
|
23176
23839
|
task: detailTask,
|
|
@@ -23182,9 +23845,10 @@ function BrainfileTUI({ filePath, width, height }) {
|
|
|
23182
23845
|
children: detailChildren,
|
|
23183
23846
|
activity: detailActivity,
|
|
23184
23847
|
cursor: state.detailCursor,
|
|
23185
|
-
scrollOffset: state.detailScroll
|
|
23848
|
+
scrollOffset: state.detailScroll,
|
|
23849
|
+
relations: detailRelations
|
|
23186
23850
|
}
|
|
23187
|
-
), /* @__PURE__ */
|
|
23851
|
+
), /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexGrow: 1 }), /* @__PURE__ */ import_react45.default.createElement(Box_default, { height: 1, flexShrink: 0 }, /* @__PURE__ */ import_react45.default.createElement(StatusMessageDisplay, { message: state.statusMessage })), /* @__PURE__ */ import_react45.default.createElement(
|
|
23188
23852
|
FooterBar,
|
|
23189
23853
|
{
|
|
23190
23854
|
width: termWidth,
|
|
@@ -23194,23 +23858,24 @@ function BrainfileTUI({ filePath, width, height }) {
|
|
|
23194
23858
|
));
|
|
23195
23859
|
}
|
|
23196
23860
|
const listWidth = detailOpen ? termWidth - detailPaneWidth : termWidth;
|
|
23197
|
-
const footerActions = detailOpen ? detailActions(detailFooterCtx) : browseActions(selectedTask, doneView);
|
|
23198
|
-
return /* @__PURE__ */
|
|
23861
|
+
const footerActions = detailOpen ? detailActions(detailFooterCtx) : decisionsView ? decisionsActions({ historyCount: decisionModel.historyCount, historyOpen: state.decisionHistory }) : browseActions(selectedTask, doneView, state.decisions.length > 0, Boolean(rows[selectedIndex]?.archived));
|
|
23862
|
+
return /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight }, /* @__PURE__ */ import_react45.default.createElement(
|
|
23199
23863
|
HeaderBar,
|
|
23200
23864
|
{
|
|
23201
23865
|
title: boardTitle,
|
|
23202
23866
|
columns: filteredColumns,
|
|
23867
|
+
counts: tabCounts,
|
|
23203
23868
|
activeColumnIndex,
|
|
23204
23869
|
width: termWidth,
|
|
23205
23870
|
filterQuery: state.filterQuery,
|
|
23206
23871
|
filterActive: state.mode === "filter" || hasFilter,
|
|
23207
23872
|
matchCount,
|
|
23208
23873
|
totalCount,
|
|
23209
|
-
panelLabel: doneView ? "done" : void 0,
|
|
23874
|
+
panelLabel: decisionsView ? "decisions" : doneView ? "done" : void 0,
|
|
23210
23875
|
activeType: typeFilterActive ? activeTypeFilter : "all",
|
|
23211
23876
|
statusLabel: syncStatus
|
|
23212
23877
|
}
|
|
23213
|
-
), /* @__PURE__ */
|
|
23878
|
+
), /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexGrow: 1, flexShrink: 0, flexDirection: "column" }, /* @__PURE__ */ import_react45.default.createElement(
|
|
23214
23879
|
BoardContent,
|
|
23215
23880
|
{
|
|
23216
23881
|
state,
|
|
@@ -23232,9 +23897,11 @@ function BrainfileTUI({ filePath, width, height }) {
|
|
|
23232
23897
|
hasFilter,
|
|
23233
23898
|
archived: doneView,
|
|
23234
23899
|
typeFilter: typeFilterActive ? activeTypeFilter : void 0,
|
|
23235
|
-
thenEdit: state.addThenEdit
|
|
23900
|
+
thenEdit: state.addThenEdit,
|
|
23901
|
+
decisionLines: decisionsView ? decisionModel.lines : void 0,
|
|
23902
|
+
detailRelations
|
|
23236
23903
|
}
|
|
23237
|
-
)), /* @__PURE__ */
|
|
23904
|
+
)), /* @__PURE__ */ import_react45.default.createElement(Box_default, { height: 1, flexShrink: 0 }, /* @__PURE__ */ import_react45.default.createElement(StatusMessageDisplay, { message: state.statusMessage })), /* @__PURE__ */ import_react45.default.createElement(
|
|
23238
23905
|
FooterBar,
|
|
23239
23906
|
{
|
|
23240
23907
|
width: termWidth,
|
|
@@ -23264,11 +23931,13 @@ function BoardContent({
|
|
|
23264
23931
|
hasFilter,
|
|
23265
23932
|
archived,
|
|
23266
23933
|
typeFilter,
|
|
23267
|
-
thenEdit
|
|
23934
|
+
thenEdit,
|
|
23935
|
+
decisionLines,
|
|
23936
|
+
detailRelations
|
|
23268
23937
|
}) {
|
|
23269
23938
|
const overlayTarget = state.detailPath.length > 0 ? detailTask : selectedTask;
|
|
23270
23939
|
if (state.mode === "move" && overlayTarget) {
|
|
23271
|
-
return /* @__PURE__ */
|
|
23940
|
+
return /* @__PURE__ */ import_react45.default.createElement(
|
|
23272
23941
|
MoveOverlay,
|
|
23273
23942
|
{
|
|
23274
23943
|
columns,
|
|
@@ -23280,7 +23949,7 @@ function BoardContent({
|
|
|
23280
23949
|
);
|
|
23281
23950
|
}
|
|
23282
23951
|
if (state.mode === "delete-confirm" && overlayTarget) {
|
|
23283
|
-
return /* @__PURE__ */
|
|
23952
|
+
return /* @__PURE__ */ import_react45.default.createElement(
|
|
23284
23953
|
DeleteConfirmOverlay,
|
|
23285
23954
|
{
|
|
23286
23955
|
taskId: overlayTarget.id,
|
|
@@ -23290,12 +23959,21 @@ function BoardContent({
|
|
|
23290
23959
|
);
|
|
23291
23960
|
}
|
|
23292
23961
|
if (state.mode === "complete-confirm" && state.completeConfirm) {
|
|
23293
|
-
return /* @__PURE__ */
|
|
23962
|
+
return /* @__PURE__ */ import_react45.default.createElement(CompleteConfirmOverlay, { target: state.completeConfirm, width: termWidth });
|
|
23294
23963
|
}
|
|
23295
23964
|
if (state.mode === "add") {
|
|
23296
|
-
return /* @__PURE__ */
|
|
23965
|
+
return /* @__PURE__ */ import_react45.default.createElement(AddOverlay, { title: state.newTaskTitle, columnName, thenEdit });
|
|
23297
23966
|
}
|
|
23298
|
-
const list = /* @__PURE__ */
|
|
23967
|
+
const list = decisionLines ? /* @__PURE__ */ import_react45.default.createElement(
|
|
23968
|
+
DecisionList,
|
|
23969
|
+
{
|
|
23970
|
+
lines: decisionLines,
|
|
23971
|
+
selectedIndex,
|
|
23972
|
+
viewportHeight,
|
|
23973
|
+
width: listWidth,
|
|
23974
|
+
emptyMessage: hasFilter ? EMPTY.noMatches : EMPTY.noDecisions
|
|
23975
|
+
}
|
|
23976
|
+
) : /* @__PURE__ */ import_react45.default.createElement(
|
|
23299
23977
|
DocumentList,
|
|
23300
23978
|
{
|
|
23301
23979
|
rows,
|
|
@@ -23314,7 +23992,7 @@ function BoardContent({
|
|
|
23314
23992
|
// shrink to fit corrupts the layout — rows silently lose height and their
|
|
23315
23993
|
// text vanishes — rather than the frame simply growing taller, which ink
|
|
23316
23994
|
// and real terminals handle fine.
|
|
23317
|
-
/* @__PURE__ */
|
|
23995
|
+
/* @__PURE__ */ import_react45.default.createElement(Box_default, { flexDirection: "row", flexGrow: 1, flexShrink: 0 }, /* @__PURE__ */ import_react45.default.createElement(Box_default, { width: listWidth, flexDirection: "column", flexShrink: 0 }, list), /* @__PURE__ */ import_react45.default.createElement(Box_default, { width: detailPaneWidth, flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ import_react45.default.createElement(
|
|
23318
23996
|
DetailView,
|
|
23319
23997
|
{
|
|
23320
23998
|
task: detailTask,
|
|
@@ -23326,7 +24004,8 @@ function BoardContent({
|
|
|
23326
24004
|
children: detailChildren,
|
|
23327
24005
|
activity: detailActivity,
|
|
23328
24006
|
cursor: state.detailCursor,
|
|
23329
|
-
scrollOffset: state.detailScroll
|
|
24007
|
+
scrollOffset: state.detailScroll,
|
|
24008
|
+
relations: detailRelations
|
|
23330
24009
|
}
|
|
23331
24010
|
)))
|
|
23332
24011
|
);
|
|
@@ -23354,9 +24033,9 @@ async function tuiCommand(options) {
|
|
|
23354
24033
|
markV2MigrationHintShown(filePath);
|
|
23355
24034
|
}
|
|
23356
24035
|
installAutosync();
|
|
23357
|
-
const fetched = fetchBeforeRead(path5.dirname(filePath));
|
|
23358
|
-
if (fetched && !fetched.ok) console.error(import_chalk4.default.yellow(syncMessage(fetched).text));
|
|
23359
|
-
const { waitUntilExit } = render_default(/* @__PURE__ */
|
|
24036
|
+
const fetched = (0, import_repo3.fetchBeforeRead)(path5.dirname(filePath));
|
|
24037
|
+
if (fetched && !fetched.ok) console.error(import_chalk4.default.yellow((0, import_repo2.syncMessage)(fetched).text));
|
|
24038
|
+
const { waitUntilExit } = render_default(/* @__PURE__ */ import_react46.default.createElement(BrainfileTUI, { filePath }));
|
|
23360
24039
|
await waitUntilExit();
|
|
23361
24040
|
process.stdout.write("\x1B[2J");
|
|
23362
24041
|
process.stdout.write("\x1B[H");
|