opendoor-plugin 0.1.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +155 -22
- package/dist/browser/pi-web-plugin.js +1918 -1395
- package/dist/package.json +6 -27
- package/dist/pi-extension/index.js +136 -0
- package/dist/server-plugin.js +32 -21
- package/package.json +20 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Generated from src/pi-web-plugin.ts. Do not edit directly.
|
|
1
|
+
// Generated from src/browser/pi-web-plugin.ts. Do not edit directly.
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
@@ -562,6 +562,21 @@ o4?.({ LitElement: i4 });
|
|
|
562
562
|
(s3.litElementVersions ??= []).push("4.2.2");
|
|
563
563
|
|
|
564
564
|
// src/doorstop-contract.ts
|
|
565
|
+
var ITEM_STATE_KEYS = [
|
|
566
|
+
"normative",
|
|
567
|
+
"non-normative",
|
|
568
|
+
"inactive",
|
|
569
|
+
"reviewed",
|
|
570
|
+
"unreviewed",
|
|
571
|
+
"suspect-link",
|
|
572
|
+
"no-child-links",
|
|
573
|
+
"no-links",
|
|
574
|
+
"unknown-link",
|
|
575
|
+
"missing-reference"
|
|
576
|
+
];
|
|
577
|
+
function isItemStateKey(value) {
|
|
578
|
+
return ITEM_STATE_KEYS.includes(value);
|
|
579
|
+
}
|
|
565
580
|
function formatUnknownError(error) {
|
|
566
581
|
return error instanceof Error ? error.message : String(error);
|
|
567
582
|
}
|
|
@@ -1303,10 +1318,12 @@ var OPENDOOR_SETTINGS_PATH = ".pi-web/opendoor.json";
|
|
|
1303
1318
|
var OPENDOOR_SETTINGS_VERSION = 1;
|
|
1304
1319
|
var DEFAULT_PUBLISH_TARGET = "./public";
|
|
1305
1320
|
var MAX_EXCLUDED_DIRECTORIES = 16;
|
|
1321
|
+
var MAX_SHOWN_ATTRIBUTES = 16;
|
|
1306
1322
|
var MAX_EXCLUDED_DIRECTORY_LENGTH = 64;
|
|
1307
1323
|
var DEFAULT_OPENDOOR_SETTINGS = Object.freeze({
|
|
1308
1324
|
publishTarget: DEFAULT_PUBLISH_TARGET,
|
|
1309
1325
|
excludedDirectories: Object.freeze([]),
|
|
1326
|
+
showAdditionalAttribute: Object.freeze([]),
|
|
1310
1327
|
commitAfterReview: false
|
|
1311
1328
|
});
|
|
1312
1329
|
var MISSING_FILE_ERROR = "Path does not exist";
|
|
@@ -1410,6 +1427,19 @@ function parseOpendoorSettings(value) {
|
|
|
1410
1427
|
excludedDirectories = parseExcludedDirectories(rawExcluded, diagnostics);
|
|
1411
1428
|
}
|
|
1412
1429
|
}
|
|
1430
|
+
let showAdditionalAttribute = [];
|
|
1431
|
+
const rawShown = value["showAdditionalAttribute"];
|
|
1432
|
+
if (rawShown !== void 0) {
|
|
1433
|
+
if (!Array.isArray(rawShown)) {
|
|
1434
|
+
diagnostics.push({
|
|
1435
|
+
severity: "warning",
|
|
1436
|
+
path: OPENDOOR_SETTINGS_PATH,
|
|
1437
|
+
message: `${OPENDOOR_SETTINGS_PATH} "showAdditionalAttribute" must be an array; using none`
|
|
1438
|
+
});
|
|
1439
|
+
} else {
|
|
1440
|
+
showAdditionalAttribute = parseShowAdditionalAttribute(rawShown, diagnostics);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1413
1443
|
let commitAfterReview = false;
|
|
1414
1444
|
const rawCommit = value["commitAfterReview"];
|
|
1415
1445
|
if (rawCommit !== void 0) {
|
|
@@ -1423,7 +1453,7 @@ function parseOpendoorSettings(value) {
|
|
|
1423
1453
|
commitAfterReview = rawCommit;
|
|
1424
1454
|
}
|
|
1425
1455
|
}
|
|
1426
|
-
return { settings: { publishTarget, excludedDirectories, commitAfterReview }, diagnostics };
|
|
1456
|
+
return { settings: { publishTarget, excludedDirectories, showAdditionalAttribute, commitAfterReview }, diagnostics };
|
|
1427
1457
|
}
|
|
1428
1458
|
function parseExcludedDirectories(entries, diagnostics) {
|
|
1429
1459
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1452,6 +1482,33 @@ function parseExcludedDirectories(entries, diagnostics) {
|
|
|
1452
1482
|
}
|
|
1453
1483
|
return result;
|
|
1454
1484
|
}
|
|
1485
|
+
function parseShowAdditionalAttribute(entries, diagnostics) {
|
|
1486
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1487
|
+
const result = [];
|
|
1488
|
+
for (const entry of entries) {
|
|
1489
|
+
const name = typeof entry === "string" ? entry.trim() : "";
|
|
1490
|
+
if (name === "") {
|
|
1491
|
+
diagnostics.push({
|
|
1492
|
+
severity: "warning",
|
|
1493
|
+
path: OPENDOOR_SETTINGS_PATH,
|
|
1494
|
+
message: `${OPENDOOR_SETTINGS_PATH} "showAdditionalAttribute" entry "${truncateEcho(String(entry))}" is not a non-empty string and was dropped`
|
|
1495
|
+
});
|
|
1496
|
+
continue;
|
|
1497
|
+
}
|
|
1498
|
+
if (seen.has(name)) continue;
|
|
1499
|
+
if (result.length >= MAX_SHOWN_ATTRIBUTES) {
|
|
1500
|
+
diagnostics.push({
|
|
1501
|
+
severity: "warning",
|
|
1502
|
+
path: OPENDOOR_SETTINGS_PATH,
|
|
1503
|
+
message: `${OPENDOOR_SETTINGS_PATH} "showAdditionalAttribute" has more than ${String(MAX_SHOWN_ATTRIBUTES)} entries; the rest were ignored`
|
|
1504
|
+
});
|
|
1505
|
+
break;
|
|
1506
|
+
}
|
|
1507
|
+
seen.add(name);
|
|
1508
|
+
result.push(name);
|
|
1509
|
+
}
|
|
1510
|
+
return result;
|
|
1511
|
+
}
|
|
1455
1512
|
function normalizeRelativePath(value) {
|
|
1456
1513
|
const collapsed = value.replace(/\/{2,}/g, "/");
|
|
1457
1514
|
return collapsed.length > 1 && collapsed.endsWith("/") ? collapsed.slice(0, -1) : collapsed;
|
|
@@ -5728,7 +5785,23 @@ function serializeReferences(references) {
|
|
|
5728
5785
|
return JSON.stringify(references === void 0 ? [] : references);
|
|
5729
5786
|
}
|
|
5730
5787
|
|
|
5731
|
-
// src/doorstop-panel-
|
|
5788
|
+
// src/browser/doorstop-panel-navigation.ts
|
|
5789
|
+
var DOORSTOP_NAV_UID = "uid";
|
|
5790
|
+
var DOORSTOP_NAV_FILE = "file";
|
|
5791
|
+
var DOORSTOP_NAV_DOC = "doc";
|
|
5792
|
+
var DOORSTOP_NAV_FILTER = "filter";
|
|
5793
|
+
var DOORSTOP_NAV_SEARCH = "search";
|
|
5794
|
+
function readPanelNavigationQuery(navigation) {
|
|
5795
|
+
const output = {};
|
|
5796
|
+
if (navigation === void 0) return output;
|
|
5797
|
+
for (const [key, value] of Object.entries(navigation.query)) {
|
|
5798
|
+
const first = Array.isArray(value) ? value[0] : value;
|
|
5799
|
+
if (typeof first === "string" && first !== "") output[key] = first;
|
|
5800
|
+
}
|
|
5801
|
+
return output;
|
|
5802
|
+
}
|
|
5803
|
+
|
|
5804
|
+
// src/browser/doorstop-panel-controller.ts
|
|
5732
5805
|
var DoorstopWorkspaceController = class {
|
|
5733
5806
|
constructor(host, context, loadJob) {
|
|
5734
5807
|
this.loading = false;
|
|
@@ -5828,25 +5901,99 @@ var DoorstopWorkspaceController = class {
|
|
|
5828
5901
|
this.loadRequest = request;
|
|
5829
5902
|
return request;
|
|
5830
5903
|
}
|
|
5831
|
-
/** Select an item UID for the detail pane.
|
|
5904
|
+
/** Select an item UID for the detail pane. Plan Phase C step C2 — a
|
|
5905
|
+
* deliberate selection change also writes the `uid` deep-link key, so
|
|
5906
|
+
* the selection is bookmarkable/back-navigable. */
|
|
5832
5907
|
selectUid(uid) {
|
|
5833
5908
|
this.selectedUid = uid;
|
|
5909
|
+
this.setNavigation(DOORSTOP_NAV_UID, uid);
|
|
5910
|
+
this.setNavigation(DOORSTOP_NAV_FILE, void 0, { replace: true });
|
|
5834
5911
|
this.requestUpdate();
|
|
5835
5912
|
}
|
|
5836
|
-
/** Select a document by prefix for the document-detail pane.
|
|
5913
|
+
/** Select a document by prefix for the document-detail pane. C2 — writes
|
|
5914
|
+
* the `doc` deep-link key. */
|
|
5837
5915
|
selectDocument(prefix) {
|
|
5838
5916
|
this.selectedDocumentPrefix = prefix;
|
|
5917
|
+
this.setNavigation(DOORSTOP_NAV_DOC, prefix);
|
|
5918
|
+
this.setNavigation(DOORSTOP_NAV_FILE, void 0, { replace: true });
|
|
5839
5919
|
this.requestUpdate();
|
|
5840
5920
|
}
|
|
5841
5921
|
setStateFilter(filter) {
|
|
5842
5922
|
this.stateFilter = filter;
|
|
5843
5923
|
if (this.result !== void 0) this.selectedUid = selectionIn(this.result, filter, this.selectedUid);
|
|
5924
|
+
this.setNavigation(DOORSTOP_NAV_FILTER, filter);
|
|
5844
5925
|
this.requestUpdate();
|
|
5845
5926
|
}
|
|
5846
5927
|
setSearch(search) {
|
|
5847
5928
|
this.search = search;
|
|
5929
|
+
this.setNavigation(DOORSTOP_NAV_SEARCH, search === "" ? void 0 : search, { replace: true });
|
|
5848
5930
|
this.requestUpdate();
|
|
5849
5931
|
}
|
|
5932
|
+
/** Apply the panel's deep-link query (URL → state). Plan Phase C step C2:
|
|
5933
|
+
* `renderDoorstopPanel` calls this on EVERY render, idempotently — values
|
|
5934
|
+
* are compared first, so re-applying the same query is a no-op, and the
|
|
5935
|
+
* query wins exactly when it differs from the controller state (back/
|
|
5936
|
+
* forward navigation re-rendering the panel with an older URL; a user
|
|
5937
|
+
* interaction re-rendering with the query its own setter just wrote).
|
|
5938
|
+
*
|
|
5939
|
+
* READ-ONLY for the navigation: this method never writes keys back (the
|
|
5940
|
+
* setters own writes — writing here would loop render → set → render).
|
|
5941
|
+
*
|
|
5942
|
+
* Validation: an unknown `filter` value is ignored (the current filter
|
|
5943
|
+
* stands — `?filter=bogus` must not crash the panel), and `uid`/`doc`
|
|
5944
|
+
* values that no longer exist in a LOADED result are dropped by the
|
|
5945
|
+
* same selectionIn reconciliation a landed load applies; before the first
|
|
5946
|
+
* load lands they apply blindly (the landing load reconciles). The C3
|
|
5947
|
+
* `file` key (chat file link) resolves only against a loaded result. */
|
|
5948
|
+
applyNavigationQuery(query) {
|
|
5949
|
+
let changed = false;
|
|
5950
|
+
const filterValue = query[DOORSTOP_NAV_FILTER];
|
|
5951
|
+
if (filterValue === void 0) {
|
|
5952
|
+
if (this.stateFilter !== void 0) {
|
|
5953
|
+
this.stateFilter = void 0;
|
|
5954
|
+
changed = true;
|
|
5955
|
+
}
|
|
5956
|
+
} else if (isItemStateKey(filterValue) && this.stateFilter !== filterValue) {
|
|
5957
|
+
this.stateFilter = filterValue;
|
|
5958
|
+
changed = true;
|
|
5959
|
+
}
|
|
5960
|
+
const filePath = query[DOORSTOP_NAV_FILE];
|
|
5961
|
+
const fileItem = filePath !== void 0 && this.result !== void 0 ? this.result.index.items.find((candidate) => candidate.path === filePath) : void 0;
|
|
5962
|
+
const uid = fileItem !== void 0 ? fileItem.uid : query[DOORSTOP_NAV_UID];
|
|
5963
|
+
if (this.selectedUid !== uid) {
|
|
5964
|
+
this.selectedUid = uid;
|
|
5965
|
+
changed = true;
|
|
5966
|
+
}
|
|
5967
|
+
const documentPrefix = fileItem !== void 0 ? fileItem.documentPrefix : query[DOORSTOP_NAV_DOC];
|
|
5968
|
+
if (this.selectedDocumentPrefix !== documentPrefix) {
|
|
5969
|
+
this.selectedDocumentPrefix = documentPrefix;
|
|
5970
|
+
changed = true;
|
|
5971
|
+
}
|
|
5972
|
+
const search = query[DOORSTOP_NAV_SEARCH] ?? "";
|
|
5973
|
+
if (this.search !== search) {
|
|
5974
|
+
this.search = search;
|
|
5975
|
+
changed = true;
|
|
5976
|
+
}
|
|
5977
|
+
if (this.result !== void 0) {
|
|
5978
|
+
const keptUid = selectionIn(this.result, this.stateFilter, this.selectedUid);
|
|
5979
|
+
if (keptUid !== this.selectedUid) {
|
|
5980
|
+
this.selectedUid = keptUid;
|
|
5981
|
+
changed = true;
|
|
5982
|
+
}
|
|
5983
|
+
const keptDocument = documentSelectionIn(this.result, this.selectedDocumentPrefix);
|
|
5984
|
+
if (keptDocument !== this.selectedDocumentPrefix) {
|
|
5985
|
+
this.selectedDocumentPrefix = keptDocument;
|
|
5986
|
+
changed = true;
|
|
5987
|
+
}
|
|
5988
|
+
}
|
|
5989
|
+
if (changed) this.requestUpdate();
|
|
5990
|
+
}
|
|
5991
|
+
/** Write one deep-link key on the CURRENT context's navigation, when the
|
|
5992
|
+
* host supplies one. `undefined` clears the key (the selection is gone);
|
|
5993
|
+
* absent navigation (older hosts, tests without one) is a silent no-op. */
|
|
5994
|
+
setNavigation(key, value, options) {
|
|
5995
|
+
this.context.navigation?.set(key, value, options);
|
|
5996
|
+
}
|
|
5850
5997
|
/** Begin a doorstop run: record the op title (element disables action
|
|
5851
5998
|
* buttons) and notify. */
|
|
5852
5999
|
beginRun(title) {
|
|
@@ -5871,6 +6018,37 @@ var DoorstopWorkspaceController = class {
|
|
|
5871
6018
|
this.lastRun = void 0;
|
|
5872
6019
|
this.requestUpdate();
|
|
5873
6020
|
}
|
|
6021
|
+
/**
|
|
6022
|
+
* Commit the "Start review session" composer-insert as the last run (relay
|
|
6023
|
+
* `opendoor-dispatch-redesign` design §3). The insert happens in the
|
|
6024
|
+
* browser, so the record carries no process data: `status: "ok"` with an
|
|
6025
|
+
* empty stdout/stderr and NO `commit` field — which is exactly what makes
|
|
6026
|
+
* the status bar render its quiet ok badge, skip the commit-narration line
|
|
6027
|
+
* and the error pre, and stay collapsed (`lastRunHasMessage` is false, so
|
|
6028
|
+
* the bar does not auto-expand). The write goes through the single
|
|
6029
|
+
* {@link commitRun} point, so it survives `invalidate()`/`load()` and is
|
|
6030
|
+
* cleared only by `dismissRun()` or the next commit — every existing
|
|
6031
|
+
* run-outcome semantic is inherited unchanged.
|
|
6032
|
+
*
|
|
6033
|
+
* `at` is REQUIRED by {@link DoorstopLastRunView} (the run-start epoch ms
|
|
6034
|
+
* every `commitRun` caller sets); `durationMs` is 0 because no run was
|
|
6035
|
+
* measured — the insert is synchronous.
|
|
6036
|
+
*/
|
|
6037
|
+
commitAskReview(uid) {
|
|
6038
|
+
this.commitRun({
|
|
6039
|
+
op: "ask-review",
|
|
6040
|
+
title: `Opendoor: review dispatch ${uid}`,
|
|
6041
|
+
status: "ok",
|
|
6042
|
+
exitCode: null,
|
|
6043
|
+
signal: null,
|
|
6044
|
+
stdout: "",
|
|
6045
|
+
stderr: "",
|
|
6046
|
+
stdoutTruncated: false,
|
|
6047
|
+
stderrTruncated: false,
|
|
6048
|
+
durationMs: 0,
|
|
6049
|
+
at: Date.now()
|
|
6050
|
+
});
|
|
6051
|
+
}
|
|
5874
6052
|
/** Run the Stage action (plan Phase C step 12): stage every Doorstop-managed
|
|
5875
6053
|
* path of the loaded index (the element hands the {@link doorstopPaths}
|
|
5876
6054
|
* list in), or — for a per-item stage — exactly one item's path with
|
|
@@ -5951,12 +6129,12 @@ var DoorstopWorkspaceController = class {
|
|
|
5951
6129
|
this.beginRun(title);
|
|
5952
6130
|
const startedAt = Date.now();
|
|
5953
6131
|
try {
|
|
5954
|
-
const
|
|
5955
|
-
if (
|
|
6132
|
+
const peer = this.context.peer;
|
|
6133
|
+
if (peer?.request === void 0) {
|
|
5956
6134
|
this.commitGitError(op, title, startedAt, "Git actions need the paired opendoor backend");
|
|
5957
6135
|
return;
|
|
5958
6136
|
}
|
|
5959
|
-
const response = await
|
|
6137
|
+
const response = await peer.request(backendOperation, input);
|
|
5960
6138
|
const parsed = parseResponse(response);
|
|
5961
6139
|
this.commitRun({
|
|
5962
6140
|
op,
|
|
@@ -6029,7 +6207,7 @@ var DoorstopWorkspaceController = class {
|
|
|
6029
6207
|
* retries instead of showing a stale failure forever). A fetch already in
|
|
6030
6208
|
* flight for the same UID is joined (never stacked). Otherwise the fetch
|
|
6031
6209
|
* runs through
|
|
6032
|
-
* `
|
|
6210
|
+
* `peer.request(DOORSTOP_BASELINE_OPERATION, { uid, path })`, parses the
|
|
6033
6211
|
* response with the contract validator, and stamp-walks the returned blobs
|
|
6034
6212
|
* newest-first (`parseDoorstopItem` + the document's config via
|
|
6035
6213
|
* `computeItemStamp(item, config, true)`) until one matches
|
|
@@ -6071,12 +6249,12 @@ var DoorstopWorkspaceController = class {
|
|
|
6071
6249
|
this.baselineVersion += 1;
|
|
6072
6250
|
this.requestUpdate();
|
|
6073
6251
|
try {
|
|
6074
|
-
const
|
|
6075
|
-
if (
|
|
6252
|
+
const peer = this.context.peer;
|
|
6253
|
+
if (peer?.request === void 0) {
|
|
6076
6254
|
this.commitBaseline(uid, { key, view: { state: "no-git" } });
|
|
6077
6255
|
return;
|
|
6078
6256
|
}
|
|
6079
|
-
const response = await
|
|
6257
|
+
const response = await peer.request(DOORSTOP_BASELINE_OPERATION, { uid, path: item.path });
|
|
6080
6258
|
if (!this.host.isConnected) return;
|
|
6081
6259
|
const parsed = parseDoorstopBaselineResponse(response);
|
|
6082
6260
|
if (!parsed.git) {
|
|
@@ -6145,12 +6323,12 @@ var DoorstopWorkspaceController = class {
|
|
|
6145
6323
|
this.gitStatusInFlight = true;
|
|
6146
6324
|
this.requestUpdate();
|
|
6147
6325
|
try {
|
|
6148
|
-
const
|
|
6149
|
-
if (
|
|
6326
|
+
const peer = this.context.peer;
|
|
6327
|
+
if (peer?.request === void 0) {
|
|
6150
6328
|
this.gitStatusView = { state: "no-git" };
|
|
6151
6329
|
return;
|
|
6152
6330
|
}
|
|
6153
|
-
const response = await
|
|
6331
|
+
const response = await peer.request(DOORSTOP_GIT_STATUS_OPERATION, {});
|
|
6154
6332
|
if (!this.host.isConnected || this.gitStatusView?.state !== "loading") return;
|
|
6155
6333
|
const parsed = parseDoorstopGitStatusResponse(response);
|
|
6156
6334
|
this.gitStatusView = parsed.git ? { state: "ready", response: parsed } : { state: "no-git" };
|
|
@@ -6219,7 +6397,7 @@ function doorstopPaths(result) {
|
|
|
6219
6397
|
return paths;
|
|
6220
6398
|
}
|
|
6221
6399
|
|
|
6222
|
-
// src/doorstop-panel.ts
|
|
6400
|
+
// src/browser/doorstop-panel.ts
|
|
6223
6401
|
var DOORSTOP_WORKSPACE_STATE_LIMIT = 8;
|
|
6224
6402
|
var MAX_ITEM_DIGITS = 24;
|
|
6225
6403
|
async function loadDoorstopWorkspace(files) {
|
|
@@ -6352,6 +6530,15 @@ function escapeRegExp(value) {
|
|
|
6352
6530
|
var DoorstopWorkspaceRegistry = class {
|
|
6353
6531
|
constructor() {
|
|
6354
6532
|
this.workspaces = /* @__PURE__ */ new Map();
|
|
6533
|
+
/** Plan Phase C step C3 — the chat file-open index: per-workspace
|
|
6534
|
+
* item-file path → { uid, documentPrefix } (plus the landed result it was
|
|
6535
|
+
* built from, kept by identity), recorded from every LANDED
|
|
6536
|
+
* load result (renderDoorstopPanel records it whenever the controller
|
|
6537
|
+
* has a result), so the SYNCHRONOUS `fileOpenQuery` hook can answer from
|
|
6538
|
+
* the last landed discovery without running one. Same LRU bound and the
|
|
6539
|
+
* same workspace keys as the controller map; an evicted workspace drops
|
|
6540
|
+
* its file-open entries with its controller. */
|
|
6541
|
+
this.fileOpenEntries = /* @__PURE__ */ new Map();
|
|
6355
6542
|
}
|
|
6356
6543
|
/** Get-or-create the controller for a workspace (the render entry point). */
|
|
6357
6544
|
for(context) {
|
|
@@ -6373,10 +6560,67 @@ var DoorstopWorkspaceRegistry = class {
|
|
|
6373
6560
|
return controller;
|
|
6374
6561
|
}
|
|
6375
6562
|
/** Panel invalidation: re-run discovery + load unconditionally for the
|
|
6376
|
-
* connected workspace (paired or unpaired: the load job is browser-side, so there is no owned-workspace gate).
|
|
6377
|
-
|
|
6563
|
+
* connected workspace (paired or unpaired: the load job is browser-side, so there is no owned-workspace gate).
|
|
6564
|
+
*
|
|
6565
|
+
* Plan Phase C step C1 — the v4 typed invalidation: `invalidation` carries
|
|
6566
|
+
* the reason (`"manual"` Refresh action / `"mutation"` a workspace file
|
|
6567
|
+
* changed / `"agent-activity"` agent work finished) and the affected
|
|
6568
|
+
* resources. The rescan stays REASON-AGNOSTIC deliberately: every reason
|
|
6569
|
+
* may have changed item files, the load job re-reads everything anyway
|
|
6570
|
+
* (no partial invalidation is possible — the index is one unit), and the
|
|
6571
|
+
* controller's re-entrant guard coalesces storms (an invalidate while a
|
|
6572
|
+
* load is running JOINS that load; only an invalidate after it lands
|
|
6573
|
+
* starts a fresh one), so `"agent-activity"` bursts cannot stack loads.
|
|
6574
|
+
* The payload is accepted (so the contribution can forward it) but not
|
|
6575
|
+
* consulted. */
|
|
6576
|
+
invalidate(context, invalidation) {
|
|
6577
|
+
void invalidation;
|
|
6378
6578
|
return this.for(context).invalidate();
|
|
6379
6579
|
}
|
|
6580
|
+
/** Record the chat file-open index for one workspace from a LANDED result
|
|
6581
|
+
* (plan Phase C step C3). Called from renderDoorstopPanel on every render
|
|
6582
|
+
* that has a result, so the index always mirrors the last landed
|
|
6583
|
+
* discovery; never throws and triggers nothing (the hook must be free of
|
|
6584
|
+
* side effects — recording a plain index from a landed result is one). */
|
|
6585
|
+
recordFileOpenIndex(context, result) {
|
|
6586
|
+
const key = workspaceContextKey(context);
|
|
6587
|
+
const existing = this.fileOpenEntries.get(key);
|
|
6588
|
+
if (existing !== void 0 && existing.result === result) {
|
|
6589
|
+
this.fileOpenEntries.delete(key);
|
|
6590
|
+
this.fileOpenEntries.set(key, existing);
|
|
6591
|
+
return;
|
|
6592
|
+
}
|
|
6593
|
+
const entries = /* @__PURE__ */ new Map();
|
|
6594
|
+
for (const item of result.index.items) {
|
|
6595
|
+
entries.set(item.path, { uid: item.uid, documentPrefix: item.documentPrefix });
|
|
6596
|
+
}
|
|
6597
|
+
this.fileOpenEntries.delete(key);
|
|
6598
|
+
this.fileOpenEntries.set(key, { result, entries });
|
|
6599
|
+
this.boundFileOpenEntries();
|
|
6600
|
+
}
|
|
6601
|
+
/** The panel's chat file-open hook (plan Phase C step C3): does the last
|
|
6602
|
+
* landed discovery of this workspace know `path` as an item file? Returns
|
|
6603
|
+
* the panel's navigation query (`{ file: path }` — the key
|
|
6604
|
+
* applyNavigationQuery resolves to the item's uid and document
|
|
6605
|
+
* selection), or undefined when the panel does not own the path (the
|
|
6606
|
+
* host then asks the next panel, or serves the Files fallback). Before
|
|
6607
|
+
* the first load lands there is no index yet → undefined (the chat link
|
|
6608
|
+
* falls back to Files; the panel still deep-links once opened and
|
|
6609
|
+
* re-rendered). */
|
|
6610
|
+
fileOpenQuery(context, path) {
|
|
6611
|
+
const entries = this.fileOpenEntries.get(workspaceContextKey(context));
|
|
6612
|
+
if (entries === void 0) return void 0;
|
|
6613
|
+
return entries.entries.has(path) ? { [DOORSTOP_NAV_FILE]: path } : void 0;
|
|
6614
|
+
}
|
|
6615
|
+
/** Keep the file-open index at the same LRU bound as the controller map
|
|
6616
|
+
* (strictly oldest-first, mirroring evictOldest). */
|
|
6617
|
+
boundFileOpenEntries() {
|
|
6618
|
+
while (this.fileOpenEntries.size > DOORSTOP_WORKSPACE_STATE_LIMIT) {
|
|
6619
|
+
const key = this.fileOpenEntries.keys().next().value;
|
|
6620
|
+
if (key === void 0) break;
|
|
6621
|
+
this.fileOpenEntries.delete(key);
|
|
6622
|
+
}
|
|
6623
|
+
}
|
|
6380
6624
|
evictOldest() {
|
|
6381
6625
|
if (this.workspaces.size < DOORSTOP_WORKSPACE_STATE_LIMIT) return;
|
|
6382
6626
|
const key = this.workspaces.keys().next().value;
|
|
@@ -6384,6 +6628,7 @@ var DoorstopWorkspaceRegistry = class {
|
|
|
6384
6628
|
const controller = this.workspaces.get(key);
|
|
6385
6629
|
if (controller !== void 0) controller.release();
|
|
6386
6630
|
this.workspaces.delete(key);
|
|
6631
|
+
this.fileOpenEntries.delete(key);
|
|
6387
6632
|
}
|
|
6388
6633
|
};
|
|
6389
6634
|
var DoorstopPanelHost = class {
|
|
@@ -6395,127 +6640,94 @@ function workspaceContextKey(context) {
|
|
|
6395
6640
|
return JSON.stringify([context.machine.id, context.workspace.projectId, context.workspace.id]);
|
|
6396
6641
|
}
|
|
6397
6642
|
|
|
6398
|
-
// src/doorstop-
|
|
6399
|
-
var
|
|
6400
|
-
var
|
|
6401
|
-
var
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
"
|
|
6410
|
-
|
|
6411
|
-
"
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
case "normative":
|
|
6416
|
-
case "non-normative":
|
|
6417
|
-
case "reviewed":
|
|
6418
|
-
return "muted";
|
|
6419
|
-
case "unreviewed":
|
|
6420
|
-
case "no-child-links":
|
|
6421
|
-
case "no-links":
|
|
6422
|
-
return "warning";
|
|
6423
|
-
case "inactive":
|
|
6424
|
-
case "suspect-link":
|
|
6425
|
-
case "unknown-link":
|
|
6426
|
-
case "missing-reference":
|
|
6427
|
-
return "danger";
|
|
6643
|
+
// src/doorstop-session-metadata.ts
|
|
6644
|
+
var OPENDOOR_SESSION_METADATA_NAMESPACE = "opendoor";
|
|
6645
|
+
var DOORSTOP_SESSION_METADATA_SCHEMA = 1;
|
|
6646
|
+
var DOORSTOP_SESSION_METADATA_MAX_UIDS = 8;
|
|
6647
|
+
var DOORSTOP_SESSION_METADATA_MAX_UID_LENGTH = 32;
|
|
6648
|
+
var DOORSTOP_SESSION_METADATA_OPS = /* @__PURE__ */ new Set(["review"]);
|
|
6649
|
+
var DOORSTOP_SESSION_METADATA_UID_PATTERN = /^[\w.-]+$/u;
|
|
6650
|
+
function isMetadataUid(value) {
|
|
6651
|
+
return typeof value === "string" && value !== "" && value.length <= DOORSTOP_SESSION_METADATA_MAX_UID_LENGTH && DOORSTOP_SESSION_METADATA_UID_PATTERN.test(value);
|
|
6652
|
+
}
|
|
6653
|
+
function parseDoorstopSessionMetadata(value) {
|
|
6654
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
6655
|
+
const record = value;
|
|
6656
|
+
if (record["schema"] !== DOORSTOP_SESSION_METADATA_SCHEMA) return void 0;
|
|
6657
|
+
const op = record["op"];
|
|
6658
|
+
if (typeof op !== "string" || !DOORSTOP_SESSION_METADATA_OPS.has(op)) {
|
|
6659
|
+
return void 0;
|
|
6428
6660
|
}
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
return "has suspect links";
|
|
6661
|
+
const uidsValue = record["uids"];
|
|
6662
|
+
if (!Array.isArray(uidsValue) || uidsValue.length === 0 || uidsValue.length > DOORSTOP_SESSION_METADATA_MAX_UIDS) {
|
|
6663
|
+
return void 0;
|
|
6664
|
+
}
|
|
6665
|
+
const uids = [];
|
|
6666
|
+
for (const uid of uidsValue) {
|
|
6667
|
+
if (!isMetadataUid(uid)) return void 0;
|
|
6668
|
+
uids.push(uid);
|
|
6438
6669
|
}
|
|
6670
|
+
return {
|
|
6671
|
+
schema: DOORSTOP_SESSION_METADATA_SCHEMA,
|
|
6672
|
+
op,
|
|
6673
|
+
uids
|
|
6674
|
+
};
|
|
6439
6675
|
}
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
const
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6676
|
+
|
|
6677
|
+
// src/browser/doorstop-session-labels.ts
|
|
6678
|
+
var DOORSTOP_PANEL_LOCAL_ID = "workspace.doorstop";
|
|
6679
|
+
var DOORSTOP_SESSION_LABEL_ID = "doorstop.review";
|
|
6680
|
+
function createDoorstopSessionLabel(runtimePluginId) {
|
|
6681
|
+
const panelId = `${runtimePluginId}:${DOORSTOP_PANEL_LOCAL_ID}`;
|
|
6682
|
+
return {
|
|
6683
|
+
id: DOORSTOP_SESSION_LABEL_ID,
|
|
6684
|
+
order: 10,
|
|
6685
|
+
items: (context) => sessionLabelItems(context, panelId)
|
|
6686
|
+
};
|
|
6450
6687
|
}
|
|
6451
|
-
function
|
|
6452
|
-
const
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6688
|
+
function sessionLabelItems(context, panelId) {
|
|
6689
|
+
const value = context.session.metadata?.[OPENDOOR_SESSION_METADATA_NAMESPACE];
|
|
6690
|
+
const metadata = parseDoorstopSessionMetadata(value);
|
|
6691
|
+
if (metadata === void 0) return [];
|
|
6692
|
+
const firstUid = metadata.uids[0];
|
|
6693
|
+
if (firstUid === void 0) return [];
|
|
6694
|
+
const rest = metadata.uids.length - 1;
|
|
6695
|
+
const text = rest === 0 ? firstUid : `${firstUid} +${String(rest)}`;
|
|
6696
|
+
const target = doorstopReopenDestination(context.machine.id, context.workspace, panelId, firstUid);
|
|
6697
|
+
return [
|
|
6698
|
+
{
|
|
6699
|
+
type: "render",
|
|
6700
|
+
render: () => b2`<a
|
|
6701
|
+
class="doorstop-session-label"
|
|
6702
|
+
href="#"
|
|
6703
|
+
title=${`Open ${metadata.uids.join(", ")} in Requirements`}
|
|
6704
|
+
@click=${(event) => {
|
|
6705
|
+
event.preventDefault();
|
|
6706
|
+
event.stopPropagation();
|
|
6707
|
+
void context.navigate(target);
|
|
6708
|
+
}}
|
|
6709
|
+
>${text}</a>`
|
|
6460
6710
|
}
|
|
6461
|
-
|
|
6462
|
-
|
|
6711
|
+
];
|
|
6712
|
+
}
|
|
6713
|
+
function doorstopReopenDestination(machineId, workspace, panelId, uid) {
|
|
6714
|
+
return {
|
|
6715
|
+
machineId,
|
|
6716
|
+
projectId: workspace.projectId,
|
|
6717
|
+
workspaceId: workspace.id,
|
|
6718
|
+
view: "workspace",
|
|
6719
|
+
tool: panelId,
|
|
6720
|
+
query: { [DOORSTOP_NAV_UID]: uid }
|
|
6721
|
+
};
|
|
6463
6722
|
}
|
|
6723
|
+
|
|
6724
|
+
// src/browser/view-model/core.ts
|
|
6464
6725
|
function shortFingerprint(fingerprint) {
|
|
6465
6726
|
return fingerprint === null ? "none" : `${fingerprint.slice(0, 8)}\u2026`;
|
|
6466
6727
|
}
|
|
6467
|
-
var FINDINGS_EMPTY_MESSAGE = "No findings \u2014 the tree is clean.";
|
|
6468
|
-
var FINDINGS_EMPTY_HINT = "Findings are plugin-local; run `doorstop` validation in the terminal for the authoritative check.";
|
|
6469
|
-
var FINDINGS_PLUGIN_LOCAL_NOTE = "plugin-local findings \u2014 `doorstop` validation in the terminal is authoritative";
|
|
6470
|
-
var FINDING_SEVERITY_RANK = {
|
|
6471
|
-
error: 0,
|
|
6472
|
-
warning: 1,
|
|
6473
|
-
info: 2
|
|
6474
|
-
};
|
|
6475
|
-
function findingsViewRows(index) {
|
|
6476
|
-
const rows = [...index.findings, ...index.diagnostics];
|
|
6477
|
-
return rows.sort((a3, b3) => FINDING_SEVERITY_RANK[a3.severity] - FINDING_SEVERITY_RANK[b3.severity]);
|
|
6478
|
-
}
|
|
6479
|
-
function findingsViewCounts(rows) {
|
|
6480
|
-
const counts = { errors: 0, warnings: 0, info: 0 };
|
|
6481
|
-
for (const row of rows) {
|
|
6482
|
-
if (row.severity === "error") counts.errors += 1;
|
|
6483
|
-
else if (row.severity === "warning") counts.warnings += 1;
|
|
6484
|
-
else counts.info += 1;
|
|
6485
|
-
}
|
|
6486
|
-
return counts;
|
|
6487
|
-
}
|
|
6488
|
-
function findingsCountText(counts) {
|
|
6489
|
-
const errors = counts.errors === 1 ? "1 error" : `${String(counts.errors)} errors`;
|
|
6490
|
-
const warnings = counts.warnings === 1 ? "1 warning" : `${String(counts.warnings)} warnings`;
|
|
6491
|
-
return `${errors} \xB7 ${warnings} \xB7 ${String(counts.info)} info`;
|
|
6492
|
-
}
|
|
6493
6728
|
function doorstopPublishTarget(result) {
|
|
6494
6729
|
return result?.settings?.publishTarget ?? DEFAULT_OPENDOOR_SETTINGS.publishTarget;
|
|
6495
6730
|
}
|
|
6496
|
-
function doorstopCommitAfterReview(result) {
|
|
6497
|
-
return result?.settings?.commitAfterReview ?? DEFAULT_OPENDOOR_SETTINGS.commitAfterReview;
|
|
6498
|
-
}
|
|
6499
|
-
function commitOutcomeText(op, outcome) {
|
|
6500
|
-
const gitRun = op === "git-stage" || op === "git-commit" || op === "git-unstage";
|
|
6501
|
-
switch (outcome.status) {
|
|
6502
|
-
case "staged":
|
|
6503
|
-
return `staged ${String(outcome.staged ?? 0)} paths`;
|
|
6504
|
-
case "unstaged":
|
|
6505
|
-
return `unstaged ${String(outcome.unstaged ?? 0)} paths`;
|
|
6506
|
-
case "committed":
|
|
6507
|
-
return gitRun ? `committed ${outcome.sha ?? "<unknown sha>"}` : `commit: ${outcome.sha ?? "<unknown sha>"}`;
|
|
6508
|
-
case "clean":
|
|
6509
|
-
if (!gitRun) return "commit: clean (already committed)";
|
|
6510
|
-
if (op === "git-stage") return "clean \u2014 nothing to stage";
|
|
6511
|
-
if (op === "git-unstage") return "clean \u2014 nothing to unstage";
|
|
6512
|
-
return "clean \u2014 nothing staged";
|
|
6513
|
-
case "skipped":
|
|
6514
|
-
return gitRun ? "skipped" : "commit: skipped (not a git repository | review failed | deadline)";
|
|
6515
|
-
case "failed":
|
|
6516
|
-
return gitRun ? `failed \u2014 ${outcome.stderr ?? "git step errored"}` : `commit: failed \u2014 ${outcome.stderr ?? "git step errored"}`;
|
|
6517
|
-
}
|
|
6518
|
-
}
|
|
6519
6731
|
function lastRunHasMessage(lastRun) {
|
|
6520
6732
|
return lastRun.stdout !== "" || lastRun.stderr !== "" || (lastRun.errorMessage ?? "") !== "" || lastRun.commit !== void 0;
|
|
6521
6733
|
}
|
|
@@ -6591,7 +6803,6 @@ function doorstopPublishCommand(result, target = doorstopPublishTarget(result))
|
|
|
6591
6803
|
const argument = PUBLISH_TARGET_SAFE_TOKEN.test(target) ? target : quoteShellArgument(target);
|
|
6592
6804
|
return `doorstop publish all ${argument}`;
|
|
6593
6805
|
}
|
|
6594
|
-
var isValidTargetUid = isValidDoorstopUid;
|
|
6595
6806
|
function documentConfigFor(index, item) {
|
|
6596
6807
|
return index.byPrefix.get(item.documentPrefix) ?? {
|
|
6597
6808
|
directoryPath: "",
|
|
@@ -6603,24 +6814,6 @@ function documentConfigFor(index, item) {
|
|
|
6603
6814
|
extra: {}
|
|
6604
6815
|
};
|
|
6605
6816
|
}
|
|
6606
|
-
function suspectParentItems(item, index) {
|
|
6607
|
-
const suspects = [];
|
|
6608
|
-
for (const link of item.links) {
|
|
6609
|
-
if (link.fingerprint === null) continue;
|
|
6610
|
-
const target = index.byUid.get(link.uid);
|
|
6611
|
-
if (target === void 0) continue;
|
|
6612
|
-
if (link.fingerprint !== computeItemStamp(target, documentConfigFor(index, target), false)) {
|
|
6613
|
-
suspects.push(target);
|
|
6614
|
-
}
|
|
6615
|
-
}
|
|
6616
|
-
return suspects;
|
|
6617
|
-
}
|
|
6618
|
-
function firstChildDocumentPrefix(index, item) {
|
|
6619
|
-
for (const document2 of index.documents) {
|
|
6620
|
-
if (document2.parentPrefix === item.documentPrefix) return document2.prefix;
|
|
6621
|
-
}
|
|
6622
|
-
return void 0;
|
|
6623
|
-
}
|
|
6624
6817
|
function jsonishText(value) {
|
|
6625
6818
|
if (value === void 0) return "undefined";
|
|
6626
6819
|
const json2 = JSON.stringify(value);
|
|
@@ -6637,68 +6830,179 @@ function referenceListText(references) {
|
|
|
6637
6830
|
return parts.join(" ");
|
|
6638
6831
|
}).join(", ");
|
|
6639
6832
|
}
|
|
6640
|
-
function itemExcerpt(item) {
|
|
6641
|
-
const header = item.header;
|
|
6642
|
-
if (header !== void 0 && header !== "") return header;
|
|
6643
|
-
const firstLine = (item.text.split("\n")[0] ?? "").trim();
|
|
6644
|
-
if (firstLine === "") return "\u2014";
|
|
6645
|
-
return firstLine.length > 80 ? `${firstLine.slice(0, 80)}\u2026` : firstLine;
|
|
6646
|
-
}
|
|
6647
6833
|
|
|
6648
|
-
//
|
|
6649
|
-
var
|
|
6650
|
-
var
|
|
6651
|
-
var
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
}
|
|
6663
|
-
update(t5, e8) {
|
|
6664
|
-
return this.render(...e8);
|
|
6665
|
-
}
|
|
6834
|
+
// src/browser/view-model/item-list.ts
|
|
6835
|
+
var EMPTY_WORKSPACE_MESSAGE = "This workspace has no Doorstop documents \u2014 run `doorstop create REQ ./reqs` in the workspace to start a requirements tree.";
|
|
6836
|
+
var ALL_DOCUMENTS_LABEL = "All";
|
|
6837
|
+
var STATE_CHIP_LABELS = {
|
|
6838
|
+
normative: "normative",
|
|
6839
|
+
"non-normative": "non-normative",
|
|
6840
|
+
inactive: "inactive",
|
|
6841
|
+
reviewed: "reviewed",
|
|
6842
|
+
unreviewed: "unreviewed",
|
|
6843
|
+
"suspect-link": "suspect link",
|
|
6844
|
+
"no-child-links": "no child links",
|
|
6845
|
+
"no-links": "no links",
|
|
6846
|
+
"unknown-link": "unknown link",
|
|
6847
|
+
"missing-reference": "missing reference"
|
|
6666
6848
|
};
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6849
|
+
function stateChipKind(key) {
|
|
6850
|
+
switch (key) {
|
|
6851
|
+
case "normative":
|
|
6852
|
+
case "non-normative":
|
|
6853
|
+
case "reviewed":
|
|
6854
|
+
return "muted";
|
|
6855
|
+
case "unreviewed":
|
|
6856
|
+
case "no-child-links":
|
|
6857
|
+
case "no-links":
|
|
6858
|
+
return "warning";
|
|
6859
|
+
case "inactive":
|
|
6860
|
+
case "suspect-link":
|
|
6861
|
+
case "unknown-link":
|
|
6862
|
+
case "missing-reference":
|
|
6863
|
+
return "danger";
|
|
6864
|
+
}
|
|
6865
|
+
}
|
|
6866
|
+
function dotTitle(dot) {
|
|
6867
|
+
switch (dot) {
|
|
6868
|
+
case "ok":
|
|
6869
|
+
return "all items reviewed, no suspect links";
|
|
6870
|
+
case "unreviewed":
|
|
6871
|
+
return "has unreviewed changes";
|
|
6872
|
+
case "suspect":
|
|
6873
|
+
return "has suspect links";
|
|
6874
|
+
}
|
|
6875
|
+
}
|
|
6876
|
+
function documentStateDots(document2, items) {
|
|
6877
|
+
const own = items.filter((item) => item.documentPrefix === document2.prefix);
|
|
6878
|
+
const dots = [];
|
|
6879
|
+
if (own.length === 0) return dots;
|
|
6880
|
+
const hasSuspect = own.some((item) => item.stateKeys.includes("suspect-link"));
|
|
6881
|
+
const hasUnreviewed = own.some((item) => item.stateKeys.includes("unreviewed"));
|
|
6882
|
+
if (!hasSuspect && !hasUnreviewed) dots.push("ok");
|
|
6883
|
+
if (hasUnreviewed) dots.push("unreviewed");
|
|
6884
|
+
if (hasSuspect) dots.push("suspect");
|
|
6885
|
+
return dots;
|
|
6886
|
+
}
|
|
6887
|
+
function filteredItems(index, documentPrefix, stateFilter, search) {
|
|
6888
|
+
const needle = search.trim().toLowerCase();
|
|
6889
|
+
return index.items.filter((item) => {
|
|
6890
|
+
if (documentPrefix !== void 0 && documentPrefix !== "" && item.documentPrefix !== documentPrefix) {
|
|
6891
|
+
return false;
|
|
6681
6892
|
}
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
const s7 = !!i8[t5];
|
|
6686
|
-
s7 === this.st.has(t5) || this.nt?.has(t5) || (s7 ? (r8.add(t5), this.st.add(t5)) : (r8.remove(t5), this.st.delete(t5)));
|
|
6893
|
+
if (stateFilter !== void 0 && !item.stateKeys.includes(stateFilter)) return false;
|
|
6894
|
+
if (needle !== "" && !`${item.uid} ${item.header ?? ""} ${item.text}`.toLowerCase().includes(needle)) {
|
|
6895
|
+
return false;
|
|
6687
6896
|
}
|
|
6688
|
-
return
|
|
6897
|
+
return true;
|
|
6898
|
+
});
|
|
6899
|
+
}
|
|
6900
|
+
function doorstopShowAdditionalAttribute(result) {
|
|
6901
|
+
return result?.settings?.showAdditionalAttribute ?? DEFAULT_OPENDOOR_SETTINGS.showAdditionalAttribute;
|
|
6902
|
+
}
|
|
6903
|
+
function itemExcerpt(item) {
|
|
6904
|
+
const header = item.header;
|
|
6905
|
+
if (header !== void 0 && header !== "") return header;
|
|
6906
|
+
const firstLine = (item.text.split("\n")[0] ?? "").trim();
|
|
6907
|
+
if (firstLine === "") return "\u2014";
|
|
6908
|
+
return firstLine.length > 80 ? `${firstLine.slice(0, 80)}\u2026` : firstLine;
|
|
6909
|
+
}
|
|
6910
|
+
function itemTextRow(item) {
|
|
6911
|
+
return item.text.trim() === "" ? void 0 : item.text;
|
|
6912
|
+
}
|
|
6913
|
+
function shownAttributeRows(item, keys) {
|
|
6914
|
+
const rows = [];
|
|
6915
|
+
for (const key of keys) {
|
|
6916
|
+
if (!Object.prototype.hasOwnProperty.call(item.attributes, key)) continue;
|
|
6917
|
+
rows.push({ key, text: jsonishText(item.attributes[key]) });
|
|
6689
6918
|
}
|
|
6690
|
-
|
|
6919
|
+
return rows;
|
|
6920
|
+
}
|
|
6921
|
+
|
|
6922
|
+
// src/browser/view-model/palette.ts
|
|
6923
|
+
function doorstopCommitAfterReview(result) {
|
|
6924
|
+
return result?.settings?.commitAfterReview ?? DEFAULT_OPENDOOR_SETTINGS.commitAfterReview;
|
|
6925
|
+
}
|
|
6926
|
+
var isValidTargetUid = isValidDoorstopUid;
|
|
6927
|
+
function suspectParentItems(item, index) {
|
|
6928
|
+
const suspects = [];
|
|
6929
|
+
for (const link of item.links) {
|
|
6930
|
+
if (link.fingerprint === null) continue;
|
|
6931
|
+
const target = index.byUid.get(link.uid);
|
|
6932
|
+
if (target === void 0) continue;
|
|
6933
|
+
if (link.fingerprint !== computeItemStamp(target, documentConfigFor(index, target), false)) {
|
|
6934
|
+
suspects.push(target);
|
|
6935
|
+
}
|
|
6936
|
+
}
|
|
6937
|
+
return suspects;
|
|
6938
|
+
}
|
|
6939
|
+
function firstChildDocumentPrefix(index, item) {
|
|
6940
|
+
for (const document2 of index.documents) {
|
|
6941
|
+
if (document2.parentPrefix === item.documentPrefix) return document2.prefix;
|
|
6942
|
+
}
|
|
6943
|
+
return void 0;
|
|
6944
|
+
}
|
|
6945
|
+
|
|
6946
|
+
// src/browser/view-model/status-bar.ts
|
|
6947
|
+
function commitOutcomeText(op, outcome) {
|
|
6948
|
+
const gitRun = op === "git-stage" || op === "git-commit" || op === "git-unstage";
|
|
6949
|
+
switch (outcome.status) {
|
|
6950
|
+
case "staged":
|
|
6951
|
+
return `staged ${String(outcome.staged ?? 0)} paths`;
|
|
6952
|
+
case "unstaged":
|
|
6953
|
+
return `unstaged ${String(outcome.unstaged ?? 0)} paths`;
|
|
6954
|
+
case "committed":
|
|
6955
|
+
return gitRun ? `committed ${outcome.sha ?? "<unknown sha>"}` : `commit: ${outcome.sha ?? "<unknown sha>"}`;
|
|
6956
|
+
case "clean":
|
|
6957
|
+
if (!gitRun) return "commit: clean (already committed)";
|
|
6958
|
+
if (op === "git-stage") return "clean \u2014 nothing to stage";
|
|
6959
|
+
if (op === "git-unstage") return "clean \u2014 nothing to unstage";
|
|
6960
|
+
return "clean \u2014 nothing staged";
|
|
6961
|
+
case "skipped":
|
|
6962
|
+
return gitRun ? "skipped" : "commit: skipped (not a git repository | review failed | deadline)";
|
|
6963
|
+
case "failed":
|
|
6964
|
+
return gitRun ? `failed \u2014 ${outcome.stderr ?? "git step errored"}` : `commit: failed \u2014 ${outcome.stderr ?? "git step errored"}`;
|
|
6965
|
+
}
|
|
6966
|
+
}
|
|
6967
|
+
|
|
6968
|
+
// src/browser/view-model/findings.ts
|
|
6969
|
+
var FINDINGS_EMPTY_MESSAGE = "No findings \u2014 the tree is clean.";
|
|
6970
|
+
var FINDINGS_EMPTY_HINT = "Findings are plugin-local; run `doorstop` validation in the terminal for the authoritative check.";
|
|
6971
|
+
var FINDINGS_PLUGIN_LOCAL_NOTE = "plugin-local findings \u2014 `doorstop` validation in the terminal is authoritative";
|
|
6972
|
+
var FINDING_SEVERITY_RANK = {
|
|
6973
|
+
error: 0,
|
|
6974
|
+
warning: 1,
|
|
6975
|
+
info: 2
|
|
6976
|
+
};
|
|
6977
|
+
function findingsViewRows(index) {
|
|
6978
|
+
const rows = [...index.findings, ...index.diagnostics];
|
|
6979
|
+
return rows.sort((a3, b3) => FINDING_SEVERITY_RANK[a3.severity] - FINDING_SEVERITY_RANK[b3.severity]);
|
|
6980
|
+
}
|
|
6981
|
+
function findingsViewCounts(rows) {
|
|
6982
|
+
const counts = { errors: 0, warnings: 0, info: 0 };
|
|
6983
|
+
for (const row of rows) {
|
|
6984
|
+
if (row.severity === "error") counts.errors += 1;
|
|
6985
|
+
else if (row.severity === "warning") counts.warnings += 1;
|
|
6986
|
+
else counts.info += 1;
|
|
6987
|
+
}
|
|
6988
|
+
return counts;
|
|
6989
|
+
}
|
|
6990
|
+
function findingsCountText(counts) {
|
|
6991
|
+
const errors = counts.errors === 1 ? "1 error" : `${String(counts.errors)} errors`;
|
|
6992
|
+
const warnings = counts.warnings === 1 ? "1 warning" : `${String(counts.warnings)} warnings`;
|
|
6993
|
+
return `${errors} \xB7 ${warnings} \xB7 ${String(counts.info)} info`;
|
|
6994
|
+
}
|
|
6691
6995
|
|
|
6692
6996
|
// node_modules/lit-html/directive-helpers.js
|
|
6693
|
-
var { I:
|
|
6694
|
-
var
|
|
6997
|
+
var { I: t3 } = j;
|
|
6998
|
+
var i5 = (o8) => o8;
|
|
6695
6999
|
var r4 = (o8) => void 0 === o8.strings;
|
|
6696
7000
|
var s4 = () => document.createComment("");
|
|
6697
7001
|
var v2 = (o8, n7, e8) => {
|
|
6698
7002
|
const l3 = o8._$AA.parentNode, d3 = void 0 === n7 ? o8._$AB : n7._$AA;
|
|
6699
7003
|
if (void 0 === e8) {
|
|
6700
7004
|
const i8 = l3.insertBefore(s4(), d3), n8 = l3.insertBefore(s4(), d3);
|
|
6701
|
-
e8 = new
|
|
7005
|
+
e8 = new t3(i8, n8, o8, o8.options);
|
|
6702
7006
|
} else {
|
|
6703
7007
|
const t5 = e8._$AB.nextSibling, n8 = e8._$AM, c6 = n8 !== o8;
|
|
6704
7008
|
if (c6) {
|
|
@@ -6708,8 +7012,8 @@ var v2 = (o8, n7, e8) => {
|
|
|
6708
7012
|
if (t5 !== d3 || c6) {
|
|
6709
7013
|
let o9 = e8._$AA;
|
|
6710
7014
|
for (; o9 !== t5; ) {
|
|
6711
|
-
const t6 =
|
|
6712
|
-
|
|
7015
|
+
const t6 = i5(o9).nextSibling;
|
|
7016
|
+
i5(l3).insertBefore(o9, d3), o9 = t6;
|
|
6713
7017
|
}
|
|
6714
7018
|
}
|
|
6715
7019
|
}
|
|
@@ -6723,18 +7027,25 @@ var h3 = (o8) => {
|
|
|
6723
7027
|
o8._$AR(), o8._$AA.remove();
|
|
6724
7028
|
};
|
|
6725
7029
|
|
|
6726
|
-
// node_modules/lit-html/
|
|
6727
|
-
var
|
|
6728
|
-
|
|
6729
|
-
|
|
7030
|
+
// node_modules/lit-html/directive.js
|
|
7031
|
+
var t4 = { ATTRIBUTE: 1, CHILD: 2, PROPERTY: 3, BOOLEAN_ATTRIBUTE: 4, EVENT: 5, ELEMENT: 6 };
|
|
7032
|
+
var e4 = (t5) => (...e8) => ({ _$litDirective$: t5, values: e8 });
|
|
7033
|
+
var i6 = class {
|
|
7034
|
+
constructor(t5) {
|
|
6730
7035
|
}
|
|
6731
|
-
|
|
6732
|
-
return this.
|
|
7036
|
+
get _$AU() {
|
|
7037
|
+
return this._$AM._$AU;
|
|
6733
7038
|
}
|
|
6734
|
-
|
|
6735
|
-
|
|
7039
|
+
_$AT(t5, e8, i8) {
|
|
7040
|
+
this._$Ct = t5, this._$AM = e8, this._$Ci = i8;
|
|
6736
7041
|
}
|
|
6737
|
-
|
|
7042
|
+
_$AS(t5, e8) {
|
|
7043
|
+
return this.update(t5, e8);
|
|
7044
|
+
}
|
|
7045
|
+
update(t5, e8) {
|
|
7046
|
+
return this.render(...e8);
|
|
7047
|
+
}
|
|
7048
|
+
};
|
|
6738
7049
|
|
|
6739
7050
|
// node_modules/lit-html/async-directive.js
|
|
6740
7051
|
var s5 = (i8, t5) => {
|
|
@@ -6768,9 +7079,9 @@ function n4(i8, t5 = false, e8 = 0) {
|
|
|
6768
7079
|
else s5(this, i8);
|
|
6769
7080
|
}
|
|
6770
7081
|
var c4 = (i8) => {
|
|
6771
|
-
i8.type ==
|
|
7082
|
+
i8.type == t4.CHILD && (i8._$AP ??= n4, i8._$AQ ??= h4);
|
|
6772
7083
|
};
|
|
6773
|
-
var f3 = class extends
|
|
7084
|
+
var f3 = class extends i6 {
|
|
6774
7085
|
constructor() {
|
|
6775
7086
|
super(...arguments), this._$AN = void 0;
|
|
6776
7087
|
}
|
|
@@ -6794,7 +7105,7 @@ var f3 = class extends i5 {
|
|
|
6794
7105
|
};
|
|
6795
7106
|
|
|
6796
7107
|
// node_modules/lit-html/directives/ref.js
|
|
6797
|
-
var
|
|
7108
|
+
var e5 = () => new h5();
|
|
6798
7109
|
var h5 = class {
|
|
6799
7110
|
};
|
|
6800
7111
|
var o6 = /* @__PURE__ */ new WeakMap();
|
|
@@ -6824,59 +7135,6 @@ var n5 = e4(class extends f3 {
|
|
|
6824
7135
|
}
|
|
6825
7136
|
});
|
|
6826
7137
|
|
|
6827
|
-
// node_modules/lit-html/directives/repeat.js
|
|
6828
|
-
var u4 = (e8, s6, t5) => {
|
|
6829
|
-
const r8 = /* @__PURE__ */ new Map();
|
|
6830
|
-
for (let l3 = s6; l3 <= t5; l3++) r8.set(e8[l3], l3);
|
|
6831
|
-
return r8;
|
|
6832
|
-
};
|
|
6833
|
-
var c5 = e4(class extends i5 {
|
|
6834
|
-
constructor(e8) {
|
|
6835
|
-
if (super(e8), e8.type !== t3.CHILD) throw Error("repeat() can only be used in text expressions");
|
|
6836
|
-
}
|
|
6837
|
-
dt(e8, s6, t5) {
|
|
6838
|
-
let r8;
|
|
6839
|
-
void 0 === t5 ? t5 = s6 : void 0 !== s6 && (r8 = s6);
|
|
6840
|
-
const l3 = [], o8 = [];
|
|
6841
|
-
let i8 = 0;
|
|
6842
|
-
for (const s7 of e8) l3[i8] = r8 ? r8(s7, i8) : i8, o8[i8] = t5(s7, i8), i8++;
|
|
6843
|
-
return { values: o8, keys: l3 };
|
|
6844
|
-
}
|
|
6845
|
-
render(e8, s6, t5) {
|
|
6846
|
-
return this.dt(e8, s6, t5).values;
|
|
6847
|
-
}
|
|
6848
|
-
update(s6, [t5, r8, c6]) {
|
|
6849
|
-
const d3 = M2(s6), { values: p4, keys: a3 } = this.dt(t5, r8, c6);
|
|
6850
|
-
if (!Array.isArray(d3)) return this.ut = a3, p4;
|
|
6851
|
-
const h6 = this.ut ??= [], v3 = [];
|
|
6852
|
-
let m3, y3, x2 = 0, j2 = d3.length - 1, k2 = 0, w2 = p4.length - 1;
|
|
6853
|
-
for (; x2 <= j2 && k2 <= w2; ) if (null === d3[x2]) x2++;
|
|
6854
|
-
else if (null === d3[j2]) j2--;
|
|
6855
|
-
else if (h6[x2] === a3[k2]) v3[k2] = u3(d3[x2], p4[k2]), x2++, k2++;
|
|
6856
|
-
else if (h6[j2] === a3[w2]) v3[w2] = u3(d3[j2], p4[w2]), j2--, w2--;
|
|
6857
|
-
else if (h6[x2] === a3[w2]) v3[w2] = u3(d3[x2], p4[w2]), v2(s6, v3[w2 + 1], d3[x2]), x2++, w2--;
|
|
6858
|
-
else if (h6[j2] === a3[k2]) v3[k2] = u3(d3[j2], p4[k2]), v2(s6, d3[x2], d3[j2]), j2--, k2++;
|
|
6859
|
-
else if (void 0 === m3 && (m3 = u4(a3, k2, w2), y3 = u4(h6, x2, j2)), m3.has(h6[x2])) if (m3.has(h6[j2])) {
|
|
6860
|
-
const e8 = y3.get(a3[k2]), t6 = void 0 !== e8 ? d3[e8] : null;
|
|
6861
|
-
if (null === t6) {
|
|
6862
|
-
const e9 = v2(s6, d3[x2]);
|
|
6863
|
-
u3(e9, p4[k2]), v3[k2] = e9;
|
|
6864
|
-
} else v3[k2] = u3(t6, p4[k2]), v2(s6, d3[x2], t6), d3[e8] = null;
|
|
6865
|
-
k2++;
|
|
6866
|
-
} else h3(d3[j2]), j2--;
|
|
6867
|
-
else h3(d3[x2]), x2++;
|
|
6868
|
-
for (; k2 <= w2; ) {
|
|
6869
|
-
const e8 = v2(s6, v3[w2 + 1]);
|
|
6870
|
-
u3(e8, p4[k2]), v3[k2++] = e8;
|
|
6871
|
-
}
|
|
6872
|
-
for (; x2 <= j2; ) {
|
|
6873
|
-
const e8 = d3[x2++];
|
|
6874
|
-
null !== e8 && h3(e8);
|
|
6875
|
-
}
|
|
6876
|
-
return this.ut = a3, p3(s6, v3), E;
|
|
6877
|
-
}
|
|
6878
|
-
});
|
|
6879
|
-
|
|
6880
7138
|
// node_modules/@lit/reactive-element/decorators/property.js
|
|
6881
7139
|
var o7 = { attribute: true, type: String, converter: u, reflect: false, hasChanged: f };
|
|
6882
7140
|
var r6 = (t5 = o7, e8, r8) => {
|
|
@@ -6912,8 +7170,8 @@ function r7(r8) {
|
|
|
6912
7170
|
return n6({ ...r8, state: true, attribute: false });
|
|
6913
7171
|
}
|
|
6914
7172
|
|
|
6915
|
-
// src/
|
|
6916
|
-
var
|
|
7173
|
+
// src/browser/styles/base.ts
|
|
7174
|
+
var baseStyles = i`
|
|
6917
7175
|
:host {
|
|
6918
7176
|
flex: 1 1 auto;
|
|
6919
7177
|
min-height: 0;
|
|
@@ -6922,7 +7180,7 @@ var panelStyles = i`
|
|
|
6922
7180
|
overflow: hidden;
|
|
6923
7181
|
color: var(--pi-text);
|
|
6924
7182
|
background: var(--pi-bg);
|
|
6925
|
-
font:
|
|
7183
|
+
font: 14px system-ui, sans-serif;
|
|
6926
7184
|
}
|
|
6927
7185
|
|
|
6928
7186
|
button {
|
|
@@ -6948,7 +7206,7 @@ var panelStyles = i`
|
|
|
6948
7206
|
border-radius: 6px;
|
|
6949
7207
|
background: var(--pi-surface);
|
|
6950
7208
|
color: var(--pi-text);
|
|
6951
|
-
font-size:
|
|
7209
|
+
font-size: 13px;
|
|
6952
7210
|
padding: 4px 6px;
|
|
6953
7211
|
}
|
|
6954
7212
|
|
|
@@ -6957,13 +7215,16 @@ var panelStyles = i`
|
|
|
6957
7215
|
border-radius: 5px;
|
|
6958
7216
|
background: var(--pi-bg);
|
|
6959
7217
|
padding: 1px 4px;
|
|
6960
|
-
font:
|
|
7218
|
+
font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
6961
7219
|
}
|
|
6962
7220
|
|
|
6963
7221
|
.doorstop-muted {
|
|
6964
7222
|
color: var(--pi-muted);
|
|
6965
7223
|
}
|
|
7224
|
+
`;
|
|
6966
7225
|
|
|
7226
|
+
// src/browser/styles/project-actions.ts
|
|
7227
|
+
var projectActionsStyles = i`
|
|
6967
7228
|
/* --- project actions (region 1) --- */
|
|
6968
7229
|
.doorstop-project-actions {
|
|
6969
7230
|
flex: 0 0 auto;
|
|
@@ -7002,7 +7263,7 @@ var panelStyles = i`
|
|
|
7002
7263
|
.doorstop-doc-chip {
|
|
7003
7264
|
border-radius: 999px;
|
|
7004
7265
|
padding: 2px 9px;
|
|
7005
|
-
font-size:
|
|
7266
|
+
font-size: 13px;
|
|
7006
7267
|
}
|
|
7007
7268
|
|
|
7008
7269
|
.doorstop-doc-chip.is-selected {
|
|
@@ -7013,12 +7274,12 @@ var panelStyles = i`
|
|
|
7013
7274
|
|
|
7014
7275
|
.doorstop-doc-arrow {
|
|
7015
7276
|
color: var(--pi-muted);
|
|
7016
|
-
font-size:
|
|
7277
|
+
font-size: 12px;
|
|
7017
7278
|
}
|
|
7018
7279
|
|
|
7019
7280
|
.doorstop-doc-count {
|
|
7020
7281
|
color: var(--pi-muted);
|
|
7021
|
-
font-size:
|
|
7282
|
+
font-size: 12px;
|
|
7022
7283
|
}
|
|
7023
7284
|
|
|
7024
7285
|
.doorstop-dot {
|
|
@@ -7053,7 +7314,7 @@ var panelStyles = i`
|
|
|
7053
7314
|
border-radius: 999px;
|
|
7054
7315
|
color: var(--pi-warning);
|
|
7055
7316
|
padding: 1px 8px;
|
|
7056
|
-
font-size:
|
|
7317
|
+
font-size: 13px;
|
|
7057
7318
|
}
|
|
7058
7319
|
|
|
7059
7320
|
/* --- project-scoped git actions (plan-add-git-actions Phase D step 17) --- */
|
|
@@ -7076,7 +7337,7 @@ var panelStyles = i`
|
|
|
7076
7337
|
background: transparent;
|
|
7077
7338
|
color: var(--pi-muted);
|
|
7078
7339
|
padding: 2px 9px;
|
|
7079
|
-
font-size:
|
|
7340
|
+
font-size: 13px;
|
|
7080
7341
|
}
|
|
7081
7342
|
|
|
7082
7343
|
.doorstop-git-status-text {
|
|
@@ -7132,7 +7393,7 @@ var panelStyles = i`
|
|
|
7132
7393
|
background: transparent;
|
|
7133
7394
|
color: var(--pi-muted);
|
|
7134
7395
|
padding: 3px 10px;
|
|
7135
|
-
font-size:
|
|
7396
|
+
font-size: 13px;
|
|
7136
7397
|
}
|
|
7137
7398
|
|
|
7138
7399
|
.doorstop-view-tab + .doorstop-view-tab {
|
|
@@ -7143,7 +7404,10 @@ var panelStyles = i`
|
|
|
7143
7404
|
background: var(--pi-selection-bg);
|
|
7144
7405
|
color: var(--pi-accent);
|
|
7145
7406
|
}
|
|
7407
|
+
`;
|
|
7146
7408
|
|
|
7409
|
+
// src/browser/styles/item-list.ts
|
|
7410
|
+
var itemListStyles = i`
|
|
7147
7411
|
/* --- viewer (regions 2 + 3; the filters row and split stack) --- */
|
|
7148
7412
|
.doorstop-viewer {
|
|
7149
7413
|
flex: 1 1 auto;
|
|
@@ -7260,7 +7524,7 @@ var panelStyles = i`
|
|
|
7260
7524
|
.doorstop-item-row {
|
|
7261
7525
|
display: grid;
|
|
7262
7526
|
grid-template-columns: max-content max-content minmax(0, 1fr) max-content;
|
|
7263
|
-
gap: 8px;
|
|
7527
|
+
gap: 2px 8px;
|
|
7264
7528
|
align-items: baseline;
|
|
7265
7529
|
width: 100%;
|
|
7266
7530
|
border: 0;
|
|
@@ -7277,7 +7541,7 @@ var panelStyles = i`
|
|
|
7277
7541
|
|
|
7278
7542
|
.doorstop-item-level {
|
|
7279
7543
|
color: var(--pi-muted);
|
|
7280
|
-
font-size:
|
|
7544
|
+
font-size: 12px;
|
|
7281
7545
|
}
|
|
7282
7546
|
|
|
7283
7547
|
.doorstop-item-summary {
|
|
@@ -7288,6 +7552,49 @@ var panelStyles = i`
|
|
|
7288
7552
|
color: var(--pi-text-secondary);
|
|
7289
7553
|
}
|
|
7290
7554
|
|
|
7555
|
+
/* A fifth column exists only when showAdditionalAttribute configures
|
|
7556
|
+
at least one name; otherwise the default rows keep the original
|
|
7557
|
+
4-column track (and its single 8px summary→chips gap). */
|
|
7558
|
+
.doorstop-items.has-item-attrs .doorstop-item-row {
|
|
7559
|
+
grid-template-columns: max-content max-content minmax(0, 1fr) minmax(0, max-content) max-content;
|
|
7560
|
+
}
|
|
7561
|
+
|
|
7562
|
+
/* The configured key: value attribute pairs (settings field
|
|
7563
|
+
showAdditionalAttribute). The column collapses to zero when the row
|
|
7564
|
+
renders nothing here (no configured attribute the item has). The
|
|
7565
|
+
max-width bounds a pathologically long value so it cannot starve
|
|
7566
|
+
the summary's fr track before the ellipsis kicks in. */
|
|
7567
|
+
.doorstop-item-attrs {
|
|
7568
|
+
min-width: 0;
|
|
7569
|
+
max-width: 40ch;
|
|
7570
|
+
overflow: hidden;
|
|
7571
|
+
text-overflow: ellipsis;
|
|
7572
|
+
white-space: nowrap;
|
|
7573
|
+
color: var(--pi-muted);
|
|
7574
|
+
font-size: 12px;
|
|
7575
|
+
}
|
|
7576
|
+
|
|
7577
|
+
/* The item body ("text" configured in showAdditionalAttribute) on its own
|
|
7578
|
+
indented second line below level/uid/summary/chips. Full-width implicit
|
|
7579
|
+
grid row; 5ch inline indent; clamped to 2 lines with ellipsis — the full
|
|
7580
|
+
body lives in the detail pane. The span only exists when rendered, so no
|
|
7581
|
+
container modifier class is needed. */
|
|
7582
|
+
.doorstop-item-text {
|
|
7583
|
+
grid-column: 1 / -1;
|
|
7584
|
+
align-self: start;
|
|
7585
|
+
padding-inline-start: 5ch;
|
|
7586
|
+
min-width: 0;
|
|
7587
|
+
color: var(--pi-muted);
|
|
7588
|
+
/* Body-like content: reads one tier above the 12px metadata. */
|
|
7589
|
+
font-size: 13px;
|
|
7590
|
+
white-space: pre-wrap;
|
|
7591
|
+
overflow-wrap: anywhere;
|
|
7592
|
+
display: -webkit-box;
|
|
7593
|
+
-webkit-box-orient: vertical;
|
|
7594
|
+
-webkit-line-clamp: 2;
|
|
7595
|
+
overflow: hidden;
|
|
7596
|
+
}
|
|
7597
|
+
|
|
7291
7598
|
.doorstop-item-chips {
|
|
7292
7599
|
display: flex;
|
|
7293
7600
|
flex-wrap: wrap;
|
|
@@ -7301,8 +7608,14 @@ var panelStyles = i`
|
|
|
7301
7608
|
keyboard path is the region-4 Stage/Unstage buttons. The same class
|
|
7302
7609
|
hosts both directions: the plus (git add) icon and the unstage
|
|
7303
7610
|
minus (git reset) icon, chosen by {@link itemUnstageable} /
|
|
7304
|
-
{@link itemStageable} precedence.
|
|
7305
|
-
|
|
7611
|
+
{@link itemStageable} precedence.
|
|
7612
|
+
|
|
7613
|
+
The per-row "insert UID into the agent prompt" arrow shares the
|
|
7614
|
+
pointer-only <span> contract and this visual box; it keeps a
|
|
7615
|
+
distinct class because the +/reset icon and the arrow stay
|
|
7616
|
+
independently evolvable. */
|
|
7617
|
+
.doorstop-item-add,
|
|
7618
|
+
.doorstop-item-insert {
|
|
7306
7619
|
display: inline-flex;
|
|
7307
7620
|
align-items: center;
|
|
7308
7621
|
align-self: center;
|
|
@@ -7313,7 +7626,8 @@ var panelStyles = i`
|
|
|
7313
7626
|
cursor: pointer;
|
|
7314
7627
|
}
|
|
7315
7628
|
|
|
7316
|
-
.doorstop-item-add:hover
|
|
7629
|
+
.doorstop-item-add:hover,
|
|
7630
|
+
.doorstop-item-insert:hover {
|
|
7317
7631
|
color: var(--pi-text);
|
|
7318
7632
|
}
|
|
7319
7633
|
|
|
@@ -7321,6 +7635,7 @@ var panelStyles = i`
|
|
|
7321
7635
|
.doorstop-chip {
|
|
7322
7636
|
border-radius: 999px;
|
|
7323
7637
|
padding: 0 7px;
|
|
7638
|
+
/* Strict parity: pi-web keeps compact chips at 11px. */
|
|
7324
7639
|
font-size: 11px;
|
|
7325
7640
|
white-space: nowrap;
|
|
7326
7641
|
}
|
|
@@ -7344,10 +7659,126 @@ var panelStyles = i`
|
|
|
7344
7659
|
border: 1px solid var(--pi-success-border);
|
|
7345
7660
|
color: var(--pi-success);
|
|
7346
7661
|
}
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
.
|
|
7350
|
-
|
|
7662
|
+
`;
|
|
7663
|
+
var findingsStyles = i`
|
|
7664
|
+
/* --- findings view (spec §7.2) --- */
|
|
7665
|
+
.doorstop-findings-view {
|
|
7666
|
+
flex: 1 1 auto;
|
|
7667
|
+
min-height: 0;
|
|
7668
|
+
overflow: auto;
|
|
7669
|
+
padding: 10px 12px;
|
|
7670
|
+
display: grid;
|
|
7671
|
+
gap: 8px;
|
|
7672
|
+
align-content: start;
|
|
7673
|
+
}
|
|
7674
|
+
|
|
7675
|
+
.doorstop-findings-head {
|
|
7676
|
+
display: flex;
|
|
7677
|
+
flex-wrap: wrap;
|
|
7678
|
+
align-items: baseline;
|
|
7679
|
+
gap: 6px 10px;
|
|
7680
|
+
}
|
|
7681
|
+
|
|
7682
|
+
.doorstop-findings-counts {
|
|
7683
|
+
font-size: 13px;
|
|
7684
|
+
font-weight: 600;
|
|
7685
|
+
}
|
|
7686
|
+
|
|
7687
|
+
.doorstop-findings-note {
|
|
7688
|
+
font-size: 12px;
|
|
7689
|
+
}
|
|
7690
|
+
|
|
7691
|
+
.doorstop-findings-list {
|
|
7692
|
+
display: grid;
|
|
7693
|
+
gap: 3px;
|
|
7694
|
+
}
|
|
7695
|
+
|
|
7696
|
+
.doorstop-finding-row {
|
|
7697
|
+
display: flex;
|
|
7698
|
+
flex-wrap: wrap;
|
|
7699
|
+
align-items: baseline;
|
|
7700
|
+
gap: 6px;
|
|
7701
|
+
border-radius: 6px;
|
|
7702
|
+
padding: 5px 8px;
|
|
7703
|
+
}
|
|
7704
|
+
|
|
7705
|
+
.doorstop-finding-row.doorstop-error {
|
|
7706
|
+
border: 1px solid var(--pi-danger);
|
|
7707
|
+
background: color-mix(in srgb, var(--pi-danger) 9%, transparent);
|
|
7708
|
+
}
|
|
7709
|
+
|
|
7710
|
+
.doorstop-finding-row.doorstop-warning {
|
|
7711
|
+
border: 1px solid var(--pi-warning-border);
|
|
7712
|
+
background: color-mix(in srgb, var(--pi-warning) 9%, transparent);
|
|
7713
|
+
}
|
|
7714
|
+
|
|
7715
|
+
.doorstop-finding-row.doorstop-info {
|
|
7716
|
+
border: 1px solid var(--pi-border-muted);
|
|
7717
|
+
color: var(--pi-muted);
|
|
7718
|
+
}
|
|
7719
|
+
|
|
7720
|
+
.doorstop-finding-row .doorstop-severity {
|
|
7721
|
+
background: var(--pi-border-muted);
|
|
7722
|
+
color: var(--pi-muted);
|
|
7723
|
+
}
|
|
7724
|
+
|
|
7725
|
+
.doorstop-finding-row.doorstop-error .doorstop-severity {
|
|
7726
|
+
background: var(--pi-danger);
|
|
7727
|
+
color: var(--pi-bg);
|
|
7728
|
+
}
|
|
7729
|
+
|
|
7730
|
+
.doorstop-finding-row.doorstop-warning .doorstop-severity {
|
|
7731
|
+
background: var(--pi-warning);
|
|
7732
|
+
color: var(--pi-bg);
|
|
7733
|
+
}
|
|
7734
|
+
|
|
7735
|
+
.doorstop-finding-uid {
|
|
7736
|
+
border: 0;
|
|
7737
|
+
border-radius: 5px;
|
|
7738
|
+
background: transparent;
|
|
7739
|
+
color: inherit;
|
|
7740
|
+
padding: 0;
|
|
7741
|
+
font-weight: 600;
|
|
7742
|
+
}
|
|
7743
|
+
|
|
7744
|
+
.doorstop-finding-uid:hover {
|
|
7745
|
+
text-decoration: underline;
|
|
7746
|
+
cursor: pointer;
|
|
7747
|
+
}
|
|
7748
|
+
|
|
7749
|
+
.doorstop-finding-message {
|
|
7750
|
+
min-width: 0;
|
|
7751
|
+
overflow-wrap: anywhere;
|
|
7752
|
+
}
|
|
7753
|
+
|
|
7754
|
+
/* --- empty states --- */
|
|
7755
|
+
.doorstop-empty {
|
|
7756
|
+
margin: 10px 12px;
|
|
7757
|
+
border: 1px dashed var(--pi-border-muted);
|
|
7758
|
+
border-radius: 8px;
|
|
7759
|
+
color: var(--pi-muted);
|
|
7760
|
+
padding: 12px;
|
|
7761
|
+
overflow: auto;
|
|
7762
|
+
}
|
|
7763
|
+
|
|
7764
|
+
.doorstop-empty p {
|
|
7765
|
+
margin: 0;
|
|
7766
|
+
}
|
|
7767
|
+
|
|
7768
|
+
.doorstop-empty button {
|
|
7769
|
+
margin-top: 10px;
|
|
7770
|
+
}
|
|
7771
|
+
|
|
7772
|
+
.doorstop-empty .doorstop-muted {
|
|
7773
|
+
margin-top: 6px;
|
|
7774
|
+
}
|
|
7775
|
+
`;
|
|
7776
|
+
|
|
7777
|
+
// src/browser/styles/detail-pane.ts
|
|
7778
|
+
var detailPaneStyles = i`
|
|
7779
|
+
/* --- detail pane (region 3) --- */
|
|
7780
|
+
.doorstop-detail {
|
|
7781
|
+
padding: 12px;
|
|
7351
7782
|
display: grid;
|
|
7352
7783
|
gap: 10px;
|
|
7353
7784
|
align-content: start;
|
|
@@ -7362,14 +7793,14 @@ var panelStyles = i`
|
|
|
7362
7793
|
|
|
7363
7794
|
.doorstop-detail-head h3 {
|
|
7364
7795
|
margin: 0;
|
|
7365
|
-
font-size:
|
|
7796
|
+
font-size: 16px;
|
|
7366
7797
|
min-width: 0;
|
|
7367
7798
|
overflow-wrap: anywhere;
|
|
7368
7799
|
}
|
|
7369
7800
|
|
|
7370
7801
|
.doorstop-level {
|
|
7371
7802
|
color: var(--pi-muted);
|
|
7372
|
-
font-size:
|
|
7803
|
+
font-size: 13px;
|
|
7373
7804
|
}
|
|
7374
7805
|
|
|
7375
7806
|
.doorstop-flags {
|
|
@@ -7383,7 +7814,7 @@ var panelStyles = i`
|
|
|
7383
7814
|
border-radius: 999px;
|
|
7384
7815
|
color: var(--pi-muted);
|
|
7385
7816
|
padding: 0 7px;
|
|
7386
|
-
font-size:
|
|
7817
|
+
font-size: 12px;
|
|
7387
7818
|
}
|
|
7388
7819
|
|
|
7389
7820
|
.doorstop-flag.is-inactive {
|
|
@@ -7406,7 +7837,7 @@ var panelStyles = i`
|
|
|
7406
7837
|
|
|
7407
7838
|
.doorstop-changes-summary {
|
|
7408
7839
|
color: var(--pi-muted);
|
|
7409
|
-
font-size:
|
|
7840
|
+
font-size: 12px;
|
|
7410
7841
|
text-transform: uppercase;
|
|
7411
7842
|
letter-spacing: 0.03em;
|
|
7412
7843
|
cursor: pointer;
|
|
@@ -7419,12 +7850,12 @@ var panelStyles = i`
|
|
|
7419
7850
|
|
|
7420
7851
|
.doorstop-changes-notice {
|
|
7421
7852
|
margin: 6px 0 0;
|
|
7422
|
-
font-size:
|
|
7853
|
+
font-size: 13px;
|
|
7423
7854
|
}
|
|
7424
7855
|
|
|
7425
7856
|
.doorstop-changes-source {
|
|
7426
7857
|
margin: 6px 0 4px;
|
|
7427
|
-
font-size:
|
|
7858
|
+
font-size: 12px;
|
|
7428
7859
|
}
|
|
7429
7860
|
|
|
7430
7861
|
.doorstop-diff-lines {
|
|
@@ -7494,7 +7925,7 @@ var panelStyles = i`
|
|
|
7494
7925
|
|
|
7495
7926
|
.doorstop-field-name {
|
|
7496
7927
|
color: var(--pi-muted);
|
|
7497
|
-
font-size:
|
|
7928
|
+
font-size: 12px;
|
|
7498
7929
|
text-transform: uppercase;
|
|
7499
7930
|
letter-spacing: 0.03em;
|
|
7500
7931
|
}
|
|
@@ -7502,7 +7933,7 @@ var panelStyles = i`
|
|
|
7502
7933
|
.doorstop-field-chip {
|
|
7503
7934
|
border-radius: 5px;
|
|
7504
7935
|
border: 1px solid var(--pi-border-muted);
|
|
7505
|
-
font-size:
|
|
7936
|
+
font-size: 13px;
|
|
7506
7937
|
padding: 1px 6px;
|
|
7507
7938
|
overflow-wrap: anywhere;
|
|
7508
7939
|
}
|
|
@@ -7525,7 +7956,7 @@ var panelStyles = i`
|
|
|
7525
7956
|
|
|
7526
7957
|
.doorstop-section-title {
|
|
7527
7958
|
color: var(--pi-muted);
|
|
7528
|
-
font-size:
|
|
7959
|
+
font-size: 12px;
|
|
7529
7960
|
text-transform: uppercase;
|
|
7530
7961
|
letter-spacing: 0.03em;
|
|
7531
7962
|
margin: 0;
|
|
@@ -7568,9 +7999,32 @@ var panelStyles = i`
|
|
|
7568
7999
|
background: var(--pi-selection-bg);
|
|
7569
8000
|
}
|
|
7570
8001
|
|
|
8002
|
+
/* The per-link trash can (pointer-only, see {@link renderLinkOut}):
|
|
8003
|
+
right-aligned in the flex row, mirroring .doorstop-item-add. */
|
|
8004
|
+
.doorstop-link-remove {
|
|
8005
|
+
display: inline-flex;
|
|
8006
|
+
align-items: center;
|
|
8007
|
+
align-self: center;
|
|
8008
|
+
flex: 0 0 auto;
|
|
8009
|
+
margin-left: auto;
|
|
8010
|
+
color: var(--pi-muted);
|
|
8011
|
+
cursor: pointer;
|
|
8012
|
+
}
|
|
8013
|
+
|
|
8014
|
+
.doorstop-link-remove:hover {
|
|
8015
|
+
color: var(--pi-text);
|
|
8016
|
+
}
|
|
8017
|
+
|
|
7571
8018
|
.doorstop-fingerprint {
|
|
7572
8019
|
color: var(--pi-muted);
|
|
7573
|
-
font-size:
|
|
8020
|
+
font-size: 12px;
|
|
8021
|
+
/* The link row wraps, which could push the trash can onto a second
|
|
8022
|
+
line on narrow panes. Let the fingerprint shrink and ellipsize
|
|
8023
|
+
instead (min-width: 0 lifts the flex min-content floor). */
|
|
8024
|
+
min-width: 0;
|
|
8025
|
+
overflow: hidden;
|
|
8026
|
+
text-overflow: ellipsis;
|
|
8027
|
+
white-space: nowrap;
|
|
7574
8028
|
}
|
|
7575
8029
|
|
|
7576
8030
|
.doorstop-attributes {
|
|
@@ -7588,7 +8042,7 @@ var panelStyles = i`
|
|
|
7588
8042
|
|
|
7589
8043
|
.doorstop-attribute dt {
|
|
7590
8044
|
color: var(--pi-muted);
|
|
7591
|
-
font-size:
|
|
8045
|
+
font-size: 13px;
|
|
7592
8046
|
}
|
|
7593
8047
|
|
|
7594
8048
|
.doorstop-attribute dd {
|
|
@@ -7629,10 +8083,14 @@ var panelStyles = i`
|
|
|
7629
8083
|
|
|
7630
8084
|
.doorstop-finding-severity {
|
|
7631
8085
|
flex: 0 0 auto;
|
|
8086
|
+
/* Strict parity: pi-web keeps compact chips at 11px. */
|
|
7632
8087
|
font-size: 11px;
|
|
7633
8088
|
font-weight: 600;
|
|
7634
8089
|
}
|
|
8090
|
+
`;
|
|
7635
8091
|
|
|
8092
|
+
// src/browser/styles/item-action-palette.ts
|
|
8093
|
+
var itemActionPaletteStyles = i`
|
|
7636
8094
|
/* --- item action palette (region 4), sized to the prompt footer --- */
|
|
7637
8095
|
.doorstop-action-palette {
|
|
7638
8096
|
flex: 0 0 auto;
|
|
@@ -7678,7 +8136,7 @@ var panelStyles = i`
|
|
|
7678
8136
|
|
|
7679
8137
|
.doorstop-op-error {
|
|
7680
8138
|
color: var(--pi-danger);
|
|
7681
|
-
font-size:
|
|
8139
|
+
font-size: 13px;
|
|
7682
8140
|
}
|
|
7683
8141
|
|
|
7684
8142
|
.doorstop-menu {
|
|
@@ -7688,7 +8146,7 @@ var panelStyles = i`
|
|
|
7688
8146
|
.doorstop-menu-items {
|
|
7689
8147
|
position: absolute;
|
|
7690
8148
|
right: 0;
|
|
7691
|
-
|
|
8149
|
+
bottom: calc(100% + 4px);
|
|
7692
8150
|
z-index: 3;
|
|
7693
8151
|
min-width: 180px;
|
|
7694
8152
|
display: grid;
|
|
@@ -7711,115 +8169,10 @@ var panelStyles = i`
|
|
|
7711
8169
|
.doorstop-menu-item:hover:not(:disabled) {
|
|
7712
8170
|
background: var(--pi-selection-bg);
|
|
7713
8171
|
}
|
|
8172
|
+
`;
|
|
7714
8173
|
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
flex: 1 1 auto;
|
|
7718
|
-
min-height: 0;
|
|
7719
|
-
overflow: auto;
|
|
7720
|
-
padding: 10px 12px;
|
|
7721
|
-
display: grid;
|
|
7722
|
-
gap: 8px;
|
|
7723
|
-
align-content: start;
|
|
7724
|
-
}
|
|
7725
|
-
|
|
7726
|
-
.doorstop-findings-head {
|
|
7727
|
-
display: flex;
|
|
7728
|
-
flex-wrap: wrap;
|
|
7729
|
-
align-items: baseline;
|
|
7730
|
-
gap: 6px 10px;
|
|
7731
|
-
}
|
|
7732
|
-
|
|
7733
|
-
.doorstop-findings-counts {
|
|
7734
|
-
font-size: 12px;
|
|
7735
|
-
font-weight: 600;
|
|
7736
|
-
}
|
|
7737
|
-
|
|
7738
|
-
.doorstop-findings-note {
|
|
7739
|
-
font-size: 11px;
|
|
7740
|
-
}
|
|
7741
|
-
|
|
7742
|
-
.doorstop-findings-list {
|
|
7743
|
-
display: grid;
|
|
7744
|
-
gap: 3px;
|
|
7745
|
-
}
|
|
7746
|
-
|
|
7747
|
-
.doorstop-finding-row {
|
|
7748
|
-
display: flex;
|
|
7749
|
-
flex-wrap: wrap;
|
|
7750
|
-
align-items: baseline;
|
|
7751
|
-
gap: 6px;
|
|
7752
|
-
border-radius: 6px;
|
|
7753
|
-
padding: 5px 8px;
|
|
7754
|
-
}
|
|
7755
|
-
|
|
7756
|
-
.doorstop-finding-row.doorstop-error {
|
|
7757
|
-
border: 1px solid var(--pi-danger);
|
|
7758
|
-
background: color-mix(in srgb, var(--pi-danger) 9%, transparent);
|
|
7759
|
-
}
|
|
7760
|
-
|
|
7761
|
-
.doorstop-finding-row.doorstop-warning {
|
|
7762
|
-
border: 1px solid var(--pi-warning-border);
|
|
7763
|
-
background: color-mix(in srgb, var(--pi-warning) 9%, transparent);
|
|
7764
|
-
}
|
|
7765
|
-
|
|
7766
|
-
.doorstop-finding-row.doorstop-info {
|
|
7767
|
-
border: 1px solid var(--pi-border-muted);
|
|
7768
|
-
color: var(--pi-muted);
|
|
7769
|
-
}
|
|
7770
|
-
|
|
7771
|
-
.doorstop-finding-row .doorstop-severity {
|
|
7772
|
-
background: var(--pi-border-muted);
|
|
7773
|
-
color: var(--pi-muted);
|
|
7774
|
-
}
|
|
7775
|
-
|
|
7776
|
-
.doorstop-finding-row.doorstop-error .doorstop-severity {
|
|
7777
|
-
background: var(--pi-danger);
|
|
7778
|
-
color: var(--pi-bg);
|
|
7779
|
-
}
|
|
7780
|
-
|
|
7781
|
-
.doorstop-finding-row.doorstop-warning .doorstop-severity {
|
|
7782
|
-
background: var(--pi-warning);
|
|
7783
|
-
color: var(--pi-bg);
|
|
7784
|
-
}
|
|
7785
|
-
|
|
7786
|
-
.doorstop-finding-uid {
|
|
7787
|
-
border: 0;
|
|
7788
|
-
border-radius: 5px;
|
|
7789
|
-
background: transparent;
|
|
7790
|
-
color: inherit;
|
|
7791
|
-
padding: 0;
|
|
7792
|
-
font-weight: 600;
|
|
7793
|
-
}
|
|
7794
|
-
|
|
7795
|
-
.doorstop-finding-uid:hover {
|
|
7796
|
-
text-decoration: underline;
|
|
7797
|
-
cursor: pointer;
|
|
7798
|
-
}
|
|
7799
|
-
|
|
7800
|
-
.doorstop-finding-message {
|
|
7801
|
-
min-width: 0;
|
|
7802
|
-
overflow-wrap: anywhere;
|
|
7803
|
-
}
|
|
7804
|
-
|
|
7805
|
-
/* --- empty states --- */
|
|
7806
|
-
.doorstop-empty {
|
|
7807
|
-
margin: 10px 12px;
|
|
7808
|
-
border: 1px dashed var(--pi-border-muted);
|
|
7809
|
-
border-radius: 8px;
|
|
7810
|
-
color: var(--pi-muted);
|
|
7811
|
-
padding: 12px;
|
|
7812
|
-
overflow: auto;
|
|
7813
|
-
}
|
|
7814
|
-
|
|
7815
|
-
.doorstop-empty p {
|
|
7816
|
-
margin: 0;
|
|
7817
|
-
}
|
|
7818
|
-
|
|
7819
|
-
.doorstop-empty .doorstop-muted {
|
|
7820
|
-
margin-top: 6px;
|
|
7821
|
-
}
|
|
7822
|
-
|
|
8174
|
+
// src/browser/styles/status-bar.ts
|
|
8175
|
+
var statusBarStyles = i`
|
|
7823
8176
|
/* --- status bar (region 5), sized to the host center status bar --- */
|
|
7824
8177
|
.doorstop-status-bar {
|
|
7825
8178
|
flex: 0 0 auto;
|
|
@@ -7837,7 +8190,7 @@ var panelStyles = i`
|
|
|
7837
8190
|
border-top: 1px solid var(--pi-border);
|
|
7838
8191
|
background: var(--pi-bg);
|
|
7839
8192
|
color: var(--pi-muted);
|
|
7840
|
-
font:
|
|
8193
|
+
font: 13px system-ui, sans-serif;
|
|
7841
8194
|
}
|
|
7842
8195
|
|
|
7843
8196
|
.doorstop-status-bar-row {
|
|
@@ -7864,7 +8217,7 @@ var panelStyles = i`
|
|
|
7864
8217
|
|
|
7865
8218
|
.doorstop-last-run-commit {
|
|
7866
8219
|
margin: 0 0 6px;
|
|
7867
|
-
font-size:
|
|
8220
|
+
font-size: 13px;
|
|
7868
8221
|
overflow-wrap: anywhere;
|
|
7869
8222
|
}
|
|
7870
8223
|
|
|
@@ -7872,6 +8225,7 @@ var panelStyles = i`
|
|
|
7872
8225
|
flex: 0 0 auto; /* the badge label keeps its full width */
|
|
7873
8226
|
border-radius: 999px;
|
|
7874
8227
|
padding: 0 7px;
|
|
8228
|
+
/* Strict parity: pi-web keeps compact chips at 11px. */
|
|
7875
8229
|
font-size: 11px;
|
|
7876
8230
|
white-space: nowrap;
|
|
7877
8231
|
}
|
|
@@ -7904,12 +8258,12 @@ var panelStyles = i`
|
|
|
7904
8258
|
text-overflow: ellipsis;
|
|
7905
8259
|
white-space: nowrap;
|
|
7906
8260
|
color: var(--pi-muted);
|
|
7907
|
-
font-size:
|
|
8261
|
+
font-size: 13px;
|
|
7908
8262
|
}
|
|
7909
8263
|
|
|
7910
8264
|
.doorstop-last-run-dismiss {
|
|
7911
8265
|
flex: 0 0 auto;
|
|
7912
|
-
/* Buttons do not inherit font; match the bar's
|
|
8266
|
+
/* Buttons do not inherit font; match the bar's 13px system-ui. */
|
|
7913
8267
|
font: inherit;
|
|
7914
8268
|
}
|
|
7915
8269
|
|
|
@@ -7919,7 +8273,7 @@ var panelStyles = i`
|
|
|
7919
8273
|
background: var(--pi-surface);
|
|
7920
8274
|
color: var(--pi-text);
|
|
7921
8275
|
padding: 6px 8px;
|
|
7922
|
-
font:
|
|
8276
|
+
font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
7923
8277
|
white-space: pre-wrap;
|
|
7924
8278
|
word-break: break-word;
|
|
7925
8279
|
overflow-wrap: anywhere;
|
|
@@ -7931,20 +8285,297 @@ var panelStyles = i`
|
|
|
7931
8285
|
.doorstop-last-run-notice {
|
|
7932
8286
|
margin: 4px 0 0;
|
|
7933
8287
|
color: var(--pi-muted);
|
|
7934
|
-
font-size:
|
|
8288
|
+
font-size: 12px;
|
|
7935
8289
|
}
|
|
7936
8290
|
`;
|
|
7937
8291
|
|
|
7938
|
-
// src/doorstop-panel-
|
|
8292
|
+
// src/browser/doorstop-panel-styles.ts
|
|
8293
|
+
var panelStyles = i`${baseStyles}${projectActionsStyles}${itemListStyles}${detailPaneStyles}${itemActionPaletteStyles}${findingsStyles}${statusBarStyles}`;
|
|
8294
|
+
|
|
8295
|
+
// node_modules/lit-html/directives/class-map.js
|
|
8296
|
+
var e7 = e4(class extends i6 {
|
|
8297
|
+
constructor(t5) {
|
|
8298
|
+
if (super(t5), t5.type !== t4.ATTRIBUTE || "class" !== t5.name || t5.strings?.length > 2) throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.");
|
|
8299
|
+
}
|
|
8300
|
+
render(t5) {
|
|
8301
|
+
return " " + Object.keys(t5).filter((s6) => t5[s6]).join(" ") + " ";
|
|
8302
|
+
}
|
|
8303
|
+
update(s6, [i8]) {
|
|
8304
|
+
if (void 0 === this.st) {
|
|
8305
|
+
this.st = /* @__PURE__ */ new Set(), void 0 !== s6.strings && (this.nt = new Set(s6.strings.join(" ").split(/\s/).filter((t5) => "" !== t5)));
|
|
8306
|
+
for (const t5 in i8) i8[t5] && !this.nt?.has(t5) && this.st.add(t5);
|
|
8307
|
+
return this.render(i8);
|
|
8308
|
+
}
|
|
8309
|
+
const r8 = s6.element.classList;
|
|
8310
|
+
for (const t5 of this.st) t5 in i8 || (r8.remove(t5), this.st.delete(t5));
|
|
8311
|
+
for (const t5 in i8) {
|
|
8312
|
+
const s7 = !!i8[t5];
|
|
8313
|
+
s7 === this.st.has(t5) || this.nt?.has(t5) || (s7 ? (r8.add(t5), this.st.add(t5)) : (r8.remove(t5), this.st.delete(t5)));
|
|
8314
|
+
}
|
|
8315
|
+
return E;
|
|
8316
|
+
}
|
|
8317
|
+
});
|
|
8318
|
+
|
|
8319
|
+
// src/browser/doorstop-panel-icons.ts
|
|
7939
8320
|
var doorstopIconSvg = w`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"/><rect width="8" height="4" x="8" y="3" rx="1"/><path d="m9 12 2 2 4-4"/></svg>`;
|
|
7940
8321
|
var refreshIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 4v6h6"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>`;
|
|
7941
8322
|
var validateIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></svg>`;
|
|
7942
8323
|
var publishIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5"/><path d="m5 12 7-7 7 7"/></svg>`;
|
|
7943
|
-
var gitStageIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><
|
|
7944
|
-
var gitUnstageIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><
|
|
8324
|
+
var gitStageIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 12h12"/><path d="M12 6v12"/></svg>`;
|
|
8325
|
+
var gitUnstageIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 12h12"/></svg>`;
|
|
7945
8326
|
var gitCommitIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>`;
|
|
8327
|
+
var insertPromptIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5"/><path d="m5 12 7-7 7 7"/></svg>`;
|
|
8328
|
+
var trashIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M10 11v6"/><path d="M14 11v6"/></svg>`;
|
|
8329
|
+
|
|
8330
|
+
// src/browser/sections/project-actions.ts
|
|
8331
|
+
function renderProjectActions(host) {
|
|
8332
|
+
const publishTarget = doorstopPublishTarget(host.result);
|
|
8333
|
+
const projectPath = host.context?.workspace.path ?? "";
|
|
8334
|
+
const projectLabel = projectPath === "" ? "Doorstop" : projectPath.split("/").filter(Boolean).pop() ?? projectPath;
|
|
8335
|
+
return b2`
|
|
8336
|
+
<section class="doorstop-project-actions">
|
|
8337
|
+
<strong class="doorstop-title" title=${projectPath === "" ? A : projectPath}>${doorstopIconSvg}${projectLabel}</strong>
|
|
8338
|
+
${renderViewToggle(host)}
|
|
8339
|
+
<div class="doorstop-toolbar-actions">
|
|
8340
|
+
${host.stale ? b2`<button type="button" class="doorstop-stale" title="Doorstop ran or files changed behind the panel — click to rescan" @click=${() => onRefreshClick(host)}>stale — refresh</button>` : A}
|
|
8341
|
+
${host.confirmSkipped ? b2`<span class="doorstop-muted doorstop-confirm-skipped" title="No confirmation dialog is available in this environment — publishing proceeded without one">confirmation skipped — publishing</span>` : A}
|
|
8342
|
+
<button type="button" class="doorstop-refresh" title="Re-read the workspace" @click=${() => onRefreshClick(host)}>${refreshIconSvg}Refresh</button>
|
|
8343
|
+
<button type="button" class="doorstop-validate" title="Run \`doorstop\` in the workspace (terminal when unpaired)" ?disabled=${host.runInProgress !== void 0} @click=${() => onValidateClick(host)}>${validateIconSvg}Run validation</button>
|
|
8344
|
+
<button type="button" class="doorstop-publish" title=${`Publish the tree to ${publishTarget}`} ?disabled=${host.runInProgress !== void 0} @click=${() => onPublishClick(host)}>${publishIconSvg}Publish HTML</button>
|
|
8345
|
+
${renderGitActions(host)}
|
|
8346
|
+
</div>
|
|
8347
|
+
</section>
|
|
8348
|
+
`;
|
|
8349
|
+
}
|
|
8350
|
+
function renderViewToggle(host) {
|
|
8351
|
+
return b2`
|
|
8352
|
+
<div class="doorstop-view-toggle" role="tablist" aria-label="Requirements panel view">
|
|
8353
|
+
<button
|
|
8354
|
+
type="button"
|
|
8355
|
+
role="tab"
|
|
8356
|
+
class=${e7({
|
|
8357
|
+
"doorstop-view-tab": true,
|
|
8358
|
+
"doorstop-view-items": true,
|
|
8359
|
+
"is-selected": host.view === "items"
|
|
8360
|
+
})}
|
|
8361
|
+
aria-selected=${host.view === "items" ? "true" : "false"}
|
|
8362
|
+
title="Item list and detail"
|
|
8363
|
+
@click=${() => {
|
|
8364
|
+
host.view = "items";
|
|
8365
|
+
}}
|
|
8366
|
+
>Items</button>
|
|
8367
|
+
<button
|
|
8368
|
+
type="button"
|
|
8369
|
+
role="tab"
|
|
8370
|
+
class=${e7({
|
|
8371
|
+
"doorstop-view-tab": true,
|
|
8372
|
+
"doorstop-view-findings": true,
|
|
8373
|
+
"is-selected": host.view === "findings"
|
|
8374
|
+
})}
|
|
8375
|
+
aria-selected=${host.view === "findings" ? "true" : "false"}
|
|
8376
|
+
title="Validation findings"
|
|
8377
|
+
@click=${() => {
|
|
8378
|
+
host.view = "findings";
|
|
8379
|
+
}}
|
|
8380
|
+
>Findings</button>
|
|
8381
|
+
</div>
|
|
8382
|
+
`;
|
|
8383
|
+
}
|
|
8384
|
+
function renderGitActions(host) {
|
|
8385
|
+
if (!host.backendActive()) return A;
|
|
8386
|
+
const hasDocuments = (host.result?.index.documents.length ?? 0) > 0;
|
|
8387
|
+
return b2`
|
|
8388
|
+
${renderGitStatus(host)}
|
|
8389
|
+
<button
|
|
8390
|
+
type="button"
|
|
8391
|
+
class="doorstop-git-stage"
|
|
8392
|
+
title="Stage all Doorstop-managed files (requirements, documents, configs)"
|
|
8393
|
+
?disabled=${host.runInProgress !== void 0 || !hasDocuments}
|
|
8394
|
+
@click=${() => onGitStageClick(host)}
|
|
8395
|
+
>${gitStageIconSvg}Stage all</button>
|
|
8396
|
+
<div class="doorstop-git-commit">
|
|
8397
|
+
<input
|
|
8398
|
+
type="text"
|
|
8399
|
+
class="doorstop-git-commit-input"
|
|
8400
|
+
placeholder="Commit message"
|
|
8401
|
+
aria-label="Git commit message"
|
|
8402
|
+
?disabled=${host.runInProgress !== void 0}
|
|
8403
|
+
${n5(host.gitCommitInputRef)}
|
|
8404
|
+
@input=${(event) => onGitCommitInput(host, event)}
|
|
8405
|
+
@keydown=${(event) => onGitCommitKeydown(host, event)}
|
|
8406
|
+
/>
|
|
8407
|
+
<button
|
|
8408
|
+
type="button"
|
|
8409
|
+
class="doorstop-git-commit-button"
|
|
8410
|
+
title="Commit the staged index with this message"
|
|
8411
|
+
?disabled=${host.runInProgress !== void 0 || host.gitCommitMessage.trim() === ""}
|
|
8412
|
+
@click=${() => onGitCommitClick(host)}
|
|
8413
|
+
>${gitCommitIconSvg}Commit</button>
|
|
8414
|
+
${host.gitActionError === void 0 ? A : b2`<span class="doorstop-op-error" role="alert">${host.gitActionError}</span>`}
|
|
8415
|
+
</div>
|
|
8416
|
+
`;
|
|
8417
|
+
}
|
|
8418
|
+
function renderGitStatus(host) {
|
|
8419
|
+
const view = host.gitStatusView;
|
|
8420
|
+
if (view === void 0) return A;
|
|
8421
|
+
const refresh = () => {
|
|
8422
|
+
void host.controller?.requestGitStatus();
|
|
8423
|
+
};
|
|
8424
|
+
switch (view.state) {
|
|
8425
|
+
case "loading":
|
|
8426
|
+
return b2`<button
|
|
8427
|
+
type="button"
|
|
8428
|
+
class="doorstop-git-status"
|
|
8429
|
+
title="Refreshing git status…"
|
|
8430
|
+
aria-busy=${host.gitStatusInFlight}
|
|
8431
|
+
@click=${refresh}
|
|
8432
|
+
><span class="doorstop-git-status-text">⎇ …</span></button>`;
|
|
8433
|
+
case "no-git":
|
|
8434
|
+
return b2`<button type="button" class="doorstop-git-status" title="Not a git repository — click to re-check" aria-busy=${host.gitStatusInFlight} @click=${refresh}><span class="doorstop-git-status-text">no git</span></button>`;
|
|
8435
|
+
case "error":
|
|
8436
|
+
return b2`<button
|
|
8437
|
+
type="button"
|
|
8438
|
+
class="doorstop-git-status doorstop-git-status-error"
|
|
8439
|
+
title=${`Git status unavailable \u2014 ${view.errorMessage ?? "request failed"} \u2014 click to retry`}
|
|
8440
|
+
aria-busy=${host.gitStatusInFlight}
|
|
8441
|
+
@click=${refresh}
|
|
8442
|
+
><span class="doorstop-git-status-text">git status error — retry</span></button>`;
|
|
8443
|
+
case "ready":
|
|
8444
|
+
return b2`<button type="button" class="doorstop-git-status" title="Git status — click to refresh" aria-busy=${host.gitStatusInFlight} @click=${refresh}><span class="doorstop-git-status-text">${gitStatusText(view.response)}</span></button>`;
|
|
8445
|
+
}
|
|
8446
|
+
}
|
|
8447
|
+
function onRefreshClick(host) {
|
|
8448
|
+
void host.controller?.invalidate();
|
|
8449
|
+
}
|
|
8450
|
+
function onValidateClick(host) {
|
|
8451
|
+
host.runDoorstop("validate", "Doorstop: validate", { op: "validate" }, "doorstop", true);
|
|
8452
|
+
}
|
|
8453
|
+
function onPublishClick(host) {
|
|
8454
|
+
const target = doorstopPublishTarget(host.result);
|
|
8455
|
+
if (typeof window.confirm === "function") {
|
|
8456
|
+
if (!window.confirm(`Publish the Doorstop tree as HTML to ${target} in the workspace terminal?`)) return;
|
|
8457
|
+
host.confirmSkipped = false;
|
|
8458
|
+
} else {
|
|
8459
|
+
host.confirmSkipped = true;
|
|
8460
|
+
}
|
|
8461
|
+
host.runDoorstop(
|
|
8462
|
+
"publish",
|
|
8463
|
+
"Doorstop: publish",
|
|
8464
|
+
{ op: "publish", target },
|
|
8465
|
+
doorstopPublishCommand(host.result, target),
|
|
8466
|
+
false
|
|
8467
|
+
);
|
|
8468
|
+
}
|
|
8469
|
+
function onGitStageClick(host) {
|
|
8470
|
+
const controller = host.controller;
|
|
8471
|
+
if (controller === void 0) return;
|
|
8472
|
+
if (controller.runInProgress !== void 0) return;
|
|
8473
|
+
const result = host.result;
|
|
8474
|
+
if (result === void 0) {
|
|
8475
|
+
host.gitActionError = "The workspace is not loaded \u2014 refresh first";
|
|
8476
|
+
return;
|
|
8477
|
+
}
|
|
8478
|
+
const paths = doorstopPaths(result);
|
|
8479
|
+
if (paths.length === 0) {
|
|
8480
|
+
host.gitActionError = "Nothing to stage \u2014 the workspace has no Doorstop-managed files";
|
|
8481
|
+
return;
|
|
8482
|
+
}
|
|
8483
|
+
host.gitActionError = void 0;
|
|
8484
|
+
void controller.runGitStage(paths);
|
|
8485
|
+
}
|
|
8486
|
+
function onGitCommitInput(host, event) {
|
|
8487
|
+
host.gitCommitMessage = event.target.value;
|
|
8488
|
+
}
|
|
8489
|
+
function onGitCommitClick(host) {
|
|
8490
|
+
gitCommitSubmit(host);
|
|
8491
|
+
}
|
|
8492
|
+
function onGitCommitKeydown(host, event) {
|
|
8493
|
+
const input = host.gitCommitInputRef.value;
|
|
8494
|
+
if (input === void 0) return;
|
|
8495
|
+
if (event.key === "Enter") {
|
|
8496
|
+
event.preventDefault();
|
|
8497
|
+
gitCommitSubmit(host);
|
|
8498
|
+
} else if (event.key === "Escape") {
|
|
8499
|
+
input.value = "";
|
|
8500
|
+
host.gitCommitMessage = "";
|
|
8501
|
+
host.gitActionError = void 0;
|
|
8502
|
+
}
|
|
8503
|
+
}
|
|
8504
|
+
function gitCommitSubmit(host) {
|
|
8505
|
+
const controller = host.controller;
|
|
8506
|
+
if (controller === void 0) return;
|
|
8507
|
+
if (controller.runInProgress !== void 0) return;
|
|
8508
|
+
const input = host.gitCommitInputRef.value;
|
|
8509
|
+
const message = input?.value.trim() ?? "";
|
|
8510
|
+
if (message === "") {
|
|
8511
|
+
host.gitActionError = "Enter a commit message";
|
|
8512
|
+
input?.focus();
|
|
8513
|
+
return;
|
|
8514
|
+
}
|
|
8515
|
+
host.gitActionError = void 0;
|
|
8516
|
+
void controller.runGitCommit(message).then(() => {
|
|
8517
|
+
const lastRun = controller.lastRun;
|
|
8518
|
+
if (lastRun?.op === "git-commit" && lastRun.status === "ok" && input !== void 0) {
|
|
8519
|
+
input.value = "";
|
|
8520
|
+
host.gitCommitMessage = "";
|
|
8521
|
+
}
|
|
8522
|
+
});
|
|
8523
|
+
}
|
|
7946
8524
|
|
|
7947
|
-
//
|
|
8525
|
+
// node_modules/lit-html/directives/repeat.js
|
|
8526
|
+
var u4 = (e8, s6, t5) => {
|
|
8527
|
+
const r8 = /* @__PURE__ */ new Map();
|
|
8528
|
+
for (let l3 = s6; l3 <= t5; l3++) r8.set(e8[l3], l3);
|
|
8529
|
+
return r8;
|
|
8530
|
+
};
|
|
8531
|
+
var c5 = e4(class extends i6 {
|
|
8532
|
+
constructor(e8) {
|
|
8533
|
+
if (super(e8), e8.type !== t4.CHILD) throw Error("repeat() can only be used in text expressions");
|
|
8534
|
+
}
|
|
8535
|
+
dt(e8, s6, t5) {
|
|
8536
|
+
let r8;
|
|
8537
|
+
void 0 === t5 ? t5 = s6 : void 0 !== s6 && (r8 = s6);
|
|
8538
|
+
const l3 = [], o8 = [];
|
|
8539
|
+
let i8 = 0;
|
|
8540
|
+
for (const s7 of e8) l3[i8] = r8 ? r8(s7, i8) : i8, o8[i8] = t5(s7, i8), i8++;
|
|
8541
|
+
return { values: o8, keys: l3 };
|
|
8542
|
+
}
|
|
8543
|
+
render(e8, s6, t5) {
|
|
8544
|
+
return this.dt(e8, s6, t5).values;
|
|
8545
|
+
}
|
|
8546
|
+
update(s6, [t5, r8, c6]) {
|
|
8547
|
+
const d3 = M2(s6), { values: p4, keys: a3 } = this.dt(t5, r8, c6);
|
|
8548
|
+
if (!Array.isArray(d3)) return this.ut = a3, p4;
|
|
8549
|
+
const h6 = this.ut ??= [], v3 = [];
|
|
8550
|
+
let m3, y3, x2 = 0, j2 = d3.length - 1, k2 = 0, w2 = p4.length - 1;
|
|
8551
|
+
for (; x2 <= j2 && k2 <= w2; ) if (null === d3[x2]) x2++;
|
|
8552
|
+
else if (null === d3[j2]) j2--;
|
|
8553
|
+
else if (h6[x2] === a3[k2]) v3[k2] = u3(d3[x2], p4[k2]), x2++, k2++;
|
|
8554
|
+
else if (h6[j2] === a3[w2]) v3[w2] = u3(d3[j2], p4[w2]), j2--, w2--;
|
|
8555
|
+
else if (h6[x2] === a3[w2]) v3[w2] = u3(d3[x2], p4[w2]), v2(s6, v3[w2 + 1], d3[x2]), x2++, w2--;
|
|
8556
|
+
else if (h6[j2] === a3[k2]) v3[k2] = u3(d3[j2], p4[k2]), v2(s6, d3[x2], d3[j2]), j2--, k2++;
|
|
8557
|
+
else if (void 0 === m3 && (m3 = u4(a3, k2, w2), y3 = u4(h6, x2, j2)), m3.has(h6[x2])) if (m3.has(h6[j2])) {
|
|
8558
|
+
const e8 = y3.get(a3[k2]), t6 = void 0 !== e8 ? d3[e8] : null;
|
|
8559
|
+
if (null === t6) {
|
|
8560
|
+
const e9 = v2(s6, d3[x2]);
|
|
8561
|
+
u3(e9, p4[k2]), v3[k2] = e9;
|
|
8562
|
+
} else v3[k2] = u3(t6, p4[k2]), v2(s6, d3[x2], t6), d3[e8] = null;
|
|
8563
|
+
k2++;
|
|
8564
|
+
} else h3(d3[j2]), j2--;
|
|
8565
|
+
else h3(d3[x2]), x2++;
|
|
8566
|
+
for (; k2 <= w2; ) {
|
|
8567
|
+
const e8 = v2(s6, v3[w2 + 1]);
|
|
8568
|
+
u3(e8, p4[k2]), v3[k2++] = e8;
|
|
8569
|
+
}
|
|
8570
|
+
for (; x2 <= j2; ) {
|
|
8571
|
+
const e8 = d3[x2++];
|
|
8572
|
+
null !== e8 && h3(e8);
|
|
8573
|
+
}
|
|
8574
|
+
return this.ut = a3, p3(s6, v3), E;
|
|
8575
|
+
}
|
|
8576
|
+
});
|
|
8577
|
+
|
|
8578
|
+
// src/browser/doorstop-prompts.ts
|
|
7948
8579
|
function andJoin(values) {
|
|
7949
8580
|
if (values.length === 0) return "";
|
|
7950
8581
|
if (values.length === 1) return values[0] ?? "";
|
|
@@ -7975,42 +8606,779 @@ function fixSuspectLinksPrompt(item, parents) {
|
|
|
7975
8606
|
`fixSuspectLinksPrompt parents must be links of ${item.uid}: ${andJoin(unlinked.map((parent) => parent.uid))} ${unlinked.length === 1 ? "is" : "are"} not among ${item.uid}'s links`
|
|
7976
8607
|
);
|
|
7977
8608
|
}
|
|
7978
|
-
const parentItems = andJoin(
|
|
7979
|
-
parents.map((parent) => `${parent.uid} (\`${parent.path}\`)`)
|
|
8609
|
+
const parentItems = andJoin(
|
|
8610
|
+
parents.map((parent) => `${parent.uid} (\`${parent.path}\`)`)
|
|
8611
|
+
);
|
|
8612
|
+
if (parents.length === 1) {
|
|
8613
|
+
return `${item.uid} has a suspect link to ${parents[0]?.uid}: the recorded fingerprint no longer matches because that parent changed after ${item.uid} was last reviewed. Read \`${item.path}\` and ${parentItems}, compare the two texts, and propose updated text for ${item.uid} that still traces correctly to the changed parent. Then run \`doorstop clear ${item.uid}\` in the workspace root to re-record the parent's current fingerprint, and run \`doorstop\` to confirm the suspect link is gone.`;
|
|
8614
|
+
}
|
|
8615
|
+
return `${item.uid} has suspect links to ${andJoin(parents.map((parent) => parent.uid))}: the recorded fingerprints no longer match because those parents changed after ${item.uid} was last reviewed. Read \`${item.path}\` and the changed parents (${parentItems}), compare the texts, and propose updated text for ${item.uid} that still traces correctly to every changed parent. Then run \`doorstop clear ${item.uid}\` in the workspace root to re-record the parents' current fingerprints, and run \`doorstop\` to confirm no suspect link remains.`;
|
|
8616
|
+
}
|
|
8617
|
+
function draftChildRequirementPrompt(parent, childPrefix) {
|
|
8618
|
+
if (childPrefix.trim() === "") {
|
|
8619
|
+
throw new TypeError(
|
|
8620
|
+
"draftChildRequirementPrompt requires a non-empty child document prefix: `doorstop add` names its document by prefix"
|
|
8621
|
+
);
|
|
8622
|
+
}
|
|
8623
|
+
return `Draft a new ${childPrefix} requirement that traces to ${parent.uid} (\`${parent.path}\`). Run \`doorstop add ${childPrefix}\` in the workspace root and note the UID it prints for the new item. Link the new item to its parent with \`doorstop link <new-uid> ${parent.uid}\`, substituting the printed UID for <new-uid>. Then fill in the new item's text so it elaborates ${parent.uid} at ${childPrefix} level, referencing any relevant files (prefer \`doorstop edit <new-uid>\` when available, otherwise edit the item file the add command created \u2014 doorstop normalizes formatting on the next command run). Finally run \`doorstop\` in the workspace root and confirm the new item introduces no WARNING or ERROR before judging whether it is ready for \`doorstop review <new-uid>\`.`;
|
|
8624
|
+
}
|
|
8625
|
+
function referencePaths(item) {
|
|
8626
|
+
const paths = item.references?.map((reference) => reference.path) ?? [];
|
|
8627
|
+
return item.ref === "" ? paths : [item.ref, ...paths];
|
|
8628
|
+
}
|
|
8629
|
+
function reviewReadinessPrompt(item, children = []) {
|
|
8630
|
+
const parents = andJoin(item.links.map((link) => link.uid));
|
|
8631
|
+
const references = referencePaths(item);
|
|
8632
|
+
const childLinks = andJoin(
|
|
8633
|
+
children.map((child) => `${child.uid} (\`${child.path}\`)`)
|
|
8634
|
+
);
|
|
8635
|
+
const facts = [
|
|
8636
|
+
item.text.trim() === "" ? "its text is empty (that alone blocks review)" : "its text is filled in",
|
|
8637
|
+
item.links.length === 0 ? "it declares no parent links" : `its recorded parent links (${parents}) are not suspect or missing a fingerprint`,
|
|
8638
|
+
children.length === 0 ? "its child links are left to the `doorstop` run below" : `its child links are declared (${childLinks})`,
|
|
8639
|
+
references.length === 0 ? "it has no file references to verify" : `its file references exist (${references.map((path) => `\`${path}\``).join(", ")})`
|
|
8640
|
+
];
|
|
8641
|
+
return `Check whether ${item.uid} is ready for \`doorstop review ${item.uid}\`. Read \`${item.path}\` and verify that ${facts.join("; ")}. Then run \`doorstop\` in the workspace root and weigh any WARNING or ERROR it reports about ${item.uid} (for example a requirement that should link to ${item.uid} but does not). Report a verdict, and if ${item.uid} is not ready, the exact fixes to make first.`;
|
|
8642
|
+
}
|
|
8643
|
+
function composeReviewCommand(item) {
|
|
8644
|
+
return `/opendoor-review ${item.uid}
|
|
8645
|
+
Review ${item.uid} (\`${item.path}\`) against its child links and references; run \`doorstop\` validation and, when it is ready, \`doorstop review ${item.uid}\`.`;
|
|
8646
|
+
}
|
|
8647
|
+
function setupProjectPrompt() {
|
|
8648
|
+
return `Set up a Doorstop requirements project in this workspace. Use the \`ask_user\` tool to ask the user two questions before creating anything: what the first requirements document should be called (default: REQ) and whether item text should be shown in the opendoor panel (default: no). Run \`git init\` if the workspace is not a git repository. Then run \`doorstop create <chosen UID prefix> ./<prefix-derived folder: REQ to reqs, URD to urds>\` in the workspace root and \`doorstop\` to confirm it validates. If \`.pi-web/opendoor.json\` does not exist, create it with {"version": 1} plus "showAdditionalAttribute": ["text"] when the user opted into showing text; leave publish.target, discovery.excludedDirectories, and commitAfterReview at their defaults. Commit the created \`.doorstop.yml\`, requirements tree, and settings file.`;
|
|
8649
|
+
}
|
|
8650
|
+
|
|
8651
|
+
// node_modules/lit-html/directives/keyed.js
|
|
8652
|
+
var i7 = e4(class extends i6 {
|
|
8653
|
+
constructor() {
|
|
8654
|
+
super(...arguments), this.key = A;
|
|
8655
|
+
}
|
|
8656
|
+
render(r8, t5) {
|
|
8657
|
+
return this.key = r8, t5;
|
|
8658
|
+
}
|
|
8659
|
+
update(r8, [t5, e8]) {
|
|
8660
|
+
return t5 !== this.key && (p3(r8), this.key = t5), e8;
|
|
8661
|
+
}
|
|
8662
|
+
});
|
|
8663
|
+
|
|
8664
|
+
// src/browser/sections/detail-pane.ts
|
|
8665
|
+
function renderDetail(host, result) {
|
|
8666
|
+
const selectedUid = host.selectedUid;
|
|
8667
|
+
if (selectedUid === void 0) {
|
|
8668
|
+
return b2`<p class="doorstop-muted doorstop-standalone">Select an item in the list to inspect its details.</p>`;
|
|
8669
|
+
}
|
|
8670
|
+
const item = result.index.byUid.get(selectedUid);
|
|
8671
|
+
if (item === void 0) {
|
|
8672
|
+
return b2`<p class="doorstop-muted doorstop-standalone">This item is no longer available — refresh the panel.</p>`;
|
|
8673
|
+
}
|
|
8674
|
+
const children = result.index.childrenByUid.get(item.uid) ?? [];
|
|
8675
|
+
const findings = result.index.findings.filter((finding) => finding.uid === item.uid);
|
|
8676
|
+
return b2`
|
|
8677
|
+
<section class="doorstop-detail" aria-label="Item details">
|
|
8678
|
+
<div class="doorstop-detail-head">
|
|
8679
|
+
<code class="doorstop-detail-uid">${item.uid}</code>
|
|
8680
|
+
<span class="doorstop-level">level ${item.level}</span>
|
|
8681
|
+
${item.header === void 0 ? A : b2`<h3>${item.header}</h3>`}
|
|
8682
|
+
</div>
|
|
8683
|
+
${renderFlags(host, item)}
|
|
8684
|
+
<div class="doorstop-state-chip-row">
|
|
8685
|
+
${item.stateKeys.map((key) => host.renderStateChip(key))}
|
|
8686
|
+
</div>
|
|
8687
|
+
<h4 class="doorstop-section-title">Text</h4>
|
|
8688
|
+
<p class=${item.text === "" ? "doorstop-text doorstop-text-empty" : "doorstop-text"}>${item.text === "" ? "empty" : item.text}</p>
|
|
8689
|
+
${renderChangesSinceReview(host, item)}
|
|
8690
|
+
<h4 class="doorstop-section-title">Parent links</h4>
|
|
8691
|
+
${renderLinksOut(host, item, result.index)}
|
|
8692
|
+
<h4 class="doorstop-section-title">Child links</h4>
|
|
8693
|
+
${renderLinksIn(host, item, result.index, children)}
|
|
8694
|
+
<h4 class="doorstop-section-title">References</h4>
|
|
8695
|
+
${renderReferences(host, item, result.index)}
|
|
8696
|
+
${Object.entries(item.attributes).length === 0 ? A : b2`
|
|
8697
|
+
<h4 class="doorstop-section-title">Extended attributes</h4>
|
|
8698
|
+
<dl class="doorstop-attributes">
|
|
8699
|
+
${Object.entries(item.attributes).map(
|
|
8700
|
+
([key, value]) => b2`<div class="doorstop-attribute"><dt>${key}</dt><dd><code>${jsonishText(value)}</code></dd></div>`
|
|
8701
|
+
)}
|
|
8702
|
+
</dl>
|
|
8703
|
+
`}
|
|
8704
|
+
<h4 class="doorstop-section-title">Findings</h4>
|
|
8705
|
+
${findings.length === 0 ? b2`<p class="doorstop-muted">No local findings for this item.</p>` : b2`<div class="doorstop-findings" aria-label="Findings for this item">
|
|
8706
|
+
${findings.map((finding) => {
|
|
8707
|
+
const kind = finding.severity === "error" ? "doorstop-error" : finding.severity === "warning" ? "doorstop-warning" : "doorstop-info";
|
|
8708
|
+
return b2`<div class=${`doorstop-finding ${kind}`}><span class="doorstop-finding-severity">${finding.severity}</span><span>${finding.message}</span></div>`;
|
|
8709
|
+
})}
|
|
8710
|
+
</div>`}
|
|
8711
|
+
</section>
|
|
8712
|
+
`;
|
|
8713
|
+
}
|
|
8714
|
+
function renderFlags(host, item) {
|
|
8715
|
+
return b2`
|
|
8716
|
+
<div class="doorstop-flags" aria-label="Item flags">
|
|
8717
|
+
<span class=${e7({ "doorstop-flag": true, "is-inactive": !item.active })}>
|
|
8718
|
+
${item.active ? "active" : "inactive"}
|
|
8719
|
+
</span>
|
|
8720
|
+
<span class="doorstop-flag">${item.normative ? "normative" : "non-normative"}</span>
|
|
8721
|
+
<span class="doorstop-flag">${item.derived ? "derived" : "non-derived"}</span>
|
|
8722
|
+
</div>
|
|
8723
|
+
`;
|
|
8724
|
+
}
|
|
8725
|
+
function renderLinksOut(host, item, index) {
|
|
8726
|
+
if (item.links.length === 0) return b2`<p class="doorstop-muted">No parent links.</p>`;
|
|
8727
|
+
return b2`
|
|
8728
|
+
<div class="doorstop-links" aria-label="Parent links">
|
|
8729
|
+
${item.links.map((link) => renderLinkOut(host, item, link, index))}
|
|
8730
|
+
</div>
|
|
8731
|
+
`;
|
|
8732
|
+
}
|
|
8733
|
+
function renderLinkOut(host, item, link, index) {
|
|
8734
|
+
const remove = b2`
|
|
8735
|
+
<span
|
|
8736
|
+
class="doorstop-link-remove"
|
|
8737
|
+
aria-hidden="true"
|
|
8738
|
+
title=${`doorstop unlink ${item.uid} ${link.uid}`}
|
|
8739
|
+
@click=${(event) => {
|
|
8740
|
+
event.stopPropagation();
|
|
8741
|
+
unlinkLink(host, item, link.uid);
|
|
8742
|
+
}}
|
|
8743
|
+
>${trashIconSvg}</span>
|
|
8744
|
+
`;
|
|
8745
|
+
const target = index.byUid.get(link.uid);
|
|
8746
|
+
if (target === void 0) {
|
|
8747
|
+
return b2`
|
|
8748
|
+
<div class="doorstop-link-row">
|
|
8749
|
+
<code>${link.uid}</code>
|
|
8750
|
+
<span class="doorstop-chip doorstop-chip-danger">unknown</span>
|
|
8751
|
+
<span class="doorstop-fingerprint">not in the index — ${link.fingerprint === null ? "no recorded fingerprint" : `recorded ${shortFingerprint(link.fingerprint)}`}</span>
|
|
8752
|
+
${remove}
|
|
8753
|
+
</div>
|
|
8754
|
+
`;
|
|
8755
|
+
}
|
|
8756
|
+
const current = computeItemStamp(target, documentConfigFor(index, target), false);
|
|
8757
|
+
const suspect = link.fingerprint !== null && link.fingerprint !== current;
|
|
8758
|
+
return b2`
|
|
8759
|
+
<button
|
|
8760
|
+
type="button"
|
|
8761
|
+
class="doorstop-link-row"
|
|
8762
|
+
data-uid=${link.uid}
|
|
8763
|
+
title=${`Open ${link.uid}`}
|
|
8764
|
+
@click=${() => {
|
|
8765
|
+
host.controller?.selectUid(link.uid);
|
|
8766
|
+
}}
|
|
8767
|
+
>
|
|
8768
|
+
<code>${link.uid}</code>
|
|
8769
|
+
<span class=${suspect ? "doorstop-chip doorstop-chip-danger" : "doorstop-chip doorstop-chip-ok"}>${suspect ? "suspect" : "ok"}</span>
|
|
8770
|
+
<span class="doorstop-fingerprint">recorded ${shortFingerprint(link.fingerprint)} · current ${shortFingerprint(current)}</span>
|
|
8771
|
+
${remove}
|
|
8772
|
+
</button>
|
|
8773
|
+
`;
|
|
8774
|
+
}
|
|
8775
|
+
function renderLinksIn(host, item, index, children) {
|
|
8776
|
+
if (children.length === 0) {
|
|
8777
|
+
return b2`<p class="doorstop-muted">No child links.</p>`;
|
|
8778
|
+
}
|
|
8779
|
+
return b2`
|
|
8780
|
+
<div class="doorstop-links" aria-label="Child links">
|
|
8781
|
+
${children.map(
|
|
8782
|
+
(child) => b2`<button type="button" class="doorstop-link-row" data-uid=${child.uid} title=${`Open ${child.uid}`} @click=${() => {
|
|
8783
|
+
host.controller?.selectUid(child.uid);
|
|
8784
|
+
}}>
|
|
8785
|
+
<code>${child.uid}</code>
|
|
8786
|
+
<span class="doorstop-muted">${child.level}</span>
|
|
8787
|
+
</button>`
|
|
8788
|
+
)}
|
|
8789
|
+
</div>
|
|
8790
|
+
`;
|
|
8791
|
+
}
|
|
8792
|
+
function renderReferences(host, item, index) {
|
|
8793
|
+
const paths = [];
|
|
8794
|
+
if (item.ref !== "") paths.push(item.ref);
|
|
8795
|
+
if (item.references !== void 0) {
|
|
8796
|
+
for (const reference of item.references) paths.push(reference.path);
|
|
8797
|
+
}
|
|
8798
|
+
if (paths.length === 0) return b2`<p class="doorstop-muted">No file references.</p>`;
|
|
8799
|
+
return b2`
|
|
8800
|
+
<div class="doorstop-links" aria-label="File references">
|
|
8801
|
+
${paths.map((path) => {
|
|
8802
|
+
const found = index.knownFilePaths.has(path);
|
|
8803
|
+
return b2`
|
|
8804
|
+
<div class="doorstop-link-row">
|
|
8805
|
+
<code>${path}</code>
|
|
8806
|
+
${found ? A : b2`<span class="doorstop-chip doorstop-chip-danger">not found</span>`}
|
|
8807
|
+
</div>
|
|
8808
|
+
`;
|
|
8809
|
+
})}
|
|
8810
|
+
</div>
|
|
8811
|
+
`;
|
|
8812
|
+
}
|
|
8813
|
+
function renderChangesSinceReview(host, item) {
|
|
8814
|
+
if (!item.stateKeys.includes("unreviewed") || item.reviewed === null) return A;
|
|
8815
|
+
if (!host.backendActive()) return A;
|
|
8816
|
+
const view = host.controller?.baselineViewFor(item);
|
|
8817
|
+
return i7(
|
|
8818
|
+
item.uid,
|
|
8819
|
+
b2`
|
|
8820
|
+
<details class="doorstop-changes" @toggle=${(event) => onChangesToggle(host, event)}>
|
|
8821
|
+
<summary class="doorstop-changes-summary">Changes since review</summary>
|
|
8822
|
+
${view === void 0 || view.state === "loading" ? b2`<p class="doorstop-muted doorstop-changes-notice">Loading baseline…</p>` : renderBaselineView(host, view)}
|
|
8823
|
+
</details>
|
|
8824
|
+
`
|
|
8825
|
+
);
|
|
8826
|
+
}
|
|
8827
|
+
function renderBaselineView(host, view) {
|
|
8828
|
+
switch (view.state) {
|
|
8829
|
+
case "loading":
|
|
8830
|
+
return b2`<p class="doorstop-muted doorstop-changes-notice">Loading baseline…</p>`;
|
|
8831
|
+
case "no-git":
|
|
8832
|
+
return b2`<p class="doorstop-muted doorstop-changes-notice">No git history — previous version unavailable</p>`;
|
|
8833
|
+
case "no-match":
|
|
8834
|
+
return b2`<p class="doorstop-muted doorstop-changes-notice">Could not locate the reviewed version (history may have been rewritten)</p>`;
|
|
8835
|
+
case "error":
|
|
8836
|
+
return b2`<p class="doorstop-muted doorstop-changes-notice">Baseline unavailable — ${view.errorMessage ?? "request failed"}</p>`;
|
|
8837
|
+
case "ready": {
|
|
8838
|
+
const diff = view.diff;
|
|
8839
|
+
if (diff === void 0) return b2`<p class="doorstop-muted doorstop-changes-notice">No changes found.</p>`;
|
|
8840
|
+
return renderBaselineDiff(host, view, diff);
|
|
8841
|
+
}
|
|
8842
|
+
}
|
|
8843
|
+
}
|
|
8844
|
+
function renderBaselineDiff(host, view, diff) {
|
|
8845
|
+
return b2`
|
|
8846
|
+
${view.source === void 0 ? A : b2`<p class="doorstop-muted doorstop-changes-source">matched via ${view.source === "review-commit" ? "review commit" : "history walk"}</p>`}
|
|
8847
|
+
${diff.text === void 0 ? b2`<p class="doorstop-muted doorstop-changes-notice">Text changed — too large to render a line diff.</p>` : b2`<div class="doorstop-diff-lines" role="list" aria-label="Text changes since review">
|
|
8848
|
+
${diff.text.map((line) => renderDiffLine(host, line))}
|
|
8849
|
+
</div>`}
|
|
8850
|
+
${diff.ref === void 0 ? A : renderFieldChange(host, "ref", diff.ref.before, diff.ref.after)}
|
|
8851
|
+
${diff.references === void 0 ? A : renderFieldChange(
|
|
8852
|
+
host,
|
|
8853
|
+
"references",
|
|
8854
|
+
referenceListText(diff.references.before),
|
|
8855
|
+
referenceListText(diff.references.after)
|
|
8856
|
+
)}
|
|
8857
|
+
${diff.linksAdded.length === 0 && diff.linksRemoved.length === 0 ? A : b2`<div class="doorstop-field-change">
|
|
8858
|
+
<span class="doorstop-field-name">links</span>
|
|
8859
|
+
${diff.linksAdded.map((uid) => b2`<code class="doorstop-field-chip doorstop-field-chip-after">+ ${uid}</code>`)}
|
|
8860
|
+
${diff.linksRemoved.map((uid) => b2`<code class="doorstop-field-chip doorstop-field-chip-before">− ${uid}</code>`)}
|
|
8861
|
+
</div>`}
|
|
8862
|
+
${diff.extended.map((change) => renderFieldChange(host, change.name, change.before, change.after))}
|
|
8863
|
+
`;
|
|
8864
|
+
}
|
|
8865
|
+
function renderDiffLine(host, line) {
|
|
8866
|
+
const mark = line.kind === "added" ? "+" : line.kind === "removed" ? "\u2212" : "";
|
|
8867
|
+
return b2`
|
|
8868
|
+
<div class=${`doorstop-diff-line is-${line.kind}`} role="listitem">
|
|
8869
|
+
<span class="doorstop-diff-mark">${mark}</span>
|
|
8870
|
+
<span class="doorstop-diff-text">${line.text === "" ? "\xA0" : line.text}</span>
|
|
8871
|
+
</div>
|
|
8872
|
+
`;
|
|
8873
|
+
}
|
|
8874
|
+
function renderFieldChange(host, label, before, after) {
|
|
8875
|
+
return b2`
|
|
8876
|
+
<div class="doorstop-field-change">
|
|
8877
|
+
<span class="doorstop-field-name">${label}</span>
|
|
8878
|
+
<code class="doorstop-field-chip doorstop-field-chip-before">${diffValueText(before)}</code>
|
|
8879
|
+
<span class="doorstop-field-arrow">→</span>
|
|
8880
|
+
<code class="doorstop-field-chip doorstop-field-chip-after">${diffValueText(after)}</code>
|
|
8881
|
+
</div>
|
|
8882
|
+
`;
|
|
8883
|
+
}
|
|
8884
|
+
function onChangesToggle(host, event) {
|
|
8885
|
+
const details = event.currentTarget;
|
|
8886
|
+
if (!(details instanceof HTMLDetailsElement) || !details.open) return;
|
|
8887
|
+
const item = host.selectedItem();
|
|
8888
|
+
if (item === void 0) return;
|
|
8889
|
+
void host.controller?.requestBaseline(item);
|
|
8890
|
+
}
|
|
8891
|
+
function unlinkLink(host, item, target) {
|
|
8892
|
+
const controller = host.controller;
|
|
8893
|
+
if (controller === void 0 || controller.runInProgress !== void 0) return;
|
|
8894
|
+
host.runDoorstop(
|
|
8895
|
+
"unlink",
|
|
8896
|
+
`Doorstop: unlink ${item.uid}`,
|
|
8897
|
+
{ op: "unlink", uid: item.uid, target },
|
|
8898
|
+
`doorstop unlink ${item.uid} ${target}`,
|
|
8899
|
+
false
|
|
8900
|
+
);
|
|
8901
|
+
}
|
|
8902
|
+
|
|
8903
|
+
// src/browser/sections/item-list.ts
|
|
8904
|
+
var DEFAULT_STATE_KEY = "normative";
|
|
8905
|
+
function renderListFilters(host, result) {
|
|
8906
|
+
return b2`
|
|
8907
|
+
<section class="doorstop-list-filters">
|
|
8908
|
+
<div class="doorstop-docs" role="list" aria-label="Doorstop documents">
|
|
8909
|
+
${renderDocumentChip(host, void 0, result)}
|
|
8910
|
+
${result.index.documents.map((document2) => renderDocumentChip(host, document2, result))}
|
|
8911
|
+
</div>
|
|
8912
|
+
<select class="doorstop-state-filter" aria-label="Filter by state" @change=${(event) => onStateFilterChange(host, event)}>
|
|
8913
|
+
<option value="" .selected=${host.stateFilter === void 0}>All states</option>
|
|
8914
|
+
${Object.entries(STATE_CHIP_LABELS).map(
|
|
8915
|
+
([key, label]) => b2`<option value=${key} .selected=${host.stateFilter === key}>${label}</option>`
|
|
8916
|
+
)}
|
|
8917
|
+
</select>
|
|
8918
|
+
<input
|
|
8919
|
+
class="doorstop-search"
|
|
8920
|
+
type="search"
|
|
8921
|
+
aria-label="Search items"
|
|
8922
|
+
placeholder="Search UID or text"
|
|
8923
|
+
.value=${host.search}
|
|
8924
|
+
@input=${(event) => onSearchInput(host, event)}
|
|
8925
|
+
/>
|
|
8926
|
+
</section>
|
|
8927
|
+
`;
|
|
8928
|
+
}
|
|
8929
|
+
function renderDocumentChip(host, document2, result) {
|
|
8930
|
+
const prefix = document2?.prefix ?? ALL_DOCUMENTS_LABEL;
|
|
8931
|
+
const count = document2 === void 0 ? result.index.items.length : result.index.items.filter((item) => item.documentPrefix === document2.prefix).length;
|
|
8932
|
+
const dots = document2 === void 0 ? [] : documentStateDots(document2, result.index.items);
|
|
8933
|
+
const selected = document2 === void 0 ? host.selectedDocumentPrefix === void 0 || host.selectedDocumentPrefix === "" : host.selectedDocumentPrefix === document2.prefix;
|
|
8934
|
+
return b2`
|
|
8935
|
+
<button
|
|
8936
|
+
type="button"
|
|
8937
|
+
role="listitem"
|
|
8938
|
+
class=${e7({ "doorstop-doc-chip": true, "is-selected": selected })}
|
|
8939
|
+
data-prefix=${document2?.prefix ?? ""}
|
|
8940
|
+
@click=${() => {
|
|
8941
|
+
host.controller?.selectDocument(document2?.prefix ?? "");
|
|
8942
|
+
}}
|
|
8943
|
+
>
|
|
8944
|
+
${document2?.parentPrefix === void 0 ? A : b2`<span class="doorstop-doc-arrow">← ${document2.parentPrefix}</span>`}
|
|
8945
|
+
<span class="doorstop-doc-prefix">${prefix}</span>
|
|
8946
|
+
<span class="doorstop-doc-count">${String(count)}</span>
|
|
8947
|
+
${dots.map((dot) => b2`<span class=${`doorstop-dot doorstop-dot-${dot}`} title=${dotTitle(dot)}></span>`)}
|
|
8948
|
+
</button>
|
|
8949
|
+
`;
|
|
8950
|
+
}
|
|
8951
|
+
function renderViewer(host) {
|
|
8952
|
+
const result = host.result;
|
|
8953
|
+
if (result === void 0) {
|
|
8954
|
+
return b2`<p class="doorstop-muted doorstop-standalone">${host.loading ? "Loading workspace\u2026" : "Run Refresh to scan for Doorstop documents."}</p>`;
|
|
8955
|
+
}
|
|
8956
|
+
if (host.view === "findings") {
|
|
8957
|
+
return renderFindingsView(host, result);
|
|
8958
|
+
}
|
|
8959
|
+
return b2`
|
|
8960
|
+
${renderListFilters(host, result)}
|
|
8961
|
+
${result.index.documents.length === 0 ? b2`
|
|
8962
|
+
${renderDiagnostics(host, result)}
|
|
8963
|
+
<section class="doorstop-empty">
|
|
8964
|
+
<p>${EMPTY_WORKSPACE_MESSAGE}</p>
|
|
8965
|
+
${host.context === void 0 ? A : b2`
|
|
8966
|
+
<button
|
|
8967
|
+
type="button"
|
|
8968
|
+
class="doorstop-setup"
|
|
8969
|
+
@click=${() => {
|
|
8970
|
+
host.insertPrompt(setupProjectPrompt());
|
|
8971
|
+
}}
|
|
8972
|
+
>Ask agent to set up the project</button>
|
|
8973
|
+
`}
|
|
8974
|
+
</section>
|
|
8975
|
+
` : b2`
|
|
8976
|
+
<section class="doorstop-split">
|
|
8977
|
+
<section class="doorstop-list">
|
|
8978
|
+
${renderDiagnostics(host, result)}
|
|
8979
|
+
${renderItemList(host, result)}
|
|
8980
|
+
</section>
|
|
8981
|
+
<section class="doorstop-detail-pane">${renderDetail(host, result)}</section>
|
|
8982
|
+
</section>
|
|
8983
|
+
`}
|
|
8984
|
+
`;
|
|
8985
|
+
}
|
|
8986
|
+
function renderItemList(host, result) {
|
|
8987
|
+
if (result.index.items.length === 0) {
|
|
8988
|
+
return b2`<p class="doorstop-muted doorstop-standalone">No Doorstop items found (item files may be binary or truncated — see the diagnostics above).</p>`;
|
|
8989
|
+
}
|
|
8990
|
+
const items = filteredItems(result.index, host.selectedDocumentPrefix, host.stateFilter, host.search);
|
|
8991
|
+
if (items.length === 0) {
|
|
8992
|
+
return b2`<p class="doorstop-muted doorstop-standalone">No items match the current document, state, or search filters.</p>`;
|
|
8993
|
+
}
|
|
8994
|
+
const gitFiles = host.readyGitFiles();
|
|
8995
|
+
const attributeKeys = doorstopShowAdditionalAttribute(result);
|
|
8996
|
+
return b2`
|
|
8997
|
+
<div
|
|
8998
|
+
class=${e7({ "doorstop-items": true, "has-item-attrs": attributeKeys.length > 0 })}
|
|
8999
|
+
role="list"
|
|
9000
|
+
aria-label="Doorstop items"
|
|
9001
|
+
>
|
|
9002
|
+
${c5(items, (item) => item.uid, (item) => renderItemRow(host, item, gitFiles, attributeKeys))}
|
|
9003
|
+
</div>
|
|
9004
|
+
`;
|
|
9005
|
+
}
|
|
9006
|
+
function renderItemRow(host, item, gitFiles, attributeKeys) {
|
|
9007
|
+
const selected = host.selectedUid === item.uid;
|
|
9008
|
+
const gitState = itemGitState(gitFiles?.get(item.path));
|
|
9009
|
+
const shownAttributes = shownAttributeRows(item, attributeKeys);
|
|
9010
|
+
const textBody = attributeKeys.includes("text") ? itemTextRow(item) : void 0;
|
|
9011
|
+
const stageable = itemStageable(gitFiles?.get(item.path));
|
|
9012
|
+
const unstageable = itemUnstageable(gitFiles?.get(item.path));
|
|
9013
|
+
return b2`
|
|
9014
|
+
<button
|
|
9015
|
+
type="button"
|
|
9016
|
+
role="listitem"
|
|
9017
|
+
class=${e7({ "doorstop-item-row": true, "is-selected": selected })}
|
|
9018
|
+
data-uid=${item.uid}
|
|
9019
|
+
@click=${() => {
|
|
9020
|
+
host.controller?.selectUid(item.uid);
|
|
9021
|
+
}}
|
|
9022
|
+
>
|
|
9023
|
+
<span class="doorstop-item-level">${item.level}</span>
|
|
9024
|
+
<code class="doorstop-item-uid">${item.uid}</code>
|
|
9025
|
+
<span class="doorstop-item-summary">${itemExcerpt(item)}</span>
|
|
9026
|
+
${shownAttributes.length === 0 ? A : b2`<span class="doorstop-item-attrs">${shownAttributes.map((row) => `${row.key}: ${row.text}`).join(" \xB7 ")}</span>`}
|
|
9027
|
+
<span class="doorstop-item-chips">
|
|
9028
|
+
${item.stateKeys.filter((key) => key !== DEFAULT_STATE_KEY).map((key) => renderStateChip(key))}
|
|
9029
|
+
${gitState === "clean" ? A : b2`<span class=${`doorstop-chip doorstop-chip-${gitChipKind(gitState)}`}>${GIT_CHIP_LABELS[gitState]}</span>`}
|
|
9030
|
+
${unstageable && host.runInProgress === void 0 ? b2`<span
|
|
9031
|
+
class="doorstop-item-add"
|
|
9032
|
+
aria-hidden="true"
|
|
9033
|
+
title=${`git reset ${item.path}`}
|
|
9034
|
+
@click=${(event) => {
|
|
9035
|
+
event.stopPropagation();
|
|
9036
|
+
host.unstageItem(item);
|
|
9037
|
+
}}
|
|
9038
|
+
>${gitUnstageIconSvg}</span>` : stageable && host.runInProgress === void 0 ? b2`<span
|
|
9039
|
+
class="doorstop-item-add"
|
|
9040
|
+
aria-hidden="true"
|
|
9041
|
+
title=${`git add ${item.path}`}
|
|
9042
|
+
@click=${(event) => {
|
|
9043
|
+
event.stopPropagation();
|
|
9044
|
+
host.stageItem(item);
|
|
9045
|
+
}}
|
|
9046
|
+
>${gitStageIconSvg}</span>` : A}
|
|
9047
|
+
${host.context === void 0 ? A : b2`<span
|
|
9048
|
+
class="doorstop-item-insert"
|
|
9049
|
+
aria-hidden="true"
|
|
9050
|
+
title=${`Insert ${item.uid} into the prompt`}
|
|
9051
|
+
@click=${(event) => {
|
|
9052
|
+
event.stopPropagation();
|
|
9053
|
+
host.insertPrompt(item.uid);
|
|
9054
|
+
}}
|
|
9055
|
+
>${insertPromptIconSvg}</span>`}
|
|
9056
|
+
</span>
|
|
9057
|
+
${textBody === void 0 ? A : b2`<span class="doorstop-item-text">${textBody}</span>`}
|
|
9058
|
+
</button>
|
|
9059
|
+
`;
|
|
9060
|
+
}
|
|
9061
|
+
function renderStateChip(key) {
|
|
9062
|
+
const kind = stateChipKind(key);
|
|
9063
|
+
return b2`<span class=${`doorstop-chip doorstop-chip-${kind}`}>${STATE_CHIP_LABELS[key]}</span>`;
|
|
9064
|
+
}
|
|
9065
|
+
function renderDiagnostics(host, result) {
|
|
9066
|
+
if (result.index.diagnostics.length === 0) return A;
|
|
9067
|
+
return b2`
|
|
9068
|
+
<section class="doorstop-diagnostics" aria-label="Workspace diagnostics">
|
|
9069
|
+
${result.index.diagnostics.map((diagnostic) => {
|
|
9070
|
+
const kind = diagnostic.severity === "error" ? "doorstop-error" : "doorstop-warning";
|
|
9071
|
+
return b2`
|
|
9072
|
+
<div class=${`doorstop-diagnostic ${kind}`}>
|
|
9073
|
+
<span class="doorstop-severity">${diagnostic.severity}</span>
|
|
9074
|
+
<span class="doorstop-diagnostic-copy">
|
|
9075
|
+
${diagnostic.path === void 0 ? A : b2`<code>${diagnostic.path}</code>`}
|
|
9076
|
+
${diagnostic.message}
|
|
9077
|
+
</span>
|
|
9078
|
+
</div>
|
|
9079
|
+
`;
|
|
9080
|
+
})}
|
|
9081
|
+
</section>
|
|
9082
|
+
`;
|
|
9083
|
+
}
|
|
9084
|
+
function renderFindingsView(host, result) {
|
|
9085
|
+
const rows = findingsViewRows(result.index);
|
|
9086
|
+
const counts = findingsViewCounts(rows);
|
|
9087
|
+
return b2`
|
|
9088
|
+
<section class="doorstop-findings-view" aria-label="Doorstop findings">
|
|
9089
|
+
<header class="doorstop-findings-head">
|
|
9090
|
+
<span class="doorstop-findings-counts" role="status" aria-label="Finding counts">${findingsCountText(counts)}</span>
|
|
9091
|
+
<span class="doorstop-findings-note doorstop-muted">${FINDINGS_PLUGIN_LOCAL_NOTE}</span>
|
|
9092
|
+
</header>
|
|
9093
|
+
${rows.length === 0 ? b2`
|
|
9094
|
+
<section class="doorstop-empty">
|
|
9095
|
+
<p>${FINDINGS_EMPTY_MESSAGE}</p>
|
|
9096
|
+
<p class="doorstop-muted">${FINDINGS_EMPTY_HINT}</p>
|
|
9097
|
+
</section>
|
|
9098
|
+
` : b2`<div class="doorstop-findings-list" role="list" aria-label="Validation findings">
|
|
9099
|
+
${rows.map((row) => renderFindingRow(host, row, result.index))}
|
|
9100
|
+
</div>`}
|
|
9101
|
+
</section>
|
|
9102
|
+
`;
|
|
9103
|
+
}
|
|
9104
|
+
function renderFindingRow(host, row, index) {
|
|
9105
|
+
const kind = row.severity === "error" ? "doorstop-error" : row.severity === "warning" ? "doorstop-warning" : "doorstop-info";
|
|
9106
|
+
const uid = row.uid;
|
|
9107
|
+
const navigable = uid !== void 0 && index.byUid.has(uid);
|
|
9108
|
+
return b2`
|
|
9109
|
+
<div class=${`doorstop-finding-row ${kind}`} role="listitem">
|
|
9110
|
+
<span class="doorstop-severity">${row.severity}</span>
|
|
9111
|
+
${uid === void 0 ? A : navigable ? b2`<button type="button" class="doorstop-finding-uid" data-uid=${uid} title=${`Show ${uid} in the item list`} @click=${() => {
|
|
9112
|
+
selectFindingTarget(host, uid);
|
|
9113
|
+
}}><code>${uid}</code></button>` : b2`<code class="doorstop-finding-uid">${uid}</code>`}
|
|
9114
|
+
${row.path === void 0 ? A : b2`<code class="doorstop-finding-path">${row.path}</code>`}
|
|
9115
|
+
<span class="doorstop-finding-message">${row.message}</span>
|
|
9116
|
+
</div>
|
|
9117
|
+
`;
|
|
9118
|
+
}
|
|
9119
|
+
function selectFindingTarget(host, uid) {
|
|
9120
|
+
const controller = host.controller;
|
|
9121
|
+
if (controller === void 0) return;
|
|
9122
|
+
controller.selectDocument("");
|
|
9123
|
+
controller.setStateFilter(void 0);
|
|
9124
|
+
controller.setSearch("");
|
|
9125
|
+
controller.selectUid(uid);
|
|
9126
|
+
host.view = "items";
|
|
9127
|
+
}
|
|
9128
|
+
function onStateFilterChange(host, event) {
|
|
9129
|
+
const value = event.target.value;
|
|
9130
|
+
host.controller?.setStateFilter(value === "" ? void 0 : value);
|
|
9131
|
+
}
|
|
9132
|
+
function onSearchInput(host, event) {
|
|
9133
|
+
host.controller?.setSearch(event.target.value);
|
|
9134
|
+
}
|
|
9135
|
+
|
|
9136
|
+
// src/browser/sections/item-action-palette.ts
|
|
9137
|
+
function renderItemActionPalette(host) {
|
|
9138
|
+
if (host.view !== "items") return A;
|
|
9139
|
+
const result = host.result;
|
|
9140
|
+
if (result === void 0 || result.index.documents.length === 0) return A;
|
|
9141
|
+
const item = host.selectedItem();
|
|
9142
|
+
if (item === void 0) {
|
|
9143
|
+
return b2`
|
|
9144
|
+
<section class="doorstop-action-palette" aria-label="Item actions">
|
|
9145
|
+
<span class="doorstop-muted doorstop-palette-placeholder">Select an item…</span>
|
|
9146
|
+
</section>
|
|
9147
|
+
`;
|
|
9148
|
+
}
|
|
9149
|
+
const suspects = suspectParentItems(item, result.index);
|
|
9150
|
+
return b2`
|
|
9151
|
+
<section class="doorstop-action-palette" aria-label="Item actions">
|
|
9152
|
+
${renderActionRow(host, item, result.index, suspects)}
|
|
9153
|
+
</section>
|
|
9154
|
+
`;
|
|
9155
|
+
}
|
|
9156
|
+
function renderActionRow(host, item, index, suspects) {
|
|
9157
|
+
const reviewed = item.stateKeys.includes("reviewed");
|
|
9158
|
+
const suspectUids = suspects.length > 0 ? suspects.map((parent) => parent.uid) : [];
|
|
9159
|
+
const gitStatus = host.readyGitStatus();
|
|
9160
|
+
const gitFile = gitStatus === void 0 ? void 0 : gitStatusFileFor(gitStatus, item.path);
|
|
9161
|
+
const stageable = itemStageable(gitFile);
|
|
9162
|
+
const unstageable = itemUnstageable(gitFile);
|
|
9163
|
+
return b2`
|
|
9164
|
+
<button
|
|
9165
|
+
type="button"
|
|
9166
|
+
class="doorstop-review"
|
|
9167
|
+
?disabled=${reviewed || host.runInProgress !== void 0}
|
|
9168
|
+
title=${reviewed ? `${item.uid} is already reviewed against its current fingerprint` : `Mark ${item.uid} reviewed`}
|
|
9169
|
+
@click=${() => {
|
|
9170
|
+
reviewItem(host, item);
|
|
9171
|
+
}}
|
|
9172
|
+
>Approve</button>
|
|
9173
|
+
<button
|
|
9174
|
+
type="button"
|
|
9175
|
+
class="doorstop-clear"
|
|
9176
|
+
?disabled=${suspects.length === 0 || host.runInProgress !== void 0}
|
|
9177
|
+
title=${suspects.length === 0 ? `No suspect links to clear` : `Re-record the parent fingerprints of ${item.uid}`}
|
|
9178
|
+
@click=${() => {
|
|
9179
|
+
clearSuspects(host, item, suspectUids);
|
|
9180
|
+
}}
|
|
9181
|
+
>Clear suspect links</button>
|
|
9182
|
+
${gitStatus === void 0 ? A : b2`<button
|
|
9183
|
+
type="button"
|
|
9184
|
+
class="doorstop-item-stage"
|
|
9185
|
+
?disabled=${host.runInProgress !== void 0 || !stageable}
|
|
9186
|
+
title=${stageable ? `Stage ${item.path}` : `${item.uid} has no unstaged changes \u2014 commit next`}
|
|
9187
|
+
@click=${() => {
|
|
9188
|
+
host.stageItem(item);
|
|
9189
|
+
}}
|
|
9190
|
+
>${gitStageIconSvg}Stage</button>
|
|
9191
|
+
<button
|
|
9192
|
+
type="button"
|
|
9193
|
+
class="doorstop-item-unstage"
|
|
9194
|
+
?disabled=${host.runInProgress !== void 0 || !unstageable}
|
|
9195
|
+
title=${unstageable ? `Unstage ${item.path} (git reset)` : `${item.uid} has nothing staged`}
|
|
9196
|
+
@click=${() => {
|
|
9197
|
+
host.unstageItem(item);
|
|
9198
|
+
}}
|
|
9199
|
+
>${gitUnstageIconSvg}Unstage</button>`}
|
|
9200
|
+
<div class="doorstop-op">
|
|
9201
|
+
<input
|
|
9202
|
+
type="text"
|
|
9203
|
+
class="doorstop-target-input"
|
|
9204
|
+
data-op="link"
|
|
9205
|
+
placeholder="parent UID"
|
|
9206
|
+
${n5(host.linkInputRef)}
|
|
9207
|
+
@keydown=${(event) => onTargetKeydown(host, event)}
|
|
9208
|
+
/>
|
|
9209
|
+
<button type="button" class="doorstop-link" title=${`doorstop link ${item.uid} <target>`} ?disabled=${host.runInProgress !== void 0} @click=${() => {
|
|
9210
|
+
runTargetOp(host, "link", host.linkInputRef, item);
|
|
9211
|
+
}}>Link</button>
|
|
9212
|
+
</div>
|
|
9213
|
+
${host.targetError === void 0 ? A : b2`<span class="doorstop-op-error" role="alert">${host.targetError}</span>`}
|
|
9214
|
+
${renderAskMenu(host, item, index, suspects)}
|
|
9215
|
+
`;
|
|
9216
|
+
}
|
|
9217
|
+
function renderAskMenu(host, item, index, suspects) {
|
|
9218
|
+
const children = index.childrenByUid.get(item.uid) ?? [];
|
|
9219
|
+
const childPrefix = firstChildDocumentPrefix(index, item);
|
|
9220
|
+
return b2`
|
|
9221
|
+
<div class="doorstop-menu">
|
|
9222
|
+
<button
|
|
9223
|
+
type="button"
|
|
9224
|
+
class="doorstop-menu-toggle"
|
|
9225
|
+
aria-expanded=${host.askMenuOpen ? "true" : "false"}
|
|
9226
|
+
@click=${() => onAskMenuToggle(host)}
|
|
9227
|
+
>Ask agent</button>
|
|
9228
|
+
${host.askMenuOpen ? b2`
|
|
9229
|
+
<div class="doorstop-menu-items" role="menu" aria-label="Ask the agent about ${item.uid}">
|
|
9230
|
+
<button type="button" role="menuitem" class="doorstop-menu-item doorstop-explain" @click=${() => {
|
|
9231
|
+
host.insertPrompt(explainItemPrompt(item));
|
|
9232
|
+
}}>Explain</button>
|
|
9233
|
+
<button
|
|
9234
|
+
type="button"
|
|
9235
|
+
role="menuitem"
|
|
9236
|
+
class="doorstop-menu-item doorstop-fix-suspects"
|
|
9237
|
+
?disabled=${suspects.length === 0}
|
|
9238
|
+
title=${suspects.length === 0 ? "No suspect links \u2014 nothing to fix" : `Fix the suspect links of ${item.uid}`}
|
|
9239
|
+
@click=${() => {
|
|
9240
|
+
host.insertPrompt(fixSuspectLinksPrompt(item, suspects));
|
|
9241
|
+
}}
|
|
9242
|
+
>Fix suspect links</button>
|
|
9243
|
+
<button
|
|
9244
|
+
type="button"
|
|
9245
|
+
role="menuitem"
|
|
9246
|
+
class="doorstop-menu-item doorstop-draft-child"
|
|
9247
|
+
?disabled=${childPrefix === void 0}
|
|
9248
|
+
title=${childPrefix === void 0 ? "No child document to add to" : `Draft a child item in the ${childPrefix} document`}
|
|
9249
|
+
@click=${() => {
|
|
9250
|
+
if (childPrefix !== void 0) host.insertPrompt(draftChildRequirementPrompt(item, childPrefix));
|
|
9251
|
+
}}
|
|
9252
|
+
>Draft child requirement</button>
|
|
9253
|
+
<button type="button" role="menuitem" class="doorstop-menu-item doorstop-review-readiness" @click=${() => {
|
|
9254
|
+
host.insertPrompt(reviewReadinessPrompt(item, children));
|
|
9255
|
+
}}>Review readiness</button>
|
|
9256
|
+
<button
|
|
9257
|
+
type="button"
|
|
9258
|
+
role="menuitem"
|
|
9259
|
+
class="doorstop-menu-item doorstop-start-review-session"
|
|
9260
|
+
title=${`Start an independent review session for ${item.uid} (inserts /opendoor-review)`}
|
|
9261
|
+
@click=${() => {
|
|
9262
|
+
insertReviewCommand(host, item);
|
|
9263
|
+
}}
|
|
9264
|
+
>Start review session</button>
|
|
9265
|
+
</div>
|
|
9266
|
+
` : A}
|
|
9267
|
+
</div>
|
|
9268
|
+
`;
|
|
9269
|
+
}
|
|
9270
|
+
function insertReviewCommand(host, item) {
|
|
9271
|
+
if (host.context === void 0) return;
|
|
9272
|
+
host.insertPrompt(composeReviewCommand(item));
|
|
9273
|
+
host.controller?.commitAskReview(item.uid);
|
|
9274
|
+
}
|
|
9275
|
+
function reviewItem(host, item) {
|
|
9276
|
+
const commit = doorstopCommitAfterReview(host.result);
|
|
9277
|
+
host.runDoorstop(
|
|
9278
|
+
"review",
|
|
9279
|
+
`Doorstop: review ${item.uid}`,
|
|
9280
|
+
commit ? { op: "review", uid: item.uid, commit: true } : { op: "review", uid: item.uid },
|
|
9281
|
+
`doorstop review ${item.uid}`,
|
|
9282
|
+
false
|
|
9283
|
+
);
|
|
9284
|
+
}
|
|
9285
|
+
function clearSuspects(host, item, suspectUids) {
|
|
9286
|
+
if (suspectUids.length === 0) return;
|
|
9287
|
+
host.runDoorstop(
|
|
9288
|
+
"clear",
|
|
9289
|
+
"Doorstop: clear suspect links",
|
|
9290
|
+
{ op: "clear", uid: item.uid, parents: suspectUids },
|
|
9291
|
+
`doorstop clear ${item.uid} ${suspectUids.join(" ")}`,
|
|
9292
|
+
false
|
|
9293
|
+
);
|
|
9294
|
+
}
|
|
9295
|
+
function runTargetOp(host, op, inputRef, item) {
|
|
9296
|
+
const input = inputRef.value;
|
|
9297
|
+
const target = input?.value.trim() ?? "";
|
|
9298
|
+
if (target === "") {
|
|
9299
|
+
host.targetError = `Enter a link target UID (e.g. ${item.documentPrefix}0001)`;
|
|
9300
|
+
input?.focus();
|
|
9301
|
+
return;
|
|
9302
|
+
}
|
|
9303
|
+
if (!isValidTargetUid(target)) {
|
|
9304
|
+
host.targetError = `Invalid link target "${target}" \u2014 expected a Doorstop UID like ${item.documentPrefix}0001 (no spaces, quotes, or special characters)`;
|
|
9305
|
+
input?.focus();
|
|
9306
|
+
return;
|
|
9307
|
+
}
|
|
9308
|
+
host.targetError = void 0;
|
|
9309
|
+
if (input !== void 0) input.value = "";
|
|
9310
|
+
host.runDoorstop(
|
|
9311
|
+
op,
|
|
9312
|
+
`Doorstop: ${op} ${item.uid}`,
|
|
9313
|
+
{ op, uid: item.uid, target },
|
|
9314
|
+
`doorstop ${op} ${item.uid} ${target}`,
|
|
9315
|
+
false
|
|
7980
9316
|
);
|
|
7981
|
-
|
|
7982
|
-
|
|
9317
|
+
}
|
|
9318
|
+
function onTargetKeydown(host, event) {
|
|
9319
|
+
const input = event.target;
|
|
9320
|
+
if (input.getAttribute("data-op") !== "link") return;
|
|
9321
|
+
if (event.key === "Enter") {
|
|
9322
|
+
event.preventDefault();
|
|
9323
|
+
const item = host.selectedItem();
|
|
9324
|
+
if (item !== void 0) runTargetOp(host, "link", host.linkInputRef, item);
|
|
9325
|
+
} else if (event.key === "Escape") {
|
|
9326
|
+
input.value = "";
|
|
7983
9327
|
}
|
|
7984
|
-
return `${item.uid} has suspect links to ${andJoin(parents.map((parent) => parent.uid))}: the recorded fingerprints no longer match because those parents changed after ${item.uid} was last reviewed. Read \`${item.path}\` and the changed parents (${parentItems}), compare the texts, and propose updated text for ${item.uid} that still traces correctly to every changed parent. Then run \`doorstop clear ${item.uid}\` in the workspace root to re-record the parents' current fingerprints, and run \`doorstop\` to confirm no suspect link remains.`;
|
|
7985
9328
|
}
|
|
7986
|
-
function
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
9329
|
+
function onAskMenuToggle(host) {
|
|
9330
|
+
host.askMenuOpen = !host.askMenuOpen;
|
|
9331
|
+
}
|
|
9332
|
+
|
|
9333
|
+
// src/browser/sections/status-bar.ts
|
|
9334
|
+
function renderStatusBar(host) {
|
|
9335
|
+
const lastRun = host.lastRun;
|
|
9336
|
+
if (lastRun === void 0) return A;
|
|
9337
|
+
const statusLabel = lastRun.status === "killed" ? "killed (timeout)" : lastRun.status;
|
|
9338
|
+
const detailParts = [lastRun.title, `${String(lastRun.durationMs)} ms`];
|
|
9339
|
+
if (lastRun.exitCode !== null && (lastRun.status === "failed" || lastRun.status === "killed")) {
|
|
9340
|
+
detailParts.push(`exit ${String(lastRun.exitCode)}`);
|
|
7991
9341
|
}
|
|
7992
|
-
|
|
9342
|
+
if (lastRun.signal !== null) detailParts.push(lastRun.signal);
|
|
9343
|
+
return b2`
|
|
9344
|
+
<section class="doorstop-status-bar" aria-label="Last run">
|
|
9345
|
+
<div class="doorstop-status-bar-row">
|
|
9346
|
+
<span class=${`doorstop-last-run-status is-${lastRun.status}`}>${statusLabel}</span>
|
|
9347
|
+
<span class="doorstop-last-run-meta">${detailParts.join(" \xB7 ")}</span>
|
|
9348
|
+
<button
|
|
9349
|
+
type="button"
|
|
9350
|
+
class="doorstop-last-run-dismiss"
|
|
9351
|
+
title="Dismiss the last run output"
|
|
9352
|
+
@click=${() => onDismissRun(host)}
|
|
9353
|
+
>Dismiss</button>
|
|
9354
|
+
</div>
|
|
9355
|
+
${host.statusExpanded ? renderStatusBarBody(host, lastRun) : A}
|
|
9356
|
+
</section>
|
|
9357
|
+
`;
|
|
7993
9358
|
}
|
|
7994
|
-
function
|
|
7995
|
-
|
|
7996
|
-
|
|
9359
|
+
function renderStatusBarBody(host, lastRun) {
|
|
9360
|
+
return b2`
|
|
9361
|
+
<div class="doorstop-last-run">
|
|
9362
|
+
${lastRun.commit === void 0 ? A : b2`<p class="doorstop-last-run-commit doorstop-muted">${commitOutcomeText(lastRun.op, lastRun.commit)}</p>`}
|
|
9363
|
+
${lastRun.status === "error" ? b2`<pre class="doorstop-last-run-pre">${lastRun.errorMessage ?? ""}</pre>` : A}
|
|
9364
|
+
${lastRun.stdout === "" ? A : b2`
|
|
9365
|
+
<pre class="doorstop-last-run-pre">${lastRun.stdout}</pre>
|
|
9366
|
+
${lastRun.stdoutTruncated ? b2`<p class="doorstop-last-run-notice">stdout truncated by the host stream limit (2 MiB) — output not fully captured</p>` : A}
|
|
9367
|
+
`}
|
|
9368
|
+
${lastRun.stderr === "" ? A : b2`
|
|
9369
|
+
<pre class="doorstop-last-run-pre">${lastRun.stderr}</pre>
|
|
9370
|
+
${lastRun.stderrTruncated ? b2`<p class="doorstop-last-run-notice">stderr truncated by the host stream limit (2 MiB) — output not fully captured</p>` : A}
|
|
9371
|
+
`}
|
|
9372
|
+
${lastRun.stdout === "" && lastRun.stderr === "" && lastRun.status !== "error" ? b2`<p class="doorstop-last-run-notice">No output captured.</p>` : A}
|
|
9373
|
+
</div>
|
|
9374
|
+
`;
|
|
7997
9375
|
}
|
|
7998
|
-
function
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
const childLinks = andJoin(
|
|
8002
|
-
children.map((child) => `${child.uid} (\`${child.path}\`)`)
|
|
8003
|
-
);
|
|
8004
|
-
const facts = [
|
|
8005
|
-
item.text.trim() === "" ? "its text is empty (that alone blocks review)" : "its text is filled in",
|
|
8006
|
-
item.links.length === 0 ? "it declares no parent links" : `its recorded parent links (${parents}) are not suspect or missing a fingerprint`,
|
|
8007
|
-
children.length === 0 ? "its child links are left to the `doorstop` run below" : `its child links are declared (${childLinks})`,
|
|
8008
|
-
references.length === 0 ? "it has no file references to verify" : `its file references exist (${references.map((path) => `\`${path}\``).join(", ")})`
|
|
8009
|
-
];
|
|
8010
|
-
return `Check whether ${item.uid} is ready for \`doorstop review ${item.uid}\`. Read \`${item.path}\` and verify that ${facts.join("; ")}. Then run \`doorstop\` in the workspace root and weigh any WARNING or ERROR it reports about ${item.uid} (for example a requirement that should link to ${item.uid} but does not). Report a verdict, and if ${item.uid} is not ready, the exact fixes to make first.`;
|
|
9376
|
+
function onDismissRun(host) {
|
|
9377
|
+
host.statusExpanded = false;
|
|
9378
|
+
host.controller?.dismissRun();
|
|
8011
9379
|
}
|
|
8012
9380
|
|
|
8013
|
-
// src/doorstop-panel-element.ts
|
|
9381
|
+
// src/browser/doorstop-panel-element.ts
|
|
8014
9382
|
var bodyElementTag = "pi-web-opendoor-panel-body";
|
|
8015
9383
|
function defineDoorstopPanelElements() {
|
|
8016
9384
|
defineDoorstopPanelBodyElement();
|
|
@@ -8030,90 +9398,10 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8030
9398
|
this.confirmSkipped = false;
|
|
8031
9399
|
this.view = "items";
|
|
8032
9400
|
this.statusExpanded = false;
|
|
8033
|
-
|
|
8034
|
-
this.linkInputRef =
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
this.onDismissRun = () => {
|
|
8038
|
-
this.statusExpanded = false;
|
|
8039
|
-
this.controller?.dismissRun();
|
|
8040
|
-
};
|
|
8041
|
-
this.onRefreshClick = () => {
|
|
8042
|
-
void this.controller?.invalidate();
|
|
8043
|
-
};
|
|
8044
|
-
this.onValidateClick = () => {
|
|
8045
|
-
this.runDoorstop("validate", "Doorstop: validate", { op: "validate" }, "doorstop", true);
|
|
8046
|
-
};
|
|
8047
|
-
this.onPublishClick = () => {
|
|
8048
|
-
const target = doorstopPublishTarget(this.result);
|
|
8049
|
-
if (typeof window.confirm === "function") {
|
|
8050
|
-
if (!window.confirm(`Publish the Doorstop tree as HTML to ${target} in the workspace terminal?`)) return;
|
|
8051
|
-
this.confirmSkipped = false;
|
|
8052
|
-
} else {
|
|
8053
|
-
this.confirmSkipped = true;
|
|
8054
|
-
}
|
|
8055
|
-
this.runDoorstop(
|
|
8056
|
-
"publish",
|
|
8057
|
-
"Doorstop: publish",
|
|
8058
|
-
{ op: "publish", target },
|
|
8059
|
-
doorstopPublishCommand(this.result, target),
|
|
8060
|
-
false
|
|
8061
|
-
);
|
|
8062
|
-
};
|
|
8063
|
-
this.onGitCommitInput = (event) => {
|
|
8064
|
-
this.gitCommitMessage = event.target.value;
|
|
8065
|
-
};
|
|
8066
|
-
this.onGitStageClick = () => {
|
|
8067
|
-
const controller = this.controller;
|
|
8068
|
-
if (controller === void 0) return;
|
|
8069
|
-
if (controller.runInProgress !== void 0) return;
|
|
8070
|
-
const result = this.result;
|
|
8071
|
-
if (result === void 0) {
|
|
8072
|
-
this.gitActionError = "The workspace is not loaded \u2014 refresh first";
|
|
8073
|
-
return;
|
|
8074
|
-
}
|
|
8075
|
-
const paths = doorstopPaths(result);
|
|
8076
|
-
if (paths.length === 0) {
|
|
8077
|
-
this.gitActionError = "Nothing to stage \u2014 the workspace has no Doorstop-managed files";
|
|
8078
|
-
return;
|
|
8079
|
-
}
|
|
8080
|
-
this.gitActionError = void 0;
|
|
8081
|
-
void controller.runGitStage(paths);
|
|
8082
|
-
};
|
|
8083
|
-
this.onGitCommitClick = () => {
|
|
8084
|
-
this.gitCommitSubmit();
|
|
8085
|
-
};
|
|
8086
|
-
this.onGitCommitKeydown = (event) => {
|
|
8087
|
-
const input = this.gitCommitInputRef.value;
|
|
8088
|
-
if (input === void 0) return;
|
|
8089
|
-
if (event.key === "Enter") {
|
|
8090
|
-
event.preventDefault();
|
|
8091
|
-
this.gitCommitSubmit();
|
|
8092
|
-
} else if (event.key === "Escape") {
|
|
8093
|
-
input.value = "";
|
|
8094
|
-
this.gitCommitMessage = "";
|
|
8095
|
-
this.gitActionError = void 0;
|
|
8096
|
-
}
|
|
8097
|
-
};
|
|
8098
|
-
this.onStateFilterChange = (event) => {
|
|
8099
|
-
const value = event.target.value;
|
|
8100
|
-
this.controller?.setStateFilter(value === "" ? void 0 : value);
|
|
8101
|
-
};
|
|
8102
|
-
this.onSearchInput = (event) => {
|
|
8103
|
-
this.controller?.setSearch(event.target.value);
|
|
8104
|
-
};
|
|
8105
|
-
this.onTargetKeydown = (event) => {
|
|
8106
|
-
const input = event.target;
|
|
8107
|
-
const op = input.getAttribute("data-op");
|
|
8108
|
-
if (op !== "unlink" && op !== "link") return;
|
|
8109
|
-
if (event.key === "Enter") {
|
|
8110
|
-
event.preventDefault();
|
|
8111
|
-
const item = this.selectedItem();
|
|
8112
|
-
if (item !== void 0) this.runTargetOp(op, op === "unlink" ? this.unlinkInputRef : this.linkInputRef, item);
|
|
8113
|
-
} else if (event.key === "Escape") {
|
|
8114
|
-
input.value = "";
|
|
8115
|
-
}
|
|
8116
|
-
};
|
|
9401
|
+
/** section-API: the palette's free-text Link target input. */
|
|
9402
|
+
this.linkInputRef = e5();
|
|
9403
|
+
/** section-API: the toolbar's commit-message input. */
|
|
9404
|
+
this.gitCommitInputRef = e5();
|
|
8117
9405
|
this.onDocumentClick = (event) => {
|
|
8118
9406
|
if (!this.askMenuOpen) return;
|
|
8119
9407
|
const menuRoot = this.shadowRoot?.querySelector(".doorstop-menu");
|
|
@@ -8132,16 +9420,6 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8132
9420
|
this.askMenuOpen = false;
|
|
8133
9421
|
}
|
|
8134
9422
|
};
|
|
8135
|
-
this.onAskMenuToggle = () => {
|
|
8136
|
-
this.askMenuOpen = !this.askMenuOpen;
|
|
8137
|
-
};
|
|
8138
|
-
this.onChangesToggle = (event) => {
|
|
8139
|
-
const details = event.currentTarget;
|
|
8140
|
-
if (!(details instanceof HTMLDetailsElement) || !details.open) return;
|
|
8141
|
-
const item = this.selectedItem();
|
|
8142
|
-
if (item === void 0) return;
|
|
8143
|
-
void this.controller?.requestBaseline(item);
|
|
8144
|
-
};
|
|
8145
9423
|
}
|
|
8146
9424
|
static {
|
|
8147
9425
|
this.styles = panelStyles;
|
|
@@ -8187,9 +9465,8 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8187
9465
|
* expand handler alone would strand the section on "Loading baseline…"
|
|
8188
9466
|
* with no fetch in flight. `requestBaseline` installs its "loading"
|
|
8189
9467
|
* view synchronously, so this cannot loop. (A selection switch needs
|
|
8190
|
-
* no help here —
|
|
8191
|
-
*
|
|
8192
|
-
* `toggle`.)
|
|
9468
|
+
* no help here — the section keys the node by UID, so Lit recreates it
|
|
9469
|
+
* closed and the user's expand fires a real `toggle`.)
|
|
8193
9470
|
*/
|
|
8194
9471
|
updated() {
|
|
8195
9472
|
this.ensureGitStatus();
|
|
@@ -8217,318 +9494,28 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8217
9494
|
}
|
|
8218
9495
|
render() {
|
|
8219
9496
|
return b2`
|
|
8220
|
-
${
|
|
9497
|
+
${renderProjectActions(this)}
|
|
8221
9498
|
${this.error === void 0 ? A : b2`<div class="doorstop-error" role="alert">${this.error}</div>`}
|
|
8222
|
-
<section class="doorstop-viewer">${
|
|
8223
|
-
${
|
|
8224
|
-
${
|
|
8225
|
-
`;
|
|
8226
|
-
}
|
|
8227
|
-
// --- project actions (region 1) -----------------------------------------------
|
|
8228
|
-
renderProjectActions() {
|
|
8229
|
-
const publishTarget = doorstopPublishTarget(this.result);
|
|
8230
|
-
const projectPath = this.context?.workspace.path ?? "";
|
|
8231
|
-
const projectLabel = projectPath === "" ? "Doorstop" : projectPath.split("/").filter(Boolean).pop() ?? projectPath;
|
|
8232
|
-
return b2`
|
|
8233
|
-
<section class="doorstop-project-actions">
|
|
8234
|
-
<strong class="doorstop-title" title=${projectPath === "" ? A : projectPath}>${doorstopIconSvg}${projectLabel}</strong>
|
|
8235
|
-
${this.renderViewToggle()}
|
|
8236
|
-
<div class="doorstop-toolbar-actions">
|
|
8237
|
-
${this.stale ? b2`<button type="button" class="doorstop-stale" title="Doorstop ran or files changed behind the panel — click to rescan" @click=${this.onRefreshClick}>stale — refresh</button>` : A}
|
|
8238
|
-
${this.confirmSkipped ? b2`<span class="doorstop-muted doorstop-confirm-skipped" title="No confirmation dialog is available in this environment — publishing proceeded without one">confirmation skipped — publishing</span>` : A}
|
|
8239
|
-
<button type="button" class="doorstop-refresh" title="Re-read the workspace" @click=${this.onRefreshClick}>${refreshIconSvg}Refresh</button>
|
|
8240
|
-
<button type="button" class="doorstop-validate" title="Run \`doorstop\` in the workspace (terminal when unpaired)" ?disabled=${this.runInProgress !== void 0} @click=${this.onValidateClick}>${validateIconSvg}Run validation</button>
|
|
8241
|
-
<button type="button" class="doorstop-publish" title=${`Publish the tree to ${publishTarget}`} ?disabled=${this.runInProgress !== void 0} @click=${this.onPublishClick}>${publishIconSvg}Publish HTML</button>
|
|
8242
|
-
${this.renderGitActions()}
|
|
8243
|
-
</div>
|
|
8244
|
-
</section>
|
|
9499
|
+
<section class="doorstop-viewer">${renderViewer(this)}</section>
|
|
9500
|
+
${renderItemActionPalette(this)}
|
|
9501
|
+
${renderStatusBar(this)}
|
|
8245
9502
|
`;
|
|
8246
9503
|
}
|
|
8247
|
-
/**
|
|
8248
|
-
*
|
|
8249
|
-
*
|
|
8250
|
-
* the
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
class="doorstop-git-stage"
|
|
8260
|
-
title="Stage all Doorstop-managed files (requirements, documents, configs)"
|
|
8261
|
-
?disabled=${this.runInProgress !== void 0 || !hasDocuments}
|
|
8262
|
-
@click=${this.onGitStageClick}
|
|
8263
|
-
>${gitStageIconSvg}Stage all</button>
|
|
8264
|
-
<div class="doorstop-git-commit">
|
|
8265
|
-
<input
|
|
8266
|
-
type="text"
|
|
8267
|
-
class="doorstop-git-commit-input"
|
|
8268
|
-
placeholder="Commit message"
|
|
8269
|
-
aria-label="Git commit message"
|
|
8270
|
-
?disabled=${this.runInProgress !== void 0}
|
|
8271
|
-
${n5(this.gitCommitInputRef)}
|
|
8272
|
-
@input=${this.onGitCommitInput}
|
|
8273
|
-
@keydown=${this.onGitCommitKeydown}
|
|
8274
|
-
/>
|
|
8275
|
-
<button
|
|
8276
|
-
type="button"
|
|
8277
|
-
class="doorstop-git-commit-button"
|
|
8278
|
-
title="Commit the staged index with this message"
|
|
8279
|
-
?disabled=${this.runInProgress !== void 0 || this.gitCommitMessage.trim() === ""}
|
|
8280
|
-
@click=${this.onGitCommitClick}
|
|
8281
|
-
>${gitCommitIconSvg}Commit</button>
|
|
8282
|
-
${this.gitActionError === void 0 ? A : b2`<span class="doorstop-op-error" role="alert">${this.gitActionError}</span>`}
|
|
8283
|
-
</div>
|
|
8284
|
-
`;
|
|
8285
|
-
}
|
|
8286
|
-
renderGitStatus() {
|
|
8287
|
-
const view = this.gitStatusView;
|
|
8288
|
-
if (view === void 0) return A;
|
|
8289
|
-
const refresh = () => {
|
|
8290
|
-
void this.controller?.requestGitStatus();
|
|
8291
|
-
};
|
|
8292
|
-
switch (view.state) {
|
|
8293
|
-
case "loading":
|
|
8294
|
-
return b2`<button
|
|
8295
|
-
type="button"
|
|
8296
|
-
class="doorstop-git-status"
|
|
8297
|
-
title="Refreshing git status…"
|
|
8298
|
-
aria-busy=${this.gitStatusInFlight}
|
|
8299
|
-
@click=${refresh}
|
|
8300
|
-
><span class="doorstop-git-status-text">⎇ …</span></button>`;
|
|
8301
|
-
case "no-git":
|
|
8302
|
-
return b2`<button type="button" class="doorstop-git-status" title="Not a git repository — click to re-check" aria-busy=${this.gitStatusInFlight} @click=${refresh}><span class="doorstop-git-status-text">no git</span></button>`;
|
|
8303
|
-
case "error":
|
|
8304
|
-
return b2`<button
|
|
8305
|
-
type="button"
|
|
8306
|
-
class="doorstop-git-status doorstop-git-status-error"
|
|
8307
|
-
title=${`Git status unavailable \u2014 ${view.errorMessage ?? "request failed"} \u2014 click to retry`}
|
|
8308
|
-
aria-busy=${this.gitStatusInFlight}
|
|
8309
|
-
@click=${refresh}
|
|
8310
|
-
><span class="doorstop-git-status-text">git status error — retry</span></button>`;
|
|
8311
|
-
case "ready":
|
|
8312
|
-
return b2`<button type="button" class="doorstop-git-status" title="Git status — click to refresh" aria-busy=${this.gitStatusInFlight} @click=${refresh}><span class="doorstop-git-status-text">${gitStatusText(view.response)}</span></button>`;
|
|
8313
|
-
}
|
|
8314
|
-
}
|
|
8315
|
-
// --- list filters (above the item list) ---------------------------------------
|
|
8316
|
-
renderListFilters(result) {
|
|
8317
|
-
return b2`
|
|
8318
|
-
<section class="doorstop-list-filters">
|
|
8319
|
-
<div class="doorstop-docs" role="list" aria-label="Doorstop documents">
|
|
8320
|
-
${this.renderDocumentChip(void 0, result)}
|
|
8321
|
-
${result.index.documents.map((document2) => this.renderDocumentChip(document2, result))}
|
|
8322
|
-
</div>
|
|
8323
|
-
<select class="doorstop-state-filter" aria-label="Filter by state" @change=${this.onStateFilterChange}>
|
|
8324
|
-
<option value="" .selected=${this.stateFilter === void 0}>All states</option>
|
|
8325
|
-
${Object.entries(STATE_CHIP_LABELS).map(
|
|
8326
|
-
([key, label]) => b2`<option value=${key} .selected=${this.stateFilter === key}>${label}</option>`
|
|
8327
|
-
)}
|
|
8328
|
-
</select>
|
|
8329
|
-
<input
|
|
8330
|
-
class="doorstop-search"
|
|
8331
|
-
type="search"
|
|
8332
|
-
aria-label="Search items"
|
|
8333
|
-
placeholder="Search UID or text"
|
|
8334
|
-
.value=${this.search}
|
|
8335
|
-
@input=${this.onSearchInput}
|
|
8336
|
-
/>
|
|
8337
|
-
</section>
|
|
8338
|
-
`;
|
|
8339
|
-
}
|
|
8340
|
-
/**
|
|
8341
|
-
* Expansion is CONTENT-DRIVEN (see `willUpdate`); there is no manual
|
|
8342
|
-
* expand/collapse toggle.
|
|
8343
|
-
*/
|
|
8344
|
-
renderStatusBar() {
|
|
8345
|
-
const lastRun = this.lastRun;
|
|
8346
|
-
if (lastRun === void 0) return A;
|
|
8347
|
-
const statusLabel = lastRun.status === "killed" ? "killed (timeout)" : lastRun.status;
|
|
8348
|
-
const detailParts = [lastRun.title, `${String(lastRun.durationMs)} ms`];
|
|
8349
|
-
if (lastRun.exitCode !== null && (lastRun.status === "failed" || lastRun.status === "killed")) {
|
|
8350
|
-
detailParts.push(`exit ${String(lastRun.exitCode)}`);
|
|
8351
|
-
}
|
|
8352
|
-
if (lastRun.signal !== null) detailParts.push(lastRun.signal);
|
|
8353
|
-
return b2`
|
|
8354
|
-
<section class="doorstop-status-bar" aria-label="Last run">
|
|
8355
|
-
<div class="doorstop-status-bar-row">
|
|
8356
|
-
<span class=${`doorstop-last-run-status is-${lastRun.status}`}>${statusLabel}</span>
|
|
8357
|
-
<span class="doorstop-last-run-meta">${detailParts.join(" \xB7 ")}</span>
|
|
8358
|
-
<button
|
|
8359
|
-
type="button"
|
|
8360
|
-
class="doorstop-last-run-dismiss"
|
|
8361
|
-
title="Dismiss the last run output"
|
|
8362
|
-
@click=${this.onDismissRun}
|
|
8363
|
-
>Dismiss</button>
|
|
8364
|
-
</div>
|
|
8365
|
-
${this.statusExpanded ? this.renderStatusBarBody(lastRun) : A}
|
|
8366
|
-
</section>
|
|
8367
|
-
`;
|
|
8368
|
-
}
|
|
8369
|
-
renderStatusBarBody(lastRun) {
|
|
8370
|
-
return b2`
|
|
8371
|
-
<div class="doorstop-last-run">
|
|
8372
|
-
${lastRun.commit === void 0 ? A : b2`<p class="doorstop-last-run-commit doorstop-muted">${commitOutcomeText(lastRun.op, lastRun.commit)}</p>`}
|
|
8373
|
-
${lastRun.status === "error" ? b2`<pre class="doorstop-last-run-pre">${lastRun.errorMessage ?? ""}</pre>` : A}
|
|
8374
|
-
${lastRun.stdout === "" ? A : b2`
|
|
8375
|
-
<pre class="doorstop-last-run-pre">${lastRun.stdout}</pre>
|
|
8376
|
-
${lastRun.stdoutTruncated ? b2`<p class="doorstop-last-run-notice">stdout truncated by the host stream limit (2 MiB) — output not fully captured</p>` : A}
|
|
8377
|
-
`}
|
|
8378
|
-
${lastRun.stderr === "" ? A : b2`
|
|
8379
|
-
<pre class="doorstop-last-run-pre">${lastRun.stderr}</pre>
|
|
8380
|
-
${lastRun.stderrTruncated ? b2`<p class="doorstop-last-run-notice">stderr truncated by the host stream limit (2 MiB) — output not fully captured</p>` : A}
|
|
8381
|
-
`}
|
|
8382
|
-
${lastRun.stdout === "" && lastRun.stderr === "" && lastRun.status !== "error" ? b2`<p class="doorstop-last-run-notice">No output captured.</p>` : A}
|
|
8383
|
-
</div>
|
|
8384
|
-
`;
|
|
8385
|
-
}
|
|
8386
|
-
renderViewToggle() {
|
|
8387
|
-
return b2`
|
|
8388
|
-
<div class="doorstop-view-toggle" role="tablist" aria-label="Requirements panel view">
|
|
8389
|
-
<button
|
|
8390
|
-
type="button"
|
|
8391
|
-
role="tab"
|
|
8392
|
-
class=${e5({
|
|
8393
|
-
"doorstop-view-tab": true,
|
|
8394
|
-
"doorstop-view-items": true,
|
|
8395
|
-
"is-selected": this.view === "items"
|
|
8396
|
-
})}
|
|
8397
|
-
aria-selected=${this.view === "items" ? "true" : "false"}
|
|
8398
|
-
title="Item list and detail"
|
|
8399
|
-
@click=${() => {
|
|
8400
|
-
this.view = "items";
|
|
8401
|
-
}}
|
|
8402
|
-
>Items</button>
|
|
8403
|
-
<button
|
|
8404
|
-
type="button"
|
|
8405
|
-
role="tab"
|
|
8406
|
-
class=${e5({
|
|
8407
|
-
"doorstop-view-tab": true,
|
|
8408
|
-
"doorstop-view-findings": true,
|
|
8409
|
-
"is-selected": this.view === "findings"
|
|
8410
|
-
})}
|
|
8411
|
-
aria-selected=${this.view === "findings" ? "true" : "false"}
|
|
8412
|
-
title="Validation findings"
|
|
8413
|
-
@click=${() => {
|
|
8414
|
-
this.view = "findings";
|
|
8415
|
-
}}
|
|
8416
|
-
>Findings</button>
|
|
8417
|
-
</div>
|
|
8418
|
-
`;
|
|
8419
|
-
}
|
|
8420
|
-
renderDocumentChip(document2, result) {
|
|
8421
|
-
const prefix = document2?.prefix ?? ALL_DOCUMENTS_LABEL;
|
|
8422
|
-
const count = document2 === void 0 ? result.index.items.length : result.index.items.filter((item) => item.documentPrefix === document2.prefix).length;
|
|
8423
|
-
const dots = document2 === void 0 ? [] : documentStateDots(document2, result.index.items);
|
|
8424
|
-
const selected = document2 === void 0 ? this.selectedDocumentPrefix === void 0 || this.selectedDocumentPrefix === "" : this.selectedDocumentPrefix === document2.prefix;
|
|
8425
|
-
return b2`
|
|
8426
|
-
<button
|
|
8427
|
-
type="button"
|
|
8428
|
-
role="listitem"
|
|
8429
|
-
class=${e5({ "doorstop-doc-chip": true, "is-selected": selected })}
|
|
8430
|
-
data-prefix=${document2?.prefix ?? ""}
|
|
8431
|
-
@click=${() => {
|
|
8432
|
-
this.controller?.selectDocument(document2?.prefix ?? "");
|
|
8433
|
-
}}
|
|
8434
|
-
>
|
|
8435
|
-
${document2?.parentPrefix === void 0 ? A : b2`<span class="doorstop-doc-arrow">← ${document2.parentPrefix}</span>`}
|
|
8436
|
-
<span class="doorstop-doc-prefix">${prefix}</span>
|
|
8437
|
-
<span class="doorstop-doc-count">${String(count)}</span>
|
|
8438
|
-
${dots.map((dot) => b2`<span class=${`doorstop-dot doorstop-dot-${dot}`} title=${dotTitle(dot)}></span>`)}
|
|
8439
|
-
</button>
|
|
8440
|
-
`;
|
|
8441
|
-
}
|
|
8442
|
-
// --- item list ------------------------------------------------------------------
|
|
8443
|
-
renderViewer() {
|
|
8444
|
-
const result = this.result;
|
|
8445
|
-
if (result === void 0) {
|
|
8446
|
-
return b2`<p class="doorstop-muted doorstop-standalone">${this.loading ? "Loading workspace\u2026" : "Run Refresh to scan for Doorstop documents."}</p>`;
|
|
8447
|
-
}
|
|
8448
|
-
if (this.view === "findings") {
|
|
8449
|
-
return this.renderFindingsView(result);
|
|
8450
|
-
}
|
|
8451
|
-
return b2`
|
|
8452
|
-
${this.renderListFilters(result)}
|
|
8453
|
-
${result.index.documents.length === 0 ? b2`
|
|
8454
|
-
${this.renderDiagnostics(result)}
|
|
8455
|
-
<section class="doorstop-empty"><p>${EMPTY_WORKSPACE_MESSAGE}</p></section>
|
|
8456
|
-
` : b2`
|
|
8457
|
-
<section class="doorstop-split">
|
|
8458
|
-
<section class="doorstop-list">
|
|
8459
|
-
${this.renderDiagnostics(result)}
|
|
8460
|
-
${this.renderItemList(result)}
|
|
8461
|
-
</section>
|
|
8462
|
-
<section class="doorstop-detail-pane">${this.renderDetail(result)}</section>
|
|
8463
|
-
</section>
|
|
8464
|
-
`}
|
|
8465
|
-
`;
|
|
8466
|
-
}
|
|
8467
|
-
renderFindingsView(result) {
|
|
8468
|
-
const rows = findingsViewRows(result.index);
|
|
8469
|
-
const counts = findingsViewCounts(rows);
|
|
8470
|
-
return b2`
|
|
8471
|
-
<section class="doorstop-findings-view" aria-label="Doorstop findings">
|
|
8472
|
-
<header class="doorstop-findings-head">
|
|
8473
|
-
<span class="doorstop-findings-counts" role="status" aria-label="Finding counts">${findingsCountText(counts)}</span>
|
|
8474
|
-
<span class="doorstop-findings-note doorstop-muted">${FINDINGS_PLUGIN_LOCAL_NOTE}</span>
|
|
8475
|
-
</header>
|
|
8476
|
-
${rows.length === 0 ? b2`
|
|
8477
|
-
<section class="doorstop-empty">
|
|
8478
|
-
<p>${FINDINGS_EMPTY_MESSAGE}</p>
|
|
8479
|
-
<p class="doorstop-muted">${FINDINGS_EMPTY_HINT}</p>
|
|
8480
|
-
</section>
|
|
8481
|
-
` : b2`<div class="doorstop-findings-list" role="list" aria-label="Validation findings">
|
|
8482
|
-
${rows.map((row) => this.renderFindingRow(row, result.index))}
|
|
8483
|
-
</div>`}
|
|
8484
|
-
</section>
|
|
8485
|
-
`;
|
|
8486
|
-
}
|
|
8487
|
-
renderFindingRow(row, index) {
|
|
8488
|
-
const kind = row.severity === "error" ? "doorstop-error" : row.severity === "warning" ? "doorstop-warning" : "doorstop-info";
|
|
8489
|
-
const uid = row.uid;
|
|
8490
|
-
const navigable = uid !== void 0 && index.byUid.has(uid);
|
|
8491
|
-
return b2`
|
|
8492
|
-
<div class=${`doorstop-finding-row ${kind}`} role="listitem">
|
|
8493
|
-
<span class="doorstop-severity">${row.severity}</span>
|
|
8494
|
-
${uid === void 0 ? A : navigable ? b2`<button type="button" class="doorstop-finding-uid" data-uid=${uid} title=${`Show ${uid} in the item list`} @click=${() => {
|
|
8495
|
-
this.selectFindingTarget(uid);
|
|
8496
|
-
}}><code>${uid}</code></button>` : b2`<code class="doorstop-finding-uid">${uid}</code>`}
|
|
8497
|
-
${row.path === void 0 ? A : b2`<code class="doorstop-finding-path">${row.path}</code>`}
|
|
8498
|
-
<span class="doorstop-finding-message">${row.message}</span>
|
|
8499
|
-
</div>
|
|
8500
|
-
`;
|
|
8501
|
-
}
|
|
8502
|
-
selectFindingTarget(uid) {
|
|
8503
|
-
const controller = this.controller;
|
|
8504
|
-
if (controller === void 0) return;
|
|
8505
|
-
controller.selectDocument("");
|
|
8506
|
-
controller.setStateFilter(void 0);
|
|
8507
|
-
controller.setSearch("");
|
|
8508
|
-
controller.selectUid(uid);
|
|
8509
|
-
this.view = "items";
|
|
8510
|
-
}
|
|
8511
|
-
renderDiagnostics(result) {
|
|
8512
|
-
if (result.index.diagnostics.length === 0) return A;
|
|
8513
|
-
return b2`
|
|
8514
|
-
<section class="doorstop-diagnostics" aria-label="Workspace diagnostics">
|
|
8515
|
-
${result.index.diagnostics.map((diagnostic) => {
|
|
8516
|
-
const kind = diagnostic.severity === "error" ? "doorstop-error" : "doorstop-warning";
|
|
8517
|
-
return b2`
|
|
8518
|
-
<div class=${`doorstop-diagnostic ${kind}`}>
|
|
8519
|
-
<span class="doorstop-severity">${diagnostic.severity}</span>
|
|
8520
|
-
<span class="doorstop-diagnostic-copy">
|
|
8521
|
-
${diagnostic.path === void 0 ? A : b2`<code>${diagnostic.path}</code>`}
|
|
8522
|
-
${diagnostic.message}
|
|
8523
|
-
</span>
|
|
8524
|
-
</div>
|
|
8525
|
-
`;
|
|
8526
|
-
})}
|
|
8527
|
-
</section>
|
|
8528
|
-
`;
|
|
9504
|
+
/** section-API: the baseline fetch and the review→commit pipeline live
|
|
9505
|
+
* server-side, so this gates the sections that need them. The v4 peer
|
|
9506
|
+
* is the exact revision-paired path to this package's own active server
|
|
9507
|
+
* entry; v4 also removed the provider `capabilities.request` field, so
|
|
9508
|
+
* `peer.request` presence is the paired-backend gate (a legal
|
|
9509
|
+
* openChannel-only peer is NOT paired for these request-shaped
|
|
9510
|
+
* sections). The opendoor provider ownership check is kept for the
|
|
9511
|
+
* ownership-based information display (behavior-preserving Phase A;
|
|
9512
|
+
* Phase C revisits it). */
|
|
9513
|
+
backendActive() {
|
|
9514
|
+
const context = this.context;
|
|
9515
|
+
return context?.peer?.request !== void 0 && context.workspace.provider?.pluginId === OPENDOOR_PLUGIN_ID;
|
|
8529
9516
|
}
|
|
8530
|
-
/**
|
|
8531
|
-
* ready view, and an UNTRUNCATED `files` list. `undefined` when
|
|
9517
|
+
/** section-API: the status response usable for PER-ITEM git UI: a paired
|
|
9518
|
+
* install, a ready view, and an UNTRUNCATED `files` list. `undefined` when
|
|
8532
9519
|
* unpaired / loading / no-git / error / truncated, so the item list and
|
|
8533
9520
|
* action row render no chips, no per-row add, and no svg inside the row
|
|
8534
9521
|
* (the XSS assertion's structural dependency). The strip reads
|
|
@@ -8541,457 +9528,24 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8541
9528
|
if (gitStatusFilesTruncated(view.response)) return void 0;
|
|
8542
9529
|
return view.response;
|
|
8543
9530
|
}
|
|
8544
|
-
/**
|
|
8545
|
-
* git UI is unavailable (see {@link readyGitStatus}). */
|
|
9531
|
+
/** section-API: the path→git-file map for one list render; `undefined`
|
|
9532
|
+
* when per-item git UI is unavailable (see {@link readyGitStatus}). */
|
|
8546
9533
|
readyGitFiles() {
|
|
8547
9534
|
const response = this.readyGitStatus();
|
|
8548
9535
|
return response === void 0 ? void 0 : gitStatusFilesByPath(response);
|
|
8549
9536
|
}
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
}
|
|
8554
|
-
const items = filteredItems(result.index, this.selectedDocumentPrefix, this.stateFilter, this.search);
|
|
8555
|
-
if (items.length === 0) {
|
|
8556
|
-
return b2`<p class="doorstop-muted doorstop-standalone">No items match the current document, state, or search filters.</p>`;
|
|
8557
|
-
}
|
|
8558
|
-
const gitFiles = this.readyGitFiles();
|
|
8559
|
-
return b2`
|
|
8560
|
-
<div class="doorstop-items" role="list" aria-label="Doorstop items">
|
|
8561
|
-
${c5(items, (item) => item.uid, (item) => this.renderItemRow(item, gitFiles))}
|
|
8562
|
-
</div>
|
|
8563
|
-
`;
|
|
8564
|
-
}
|
|
8565
|
-
renderItemRow(item, gitFiles) {
|
|
8566
|
-
const selected = this.selectedUid === item.uid;
|
|
8567
|
-
const gitState = itemGitState(gitFiles?.get(item.path));
|
|
8568
|
-
const stageable = itemStageable(gitFiles?.get(item.path));
|
|
8569
|
-
const unstageable = itemUnstageable(gitFiles?.get(item.path));
|
|
8570
|
-
return b2`
|
|
8571
|
-
<button
|
|
8572
|
-
type="button"
|
|
8573
|
-
role="listitem"
|
|
8574
|
-
class=${e5({ "doorstop-item-row": true, "is-selected": selected })}
|
|
8575
|
-
data-uid=${item.uid}
|
|
8576
|
-
@click=${() => {
|
|
8577
|
-
this.controller?.selectUid(item.uid);
|
|
8578
|
-
}}
|
|
8579
|
-
>
|
|
8580
|
-
<span class="doorstop-item-level">${item.level}</span>
|
|
8581
|
-
<code class="doorstop-item-uid">${item.uid}</code>
|
|
8582
|
-
<span class="doorstop-item-summary">${itemExcerpt(item)}</span>
|
|
8583
|
-
<span class="doorstop-item-chips">
|
|
8584
|
-
${item.stateKeys.map((key) => this.renderStateChip(key))}
|
|
8585
|
-
${gitState === "clean" ? A : b2`<span class=${`doorstop-chip doorstop-chip-${gitChipKind(gitState)}`}>${GIT_CHIP_LABELS[gitState]}</span>`}
|
|
8586
|
-
${unstageable && this.runInProgress === void 0 ? b2`<span
|
|
8587
|
-
class="doorstop-item-add"
|
|
8588
|
-
aria-hidden="true"
|
|
8589
|
-
title=${`git reset ${item.path}`}
|
|
8590
|
-
@click=${(event) => {
|
|
8591
|
-
event.stopPropagation();
|
|
8592
|
-
this.unstageItem(item);
|
|
8593
|
-
}}
|
|
8594
|
-
>${gitUnstageIconSvg}</span>` : stageable && this.runInProgress === void 0 ? b2`<span
|
|
8595
|
-
class="doorstop-item-add"
|
|
8596
|
-
aria-hidden="true"
|
|
8597
|
-
title=${`git add ${item.path}`}
|
|
8598
|
-
@click=${(event) => {
|
|
8599
|
-
event.stopPropagation();
|
|
8600
|
-
this.stageItem(item);
|
|
8601
|
-
}}
|
|
8602
|
-
>${gitStageIconSvg}</span>` : A}
|
|
8603
|
-
</span>
|
|
8604
|
-
</button>
|
|
8605
|
-
`;
|
|
8606
|
-
}
|
|
8607
|
-
renderStateChip(key) {
|
|
8608
|
-
const kind = stateChipKind(key);
|
|
8609
|
-
return b2`<span class=${`doorstop-chip doorstop-chip-${kind}`}>${STATE_CHIP_LABELS[key]}</span>`;
|
|
8610
|
-
}
|
|
8611
|
-
/** The baseline fetch and the review→commit pipeline live server-side,
|
|
8612
|
-
* so this gates the sections that need them. */
|
|
8613
|
-
backendActive() {
|
|
8614
|
-
const context = this.context;
|
|
8615
|
-
return context?.backend !== void 0 && context.workspace.provider?.pluginId === OPENDOOR_PLUGIN_ID && context.workspace.provider?.capabilities.request !== false;
|
|
8616
|
-
}
|
|
8617
|
-
/**
|
|
8618
|
-
* The `<details>` is wrapped in `keyed(item.uid, …)` so a selection
|
|
8619
|
-
* switch RE-CREATES the node (fresh, collapsed, with a real `toggle` on
|
|
8620
|
-
* expand) instead of Lit reusing the old item's open node — a reused
|
|
8621
|
-
* node fires no `toggle`, which would strand the new item on
|
|
8622
|
-
* "Loading baseline…". {@link updated} covers the remaining reuse
|
|
8623
|
-
* hole (same item re-rendered after its cache key changed).
|
|
8624
|
-
*/
|
|
8625
|
-
renderChangesSinceReview(item) {
|
|
8626
|
-
if (!item.stateKeys.includes("unreviewed") || item.reviewed === null) return A;
|
|
8627
|
-
if (!this.backendActive()) return A;
|
|
8628
|
-
const view = this.controller?.baselineViewFor(item);
|
|
8629
|
-
return i7(
|
|
8630
|
-
item.uid,
|
|
8631
|
-
b2`
|
|
8632
|
-
<details class="doorstop-changes" @toggle=${this.onChangesToggle}>
|
|
8633
|
-
<summary class="doorstop-changes-summary">Changes since review</summary>
|
|
8634
|
-
${view === void 0 || view.state === "loading" ? b2`<p class="doorstop-muted doorstop-changes-notice">Loading baseline…</p>` : this.renderBaselineView(view)}
|
|
8635
|
-
</details>
|
|
8636
|
-
`
|
|
8637
|
-
);
|
|
8638
|
-
}
|
|
8639
|
-
renderBaselineView(view) {
|
|
8640
|
-
switch (view.state) {
|
|
8641
|
-
case "loading":
|
|
8642
|
-
return b2`<p class="doorstop-muted doorstop-changes-notice">Loading baseline…</p>`;
|
|
8643
|
-
case "no-git":
|
|
8644
|
-
return b2`<p class="doorstop-muted doorstop-changes-notice">No git history — previous version unavailable</p>`;
|
|
8645
|
-
case "no-match":
|
|
8646
|
-
return b2`<p class="doorstop-muted doorstop-changes-notice">Could not locate the reviewed version (history may have been rewritten)</p>`;
|
|
8647
|
-
case "error":
|
|
8648
|
-
return b2`<p class="doorstop-muted doorstop-changes-notice">Baseline unavailable — ${view.errorMessage ?? "request failed"}</p>`;
|
|
8649
|
-
case "ready": {
|
|
8650
|
-
const diff = view.diff;
|
|
8651
|
-
if (diff === void 0) return b2`<p class="doorstop-muted doorstop-changes-notice">No changes found.</p>`;
|
|
8652
|
-
return this.renderBaselineDiff(view, diff);
|
|
8653
|
-
}
|
|
8654
|
-
}
|
|
8655
|
-
}
|
|
8656
|
-
renderBaselineDiff(view, diff) {
|
|
8657
|
-
return b2`
|
|
8658
|
-
${view.source === void 0 ? A : b2`<p class="doorstop-muted doorstop-changes-source">matched via ${view.source === "review-commit" ? "review commit" : "history walk"}</p>`}
|
|
8659
|
-
${diff.text === void 0 ? b2`<p class="doorstop-muted doorstop-changes-notice">Text changed — too large to render a line diff.</p>` : b2`<div class="doorstop-diff-lines" role="list" aria-label="Text changes since review">
|
|
8660
|
-
${diff.text.map((line) => this.renderDiffLine(line))}
|
|
8661
|
-
</div>`}
|
|
8662
|
-
${diff.ref === void 0 ? A : this.renderFieldChange("ref", diff.ref.before, diff.ref.after)}
|
|
8663
|
-
${diff.references === void 0 ? A : this.renderFieldChange(
|
|
8664
|
-
"references",
|
|
8665
|
-
referenceListText(diff.references.before),
|
|
8666
|
-
referenceListText(diff.references.after)
|
|
8667
|
-
)}
|
|
8668
|
-
${diff.linksAdded.length === 0 && diff.linksRemoved.length === 0 ? A : b2`<div class="doorstop-field-change">
|
|
8669
|
-
<span class="doorstop-field-name">links</span>
|
|
8670
|
-
${diff.linksAdded.map((uid) => b2`<code class="doorstop-field-chip doorstop-field-chip-after">+ ${uid}</code>`)}
|
|
8671
|
-
${diff.linksRemoved.map((uid) => b2`<code class="doorstop-field-chip doorstop-field-chip-before">− ${uid}</code>`)}
|
|
8672
|
-
</div>`}
|
|
8673
|
-
${diff.extended.map((change) => this.renderFieldChange(change.name, change.before, change.after))}
|
|
8674
|
-
`;
|
|
8675
|
-
}
|
|
8676
|
-
renderDiffLine(line) {
|
|
8677
|
-
const mark = line.kind === "added" ? "+" : line.kind === "removed" ? "\u2212" : "";
|
|
8678
|
-
return b2`
|
|
8679
|
-
<div class=${`doorstop-diff-line is-${line.kind}`} role="listitem">
|
|
8680
|
-
<span class="doorstop-diff-mark">${mark}</span>
|
|
8681
|
-
<span class="doorstop-diff-text">${line.text === "" ? "\xA0" : line.text}</span>
|
|
8682
|
-
</div>
|
|
8683
|
-
`;
|
|
8684
|
-
}
|
|
8685
|
-
renderFieldChange(label, before, after) {
|
|
8686
|
-
return b2`
|
|
8687
|
-
<div class="doorstop-field-change">
|
|
8688
|
-
<span class="doorstop-field-name">${label}</span>
|
|
8689
|
-
<code class="doorstop-field-chip doorstop-field-chip-before">${diffValueText(before)}</code>
|
|
8690
|
-
<span class="doorstop-field-arrow">→</span>
|
|
8691
|
-
<code class="doorstop-field-chip doorstop-field-chip-after">${diffValueText(after)}</code>
|
|
8692
|
-
</div>
|
|
8693
|
-
`;
|
|
8694
|
-
}
|
|
8695
|
-
// --- detail pane ------------------------------------------------------------------
|
|
8696
|
-
// v1 deliberately renders the item text as escaped text — no markdown,
|
|
8697
|
-
// injection-safe by construction.
|
|
8698
|
-
renderDetail(result) {
|
|
8699
|
-
const selectedUid = this.selectedUid;
|
|
8700
|
-
if (selectedUid === void 0) {
|
|
8701
|
-
return b2`<p class="doorstop-muted doorstop-standalone">Select an item in the list to inspect its details.</p>`;
|
|
8702
|
-
}
|
|
8703
|
-
const item = result.index.byUid.get(selectedUid);
|
|
8704
|
-
if (item === void 0) {
|
|
8705
|
-
return b2`<p class="doorstop-muted doorstop-standalone">This item is no longer available — refresh the panel.</p>`;
|
|
8706
|
-
}
|
|
8707
|
-
const children = result.index.childrenByUid.get(item.uid) ?? [];
|
|
8708
|
-
const findings = result.index.findings.filter((finding) => finding.uid === item.uid);
|
|
8709
|
-
return b2`
|
|
8710
|
-
<section class="doorstop-detail" aria-label="Item details">
|
|
8711
|
-
<div class="doorstop-detail-head">
|
|
8712
|
-
<code class="doorstop-detail-uid">${item.uid}</code>
|
|
8713
|
-
<span class="doorstop-level">level ${item.level}</span>
|
|
8714
|
-
${item.header === void 0 ? A : b2`<h3>${item.header}</h3>`}
|
|
8715
|
-
</div>
|
|
8716
|
-
${this.renderFlags(item)}
|
|
8717
|
-
<div class="doorstop-state-chip-row">
|
|
8718
|
-
${item.stateKeys.map((key) => this.renderStateChip(key))}
|
|
8719
|
-
</div>
|
|
8720
|
-
${this.renderChangesSinceReview(item)}
|
|
8721
|
-
<h4 class="doorstop-section-title">Text</h4>
|
|
8722
|
-
<p class=${item.text === "" ? "doorstop-text doorstop-text-empty" : "doorstop-text"}>${item.text === "" ? "empty" : item.text}</p>
|
|
8723
|
-
<h4 class="doorstop-section-title">Parent links</h4>
|
|
8724
|
-
${this.renderLinksOut(item, result.index)}
|
|
8725
|
-
<h4 class="doorstop-section-title">Child links</h4>
|
|
8726
|
-
${this.renderLinksIn(item, result.index, children)}
|
|
8727
|
-
<h4 class="doorstop-section-title">References</h4>
|
|
8728
|
-
${this.renderReferences(item, result.index)}
|
|
8729
|
-
${Object.entries(item.attributes).length === 0 ? A : b2`
|
|
8730
|
-
<h4 class="doorstop-section-title">Extended attributes</h4>
|
|
8731
|
-
<dl class="doorstop-attributes">
|
|
8732
|
-
${Object.entries(item.attributes).map(
|
|
8733
|
-
([key, value]) => b2`<div class="doorstop-attribute"><dt>${key}</dt><dd><code>${jsonishText(value)}</code></dd></div>`
|
|
8734
|
-
)}
|
|
8735
|
-
</dl>
|
|
8736
|
-
`}
|
|
8737
|
-
<h4 class="doorstop-section-title">Findings</h4>
|
|
8738
|
-
${findings.length === 0 ? b2`<p class="doorstop-muted">No local findings for this item.</p>` : b2`<div class="doorstop-findings" aria-label="Findings for this item">
|
|
8739
|
-
${findings.map((finding) => {
|
|
8740
|
-
const kind = finding.severity === "error" ? "doorstop-error" : finding.severity === "warning" ? "doorstop-warning" : "doorstop-info";
|
|
8741
|
-
return b2`<div class=${`doorstop-finding ${kind}`}><span class="doorstop-finding-severity">${finding.severity}</span><span>${finding.message}</span></div>`;
|
|
8742
|
-
})}
|
|
8743
|
-
</div>`}
|
|
8744
|
-
</section>
|
|
8745
|
-
`;
|
|
8746
|
-
}
|
|
8747
|
-
// --- item action palette (region 4) --------------------------------------------
|
|
8748
|
-
renderItemActionPalette() {
|
|
8749
|
-
if (this.view !== "items") return A;
|
|
8750
|
-
const result = this.result;
|
|
8751
|
-
if (result === void 0 || result.index.documents.length === 0) return A;
|
|
8752
|
-
const item = this.selectedItem();
|
|
8753
|
-
if (item === void 0) {
|
|
8754
|
-
return b2`
|
|
8755
|
-
<section class="doorstop-action-palette" aria-label="Item actions">
|
|
8756
|
-
<span class="doorstop-muted doorstop-palette-placeholder">Select an item…</span>
|
|
8757
|
-
</section>
|
|
8758
|
-
`;
|
|
8759
|
-
}
|
|
8760
|
-
const suspects = suspectParentItems(item, result.index);
|
|
8761
|
-
return b2`
|
|
8762
|
-
<section class="doorstop-action-palette" aria-label="Item actions">
|
|
8763
|
-
${this.renderActionRow(item, result.index, suspects)}
|
|
8764
|
-
</section>
|
|
8765
|
-
`;
|
|
8766
|
-
}
|
|
8767
|
-
renderFlags(item) {
|
|
8768
|
-
return b2`
|
|
8769
|
-
<div class="doorstop-flags" aria-label="Item flags">
|
|
8770
|
-
<span class=${e5({ "doorstop-flag": true, "is-inactive": !item.active })}>
|
|
8771
|
-
${item.active ? "active" : "inactive"}
|
|
8772
|
-
</span>
|
|
8773
|
-
<span class="doorstop-flag">${item.normative ? "normative" : "non-normative"}</span>
|
|
8774
|
-
<span class="doorstop-flag">${item.derived ? "derived" : "non-derived"}</span>
|
|
8775
|
-
</div>
|
|
8776
|
-
`;
|
|
8777
|
-
}
|
|
8778
|
-
/** `computeItemStamp(parent, config, false)` is exactly the
|
|
8779
|
-
* link-record stamp the state chain compares against. */
|
|
8780
|
-
renderLinksOut(item, index) {
|
|
8781
|
-
if (item.links.length === 0) return b2`<p class="doorstop-muted">No parent links.</p>`;
|
|
8782
|
-
return b2`
|
|
8783
|
-
<div class="doorstop-links" aria-label="Parent links">
|
|
8784
|
-
${item.links.map((link) => this.renderLinkOut(link, index))}
|
|
8785
|
-
</div>
|
|
8786
|
-
`;
|
|
8787
|
-
}
|
|
8788
|
-
renderLinkOut(link, index) {
|
|
8789
|
-
const target = index.byUid.get(link.uid);
|
|
8790
|
-
if (target === void 0) {
|
|
8791
|
-
return b2`
|
|
8792
|
-
<div class="doorstop-link-row">
|
|
8793
|
-
<code>${link.uid}</code>
|
|
8794
|
-
<span class="doorstop-chip doorstop-chip-danger">unknown</span>
|
|
8795
|
-
<span class="doorstop-fingerprint">not in the index — ${link.fingerprint === null ? "no recorded fingerprint" : `recorded ${shortFingerprint(link.fingerprint)}`}</span>
|
|
8796
|
-
</div>
|
|
8797
|
-
`;
|
|
8798
|
-
}
|
|
8799
|
-
const current = computeItemStamp(target, documentConfigFor(index, target), false);
|
|
8800
|
-
const suspect = link.fingerprint !== null && link.fingerprint !== current;
|
|
8801
|
-
return b2`
|
|
8802
|
-
<button
|
|
8803
|
-
type="button"
|
|
8804
|
-
class="doorstop-link-row"
|
|
8805
|
-
data-uid=${link.uid}
|
|
8806
|
-
title=${`Open ${link.uid}`}
|
|
8807
|
-
@click=${() => {
|
|
8808
|
-
this.controller?.selectUid(link.uid);
|
|
8809
|
-
}}
|
|
8810
|
-
>
|
|
8811
|
-
<code>${link.uid}</code>
|
|
8812
|
-
<span class=${suspect ? "doorstop-chip doorstop-chip-danger" : "doorstop-chip doorstop-chip-ok"}>${suspect ? "suspect" : "ok"}</span>
|
|
8813
|
-
<span class="doorstop-fingerprint">recorded ${shortFingerprint(link.fingerprint)} · current ${shortFingerprint(current)}</span>
|
|
8814
|
-
</button>
|
|
8815
|
-
`;
|
|
8816
|
-
}
|
|
8817
|
-
renderLinksIn(item, index, children) {
|
|
8818
|
-
if (children.length === 0) {
|
|
8819
|
-
return b2`<p class="doorstop-muted">No child links.</p>`;
|
|
8820
|
-
}
|
|
8821
|
-
return b2`
|
|
8822
|
-
<div class="doorstop-links" aria-label="Child links">
|
|
8823
|
-
${children.map(
|
|
8824
|
-
(child) => b2`<button type="button" class="doorstop-link-row" data-uid=${child.uid} title=${`Open ${child.uid}`} @click=${() => {
|
|
8825
|
-
this.controller?.selectUid(child.uid);
|
|
8826
|
-
}}>
|
|
8827
|
-
<code>${child.uid}</code>
|
|
8828
|
-
<span class="doorstop-muted">${child.level}</span>
|
|
8829
|
-
</button>`
|
|
8830
|
-
)}
|
|
8831
|
-
</div>
|
|
8832
|
-
`;
|
|
8833
|
-
}
|
|
8834
|
-
renderReferences(item, index) {
|
|
8835
|
-
const paths = [];
|
|
8836
|
-
if (item.ref !== "") paths.push(item.ref);
|
|
8837
|
-
if (item.references !== void 0) {
|
|
8838
|
-
for (const reference of item.references) paths.push(reference.path);
|
|
8839
|
-
}
|
|
8840
|
-
if (paths.length === 0) return b2`<p class="doorstop-muted">No file references.</p>`;
|
|
8841
|
-
return b2`
|
|
8842
|
-
<div class="doorstop-links" aria-label="File references">
|
|
8843
|
-
${paths.map((path) => {
|
|
8844
|
-
const found = index.knownFilePaths.has(path);
|
|
8845
|
-
return b2`
|
|
8846
|
-
<div class="doorstop-link-row">
|
|
8847
|
-
<code>${path}</code>
|
|
8848
|
-
${found ? A : b2`<span class="doorstop-chip doorstop-chip-danger">not found</span>`}
|
|
8849
|
-
</div>
|
|
8850
|
-
`;
|
|
8851
|
-
})}
|
|
8852
|
-
</div>
|
|
8853
|
-
`;
|
|
8854
|
-
}
|
|
8855
|
-
// --- action row ------------------------------------------------------------------
|
|
8856
|
-
renderActionRow(item, index, suspects) {
|
|
8857
|
-
const reviewed = item.stateKeys.includes("reviewed");
|
|
8858
|
-
const suspectUids = suspects.length > 0 ? suspects.map((parent) => parent.uid) : [];
|
|
8859
|
-
const gitStatus = this.readyGitStatus();
|
|
8860
|
-
const gitFile = gitStatus === void 0 ? void 0 : gitStatusFileFor(gitStatus, item.path);
|
|
8861
|
-
const stageable = itemStageable(gitFile);
|
|
8862
|
-
const unstageable = itemUnstageable(gitFile);
|
|
8863
|
-
return b2`
|
|
8864
|
-
<button
|
|
8865
|
-
type="button"
|
|
8866
|
-
class="doorstop-review"
|
|
8867
|
-
?disabled=${reviewed || this.runInProgress !== void 0}
|
|
8868
|
-
title=${reviewed ? `${item.uid} is already reviewed against its current fingerprint` : `Mark ${item.uid} reviewed`}
|
|
8869
|
-
@click=${() => {
|
|
8870
|
-
this.reviewItem(item);
|
|
8871
|
-
}}
|
|
8872
|
-
>Review</button>
|
|
8873
|
-
<button
|
|
8874
|
-
type="button"
|
|
8875
|
-
class="doorstop-clear"
|
|
8876
|
-
?disabled=${suspects.length === 0 || this.runInProgress !== void 0}
|
|
8877
|
-
title=${suspects.length === 0 ? `No suspect links to clear` : `Re-record the parent fingerprints of ${item.uid}`}
|
|
8878
|
-
@click=${() => {
|
|
8879
|
-
this.clearSuspects(item, suspectUids);
|
|
8880
|
-
}}
|
|
8881
|
-
>Clear suspect links</button>
|
|
8882
|
-
<button
|
|
8883
|
-
type="button"
|
|
8884
|
-
class="doorstop-edit"
|
|
8885
|
-
?disabled=${this.runInProgress !== void 0}
|
|
8886
|
-
title=${`Open ${item.uid} in the editor`}
|
|
8887
|
-
@click=${() => {
|
|
8888
|
-
this.editItem(item);
|
|
8889
|
-
}}
|
|
8890
|
-
>Edit</button>
|
|
8891
|
-
${gitStatus === void 0 ? A : b2`<button
|
|
8892
|
-
type="button"
|
|
8893
|
-
class="doorstop-item-stage"
|
|
8894
|
-
?disabled=${this.runInProgress !== void 0 || !stageable}
|
|
8895
|
-
title=${stageable ? `Stage ${item.path}` : `${item.uid} has no unstaged changes \u2014 commit next`}
|
|
8896
|
-
@click=${() => {
|
|
8897
|
-
this.stageItem(item);
|
|
8898
|
-
}}
|
|
8899
|
-
>${gitStageIconSvg}Stage</button>
|
|
8900
|
-
<button
|
|
8901
|
-
type="button"
|
|
8902
|
-
class="doorstop-item-unstage"
|
|
8903
|
-
?disabled=${this.runInProgress !== void 0 || !unstageable}
|
|
8904
|
-
title=${unstageable ? `Unstage ${item.path} (git reset)` : `${item.uid} has nothing staged`}
|
|
8905
|
-
@click=${() => {
|
|
8906
|
-
this.unstageItem(item);
|
|
8907
|
-
}}
|
|
8908
|
-
>${gitUnstageIconSvg}Unstage</button>`}
|
|
8909
|
-
<div class="doorstop-op">
|
|
8910
|
-
<input
|
|
8911
|
-
type="text"
|
|
8912
|
-
class="doorstop-target-input"
|
|
8913
|
-
data-op="unlink"
|
|
8914
|
-
placeholder="parent UID"
|
|
8915
|
-
${n5(this.unlinkInputRef)}
|
|
8916
|
-
@keydown=${this.onTargetKeydown}
|
|
8917
|
-
/>
|
|
8918
|
-
<button type="button" class="doorstop-unlink" title=${`doorstop unlink ${item.uid} <target>`} ?disabled=${this.runInProgress !== void 0} @click=${() => {
|
|
8919
|
-
this.runTargetOp("unlink", this.unlinkInputRef, item);
|
|
8920
|
-
}}>Unlink</button>
|
|
8921
|
-
</div>
|
|
8922
|
-
<div class="doorstop-op">
|
|
8923
|
-
<input
|
|
8924
|
-
type="text"
|
|
8925
|
-
class="doorstop-target-input"
|
|
8926
|
-
data-op="link"
|
|
8927
|
-
placeholder="parent UID"
|
|
8928
|
-
${n5(this.linkInputRef)}
|
|
8929
|
-
@keydown=${this.onTargetKeydown}
|
|
8930
|
-
/>
|
|
8931
|
-
<button type="button" class="doorstop-link" title=${`doorstop link ${item.uid} <target>`} ?disabled=${this.runInProgress !== void 0} @click=${() => {
|
|
8932
|
-
this.runTargetOp("link", this.linkInputRef, item);
|
|
8933
|
-
}}>Link</button>
|
|
8934
|
-
</div>
|
|
8935
|
-
${this.targetError === void 0 ? A : b2`<span class="doorstop-op-error" role="alert">${this.targetError}</span>`}
|
|
8936
|
-
${this.renderAskMenu(item, index, suspects)}
|
|
8937
|
-
`;
|
|
8938
|
-
}
|
|
8939
|
-
renderAskMenu(item, index, suspects) {
|
|
8940
|
-
const children = index.childrenByUid.get(item.uid) ?? [];
|
|
8941
|
-
const childPrefix = firstChildDocumentPrefix(index, item);
|
|
8942
|
-
return b2`
|
|
8943
|
-
<div class="doorstop-menu">
|
|
8944
|
-
<button
|
|
8945
|
-
type="button"
|
|
8946
|
-
class="doorstop-menu-toggle"
|
|
8947
|
-
aria-expanded=${this.askMenuOpen ? "true" : "false"}
|
|
8948
|
-
@click=${this.onAskMenuToggle}
|
|
8949
|
-
>Ask agent</button>
|
|
8950
|
-
${this.askMenuOpen ? b2`
|
|
8951
|
-
<div class="doorstop-menu-items" role="menu" aria-label="Ask the agent about ${item.uid}">
|
|
8952
|
-
<button type="button" role="menuitem" class="doorstop-menu-item doorstop-explain" @click=${() => {
|
|
8953
|
-
this.insertPrompt(explainItemPrompt(item));
|
|
8954
|
-
}}>Explain</button>
|
|
8955
|
-
<button
|
|
8956
|
-
type="button"
|
|
8957
|
-
role="menuitem"
|
|
8958
|
-
class="doorstop-menu-item doorstop-fix-suspects"
|
|
8959
|
-
?disabled=${suspects.length === 0}
|
|
8960
|
-
title=${suspects.length === 0 ? "No suspect links \u2014 nothing to fix" : `Fix the suspect links of ${item.uid}`}
|
|
8961
|
-
@click=${() => {
|
|
8962
|
-
this.insertPrompt(fixSuspectLinksPrompt(item, suspects));
|
|
8963
|
-
}}
|
|
8964
|
-
>Fix suspect links</button>
|
|
8965
|
-
<button
|
|
8966
|
-
type="button"
|
|
8967
|
-
role="menuitem"
|
|
8968
|
-
class="doorstop-menu-item doorstop-draft-child"
|
|
8969
|
-
?disabled=${childPrefix === void 0}
|
|
8970
|
-
title=${childPrefix === void 0 ? "No child document to add to" : `Draft a child item in the ${childPrefix} document`}
|
|
8971
|
-
@click=${() => {
|
|
8972
|
-
if (childPrefix !== void 0) this.insertPrompt(draftChildRequirementPrompt(item, childPrefix));
|
|
8973
|
-
}}
|
|
8974
|
-
>Draft child requirement</button>
|
|
8975
|
-
<button type="button" role="menuitem" class="doorstop-menu-item doorstop-review-readiness" @click=${() => {
|
|
8976
|
-
this.insertPrompt(reviewReadinessPrompt(item, children));
|
|
8977
|
-
}}>Review readiness</button>
|
|
8978
|
-
</div>
|
|
8979
|
-
` : A}
|
|
8980
|
-
</div>
|
|
8981
|
-
`;
|
|
8982
|
-
}
|
|
8983
|
-
/** Stage one item's own file — the same `doorstop.git-stage` operation
|
|
8984
|
-
* with `[item.path]`; the shared run dispatch narrates the uid. Shared
|
|
8985
|
-
* by the row's `git add` span and the palette's Stage button. */
|
|
9537
|
+
/** section-API: stage one item's own file — the same `doorstop.git-stage`
|
|
9538
|
+
* operation with `[item.path]`; the shared run dispatch narrates the uid.
|
|
9539
|
+
* Shared by the row's `git add` span and the palette's Stage button. */
|
|
8986
9540
|
stageItem(item) {
|
|
8987
9541
|
const controller = this.controller;
|
|
8988
9542
|
if (controller === void 0 || controller.runInProgress !== void 0) return;
|
|
8989
9543
|
void controller.runGitStage([item.path], `Git: stage ${item.uid}`);
|
|
8990
9544
|
}
|
|
8991
|
-
/**
|
|
8992
|
-
* `[item.path]`; the shared run dispatch narrates the uid.
|
|
8993
|
-
* the row's minus span and the palette's Unstage button. The
|
|
8994
|
-
* mirrors {@link stageItem}: a run already in flight must never be
|
|
9545
|
+
/** section-API: unstage one item's own file — the `doorstop.git-unstage`
|
|
9546
|
+
* inverse with `[item.path]`; the shared run dispatch narrates the uid.
|
|
9547
|
+
* Shared by the row's minus span and the palette's Unstage button. The
|
|
9548
|
+
* guard mirrors {@link stageItem}: a run already in flight must never be
|
|
8995
9549
|
* overlapped (the disabled states cover pointer clicks; this covers
|
|
8996
9550
|
* every call path). */
|
|
8997
9551
|
unstageItem(item) {
|
|
@@ -8999,85 +9553,7 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8999
9553
|
if (controller === void 0 || controller.runInProgress !== void 0) return;
|
|
9000
9554
|
void controller.runGitUnstage([item.path], `Git: unstage ${item.uid}`);
|
|
9001
9555
|
}
|
|
9002
|
-
/**
|
|
9003
|
-
* The browser must never send an empty commit message: the server
|
|
9004
|
-
* rejects it, and git with an empty `-m` would hang the exec until the
|
|
9005
|
-
* deadline. The input is cleared only on SUCCESS so a `failed` commit
|
|
9006
|
-
* (hook stderr, missing identity) keeps the message for a corrected
|
|
9007
|
-
* retry.
|
|
9008
|
-
*/
|
|
9009
|
-
gitCommitSubmit() {
|
|
9010
|
-
const controller = this.controller;
|
|
9011
|
-
if (controller === void 0) return;
|
|
9012
|
-
if (controller.runInProgress !== void 0) return;
|
|
9013
|
-
const input = this.gitCommitInputRef.value;
|
|
9014
|
-
const message = input?.value.trim() ?? "";
|
|
9015
|
-
if (message === "") {
|
|
9016
|
-
this.gitActionError = "Enter a commit message";
|
|
9017
|
-
input?.focus();
|
|
9018
|
-
return;
|
|
9019
|
-
}
|
|
9020
|
-
this.gitActionError = void 0;
|
|
9021
|
-
void controller.runGitCommit(message).then(() => {
|
|
9022
|
-
const lastRun = controller.lastRun;
|
|
9023
|
-
if (lastRun?.op === "git-commit" && lastRun.status === "ok" && input !== void 0) {
|
|
9024
|
-
input.value = "";
|
|
9025
|
-
this.gitCommitMessage = "";
|
|
9026
|
-
}
|
|
9027
|
-
});
|
|
9028
|
-
}
|
|
9029
|
-
reviewItem(item) {
|
|
9030
|
-
const commit = doorstopCommitAfterReview(this.result);
|
|
9031
|
-
this.runDoorstop(
|
|
9032
|
-
"review",
|
|
9033
|
-
`Doorstop: review ${item.uid}`,
|
|
9034
|
-
commit ? { op: "review", uid: item.uid, commit: true } : { op: "review", uid: item.uid },
|
|
9035
|
-
`doorstop review ${item.uid}`,
|
|
9036
|
-
false
|
|
9037
|
-
);
|
|
9038
|
-
}
|
|
9039
|
-
clearSuspects(item, suspectUids) {
|
|
9040
|
-
if (suspectUids.length === 0) return;
|
|
9041
|
-
this.runDoorstop(
|
|
9042
|
-
"clear",
|
|
9043
|
-
"Doorstop: clear suspect links",
|
|
9044
|
-
{ op: "clear", uid: item.uid, parents: suspectUids },
|
|
9045
|
-
`doorstop clear ${item.uid} ${suspectUids.join(" ")}`,
|
|
9046
|
-
false
|
|
9047
|
-
);
|
|
9048
|
-
}
|
|
9049
|
-
editItem(item) {
|
|
9050
|
-
this.runDoorstop(
|
|
9051
|
-
"edit",
|
|
9052
|
-
`Doorstop: edit ${item.uid}`,
|
|
9053
|
-
{ op: "edit", uid: item.uid },
|
|
9054
|
-
`doorstop edit ${item.uid}`,
|
|
9055
|
-
false
|
|
9056
|
-
);
|
|
9057
|
-
}
|
|
9058
|
-
runTargetOp(op, inputRef, item) {
|
|
9059
|
-
const input = inputRef.value;
|
|
9060
|
-
const target = input?.value.trim() ?? "";
|
|
9061
|
-
if (target === "") {
|
|
9062
|
-
this.targetError = `Enter a ${op} target UID (e.g. ${item.documentPrefix}0001)`;
|
|
9063
|
-
input?.focus();
|
|
9064
|
-
return;
|
|
9065
|
-
}
|
|
9066
|
-
if (!isValidTargetUid(target)) {
|
|
9067
|
-
this.targetError = `Invalid ${op} target "${target}" \u2014 expected a Doorstop UID like ${item.documentPrefix}0001 (no spaces, quotes, or special characters)`;
|
|
9068
|
-
input?.focus();
|
|
9069
|
-
return;
|
|
9070
|
-
}
|
|
9071
|
-
this.targetError = void 0;
|
|
9072
|
-
if (input !== void 0) input.value = "";
|
|
9073
|
-
this.runDoorstop(
|
|
9074
|
-
op,
|
|
9075
|
-
`Doorstop: ${op} ${item.uid}`,
|
|
9076
|
-
{ op, uid: item.uid, target },
|
|
9077
|
-
`doorstop ${op} ${item.uid} ${target}`,
|
|
9078
|
-
false
|
|
9079
|
-
);
|
|
9080
|
-
}
|
|
9556
|
+
/** section-API: insert an Ask-agent prompt and close the menu. */
|
|
9081
9557
|
insertPrompt(text) {
|
|
9082
9558
|
const context = this.context;
|
|
9083
9559
|
if (context === void 0) return;
|
|
@@ -9088,9 +9564,9 @@ function defineDoorstopPanelBodyElement() {
|
|
|
9088
9564
|
/**
|
|
9089
9565
|
* The single dispatch surface of every panel action, with TWO paths:
|
|
9090
9566
|
*
|
|
9091
|
-
* -
|
|
9092
|
-
* with an active
|
|
9093
|
-
* `context.
|
|
9567
|
+
* - PEER path when the workspace is owned by the opendoor provider
|
|
9568
|
+
* with an active peer: the STRUCTURED `input` goes through
|
|
9569
|
+
* `context.peer.request("doorstop.run", …)` — argv is built
|
|
9094
9570
|
* server-side, so the browser never shell-quotes on this path. A
|
|
9095
9571
|
* rejected request commits `status: "error"` and does NOT
|
|
9096
9572
|
* invalidate (the run wrote nothing to the workspace).
|
|
@@ -9106,8 +9582,8 @@ function defineDoorstopPanelBodyElement() {
|
|
|
9106
9582
|
const controller = this.controller;
|
|
9107
9583
|
if (context === void 0 || controller === void 0) return;
|
|
9108
9584
|
if (controller.runInProgress !== void 0) return;
|
|
9109
|
-
if (context.
|
|
9110
|
-
void this.runDoorstopBackend(op, title, input, context.
|
|
9585
|
+
if (context.peer?.request !== void 0 && context.workspace.provider?.pluginId === OPENDOOR_PLUGIN_ID) {
|
|
9586
|
+
void this.runDoorstopBackend(op, title, input, context.peer);
|
|
9111
9587
|
return;
|
|
9112
9588
|
}
|
|
9113
9589
|
const terminal = context.terminal;
|
|
@@ -9121,13 +9597,15 @@ function defineDoorstopPanelBodyElement() {
|
|
|
9121
9597
|
}).catch(() => {
|
|
9122
9598
|
});
|
|
9123
9599
|
}
|
|
9124
|
-
async runDoorstopBackend(op, title, input,
|
|
9600
|
+
async runDoorstopBackend(op, title, input, peer) {
|
|
9125
9601
|
const controller = this.controller;
|
|
9126
9602
|
if (controller === void 0) return;
|
|
9603
|
+
const request = peer.request;
|
|
9604
|
+
if (request === void 0) return;
|
|
9127
9605
|
controller.beginRun(title);
|
|
9128
9606
|
const startedAt = Date.now();
|
|
9129
9607
|
try {
|
|
9130
|
-
const response = await
|
|
9608
|
+
const response = await request(DOORSTOP_RUN_OPERATION, input);
|
|
9131
9609
|
const parsed = parseDoorstopRunResponse(response);
|
|
9132
9610
|
const status = parsed.signal !== null ? "killed" : parsed.exitCode === 0 ? "ok" : "failed";
|
|
9133
9611
|
controller.commitRun({
|
|
@@ -9167,12 +9645,21 @@ function defineDoorstopPanelBodyElement() {
|
|
|
9167
9645
|
controller.endRun();
|
|
9168
9646
|
}
|
|
9169
9647
|
}
|
|
9648
|
+
/** section-API: the currently selected item, resolved against the
|
|
9649
|
+
* loaded index. */
|
|
9170
9650
|
selectedItem() {
|
|
9171
9651
|
const result = this.result;
|
|
9172
9652
|
const uid = this.selectedUid;
|
|
9173
9653
|
if (result === void 0 || uid === void 0) return void 0;
|
|
9174
9654
|
return result.index.byUid.get(uid);
|
|
9175
9655
|
}
|
|
9656
|
+
/** section-API: the shared state chip (item rows + detail head). The
|
|
9657
|
+
* renderer is a free function in `sections/item-list.ts`; this delegate
|
|
9658
|
+
* lets detail-pane reach it through the host, keeping the runtime import
|
|
9659
|
+
* graph one-way (coordinator → sections). */
|
|
9660
|
+
renderStateChip(key) {
|
|
9661
|
+
return renderStateChip(key);
|
|
9662
|
+
}
|
|
9176
9663
|
}
|
|
9177
9664
|
__decorateClass([
|
|
9178
9665
|
n6({ attribute: false })
|
|
@@ -9252,18 +9739,19 @@ function defineCustomElementOnce(tag, define) {
|
|
|
9252
9739
|
define();
|
|
9253
9740
|
}
|
|
9254
9741
|
|
|
9255
|
-
// src/doorstop-contributions.ts
|
|
9256
|
-
var
|
|
9742
|
+
// src/browser/doorstop-contributions.ts
|
|
9743
|
+
var DOORSTOP_PANEL_LOCAL_ID2 = "workspace.doorstop";
|
|
9257
9744
|
var DOORSTOP_LABEL_STATE_LIMIT = 8;
|
|
9258
9745
|
function createOpendoorBrowserContributions(runtimePluginId, _html, _svg) {
|
|
9259
|
-
const panelId = `${runtimePluginId}:${
|
|
9746
|
+
const panelId = `${runtimePluginId}:${DOORSTOP_PANEL_LOCAL_ID2}`;
|
|
9260
9747
|
const registry = new DoorstopWorkspaceRegistry();
|
|
9261
9748
|
const labelCache = new DoorstopLabelCache();
|
|
9262
9749
|
defineDoorstopPanelElements();
|
|
9263
9750
|
return {
|
|
9264
9751
|
actions: createDoorstopActions(panelId),
|
|
9265
9752
|
workspacePanels: [createDoorstopPanel(registry)],
|
|
9266
|
-
workspaceLabels: [createDoorstopLabel(labelCache)]
|
|
9753
|
+
workspaceLabels: [createDoorstopLabel(labelCache)],
|
|
9754
|
+
sessionLabels: [createDoorstopSessionLabel(runtimePluginId)]
|
|
9267
9755
|
};
|
|
9268
9756
|
}
|
|
9269
9757
|
function createDoorstopActions(panelId) {
|
|
@@ -9282,7 +9770,7 @@ function createDoorstopActions(panelId) {
|
|
|
9282
9770
|
// Enabled unconditionally: the Requirements panel is useful for every
|
|
9283
9771
|
// workspace (paired or unpaired — it needs no owned provider).
|
|
9284
9772
|
run: (context) => {
|
|
9285
|
-
context.
|
|
9773
|
+
context.selectWorkspaceTool(panelId);
|
|
9286
9774
|
}
|
|
9287
9775
|
},
|
|
9288
9776
|
{
|
|
@@ -9303,7 +9791,7 @@ function createDoorstopActions(panelId) {
|
|
|
9303
9791
|
}
|
|
9304
9792
|
function createDoorstopPanel(registry) {
|
|
9305
9793
|
return {
|
|
9306
|
-
id:
|
|
9794
|
+
id: DOORSTOP_PANEL_LOCAL_ID2,
|
|
9307
9795
|
title: "Requirements",
|
|
9308
9796
|
// Door/archive icon (currentColor): a filing cabinet with the doorstop
|
|
9309
9797
|
// checklist mark — the panel title icon, created with this module's own
|
|
@@ -9315,18 +9803,53 @@ function createDoorstopPanel(registry) {
|
|
|
9315
9803
|
<path d="m9 12 2 2 4-4"></path>
|
|
9316
9804
|
</svg>
|
|
9317
9805
|
`,
|
|
9318
|
-
order:
|
|
9806
|
+
// Plan Phase C step C3 follow-up — order 5: the host's chat-file-link
|
|
9807
|
+
// dispatch (resolveWorkspaceFileOpen) opens the FIRST ACCEPTING panel,
|
|
9808
|
+
// iterating panels by `order` ascending, and the bundled Files panel is
|
|
9809
|
+
// order 10 and answers `{ file: path }` for EVERY path unconditionally —
|
|
9810
|
+
// at order 60 this panel's `fileOpenQuery` was never consulted at all.
|
|
9811
|
+
// Sitting below 10 is the only host-supported way to claim doorstop item
|
|
9812
|
+
// files for the Requirements panel; the accepted consequence is that the
|
|
9813
|
+
// Requirements tab precedes Files in the workspace tab bar.
|
|
9814
|
+
order: 5,
|
|
9319
9815
|
// The panel is always visible (paired or unpaired): when the workspace
|
|
9320
9816
|
// is opendoor-owned the run actions use the backend, otherwise the
|
|
9321
9817
|
// terminal fallback keeps every action working; the discovery-based
|
|
9322
9818
|
// empty state carries the "no documents" story (feature spec §7.1).
|
|
9323
9819
|
visible: () => true,
|
|
9324
|
-
|
|
9820
|
+
// Plan Phase C step C1 — typed invalidation (browser API v4): declare the
|
|
9821
|
+
// fixed workspace resources whose automatic invalidations this panel
|
|
9822
|
+
// receives. With `workspace.files` declared, the host calls onInvalidate
|
|
9823
|
+
// not only for the manual Refresh action (refreshWorkspacePanels) but
|
|
9824
|
+
// also when workspace files change (`reason: "mutation"` — e.g. the
|
|
9825
|
+
// review-commit pipeline's commits landing, or any other plugin's file
|
|
9826
|
+
// writes) and when agent work finishes in the workspace (`reason:
|
|
9827
|
+
// "agent-activity"` — an agent editing doorstop documents refreshes the
|
|
9828
|
+
// panel without a manual Refresh). The reason is forwarded to the
|
|
9829
|
+
// registry, which rescans unconditionally (see
|
|
9830
|
+
// DoorstopWorkspaceRegistry.invalidate).
|
|
9831
|
+
invalidationResources: ["workspace.files"],
|
|
9832
|
+
onInvalidate: (context, invalidation) => registry.invalidate(context, invalidation),
|
|
9833
|
+
// Plan Phase C step C3 — chat file links: when the host resolves a
|
|
9834
|
+
// Markdown link to a workspace-relative path, this hook answers with the
|
|
9835
|
+
// panel's navigation query IF the panel owns the path (its last landed
|
|
9836
|
+
// discovery indexed it as an item file), so the chat opens the item IN
|
|
9837
|
+
// the Requirements panel instead of the Files fallback. Synchronous and
|
|
9838
|
+
// side-effect-free: it answers purely from the registry's recorded index
|
|
9839
|
+
// (never loads, never renders); unknown paths and pre-first-load panels
|
|
9840
|
+
// return undefined (the host asks the next panel / serves Files).
|
|
9841
|
+
fileOpenQuery: (context, path) => registry.fileOpenQuery(context, path),
|
|
9325
9842
|
render: (context) => renderDoorstopPanel(registry, context)
|
|
9326
9843
|
};
|
|
9327
9844
|
}
|
|
9328
9845
|
function renderDoorstopPanel(registry, context) {
|
|
9329
9846
|
const controller = registry.for(context);
|
|
9847
|
+
if (context.navigation !== void 0) {
|
|
9848
|
+
controller.applyNavigationQuery(readPanelNavigationQuery(context.navigation));
|
|
9849
|
+
}
|
|
9850
|
+
if (controller.result !== void 0) {
|
|
9851
|
+
registry.recordFileOpenIndex(context, controller.result);
|
|
9852
|
+
}
|
|
9330
9853
|
return b2`
|
|
9331
9854
|
<pi-web-opendoor-panel-body
|
|
9332
9855
|
.controller=${controller}
|
|
@@ -9425,12 +9948,12 @@ function doorstopLabelKey(context) {
|
|
|
9425
9948
|
return JSON.stringify([context.machine.id, context.workspace.projectId, context.workspace.id]);
|
|
9426
9949
|
}
|
|
9427
9950
|
|
|
9428
|
-
// src/pi-web-plugin.ts
|
|
9951
|
+
// src/browser/pi-web-plugin.ts
|
|
9429
9952
|
var plugin = {
|
|
9430
|
-
apiVersion:
|
|
9953
|
+
apiVersion: 4,
|
|
9431
9954
|
name: "Opendoor",
|
|
9432
|
-
activate: ({ runtimePluginId, html, svg
|
|
9433
|
-
contributions: createOpendoorBrowserContributions(runtimePluginId, html,
|
|
9955
|
+
activate: ({ runtimePluginId, html, svg }) => ({
|
|
9956
|
+
contributions: createOpendoorBrowserContributions(runtimePluginId, html, svg)
|
|
9434
9957
|
})
|
|
9435
9958
|
};
|
|
9436
9959
|
var pi_web_plugin_default = plugin;
|
|
@@ -9451,7 +9974,6 @@ lit-html/lit-html.js:
|
|
|
9451
9974
|
lit-element/lit-element.js:
|
|
9452
9975
|
lit-html/directive.js:
|
|
9453
9976
|
lit-html/async-directive.js:
|
|
9454
|
-
lit-html/directives/repeat.js:
|
|
9455
9977
|
@lit/reactive-element/decorators/custom-element.js:
|
|
9456
9978
|
@lit/reactive-element/decorators/property.js:
|
|
9457
9979
|
@lit/reactive-element/decorators/state.js:
|
|
@@ -9461,6 +9983,7 @@ lit-html/directives/repeat.js:
|
|
|
9461
9983
|
@lit/reactive-element/decorators/query-all.js:
|
|
9462
9984
|
@lit/reactive-element/decorators/query-async.js:
|
|
9463
9985
|
@lit/reactive-element/decorators/query-assigned-nodes.js:
|
|
9986
|
+
lit-html/directives/repeat.js:
|
|
9464
9987
|
(**
|
|
9465
9988
|
* @license
|
|
9466
9989
|
* Copyright 2017 Google LLC
|
|
@@ -9474,13 +9997,6 @@ lit-html/is-server.js:
|
|
|
9474
9997
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
9475
9998
|
*)
|
|
9476
9999
|
|
|
9477
|
-
lit-html/directives/class-map.js:
|
|
9478
|
-
(**
|
|
9479
|
-
* @license
|
|
9480
|
-
* Copyright 2018 Google LLC
|
|
9481
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
9482
|
-
*)
|
|
9483
|
-
|
|
9484
10000
|
lit-html/directive-helpers.js:
|
|
9485
10001
|
lit-html/directives/ref.js:
|
|
9486
10002
|
(**
|
|
@@ -9489,11 +10005,18 @@ lit-html/directives/ref.js:
|
|
|
9489
10005
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
9490
10006
|
*)
|
|
9491
10007
|
|
|
9492
|
-
lit-html/directives/keyed.js:
|
|
9493
10008
|
@lit/reactive-element/decorators/query-assigned-elements.js:
|
|
10009
|
+
lit-html/directives/keyed.js:
|
|
9494
10010
|
(**
|
|
9495
10011
|
* @license
|
|
9496
10012
|
* Copyright 2021 Google LLC
|
|
9497
10013
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
9498
10014
|
*)
|
|
10015
|
+
|
|
10016
|
+
lit-html/directives/class-map.js:
|
|
10017
|
+
(**
|
|
10018
|
+
* @license
|
|
10019
|
+
* Copyright 2018 Google LLC
|
|
10020
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
10021
|
+
*)
|
|
9499
10022
|
*/
|