opendoor-plugin 0.2.0 → 0.5.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 +222 -243
- package/dist/browser/pi-web-plugin.js +2121 -1452
- package/dist/package.json +6 -29
- package/dist/pi-extension/index.js +136 -0
- package/dist/server-plugin.js +54 -21
- package/package.json +19 -10
|
@@ -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,68 @@ 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 typed invalidation (browser API v4,
|
|
6566
|
+
* unchanged on v5): `invalidation` carries
|
|
6567
|
+
* the reason (`"manual"` Refresh action / `"mutation"` a workspace file
|
|
6568
|
+
* changed / `"agent-activity"` agent work finished) and the affected
|
|
6569
|
+
* resources. The rescan stays REASON-AGNOSTIC deliberately: every reason
|
|
6570
|
+
* may have changed item files, the load job re-reads everything anyway
|
|
6571
|
+
* (no partial invalidation is possible — the index is one unit), and the
|
|
6572
|
+
* controller's re-entrant guard coalesces storms (an invalidate while a
|
|
6573
|
+
* load is running JOINS that load; only an invalidate after it lands
|
|
6574
|
+
* starts a fresh one), so `"agent-activity"` bursts cannot stack loads.
|
|
6575
|
+
* The payload is accepted (so the contribution can forward it) but not
|
|
6576
|
+
* consulted. */
|
|
6577
|
+
invalidate(context, invalidation) {
|
|
6578
|
+
void invalidation;
|
|
6378
6579
|
return this.for(context).invalidate();
|
|
6379
6580
|
}
|
|
6581
|
+
/** Record the chat file-open index for one workspace from a LANDED result
|
|
6582
|
+
* (plan Phase C step C3). Called from renderDoorstopPanel on every render
|
|
6583
|
+
* that has a result, so the index always mirrors the last landed
|
|
6584
|
+
* discovery; never throws and triggers nothing (the hook must be free of
|
|
6585
|
+
* side effects — recording a plain index from a landed result is one). */
|
|
6586
|
+
recordFileOpenIndex(context, result) {
|
|
6587
|
+
const key = workspaceContextKey(context);
|
|
6588
|
+
const existing = this.fileOpenEntries.get(key);
|
|
6589
|
+
if (existing !== void 0 && existing.result === result) {
|
|
6590
|
+
this.fileOpenEntries.delete(key);
|
|
6591
|
+
this.fileOpenEntries.set(key, existing);
|
|
6592
|
+
return;
|
|
6593
|
+
}
|
|
6594
|
+
const entries = /* @__PURE__ */ new Map();
|
|
6595
|
+
for (const item of result.index.items) {
|
|
6596
|
+
entries.set(item.path, { uid: item.uid, documentPrefix: item.documentPrefix });
|
|
6597
|
+
}
|
|
6598
|
+
this.fileOpenEntries.delete(key);
|
|
6599
|
+
this.fileOpenEntries.set(key, { result, entries });
|
|
6600
|
+
this.boundFileOpenEntries();
|
|
6601
|
+
}
|
|
6602
|
+
/** The panel's chat file-open hook (plan Phase C step C3): does the last
|
|
6603
|
+
* landed discovery of this workspace know `path` as an item file? Returns
|
|
6604
|
+
* the panel's navigation query (`{ file: path }` — the key
|
|
6605
|
+
* applyNavigationQuery resolves to the item's uid and document
|
|
6606
|
+
* selection), or undefined when the panel does not own the path (the
|
|
6607
|
+
* host then asks the next panel, or serves the Files fallback). Before
|
|
6608
|
+
* the first load lands there is no index yet → undefined (the chat link
|
|
6609
|
+
* falls back to Files; the panel still deep-links once opened and
|
|
6610
|
+
* re-rendered). */
|
|
6611
|
+
fileOpenQuery(context, path) {
|
|
6612
|
+
const entries = this.fileOpenEntries.get(workspaceContextKey(context));
|
|
6613
|
+
if (entries === void 0) return void 0;
|
|
6614
|
+
return entries.entries.has(path) ? { [DOORSTOP_NAV_FILE]: path } : void 0;
|
|
6615
|
+
}
|
|
6616
|
+
/** Keep the file-open index at the same LRU bound as the controller map
|
|
6617
|
+
* (strictly oldest-first, mirroring evictOldest). */
|
|
6618
|
+
boundFileOpenEntries() {
|
|
6619
|
+
while (this.fileOpenEntries.size > DOORSTOP_WORKSPACE_STATE_LIMIT) {
|
|
6620
|
+
const key = this.fileOpenEntries.keys().next().value;
|
|
6621
|
+
if (key === void 0) break;
|
|
6622
|
+
this.fileOpenEntries.delete(key);
|
|
6623
|
+
}
|
|
6624
|
+
}
|
|
6380
6625
|
evictOldest() {
|
|
6381
6626
|
if (this.workspaces.size < DOORSTOP_WORKSPACE_STATE_LIMIT) return;
|
|
6382
6627
|
const key = this.workspaces.keys().next().value;
|
|
@@ -6384,6 +6629,7 @@ var DoorstopWorkspaceRegistry = class {
|
|
|
6384
6629
|
const controller = this.workspaces.get(key);
|
|
6385
6630
|
if (controller !== void 0) controller.release();
|
|
6386
6631
|
this.workspaces.delete(key);
|
|
6632
|
+
this.fileOpenEntries.delete(key);
|
|
6387
6633
|
}
|
|
6388
6634
|
};
|
|
6389
6635
|
var DoorstopPanelHost = class {
|
|
@@ -6395,127 +6641,94 @@ function workspaceContextKey(context) {
|
|
|
6395
6641
|
return JSON.stringify([context.machine.id, context.workspace.projectId, context.workspace.id]);
|
|
6396
6642
|
}
|
|
6397
6643
|
|
|
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";
|
|
6644
|
+
// src/doorstop-session-metadata.ts
|
|
6645
|
+
var OPENDOOR_SESSION_METADATA_NAMESPACE = "opendoor";
|
|
6646
|
+
var DOORSTOP_SESSION_METADATA_SCHEMA = 1;
|
|
6647
|
+
var DOORSTOP_SESSION_METADATA_MAX_UIDS = 8;
|
|
6648
|
+
var DOORSTOP_SESSION_METADATA_MAX_UID_LENGTH = 32;
|
|
6649
|
+
var DOORSTOP_SESSION_METADATA_OPS = /* @__PURE__ */ new Set(["review"]);
|
|
6650
|
+
var DOORSTOP_SESSION_METADATA_UID_PATTERN = /^[\w.-]+$/u;
|
|
6651
|
+
function isMetadataUid(value) {
|
|
6652
|
+
return typeof value === "string" && value !== "" && value.length <= DOORSTOP_SESSION_METADATA_MAX_UID_LENGTH && DOORSTOP_SESSION_METADATA_UID_PATTERN.test(value);
|
|
6653
|
+
}
|
|
6654
|
+
function parseDoorstopSessionMetadata(value) {
|
|
6655
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
6656
|
+
const record = value;
|
|
6657
|
+
if (record["schema"] !== DOORSTOP_SESSION_METADATA_SCHEMA) return void 0;
|
|
6658
|
+
const op = record["op"];
|
|
6659
|
+
if (typeof op !== "string" || !DOORSTOP_SESSION_METADATA_OPS.has(op)) {
|
|
6660
|
+
return void 0;
|
|
6428
6661
|
}
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
return "has suspect links";
|
|
6662
|
+
const uidsValue = record["uids"];
|
|
6663
|
+
if (!Array.isArray(uidsValue) || uidsValue.length === 0 || uidsValue.length > DOORSTOP_SESSION_METADATA_MAX_UIDS) {
|
|
6664
|
+
return void 0;
|
|
6665
|
+
}
|
|
6666
|
+
const uids = [];
|
|
6667
|
+
for (const uid of uidsValue) {
|
|
6668
|
+
if (!isMetadataUid(uid)) return void 0;
|
|
6669
|
+
uids.push(uid);
|
|
6438
6670
|
}
|
|
6671
|
+
return {
|
|
6672
|
+
schema: DOORSTOP_SESSION_METADATA_SCHEMA,
|
|
6673
|
+
op,
|
|
6674
|
+
uids
|
|
6675
|
+
};
|
|
6439
6676
|
}
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
const
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6677
|
+
|
|
6678
|
+
// src/browser/doorstop-session-labels.ts
|
|
6679
|
+
var DOORSTOP_PANEL_LOCAL_ID = "workspace.doorstop";
|
|
6680
|
+
var DOORSTOP_SESSION_LABEL_ID = "doorstop.review";
|
|
6681
|
+
function createDoorstopSessionLabel(runtimePluginId) {
|
|
6682
|
+
const panelId = `${runtimePluginId}:${DOORSTOP_PANEL_LOCAL_ID}`;
|
|
6683
|
+
return {
|
|
6684
|
+
id: DOORSTOP_SESSION_LABEL_ID,
|
|
6685
|
+
order: 10,
|
|
6686
|
+
items: (context) => sessionLabelItems(context, panelId)
|
|
6687
|
+
};
|
|
6450
6688
|
}
|
|
6451
|
-
function
|
|
6452
|
-
const
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6689
|
+
function sessionLabelItems(context, panelId) {
|
|
6690
|
+
const value = context.session.metadata?.[OPENDOOR_SESSION_METADATA_NAMESPACE];
|
|
6691
|
+
const metadata = parseDoorstopSessionMetadata(value);
|
|
6692
|
+
if (metadata === void 0) return [];
|
|
6693
|
+
const firstUid = metadata.uids[0];
|
|
6694
|
+
if (firstUid === void 0) return [];
|
|
6695
|
+
const rest = metadata.uids.length - 1;
|
|
6696
|
+
const text = rest === 0 ? firstUid : `${firstUid} +${String(rest)}`;
|
|
6697
|
+
const target = doorstopReopenDestination(context.machine.id, context.workspace, panelId, firstUid);
|
|
6698
|
+
return [
|
|
6699
|
+
{
|
|
6700
|
+
type: "render",
|
|
6701
|
+
render: () => b2`<a
|
|
6702
|
+
class="doorstop-session-label"
|
|
6703
|
+
href="#"
|
|
6704
|
+
title=${`Open ${metadata.uids.join(", ")} in Requirements`}
|
|
6705
|
+
@click=${(event) => {
|
|
6706
|
+
event.preventDefault();
|
|
6707
|
+
event.stopPropagation();
|
|
6708
|
+
void context.navigate(target);
|
|
6709
|
+
}}
|
|
6710
|
+
>${text}</a>`
|
|
6460
6711
|
}
|
|
6461
|
-
|
|
6462
|
-
|
|
6712
|
+
];
|
|
6713
|
+
}
|
|
6714
|
+
function doorstopReopenDestination(machineId, workspace, panelId, uid) {
|
|
6715
|
+
return {
|
|
6716
|
+
machineId,
|
|
6717
|
+
projectId: workspace.projectId,
|
|
6718
|
+
workspaceId: workspace.id,
|
|
6719
|
+
view: "workspace",
|
|
6720
|
+
tool: panelId,
|
|
6721
|
+
query: { [DOORSTOP_NAV_UID]: uid }
|
|
6722
|
+
};
|
|
6463
6723
|
}
|
|
6724
|
+
|
|
6725
|
+
// src/browser/view-model/core.ts
|
|
6464
6726
|
function shortFingerprint(fingerprint) {
|
|
6465
6727
|
return fingerprint === null ? "none" : `${fingerprint.slice(0, 8)}\u2026`;
|
|
6466
6728
|
}
|
|
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
6729
|
function doorstopPublishTarget(result) {
|
|
6494
6730
|
return result?.settings?.publishTarget ?? DEFAULT_OPENDOOR_SETTINGS.publishTarget;
|
|
6495
6731
|
}
|
|
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
6732
|
function lastRunHasMessage(lastRun) {
|
|
6520
6733
|
return lastRun.stdout !== "" || lastRun.stderr !== "" || (lastRun.errorMessage ?? "") !== "" || lastRun.commit !== void 0;
|
|
6521
6734
|
}
|
|
@@ -6591,7 +6804,6 @@ function doorstopPublishCommand(result, target = doorstopPublishTarget(result))
|
|
|
6591
6804
|
const argument = PUBLISH_TARGET_SAFE_TOKEN.test(target) ? target : quoteShellArgument(target);
|
|
6592
6805
|
return `doorstop publish all ${argument}`;
|
|
6593
6806
|
}
|
|
6594
|
-
var isValidTargetUid = isValidDoorstopUid;
|
|
6595
6807
|
function documentConfigFor(index, item) {
|
|
6596
6808
|
return index.byPrefix.get(item.documentPrefix) ?? {
|
|
6597
6809
|
directoryPath: "",
|
|
@@ -6603,29 +6815,15 @@ function documentConfigFor(index, item) {
|
|
|
6603
6815
|
extra: {}
|
|
6604
6816
|
};
|
|
6605
6817
|
}
|
|
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
6818
|
function jsonishText(value) {
|
|
6625
6819
|
if (value === void 0) return "undefined";
|
|
6626
6820
|
const json2 = JSON.stringify(value);
|
|
6627
6821
|
return json2 === void 0 ? String(value) : json2;
|
|
6628
6822
|
}
|
|
6823
|
+
var DOORSTOP_MARKDOWN_ATTRIBUTES = Object.freeze(["rationale"]);
|
|
6824
|
+
function isMarkdownAttribute(key, value) {
|
|
6825
|
+
return typeof value === "string" && DOORSTOP_MARKDOWN_ATTRIBUTES.includes(key.toLowerCase());
|
|
6826
|
+
}
|
|
6629
6827
|
function diffValueText(value) {
|
|
6630
6828
|
return value === void 0 ? "\u2014" : jsonishText(value);
|
|
6631
6829
|
}
|
|
@@ -6637,68 +6835,179 @@ function referenceListText(references) {
|
|
|
6637
6835
|
return parts.join(" ");
|
|
6638
6836
|
}).join(", ");
|
|
6639
6837
|
}
|
|
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
6838
|
|
|
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
|
-
}
|
|
6839
|
+
// src/browser/view-model/item-list.ts
|
|
6840
|
+
var EMPTY_WORKSPACE_MESSAGE = "This workspace has no Doorstop documents \u2014 run `doorstop create REQ ./reqs` in the workspace to start a requirements tree.";
|
|
6841
|
+
var ALL_DOCUMENTS_LABEL = "All";
|
|
6842
|
+
var STATE_CHIP_LABELS = {
|
|
6843
|
+
normative: "normative",
|
|
6844
|
+
"non-normative": "non-normative",
|
|
6845
|
+
inactive: "inactive",
|
|
6846
|
+
reviewed: "reviewed",
|
|
6847
|
+
unreviewed: "unreviewed",
|
|
6848
|
+
"suspect-link": "suspect link",
|
|
6849
|
+
"no-child-links": "no child links",
|
|
6850
|
+
"no-links": "no links",
|
|
6851
|
+
"unknown-link": "unknown link",
|
|
6852
|
+
"missing-reference": "missing reference"
|
|
6666
6853
|
};
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6854
|
+
function stateChipKind(key) {
|
|
6855
|
+
switch (key) {
|
|
6856
|
+
case "normative":
|
|
6857
|
+
case "non-normative":
|
|
6858
|
+
case "reviewed":
|
|
6859
|
+
return "muted";
|
|
6860
|
+
case "unreviewed":
|
|
6861
|
+
case "no-child-links":
|
|
6862
|
+
case "no-links":
|
|
6863
|
+
return "warning";
|
|
6864
|
+
case "inactive":
|
|
6865
|
+
case "suspect-link":
|
|
6866
|
+
case "unknown-link":
|
|
6867
|
+
case "missing-reference":
|
|
6868
|
+
return "danger";
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6871
|
+
function dotTitle(dot) {
|
|
6872
|
+
switch (dot) {
|
|
6873
|
+
case "ok":
|
|
6874
|
+
return "all items reviewed, no suspect links";
|
|
6875
|
+
case "unreviewed":
|
|
6876
|
+
return "has unreviewed changes";
|
|
6877
|
+
case "suspect":
|
|
6878
|
+
return "has suspect links";
|
|
6879
|
+
}
|
|
6880
|
+
}
|
|
6881
|
+
function documentStateDots(document2, items) {
|
|
6882
|
+
const own = items.filter((item) => item.documentPrefix === document2.prefix);
|
|
6883
|
+
const dots = [];
|
|
6884
|
+
if (own.length === 0) return dots;
|
|
6885
|
+
const hasSuspect = own.some((item) => item.stateKeys.includes("suspect-link"));
|
|
6886
|
+
const hasUnreviewed = own.some((item) => item.stateKeys.includes("unreviewed"));
|
|
6887
|
+
if (!hasSuspect && !hasUnreviewed) dots.push("ok");
|
|
6888
|
+
if (hasUnreviewed) dots.push("unreviewed");
|
|
6889
|
+
if (hasSuspect) dots.push("suspect");
|
|
6890
|
+
return dots;
|
|
6891
|
+
}
|
|
6892
|
+
function filteredItems(index, documentPrefix, stateFilter, search) {
|
|
6893
|
+
const needle = search.trim().toLowerCase();
|
|
6894
|
+
return index.items.filter((item) => {
|
|
6895
|
+
if (documentPrefix !== void 0 && documentPrefix !== "" && item.documentPrefix !== documentPrefix) {
|
|
6896
|
+
return false;
|
|
6681
6897
|
}
|
|
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)));
|
|
6898
|
+
if (stateFilter !== void 0 && !item.stateKeys.includes(stateFilter)) return false;
|
|
6899
|
+
if (needle !== "" && !`${item.uid} ${item.header ?? ""} ${item.text}`.toLowerCase().includes(needle)) {
|
|
6900
|
+
return false;
|
|
6687
6901
|
}
|
|
6688
|
-
return
|
|
6902
|
+
return true;
|
|
6903
|
+
});
|
|
6904
|
+
}
|
|
6905
|
+
function doorstopShowAdditionalAttribute(result) {
|
|
6906
|
+
return result?.settings?.showAdditionalAttribute ?? DEFAULT_OPENDOOR_SETTINGS.showAdditionalAttribute;
|
|
6907
|
+
}
|
|
6908
|
+
function itemExcerpt(item) {
|
|
6909
|
+
const header = item.header;
|
|
6910
|
+
if (header !== void 0 && header !== "") return header;
|
|
6911
|
+
const firstLine = (item.text.split("\n")[0] ?? "").trim();
|
|
6912
|
+
if (firstLine === "") return "\u2014";
|
|
6913
|
+
return firstLine.length > 80 ? `${firstLine.slice(0, 80)}\u2026` : firstLine;
|
|
6914
|
+
}
|
|
6915
|
+
function itemTextRow(item) {
|
|
6916
|
+
return item.text.trim() === "" ? void 0 : item.text;
|
|
6917
|
+
}
|
|
6918
|
+
function shownAttributeRows(item, keys) {
|
|
6919
|
+
const rows = [];
|
|
6920
|
+
for (const key of keys) {
|
|
6921
|
+
if (!Object.prototype.hasOwnProperty.call(item.attributes, key)) continue;
|
|
6922
|
+
rows.push({ key, text: jsonishText(item.attributes[key]) });
|
|
6689
6923
|
}
|
|
6690
|
-
|
|
6924
|
+
return rows;
|
|
6925
|
+
}
|
|
6926
|
+
|
|
6927
|
+
// src/browser/view-model/palette.ts
|
|
6928
|
+
function doorstopCommitAfterReview(result) {
|
|
6929
|
+
return result?.settings?.commitAfterReview ?? DEFAULT_OPENDOOR_SETTINGS.commitAfterReview;
|
|
6930
|
+
}
|
|
6931
|
+
var isValidTargetUid = isValidDoorstopUid;
|
|
6932
|
+
function suspectParentItems(item, index) {
|
|
6933
|
+
const suspects = [];
|
|
6934
|
+
for (const link of item.links) {
|
|
6935
|
+
if (link.fingerprint === null) continue;
|
|
6936
|
+
const target = index.byUid.get(link.uid);
|
|
6937
|
+
if (target === void 0) continue;
|
|
6938
|
+
if (link.fingerprint !== computeItemStamp(target, documentConfigFor(index, target), false)) {
|
|
6939
|
+
suspects.push(target);
|
|
6940
|
+
}
|
|
6941
|
+
}
|
|
6942
|
+
return suspects;
|
|
6943
|
+
}
|
|
6944
|
+
function firstChildDocumentPrefix(index, item) {
|
|
6945
|
+
for (const document2 of index.documents) {
|
|
6946
|
+
if (document2.parentPrefix === item.documentPrefix) return document2.prefix;
|
|
6947
|
+
}
|
|
6948
|
+
return void 0;
|
|
6949
|
+
}
|
|
6950
|
+
|
|
6951
|
+
// src/browser/view-model/status-bar.ts
|
|
6952
|
+
function commitOutcomeText(op, outcome) {
|
|
6953
|
+
const gitRun = op === "git-stage" || op === "git-commit" || op === "git-unstage";
|
|
6954
|
+
switch (outcome.status) {
|
|
6955
|
+
case "staged":
|
|
6956
|
+
return `staged ${String(outcome.staged ?? 0)} paths`;
|
|
6957
|
+
case "unstaged":
|
|
6958
|
+
return `unstaged ${String(outcome.unstaged ?? 0)} paths`;
|
|
6959
|
+
case "committed":
|
|
6960
|
+
return gitRun ? `committed ${outcome.sha ?? "<unknown sha>"}` : `commit: ${outcome.sha ?? "<unknown sha>"}`;
|
|
6961
|
+
case "clean":
|
|
6962
|
+
if (!gitRun) return "commit: clean (already committed)";
|
|
6963
|
+
if (op === "git-stage") return "clean \u2014 nothing to stage";
|
|
6964
|
+
if (op === "git-unstage") return "clean \u2014 nothing to unstage";
|
|
6965
|
+
return "clean \u2014 nothing staged";
|
|
6966
|
+
case "skipped":
|
|
6967
|
+
return gitRun ? "skipped" : "commit: skipped (not a git repository | review failed | deadline)";
|
|
6968
|
+
case "failed":
|
|
6969
|
+
return gitRun ? `failed \u2014 ${outcome.stderr ?? "git step errored"}` : `commit: failed \u2014 ${outcome.stderr ?? "git step errored"}`;
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
|
|
6973
|
+
// src/browser/view-model/findings.ts
|
|
6974
|
+
var FINDINGS_EMPTY_MESSAGE = "No findings \u2014 the tree is clean.";
|
|
6975
|
+
var FINDINGS_EMPTY_HINT = "Findings are plugin-local; run `doorstop` validation in the terminal for the authoritative check.";
|
|
6976
|
+
var FINDINGS_PLUGIN_LOCAL_NOTE = "plugin-local findings \u2014 `doorstop` validation in the terminal is authoritative";
|
|
6977
|
+
var FINDING_SEVERITY_RANK = {
|
|
6978
|
+
error: 0,
|
|
6979
|
+
warning: 1,
|
|
6980
|
+
info: 2
|
|
6981
|
+
};
|
|
6982
|
+
function findingsViewRows(index) {
|
|
6983
|
+
const rows = [...index.findings, ...index.diagnostics];
|
|
6984
|
+
return rows.sort((a3, b3) => FINDING_SEVERITY_RANK[a3.severity] - FINDING_SEVERITY_RANK[b3.severity]);
|
|
6985
|
+
}
|
|
6986
|
+
function findingsViewCounts(rows) {
|
|
6987
|
+
const counts = { errors: 0, warnings: 0, info: 0 };
|
|
6988
|
+
for (const row of rows) {
|
|
6989
|
+
if (row.severity === "error") counts.errors += 1;
|
|
6990
|
+
else if (row.severity === "warning") counts.warnings += 1;
|
|
6991
|
+
else counts.info += 1;
|
|
6992
|
+
}
|
|
6993
|
+
return counts;
|
|
6994
|
+
}
|
|
6995
|
+
function findingsCountText(counts) {
|
|
6996
|
+
const errors = counts.errors === 1 ? "1 error" : `${String(counts.errors)} errors`;
|
|
6997
|
+
const warnings = counts.warnings === 1 ? "1 warning" : `${String(counts.warnings)} warnings`;
|
|
6998
|
+
return `${errors} \xB7 ${warnings} \xB7 ${String(counts.info)} info`;
|
|
6999
|
+
}
|
|
6691
7000
|
|
|
6692
7001
|
// node_modules/lit-html/directive-helpers.js
|
|
6693
|
-
var { I:
|
|
6694
|
-
var
|
|
7002
|
+
var { I: t3 } = j;
|
|
7003
|
+
var i5 = (o8) => o8;
|
|
6695
7004
|
var r4 = (o8) => void 0 === o8.strings;
|
|
6696
7005
|
var s4 = () => document.createComment("");
|
|
6697
7006
|
var v2 = (o8, n7, e8) => {
|
|
6698
7007
|
const l3 = o8._$AA.parentNode, d3 = void 0 === n7 ? o8._$AB : n7._$AA;
|
|
6699
7008
|
if (void 0 === e8) {
|
|
6700
7009
|
const i8 = l3.insertBefore(s4(), d3), n8 = l3.insertBefore(s4(), d3);
|
|
6701
|
-
e8 = new
|
|
7010
|
+
e8 = new t3(i8, n8, o8, o8.options);
|
|
6702
7011
|
} else {
|
|
6703
7012
|
const t5 = e8._$AB.nextSibling, n8 = e8._$AM, c6 = n8 !== o8;
|
|
6704
7013
|
if (c6) {
|
|
@@ -6708,8 +7017,8 @@ var v2 = (o8, n7, e8) => {
|
|
|
6708
7017
|
if (t5 !== d3 || c6) {
|
|
6709
7018
|
let o9 = e8._$AA;
|
|
6710
7019
|
for (; o9 !== t5; ) {
|
|
6711
|
-
const t6 =
|
|
6712
|
-
|
|
7020
|
+
const t6 = i5(o9).nextSibling;
|
|
7021
|
+
i5(l3).insertBefore(o9, d3), o9 = t6;
|
|
6713
7022
|
}
|
|
6714
7023
|
}
|
|
6715
7024
|
}
|
|
@@ -6723,18 +7032,25 @@ var h3 = (o8) => {
|
|
|
6723
7032
|
o8._$AR(), o8._$AA.remove();
|
|
6724
7033
|
};
|
|
6725
7034
|
|
|
6726
|
-
// node_modules/lit-html/
|
|
6727
|
-
var
|
|
6728
|
-
|
|
6729
|
-
|
|
7035
|
+
// node_modules/lit-html/directive.js
|
|
7036
|
+
var t4 = { ATTRIBUTE: 1, CHILD: 2, PROPERTY: 3, BOOLEAN_ATTRIBUTE: 4, EVENT: 5, ELEMENT: 6 };
|
|
7037
|
+
var e4 = (t5) => (...e8) => ({ _$litDirective$: t5, values: e8 });
|
|
7038
|
+
var i6 = class {
|
|
7039
|
+
constructor(t5) {
|
|
6730
7040
|
}
|
|
6731
|
-
|
|
6732
|
-
return this.
|
|
7041
|
+
get _$AU() {
|
|
7042
|
+
return this._$AM._$AU;
|
|
6733
7043
|
}
|
|
6734
|
-
|
|
6735
|
-
|
|
7044
|
+
_$AT(t5, e8, i8) {
|
|
7045
|
+
this._$Ct = t5, this._$AM = e8, this._$Ci = i8;
|
|
6736
7046
|
}
|
|
6737
|
-
|
|
7047
|
+
_$AS(t5, e8) {
|
|
7048
|
+
return this.update(t5, e8);
|
|
7049
|
+
}
|
|
7050
|
+
update(t5, e8) {
|
|
7051
|
+
return this.render(...e8);
|
|
7052
|
+
}
|
|
7053
|
+
};
|
|
6738
7054
|
|
|
6739
7055
|
// node_modules/lit-html/async-directive.js
|
|
6740
7056
|
var s5 = (i8, t5) => {
|
|
@@ -6768,9 +7084,9 @@ function n4(i8, t5 = false, e8 = 0) {
|
|
|
6768
7084
|
else s5(this, i8);
|
|
6769
7085
|
}
|
|
6770
7086
|
var c4 = (i8) => {
|
|
6771
|
-
i8.type ==
|
|
7087
|
+
i8.type == t4.CHILD && (i8._$AP ??= n4, i8._$AQ ??= h4);
|
|
6772
7088
|
};
|
|
6773
|
-
var f3 = class extends
|
|
7089
|
+
var f3 = class extends i6 {
|
|
6774
7090
|
constructor() {
|
|
6775
7091
|
super(...arguments), this._$AN = void 0;
|
|
6776
7092
|
}
|
|
@@ -6794,7 +7110,7 @@ var f3 = class extends i5 {
|
|
|
6794
7110
|
};
|
|
6795
7111
|
|
|
6796
7112
|
// node_modules/lit-html/directives/ref.js
|
|
6797
|
-
var
|
|
7113
|
+
var e5 = () => new h5();
|
|
6798
7114
|
var h5 = class {
|
|
6799
7115
|
};
|
|
6800
7116
|
var o6 = /* @__PURE__ */ new WeakMap();
|
|
@@ -6824,59 +7140,6 @@ var n5 = e4(class extends f3 {
|
|
|
6824
7140
|
}
|
|
6825
7141
|
});
|
|
6826
7142
|
|
|
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
7143
|
// node_modules/@lit/reactive-element/decorators/property.js
|
|
6881
7144
|
var o7 = { attribute: true, type: String, converter: u, reflect: false, hasChanged: f };
|
|
6882
7145
|
var r6 = (t5 = o7, e8, r8) => {
|
|
@@ -6912,8 +7175,8 @@ function r7(r8) {
|
|
|
6912
7175
|
return n6({ ...r8, state: true, attribute: false });
|
|
6913
7176
|
}
|
|
6914
7177
|
|
|
6915
|
-
// src/
|
|
6916
|
-
var
|
|
7178
|
+
// src/browser/styles/base.ts
|
|
7179
|
+
var baseStyles = i`
|
|
6917
7180
|
:host {
|
|
6918
7181
|
flex: 1 1 auto;
|
|
6919
7182
|
min-height: 0;
|
|
@@ -6922,7 +7185,7 @@ var panelStyles = i`
|
|
|
6922
7185
|
overflow: hidden;
|
|
6923
7186
|
color: var(--pi-text);
|
|
6924
7187
|
background: var(--pi-bg);
|
|
6925
|
-
font:
|
|
7188
|
+
font: 14px system-ui, sans-serif;
|
|
6926
7189
|
}
|
|
6927
7190
|
|
|
6928
7191
|
button {
|
|
@@ -6948,7 +7211,7 @@ var panelStyles = i`
|
|
|
6948
7211
|
border-radius: 6px;
|
|
6949
7212
|
background: var(--pi-surface);
|
|
6950
7213
|
color: var(--pi-text);
|
|
6951
|
-
font-size:
|
|
7214
|
+
font-size: 13px;
|
|
6952
7215
|
padding: 4px 6px;
|
|
6953
7216
|
}
|
|
6954
7217
|
|
|
@@ -6957,13 +7220,16 @@ var panelStyles = i`
|
|
|
6957
7220
|
border-radius: 5px;
|
|
6958
7221
|
background: var(--pi-bg);
|
|
6959
7222
|
padding: 1px 4px;
|
|
6960
|
-
font:
|
|
7223
|
+
font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
6961
7224
|
}
|
|
6962
7225
|
|
|
6963
7226
|
.doorstop-muted {
|
|
6964
7227
|
color: var(--pi-muted);
|
|
6965
7228
|
}
|
|
7229
|
+
`;
|
|
6966
7230
|
|
|
7231
|
+
// src/browser/styles/project-actions.ts
|
|
7232
|
+
var projectActionsStyles = i`
|
|
6967
7233
|
/* --- project actions (region 1) --- */
|
|
6968
7234
|
.doorstop-project-actions {
|
|
6969
7235
|
flex: 0 0 auto;
|
|
@@ -7002,7 +7268,7 @@ var panelStyles = i`
|
|
|
7002
7268
|
.doorstop-doc-chip {
|
|
7003
7269
|
border-radius: 999px;
|
|
7004
7270
|
padding: 2px 9px;
|
|
7005
|
-
font-size:
|
|
7271
|
+
font-size: 13px;
|
|
7006
7272
|
}
|
|
7007
7273
|
|
|
7008
7274
|
.doorstop-doc-chip.is-selected {
|
|
@@ -7013,12 +7279,12 @@ var panelStyles = i`
|
|
|
7013
7279
|
|
|
7014
7280
|
.doorstop-doc-arrow {
|
|
7015
7281
|
color: var(--pi-muted);
|
|
7016
|
-
font-size:
|
|
7282
|
+
font-size: 12px;
|
|
7017
7283
|
}
|
|
7018
7284
|
|
|
7019
7285
|
.doorstop-doc-count {
|
|
7020
7286
|
color: var(--pi-muted);
|
|
7021
|
-
font-size:
|
|
7287
|
+
font-size: 12px;
|
|
7022
7288
|
}
|
|
7023
7289
|
|
|
7024
7290
|
.doorstop-dot {
|
|
@@ -7053,7 +7319,7 @@ var panelStyles = i`
|
|
|
7053
7319
|
border-radius: 999px;
|
|
7054
7320
|
color: var(--pi-warning);
|
|
7055
7321
|
padding: 1px 8px;
|
|
7056
|
-
font-size:
|
|
7322
|
+
font-size: 13px;
|
|
7057
7323
|
}
|
|
7058
7324
|
|
|
7059
7325
|
/* --- project-scoped git actions (plan-add-git-actions Phase D step 17) --- */
|
|
@@ -7076,7 +7342,7 @@ var panelStyles = i`
|
|
|
7076
7342
|
background: transparent;
|
|
7077
7343
|
color: var(--pi-muted);
|
|
7078
7344
|
padding: 2px 9px;
|
|
7079
|
-
font-size:
|
|
7345
|
+
font-size: 13px;
|
|
7080
7346
|
}
|
|
7081
7347
|
|
|
7082
7348
|
.doorstop-git-status-text {
|
|
@@ -7132,7 +7398,7 @@ var panelStyles = i`
|
|
|
7132
7398
|
background: transparent;
|
|
7133
7399
|
color: var(--pi-muted);
|
|
7134
7400
|
padding: 3px 10px;
|
|
7135
|
-
font-size:
|
|
7401
|
+
font-size: 13px;
|
|
7136
7402
|
}
|
|
7137
7403
|
|
|
7138
7404
|
.doorstop-view-tab + .doorstop-view-tab {
|
|
@@ -7143,7 +7409,10 @@ var panelStyles = i`
|
|
|
7143
7409
|
background: var(--pi-selection-bg);
|
|
7144
7410
|
color: var(--pi-accent);
|
|
7145
7411
|
}
|
|
7412
|
+
`;
|
|
7146
7413
|
|
|
7414
|
+
// src/browser/styles/item-list.ts
|
|
7415
|
+
var itemListStyles = i`
|
|
7147
7416
|
/* --- viewer (regions 2 + 3; the filters row and split stack) --- */
|
|
7148
7417
|
.doorstop-viewer {
|
|
7149
7418
|
flex: 1 1 auto;
|
|
@@ -7260,7 +7529,7 @@ var panelStyles = i`
|
|
|
7260
7529
|
.doorstop-item-row {
|
|
7261
7530
|
display: grid;
|
|
7262
7531
|
grid-template-columns: max-content max-content minmax(0, 1fr) max-content;
|
|
7263
|
-
gap: 8px;
|
|
7532
|
+
gap: 2px 8px;
|
|
7264
7533
|
align-items: baseline;
|
|
7265
7534
|
width: 100%;
|
|
7266
7535
|
border: 0;
|
|
@@ -7277,7 +7546,7 @@ var panelStyles = i`
|
|
|
7277
7546
|
|
|
7278
7547
|
.doorstop-item-level {
|
|
7279
7548
|
color: var(--pi-muted);
|
|
7280
|
-
font-size:
|
|
7549
|
+
font-size: 12px;
|
|
7281
7550
|
}
|
|
7282
7551
|
|
|
7283
7552
|
.doorstop-item-summary {
|
|
@@ -7288,6 +7557,49 @@ var panelStyles = i`
|
|
|
7288
7557
|
color: var(--pi-text-secondary);
|
|
7289
7558
|
}
|
|
7290
7559
|
|
|
7560
|
+
/* A fifth column exists only when showAdditionalAttribute configures
|
|
7561
|
+
at least one name; otherwise the default rows keep the original
|
|
7562
|
+
4-column track (and its single 8px summary→chips gap). */
|
|
7563
|
+
.doorstop-items.has-item-attrs .doorstop-item-row {
|
|
7564
|
+
grid-template-columns: max-content max-content minmax(0, 1fr) minmax(0, max-content) max-content;
|
|
7565
|
+
}
|
|
7566
|
+
|
|
7567
|
+
/* The configured key: value attribute pairs (settings field
|
|
7568
|
+
showAdditionalAttribute). The column collapses to zero when the row
|
|
7569
|
+
renders nothing here (no configured attribute the item has). The
|
|
7570
|
+
max-width bounds a pathologically long value so it cannot starve
|
|
7571
|
+
the summary's fr track before the ellipsis kicks in. */
|
|
7572
|
+
.doorstop-item-attrs {
|
|
7573
|
+
min-width: 0;
|
|
7574
|
+
max-width: 40ch;
|
|
7575
|
+
overflow: hidden;
|
|
7576
|
+
text-overflow: ellipsis;
|
|
7577
|
+
white-space: nowrap;
|
|
7578
|
+
color: var(--pi-muted);
|
|
7579
|
+
font-size: 12px;
|
|
7580
|
+
}
|
|
7581
|
+
|
|
7582
|
+
/* The item body ("text" configured in showAdditionalAttribute) on its own
|
|
7583
|
+
indented second line below level/uid/summary/chips. Full-width implicit
|
|
7584
|
+
grid row; 5ch inline indent; clamped to 2 lines with ellipsis — the full
|
|
7585
|
+
body lives in the detail pane. The span only exists when rendered, so no
|
|
7586
|
+
container modifier class is needed. */
|
|
7587
|
+
.doorstop-item-text {
|
|
7588
|
+
grid-column: 1 / -1;
|
|
7589
|
+
align-self: start;
|
|
7590
|
+
padding-inline-start: 5ch;
|
|
7591
|
+
min-width: 0;
|
|
7592
|
+
color: var(--pi-muted);
|
|
7593
|
+
/* Body-like content: reads one tier above the 12px metadata. */
|
|
7594
|
+
font-size: 13px;
|
|
7595
|
+
white-space: pre-wrap;
|
|
7596
|
+
overflow-wrap: anywhere;
|
|
7597
|
+
display: -webkit-box;
|
|
7598
|
+
-webkit-box-orient: vertical;
|
|
7599
|
+
-webkit-line-clamp: 2;
|
|
7600
|
+
overflow: hidden;
|
|
7601
|
+
}
|
|
7602
|
+
|
|
7291
7603
|
.doorstop-item-chips {
|
|
7292
7604
|
display: flex;
|
|
7293
7605
|
flex-wrap: wrap;
|
|
@@ -7301,8 +7613,14 @@ var panelStyles = i`
|
|
|
7301
7613
|
keyboard path is the region-4 Stage/Unstage buttons. The same class
|
|
7302
7614
|
hosts both directions: the plus (git add) icon and the unstage
|
|
7303
7615
|
minus (git reset) icon, chosen by {@link itemUnstageable} /
|
|
7304
|
-
{@link itemStageable} precedence.
|
|
7305
|
-
|
|
7616
|
+
{@link itemStageable} precedence.
|
|
7617
|
+
|
|
7618
|
+
The per-row "insert UID into the agent prompt" arrow shares the
|
|
7619
|
+
pointer-only <span> contract and this visual box; it keeps a
|
|
7620
|
+
distinct class because the +/reset icon and the arrow stay
|
|
7621
|
+
independently evolvable. */
|
|
7622
|
+
.doorstop-item-add,
|
|
7623
|
+
.doorstop-item-insert {
|
|
7306
7624
|
display: inline-flex;
|
|
7307
7625
|
align-items: center;
|
|
7308
7626
|
align-self: center;
|
|
@@ -7313,7 +7631,8 @@ var panelStyles = i`
|
|
|
7313
7631
|
cursor: pointer;
|
|
7314
7632
|
}
|
|
7315
7633
|
|
|
7316
|
-
.doorstop-item-add:hover
|
|
7634
|
+
.doorstop-item-add:hover,
|
|
7635
|
+
.doorstop-item-insert:hover {
|
|
7317
7636
|
color: var(--pi-text);
|
|
7318
7637
|
}
|
|
7319
7638
|
|
|
@@ -7321,6 +7640,7 @@ var panelStyles = i`
|
|
|
7321
7640
|
.doorstop-chip {
|
|
7322
7641
|
border-radius: 999px;
|
|
7323
7642
|
padding: 0 7px;
|
|
7643
|
+
/* Strict parity: pi-web keeps compact chips at 11px. */
|
|
7324
7644
|
font-size: 11px;
|
|
7325
7645
|
white-space: nowrap;
|
|
7326
7646
|
}
|
|
@@ -7344,16 +7664,132 @@ var panelStyles = i`
|
|
|
7344
7664
|
border: 1px solid var(--pi-success-border);
|
|
7345
7665
|
color: var(--pi-success);
|
|
7346
7666
|
}
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
.
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7667
|
+
`;
|
|
7668
|
+
var findingsStyles = i`
|
|
7669
|
+
/* --- findings view (spec §7.2) --- */
|
|
7670
|
+
.doorstop-findings-view {
|
|
7671
|
+
flex: 1 1 auto;
|
|
7672
|
+
min-height: 0;
|
|
7673
|
+
overflow: auto;
|
|
7674
|
+
padding: 10px 12px;
|
|
7675
|
+
display: grid;
|
|
7676
|
+
gap: 8px;
|
|
7677
|
+
align-content: start;
|
|
7678
|
+
}
|
|
7679
|
+
|
|
7680
|
+
.doorstop-findings-head {
|
|
7681
|
+
display: flex;
|
|
7682
|
+
flex-wrap: wrap;
|
|
7683
|
+
align-items: baseline;
|
|
7684
|
+
gap: 6px 10px;
|
|
7685
|
+
}
|
|
7686
|
+
|
|
7687
|
+
.doorstop-findings-counts {
|
|
7688
|
+
font-size: 13px;
|
|
7689
|
+
font-weight: 600;
|
|
7690
|
+
}
|
|
7691
|
+
|
|
7692
|
+
.doorstop-findings-note {
|
|
7693
|
+
font-size: 12px;
|
|
7694
|
+
}
|
|
7695
|
+
|
|
7696
|
+
.doorstop-findings-list {
|
|
7697
|
+
display: grid;
|
|
7698
|
+
gap: 3px;
|
|
7699
|
+
}
|
|
7700
|
+
|
|
7701
|
+
.doorstop-finding-row {
|
|
7702
|
+
display: flex;
|
|
7703
|
+
flex-wrap: wrap;
|
|
7704
|
+
align-items: baseline;
|
|
7705
|
+
gap: 6px;
|
|
7706
|
+
border-radius: 6px;
|
|
7707
|
+
padding: 5px 8px;
|
|
7708
|
+
}
|
|
7709
|
+
|
|
7710
|
+
.doorstop-finding-row.doorstop-error {
|
|
7711
|
+
border: 1px solid var(--pi-danger);
|
|
7712
|
+
background: color-mix(in srgb, var(--pi-danger) 9%, transparent);
|
|
7713
|
+
}
|
|
7714
|
+
|
|
7715
|
+
.doorstop-finding-row.doorstop-warning {
|
|
7716
|
+
border: 1px solid var(--pi-warning-border);
|
|
7717
|
+
background: color-mix(in srgb, var(--pi-warning) 9%, transparent);
|
|
7718
|
+
}
|
|
7719
|
+
|
|
7720
|
+
.doorstop-finding-row.doorstop-info {
|
|
7721
|
+
border: 1px solid var(--pi-border-muted);
|
|
7722
|
+
color: var(--pi-muted);
|
|
7723
|
+
}
|
|
7724
|
+
|
|
7725
|
+
.doorstop-finding-row .doorstop-severity {
|
|
7726
|
+
background: var(--pi-border-muted);
|
|
7727
|
+
color: var(--pi-muted);
|
|
7728
|
+
}
|
|
7729
|
+
|
|
7730
|
+
.doorstop-finding-row.doorstop-error .doorstop-severity {
|
|
7731
|
+
background: var(--pi-danger);
|
|
7732
|
+
color: var(--pi-bg);
|
|
7733
|
+
}
|
|
7734
|
+
|
|
7735
|
+
.doorstop-finding-row.doorstop-warning .doorstop-severity {
|
|
7736
|
+
background: var(--pi-warning);
|
|
7737
|
+
color: var(--pi-bg);
|
|
7738
|
+
}
|
|
7739
|
+
|
|
7740
|
+
.doorstop-finding-uid {
|
|
7741
|
+
border: 0;
|
|
7742
|
+
border-radius: 5px;
|
|
7743
|
+
background: transparent;
|
|
7744
|
+
color: inherit;
|
|
7745
|
+
padding: 0;
|
|
7746
|
+
font-weight: 600;
|
|
7747
|
+
}
|
|
7748
|
+
|
|
7749
|
+
.doorstop-finding-uid:hover {
|
|
7750
|
+
text-decoration: underline;
|
|
7751
|
+
cursor: pointer;
|
|
7752
|
+
}
|
|
7753
|
+
|
|
7754
|
+
.doorstop-finding-message {
|
|
7755
|
+
min-width: 0;
|
|
7756
|
+
overflow-wrap: anywhere;
|
|
7757
|
+
}
|
|
7758
|
+
|
|
7759
|
+
/* --- empty states --- */
|
|
7760
|
+
.doorstop-empty {
|
|
7761
|
+
margin: 10px 12px;
|
|
7762
|
+
border: 1px dashed var(--pi-border-muted);
|
|
7763
|
+
border-radius: 8px;
|
|
7764
|
+
color: var(--pi-muted);
|
|
7765
|
+
padding: 12px;
|
|
7766
|
+
overflow: auto;
|
|
7767
|
+
}
|
|
7768
|
+
|
|
7769
|
+
.doorstop-empty p {
|
|
7770
|
+
margin: 0;
|
|
7771
|
+
}
|
|
7772
|
+
|
|
7773
|
+
.doorstop-empty button {
|
|
7774
|
+
margin-top: 10px;
|
|
7775
|
+
}
|
|
7776
|
+
|
|
7777
|
+
.doorstop-empty .doorstop-muted {
|
|
7778
|
+
margin-top: 6px;
|
|
7779
|
+
}
|
|
7780
|
+
`;
|
|
7781
|
+
|
|
7782
|
+
// src/browser/styles/detail-pane.ts
|
|
7783
|
+
var detailPaneStyles = i`
|
|
7784
|
+
/* --- detail pane (region 3) --- */
|
|
7785
|
+
.doorstop-detail {
|
|
7786
|
+
padding: 12px;
|
|
7787
|
+
display: grid;
|
|
7788
|
+
gap: 10px;
|
|
7789
|
+
align-content: start;
|
|
7790
|
+
}
|
|
7791
|
+
|
|
7792
|
+
.doorstop-detail-head {
|
|
7357
7793
|
display: flex;
|
|
7358
7794
|
flex-wrap: wrap;
|
|
7359
7795
|
align-items: baseline;
|
|
@@ -7362,14 +7798,14 @@ var panelStyles = i`
|
|
|
7362
7798
|
|
|
7363
7799
|
.doorstop-detail-head h3 {
|
|
7364
7800
|
margin: 0;
|
|
7365
|
-
font-size:
|
|
7801
|
+
font-size: 16px;
|
|
7366
7802
|
min-width: 0;
|
|
7367
7803
|
overflow-wrap: anywhere;
|
|
7368
7804
|
}
|
|
7369
7805
|
|
|
7370
7806
|
.doorstop-level {
|
|
7371
7807
|
color: var(--pi-muted);
|
|
7372
|
-
font-size:
|
|
7808
|
+
font-size: 13px;
|
|
7373
7809
|
}
|
|
7374
7810
|
|
|
7375
7811
|
.doorstop-flags {
|
|
@@ -7383,7 +7819,7 @@ var panelStyles = i`
|
|
|
7383
7819
|
border-radius: 999px;
|
|
7384
7820
|
color: var(--pi-muted);
|
|
7385
7821
|
padding: 0 7px;
|
|
7386
|
-
font-size:
|
|
7822
|
+
font-size: 12px;
|
|
7387
7823
|
}
|
|
7388
7824
|
|
|
7389
7825
|
.doorstop-flag.is-inactive {
|
|
@@ -7406,7 +7842,7 @@ var panelStyles = i`
|
|
|
7406
7842
|
|
|
7407
7843
|
.doorstop-changes-summary {
|
|
7408
7844
|
color: var(--pi-muted);
|
|
7409
|
-
font-size:
|
|
7845
|
+
font-size: 12px;
|
|
7410
7846
|
text-transform: uppercase;
|
|
7411
7847
|
letter-spacing: 0.03em;
|
|
7412
7848
|
cursor: pointer;
|
|
@@ -7419,12 +7855,12 @@ var panelStyles = i`
|
|
|
7419
7855
|
|
|
7420
7856
|
.doorstop-changes-notice {
|
|
7421
7857
|
margin: 6px 0 0;
|
|
7422
|
-
font-size:
|
|
7858
|
+
font-size: 13px;
|
|
7423
7859
|
}
|
|
7424
7860
|
|
|
7425
7861
|
.doorstop-changes-source {
|
|
7426
7862
|
margin: 6px 0 4px;
|
|
7427
|
-
font-size:
|
|
7863
|
+
font-size: 12px;
|
|
7428
7864
|
}
|
|
7429
7865
|
|
|
7430
7866
|
.doorstop-diff-lines {
|
|
@@ -7494,7 +7930,7 @@ var panelStyles = i`
|
|
|
7494
7930
|
|
|
7495
7931
|
.doorstop-field-name {
|
|
7496
7932
|
color: var(--pi-muted);
|
|
7497
|
-
font-size:
|
|
7933
|
+
font-size: 12px;
|
|
7498
7934
|
text-transform: uppercase;
|
|
7499
7935
|
letter-spacing: 0.03em;
|
|
7500
7936
|
}
|
|
@@ -7502,7 +7938,7 @@ var panelStyles = i`
|
|
|
7502
7938
|
.doorstop-field-chip {
|
|
7503
7939
|
border-radius: 5px;
|
|
7504
7940
|
border: 1px solid var(--pi-border-muted);
|
|
7505
|
-
font-size:
|
|
7941
|
+
font-size: 13px;
|
|
7506
7942
|
padding: 1px 6px;
|
|
7507
7943
|
overflow-wrap: anywhere;
|
|
7508
7944
|
}
|
|
@@ -7525,7 +7961,7 @@ var panelStyles = i`
|
|
|
7525
7961
|
|
|
7526
7962
|
.doorstop-section-title {
|
|
7527
7963
|
color: var(--pi-muted);
|
|
7528
|
-
font-size:
|
|
7964
|
+
font-size: 12px;
|
|
7529
7965
|
text-transform: uppercase;
|
|
7530
7966
|
letter-spacing: 0.03em;
|
|
7531
7967
|
margin: 0;
|
|
@@ -7546,6 +7982,107 @@ var panelStyles = i`
|
|
|
7546
7982
|
font-style: italic;
|
|
7547
7983
|
}
|
|
7548
7984
|
|
|
7985
|
+
/* Plan §2.1: the item body is host-rendered Markdown. The host renderer
|
|
7986
|
+
* mounts its nodes in THIS shadow root (the host's
|
|
7987
|
+
* pi-web-content-rendering-boundary element deliberately has no shadow
|
|
7988
|
+
* root of its own — createRenderRoot returns the element), so the prose
|
|
7989
|
+
* rules below reach them. The plain-text pre-wrap on .doorstop-text is
|
|
7990
|
+
* not appropriate inside Markdown blocks; the boundary's own source
|
|
7991
|
+
* fallback sets its own pre-wrap inline.
|
|
7992
|
+
*
|
|
7993
|
+
* This element does NOT inherit host typography: the boundary only
|
|
7994
|
+
* avoids adding a shadow root, it does not import host theme CSS into
|
|
7995
|
+
* ours, so every rule the document needs is restated here. */
|
|
7996
|
+
.doorstop-text-markdown {
|
|
7997
|
+
white-space: normal;
|
|
7998
|
+
}
|
|
7999
|
+
|
|
8000
|
+
/* Edge trimming must reach the BLOCKS, not the wrappers: the boundary
|
|
8001
|
+
* element (display: contents) and its .content-rendering-body wrapper
|
|
8002
|
+
* sit between this element and the rendered blocks, so a direct-child
|
|
8003
|
+
* (> :first-child) selector only ever matched the boundary itself. No
|
|
8004
|
+
* matching :last-child rule: the body keeps the source-fallback span
|
|
8005
|
+
* AFTER the content wrapper, so the last block is never a last-child;
|
|
8006
|
+
* its trailing 8px margin is invisible inside the bordered box. */
|
|
8007
|
+
.doorstop-text-markdown :is(p, ul, ol, pre, blockquote, .table-scroll, h1, h2, h3, h4, h5, h6):first-child {
|
|
8008
|
+
margin-top: 0;
|
|
8009
|
+
}
|
|
8010
|
+
|
|
8011
|
+
.doorstop-text-markdown :is(p, ul, ol, blockquote, .table-scroll) {
|
|
8012
|
+
margin: 0 0 8px;
|
|
8013
|
+
}
|
|
8014
|
+
|
|
8015
|
+
.doorstop-text-markdown :is(ul, ol) {
|
|
8016
|
+
padding-left: 20px;
|
|
8017
|
+
}
|
|
8018
|
+
|
|
8019
|
+
.doorstop-text-markdown :is(h1, h2, h3, h4, h5, h6) {
|
|
8020
|
+
margin: 12px 0 6px;
|
|
8021
|
+
font-size: 14px;
|
|
8022
|
+
font-weight: 600;
|
|
8023
|
+
}
|
|
8024
|
+
|
|
8025
|
+
.doorstop-text-markdown code {
|
|
8026
|
+
border: 1px solid var(--pi-border-muted);
|
|
8027
|
+
border-radius: 4px;
|
|
8028
|
+
background: var(--pi-bg);
|
|
8029
|
+
padding: 0 3px;
|
|
8030
|
+
font-size: 12px;
|
|
8031
|
+
}
|
|
8032
|
+
|
|
8033
|
+
.doorstop-text-markdown pre {
|
|
8034
|
+
border: 1px solid var(--pi-border-muted);
|
|
8035
|
+
border-radius: 6px;
|
|
8036
|
+
padding: 8px;
|
|
8037
|
+
overflow-x: auto;
|
|
8038
|
+
}
|
|
8039
|
+
|
|
8040
|
+
.doorstop-text-markdown pre code {
|
|
8041
|
+
border: 0;
|
|
8042
|
+
background: transparent;
|
|
8043
|
+
padding: 0;
|
|
8044
|
+
}
|
|
8045
|
+
|
|
8046
|
+
.doorstop-text-markdown blockquote {
|
|
8047
|
+
border-left: 3px solid var(--pi-border-muted);
|
|
8048
|
+
padding-left: 8px;
|
|
8049
|
+
color: var(--pi-muted);
|
|
8050
|
+
}
|
|
8051
|
+
|
|
8052
|
+
/* The host wraps every table in a div.table-scroll role=region
|
|
8053
|
+
* tabindex=0 and styles it ONLY from host theme CSS
|
|
8054
|
+
* (pi-web base/formattedText.css), which does not reach this
|
|
8055
|
+
* unregistered shadow root. Without these two rules a wide table is
|
|
8056
|
+
* clipped by the pane and its focusable region is unusable, so the
|
|
8057
|
+
* panel restates the host's rules (pi-web 1.202611.1).
|
|
8058
|
+
*
|
|
8059
|
+
* overflow-wrap: normal undoes the anywhere inherited from
|
|
8060
|
+
* .doorstop-text: harmless (host-identical) in prose, but inside table
|
|
8061
|
+
* cells it would let every column collapse to min-content instead of
|
|
8062
|
+
* making the table overflow into its scroll region. */
|
|
8063
|
+
.doorstop-text-markdown .table-scroll {
|
|
8064
|
+
max-width: 100%;
|
|
8065
|
+
overflow-x: auto;
|
|
8066
|
+
overflow-y: hidden;
|
|
8067
|
+
overflow-wrap: normal;
|
|
8068
|
+
}
|
|
8069
|
+
|
|
8070
|
+
.doorstop-text-markdown .table-scroll:focus-visible {
|
|
8071
|
+
outline: 1px solid var(--pi-accent);
|
|
8072
|
+
outline-offset: 2px;
|
|
8073
|
+
}
|
|
8074
|
+
|
|
8075
|
+
.doorstop-text-markdown table {
|
|
8076
|
+
border-collapse: collapse;
|
|
8077
|
+
margin: 0;
|
|
8078
|
+
}
|
|
8079
|
+
|
|
8080
|
+
.doorstop-text-markdown :is(th, td) {
|
|
8081
|
+
border: 1px solid var(--pi-border-muted);
|
|
8082
|
+
padding: 2px 6px;
|
|
8083
|
+
text-align: left;
|
|
8084
|
+
}
|
|
8085
|
+
|
|
7549
8086
|
.doorstop-links {
|
|
7550
8087
|
display: grid;
|
|
7551
8088
|
gap: 4px;
|
|
@@ -7586,7 +8123,7 @@ var panelStyles = i`
|
|
|
7586
8123
|
|
|
7587
8124
|
.doorstop-fingerprint {
|
|
7588
8125
|
color: var(--pi-muted);
|
|
7589
|
-
font-size:
|
|
8126
|
+
font-size: 12px;
|
|
7590
8127
|
/* The link row wraps, which could push the trash can onto a second
|
|
7591
8128
|
line on narrow panes. Let the fingerprint shrink and ellipsize
|
|
7592
8129
|
instead (min-width: 0 lifts the flex min-content floor). */
|
|
@@ -7611,7 +8148,7 @@ var panelStyles = i`
|
|
|
7611
8148
|
|
|
7612
8149
|
.doorstop-attribute dt {
|
|
7613
8150
|
color: var(--pi-muted);
|
|
7614
|
-
font-size:
|
|
8151
|
+
font-size: 13px;
|
|
7615
8152
|
}
|
|
7616
8153
|
|
|
7617
8154
|
.doorstop-attribute dd {
|
|
@@ -7620,6 +8157,28 @@ var panelStyles = i`
|
|
|
7620
8157
|
overflow-wrap: anywhere;
|
|
7621
8158
|
}
|
|
7622
8159
|
|
|
8160
|
+
/* Plan plan-attribute-markdown D5: an allowlisted prose attribute
|
|
8161
|
+
* (initially "rationale") is host-rendered Markdown, so its row uses a
|
|
8162
|
+
* STACKED variant — the label above a full-width block — instead of
|
|
8163
|
+
* the two-column key|value grid sized for single-line <code> values.
|
|
8164
|
+
* The dt keeps the existing small muted label rule above.
|
|
8165
|
+
*
|
|
8166
|
+
* Nothing here restates the §2.1 prose rules: the rendered container
|
|
8167
|
+
* carries the shared .doorstop-text-markdown class, so block
|
|
8168
|
+
* margins, code/pre, blockquote and the 9bc9cc9 .table-scroll fixes
|
|
8169
|
+
* apply automatically. */
|
|
8170
|
+
.doorstop-attribute.doorstop-attribute-markdown {
|
|
8171
|
+
grid-template-columns: minmax(0, 1fr);
|
|
8172
|
+
gap: 3px;
|
|
8173
|
+
}
|
|
8174
|
+
|
|
8175
|
+
/* The escaped-text fallback (no host renderer) is prose too: keep its
|
|
8176
|
+
* line breaks. This only reaches the fallback — the markdown container
|
|
8177
|
+
* inside resets white-space to normal (.doorstop-text-markdown). */
|
|
8178
|
+
.doorstop-attribute-markdown dd {
|
|
8179
|
+
white-space: pre-wrap;
|
|
8180
|
+
}
|
|
8181
|
+
|
|
7623
8182
|
.doorstop-findings {
|
|
7624
8183
|
display: grid;
|
|
7625
8184
|
gap: 4px;
|
|
@@ -7652,10 +8211,14 @@ var panelStyles = i`
|
|
|
7652
8211
|
|
|
7653
8212
|
.doorstop-finding-severity {
|
|
7654
8213
|
flex: 0 0 auto;
|
|
8214
|
+
/* Strict parity: pi-web keeps compact chips at 11px. */
|
|
7655
8215
|
font-size: 11px;
|
|
7656
8216
|
font-weight: 600;
|
|
7657
8217
|
}
|
|
8218
|
+
`;
|
|
7658
8219
|
|
|
8220
|
+
// src/browser/styles/item-action-palette.ts
|
|
8221
|
+
var itemActionPaletteStyles = i`
|
|
7659
8222
|
/* --- item action palette (region 4), sized to the prompt footer --- */
|
|
7660
8223
|
.doorstop-action-palette {
|
|
7661
8224
|
flex: 0 0 auto;
|
|
@@ -7701,7 +8264,7 @@ var panelStyles = i`
|
|
|
7701
8264
|
|
|
7702
8265
|
.doorstop-op-error {
|
|
7703
8266
|
color: var(--pi-danger);
|
|
7704
|
-
font-size:
|
|
8267
|
+
font-size: 13px;
|
|
7705
8268
|
}
|
|
7706
8269
|
|
|
7707
8270
|
.doorstop-menu {
|
|
@@ -7734,174 +8297,70 @@ var panelStyles = i`
|
|
|
7734
8297
|
.doorstop-menu-item:hover:not(:disabled) {
|
|
7735
8298
|
background: var(--pi-selection-bg);
|
|
7736
8299
|
}
|
|
8300
|
+
`;
|
|
7737
8301
|
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
8302
|
+
// src/browser/styles/status-bar.ts
|
|
8303
|
+
var statusBarStyles = i`
|
|
8304
|
+
/* --- status bar (region 5), sized to the host center status bar --- */
|
|
8305
|
+
.doorstop-status-bar {
|
|
8306
|
+
flex: 0 0 auto;
|
|
8307
|
+
display: flex;
|
|
8308
|
+
flex-direction: column;
|
|
8309
|
+
min-width: 0;
|
|
8310
|
+
/* Cap the whole bar at 38% of the panel (the pre-improvement cap),
|
|
8311
|
+
not 38vh: on a short viewport with a tall side panel a viewport
|
|
8312
|
+
unit can dwarf the panel. The cap cannot live on the body div
|
|
8313
|
+
(.doorstop-last-run) — its containing block (this bar) is
|
|
8314
|
+
content-sized, so a percentage max-height there would be treated
|
|
8315
|
+
as none — so the bar caps itself and the body shrinks (and
|
|
8316
|
+
scrolls) below it. */
|
|
8317
|
+
max-height: 38%;
|
|
8318
|
+
border-top: 1px solid var(--pi-border);
|
|
8319
|
+
background: var(--pi-bg);
|
|
8320
|
+
color: var(--pi-muted);
|
|
8321
|
+
font: 13px system-ui, sans-serif;
|
|
7747
8322
|
}
|
|
7748
8323
|
|
|
7749
|
-
.doorstop-
|
|
8324
|
+
.doorstop-status-bar-row {
|
|
8325
|
+
flex: 0 0 auto; /* the status row never collapses under the body cap */
|
|
7750
8326
|
display: flex;
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
8327
|
+
align-items: center;
|
|
8328
|
+
gap: 8px;
|
|
8329
|
+
min-width: 0;
|
|
8330
|
+
padding: 7px 12px;
|
|
8331
|
+
overflow: hidden;
|
|
8332
|
+
white-space: nowrap;
|
|
7754
8333
|
}
|
|
7755
8334
|
|
|
7756
|
-
.
|
|
7757
|
-
|
|
7758
|
-
|
|
8335
|
+
/* The status bar's expanded output body — no card chrome. Shrinks
|
|
8336
|
+
(and scrolls) when the bar hits its 38% cap; content-sized
|
|
8337
|
+
otherwise. */
|
|
8338
|
+
.doorstop-last-run {
|
|
8339
|
+
flex: 1 1 auto;
|
|
8340
|
+
min-height: 0;
|
|
8341
|
+
overflow: auto;
|
|
8342
|
+
padding: 8px 12px 10px;
|
|
8343
|
+
border-top: 1px solid var(--pi-border-muted);
|
|
7759
8344
|
}
|
|
7760
8345
|
|
|
7761
|
-
.doorstop-
|
|
8346
|
+
.doorstop-last-run-commit {
|
|
8347
|
+
margin: 0 0 6px;
|
|
8348
|
+
font-size: 13px;
|
|
8349
|
+
overflow-wrap: anywhere;
|
|
8350
|
+
}
|
|
8351
|
+
|
|
8352
|
+
.doorstop-last-run-status {
|
|
8353
|
+
flex: 0 0 auto; /* the badge label keeps its full width */
|
|
8354
|
+
border-radius: 999px;
|
|
8355
|
+
padding: 0 7px;
|
|
8356
|
+
/* Strict parity: pi-web keeps compact chips at 11px. */
|
|
7762
8357
|
font-size: 11px;
|
|
8358
|
+
white-space: nowrap;
|
|
7763
8359
|
}
|
|
7764
8360
|
|
|
7765
|
-
.doorstop-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
}
|
|
7769
|
-
|
|
7770
|
-
.doorstop-finding-row {
|
|
7771
|
-
display: flex;
|
|
7772
|
-
flex-wrap: wrap;
|
|
7773
|
-
align-items: baseline;
|
|
7774
|
-
gap: 6px;
|
|
7775
|
-
border-radius: 6px;
|
|
7776
|
-
padding: 5px 8px;
|
|
7777
|
-
}
|
|
7778
|
-
|
|
7779
|
-
.doorstop-finding-row.doorstop-error {
|
|
7780
|
-
border: 1px solid var(--pi-danger);
|
|
7781
|
-
background: color-mix(in srgb, var(--pi-danger) 9%, transparent);
|
|
7782
|
-
}
|
|
7783
|
-
|
|
7784
|
-
.doorstop-finding-row.doorstop-warning {
|
|
7785
|
-
border: 1px solid var(--pi-warning-border);
|
|
7786
|
-
background: color-mix(in srgb, var(--pi-warning) 9%, transparent);
|
|
7787
|
-
}
|
|
7788
|
-
|
|
7789
|
-
.doorstop-finding-row.doorstop-info {
|
|
7790
|
-
border: 1px solid var(--pi-border-muted);
|
|
7791
|
-
color: var(--pi-muted);
|
|
7792
|
-
}
|
|
7793
|
-
|
|
7794
|
-
.doorstop-finding-row .doorstop-severity {
|
|
7795
|
-
background: var(--pi-border-muted);
|
|
7796
|
-
color: var(--pi-muted);
|
|
7797
|
-
}
|
|
7798
|
-
|
|
7799
|
-
.doorstop-finding-row.doorstop-error .doorstop-severity {
|
|
7800
|
-
background: var(--pi-danger);
|
|
7801
|
-
color: var(--pi-bg);
|
|
7802
|
-
}
|
|
7803
|
-
|
|
7804
|
-
.doorstop-finding-row.doorstop-warning .doorstop-severity {
|
|
7805
|
-
background: var(--pi-warning);
|
|
7806
|
-
color: var(--pi-bg);
|
|
7807
|
-
}
|
|
7808
|
-
|
|
7809
|
-
.doorstop-finding-uid {
|
|
7810
|
-
border: 0;
|
|
7811
|
-
border-radius: 5px;
|
|
7812
|
-
background: transparent;
|
|
7813
|
-
color: inherit;
|
|
7814
|
-
padding: 0;
|
|
7815
|
-
font-weight: 600;
|
|
7816
|
-
}
|
|
7817
|
-
|
|
7818
|
-
.doorstop-finding-uid:hover {
|
|
7819
|
-
text-decoration: underline;
|
|
7820
|
-
cursor: pointer;
|
|
7821
|
-
}
|
|
7822
|
-
|
|
7823
|
-
.doorstop-finding-message {
|
|
7824
|
-
min-width: 0;
|
|
7825
|
-
overflow-wrap: anywhere;
|
|
7826
|
-
}
|
|
7827
|
-
|
|
7828
|
-
/* --- empty states --- */
|
|
7829
|
-
.doorstop-empty {
|
|
7830
|
-
margin: 10px 12px;
|
|
7831
|
-
border: 1px dashed var(--pi-border-muted);
|
|
7832
|
-
border-radius: 8px;
|
|
7833
|
-
color: var(--pi-muted);
|
|
7834
|
-
padding: 12px;
|
|
7835
|
-
overflow: auto;
|
|
7836
|
-
}
|
|
7837
|
-
|
|
7838
|
-
.doorstop-empty p {
|
|
7839
|
-
margin: 0;
|
|
7840
|
-
}
|
|
7841
|
-
|
|
7842
|
-
.doorstop-empty .doorstop-muted {
|
|
7843
|
-
margin-top: 6px;
|
|
7844
|
-
}
|
|
7845
|
-
|
|
7846
|
-
/* --- status bar (region 5), sized to the host center status bar --- */
|
|
7847
|
-
.doorstop-status-bar {
|
|
7848
|
-
flex: 0 0 auto;
|
|
7849
|
-
display: flex;
|
|
7850
|
-
flex-direction: column;
|
|
7851
|
-
min-width: 0;
|
|
7852
|
-
/* Cap the whole bar at 38% of the panel (the pre-improvement cap),
|
|
7853
|
-
not 38vh: on a short viewport with a tall side panel a viewport
|
|
7854
|
-
unit can dwarf the panel. The cap cannot live on the body div
|
|
7855
|
-
(.doorstop-last-run) — its containing block (this bar) is
|
|
7856
|
-
content-sized, so a percentage max-height there would be treated
|
|
7857
|
-
as none — so the bar caps itself and the body shrinks (and
|
|
7858
|
-
scrolls) below it. */
|
|
7859
|
-
max-height: 38%;
|
|
7860
|
-
border-top: 1px solid var(--pi-border);
|
|
7861
|
-
background: var(--pi-bg);
|
|
7862
|
-
color: var(--pi-muted);
|
|
7863
|
-
font: 12px system-ui, sans-serif;
|
|
7864
|
-
}
|
|
7865
|
-
|
|
7866
|
-
.doorstop-status-bar-row {
|
|
7867
|
-
flex: 0 0 auto; /* the status row never collapses under the body cap */
|
|
7868
|
-
display: flex;
|
|
7869
|
-
align-items: center;
|
|
7870
|
-
gap: 8px;
|
|
7871
|
-
min-width: 0;
|
|
7872
|
-
padding: 7px 12px;
|
|
7873
|
-
overflow: hidden;
|
|
7874
|
-
white-space: nowrap;
|
|
7875
|
-
}
|
|
7876
|
-
|
|
7877
|
-
/* The status bar's expanded output body — no card chrome. Shrinks
|
|
7878
|
-
(and scrolls) when the bar hits its 38% cap; content-sized
|
|
7879
|
-
otherwise. */
|
|
7880
|
-
.doorstop-last-run {
|
|
7881
|
-
flex: 1 1 auto;
|
|
7882
|
-
min-height: 0;
|
|
7883
|
-
overflow: auto;
|
|
7884
|
-
padding: 8px 12px 10px;
|
|
7885
|
-
border-top: 1px solid var(--pi-border-muted);
|
|
7886
|
-
}
|
|
7887
|
-
|
|
7888
|
-
.doorstop-last-run-commit {
|
|
7889
|
-
margin: 0 0 6px;
|
|
7890
|
-
font-size: 12px;
|
|
7891
|
-
overflow-wrap: anywhere;
|
|
7892
|
-
}
|
|
7893
|
-
|
|
7894
|
-
.doorstop-last-run-status {
|
|
7895
|
-
flex: 0 0 auto; /* the badge label keeps its full width */
|
|
7896
|
-
border-radius: 999px;
|
|
7897
|
-
padding: 0 7px;
|
|
7898
|
-
font-size: 11px;
|
|
7899
|
-
white-space: nowrap;
|
|
7900
|
-
}
|
|
7901
|
-
|
|
7902
|
-
.doorstop-last-run-status.is-ok {
|
|
7903
|
-
border: 1px solid var(--pi-success-border);
|
|
7904
|
-
color: var(--pi-success);
|
|
8361
|
+
.doorstop-last-run-status.is-ok {
|
|
8362
|
+
border: 1px solid var(--pi-success-border);
|
|
8363
|
+
color: var(--pi-success);
|
|
7905
8364
|
}
|
|
7906
8365
|
|
|
7907
8366
|
.doorstop-last-run-status.is-failed,
|
|
@@ -7927,12 +8386,12 @@ var panelStyles = i`
|
|
|
7927
8386
|
text-overflow: ellipsis;
|
|
7928
8387
|
white-space: nowrap;
|
|
7929
8388
|
color: var(--pi-muted);
|
|
7930
|
-
font-size:
|
|
8389
|
+
font-size: 13px;
|
|
7931
8390
|
}
|
|
7932
8391
|
|
|
7933
8392
|
.doorstop-last-run-dismiss {
|
|
7934
8393
|
flex: 0 0 auto;
|
|
7935
|
-
/* Buttons do not inherit font; match the bar's
|
|
8394
|
+
/* Buttons do not inherit font; match the bar's 13px system-ui. */
|
|
7936
8395
|
font: inherit;
|
|
7937
8396
|
}
|
|
7938
8397
|
|
|
@@ -7942,7 +8401,7 @@ var panelStyles = i`
|
|
|
7942
8401
|
background: var(--pi-surface);
|
|
7943
8402
|
color: var(--pi-text);
|
|
7944
8403
|
padding: 6px 8px;
|
|
7945
|
-
font:
|
|
8404
|
+
font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
7946
8405
|
white-space: pre-wrap;
|
|
7947
8406
|
word-break: break-word;
|
|
7948
8407
|
overflow-wrap: anywhere;
|
|
@@ -7954,21 +8413,297 @@ var panelStyles = i`
|
|
|
7954
8413
|
.doorstop-last-run-notice {
|
|
7955
8414
|
margin: 4px 0 0;
|
|
7956
8415
|
color: var(--pi-muted);
|
|
7957
|
-
font-size:
|
|
8416
|
+
font-size: 12px;
|
|
7958
8417
|
}
|
|
7959
8418
|
`;
|
|
7960
8419
|
|
|
7961
|
-
// src/doorstop-panel-
|
|
8420
|
+
// src/browser/doorstop-panel-styles.ts
|
|
8421
|
+
var panelStyles = i`${baseStyles}${projectActionsStyles}${itemListStyles}${detailPaneStyles}${itemActionPaletteStyles}${findingsStyles}${statusBarStyles}`;
|
|
8422
|
+
|
|
8423
|
+
// node_modules/lit-html/directives/class-map.js
|
|
8424
|
+
var e7 = e4(class extends i6 {
|
|
8425
|
+
constructor(t5) {
|
|
8426
|
+
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.");
|
|
8427
|
+
}
|
|
8428
|
+
render(t5) {
|
|
8429
|
+
return " " + Object.keys(t5).filter((s6) => t5[s6]).join(" ") + " ";
|
|
8430
|
+
}
|
|
8431
|
+
update(s6, [i8]) {
|
|
8432
|
+
if (void 0 === this.st) {
|
|
8433
|
+
this.st = /* @__PURE__ */ new Set(), void 0 !== s6.strings && (this.nt = new Set(s6.strings.join(" ").split(/\s/).filter((t5) => "" !== t5)));
|
|
8434
|
+
for (const t5 in i8) i8[t5] && !this.nt?.has(t5) && this.st.add(t5);
|
|
8435
|
+
return this.render(i8);
|
|
8436
|
+
}
|
|
8437
|
+
const r8 = s6.element.classList;
|
|
8438
|
+
for (const t5 of this.st) t5 in i8 || (r8.remove(t5), this.st.delete(t5));
|
|
8439
|
+
for (const t5 in i8) {
|
|
8440
|
+
const s7 = !!i8[t5];
|
|
8441
|
+
s7 === this.st.has(t5) || this.nt?.has(t5) || (s7 ? (r8.add(t5), this.st.add(t5)) : (r8.remove(t5), this.st.delete(t5)));
|
|
8442
|
+
}
|
|
8443
|
+
return E;
|
|
8444
|
+
}
|
|
8445
|
+
});
|
|
8446
|
+
|
|
8447
|
+
// src/browser/doorstop-panel-icons.ts
|
|
7962
8448
|
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>`;
|
|
7963
8449
|
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>`;
|
|
7964
8450
|
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>`;
|
|
7965
8451
|
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>`;
|
|
7966
|
-
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"><
|
|
7967
|
-
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"><
|
|
8452
|
+
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>`;
|
|
8453
|
+
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>`;
|
|
7968
8454
|
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>`;
|
|
8455
|
+
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>`;
|
|
7969
8456
|
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>`;
|
|
7970
8457
|
|
|
7971
|
-
// src/
|
|
8458
|
+
// src/browser/sections/project-actions.ts
|
|
8459
|
+
function renderProjectActions(host) {
|
|
8460
|
+
const publishTarget = doorstopPublishTarget(host.result);
|
|
8461
|
+
const projectPath = host.context?.workspace.path ?? "";
|
|
8462
|
+
const projectLabel = projectPath === "" ? "Doorstop" : projectPath.split("/").filter(Boolean).pop() ?? projectPath;
|
|
8463
|
+
return b2`
|
|
8464
|
+
<section class="doorstop-project-actions">
|
|
8465
|
+
<strong class="doorstop-title" title=${projectPath === "" ? A : projectPath}>${doorstopIconSvg}${projectLabel}</strong>
|
|
8466
|
+
${renderViewToggle(host)}
|
|
8467
|
+
<div class="doorstop-toolbar-actions">
|
|
8468
|
+
${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}
|
|
8469
|
+
${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}
|
|
8470
|
+
<button type="button" class="doorstop-refresh" title="Re-read the workspace" @click=${() => onRefreshClick(host)}>${refreshIconSvg}Refresh</button>
|
|
8471
|
+
<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>
|
|
8472
|
+
<button type="button" class="doorstop-publish" title=${`Publish the tree to ${publishTarget}`} ?disabled=${host.runInProgress !== void 0} @click=${() => onPublishClick(host)}>${publishIconSvg}Publish HTML</button>
|
|
8473
|
+
${renderGitActions(host)}
|
|
8474
|
+
</div>
|
|
8475
|
+
</section>
|
|
8476
|
+
`;
|
|
8477
|
+
}
|
|
8478
|
+
function renderViewToggle(host) {
|
|
8479
|
+
return b2`
|
|
8480
|
+
<div class="doorstop-view-toggle" role="tablist" aria-label="Requirements panel view">
|
|
8481
|
+
<button
|
|
8482
|
+
type="button"
|
|
8483
|
+
role="tab"
|
|
8484
|
+
class=${e7({
|
|
8485
|
+
"doorstop-view-tab": true,
|
|
8486
|
+
"doorstop-view-items": true,
|
|
8487
|
+
"is-selected": host.view === "items"
|
|
8488
|
+
})}
|
|
8489
|
+
aria-selected=${host.view === "items" ? "true" : "false"}
|
|
8490
|
+
title="Item list and detail"
|
|
8491
|
+
@click=${() => {
|
|
8492
|
+
host.view = "items";
|
|
8493
|
+
}}
|
|
8494
|
+
>Items</button>
|
|
8495
|
+
<button
|
|
8496
|
+
type="button"
|
|
8497
|
+
role="tab"
|
|
8498
|
+
class=${e7({
|
|
8499
|
+
"doorstop-view-tab": true,
|
|
8500
|
+
"doorstop-view-findings": true,
|
|
8501
|
+
"is-selected": host.view === "findings"
|
|
8502
|
+
})}
|
|
8503
|
+
aria-selected=${host.view === "findings" ? "true" : "false"}
|
|
8504
|
+
title="Validation findings"
|
|
8505
|
+
@click=${() => {
|
|
8506
|
+
host.view = "findings";
|
|
8507
|
+
}}
|
|
8508
|
+
>Findings</button>
|
|
8509
|
+
</div>
|
|
8510
|
+
`;
|
|
8511
|
+
}
|
|
8512
|
+
function renderGitActions(host) {
|
|
8513
|
+
if (!host.backendActive()) return A;
|
|
8514
|
+
const hasDocuments = (host.result?.index.documents.length ?? 0) > 0;
|
|
8515
|
+
return b2`
|
|
8516
|
+
${renderGitStatus(host)}
|
|
8517
|
+
<button
|
|
8518
|
+
type="button"
|
|
8519
|
+
class="doorstop-git-stage"
|
|
8520
|
+
title="Stage all Doorstop-managed files (requirements, documents, configs)"
|
|
8521
|
+
?disabled=${host.runInProgress !== void 0 || !hasDocuments}
|
|
8522
|
+
@click=${() => onGitStageClick(host)}
|
|
8523
|
+
>${gitStageIconSvg}Stage all</button>
|
|
8524
|
+
<div class="doorstop-git-commit">
|
|
8525
|
+
<input
|
|
8526
|
+
type="text"
|
|
8527
|
+
class="doorstop-git-commit-input"
|
|
8528
|
+
placeholder="Commit message"
|
|
8529
|
+
aria-label="Git commit message"
|
|
8530
|
+
?disabled=${host.runInProgress !== void 0}
|
|
8531
|
+
${n5(host.gitCommitInputRef)}
|
|
8532
|
+
@input=${(event) => onGitCommitInput(host, event)}
|
|
8533
|
+
@keydown=${(event) => onGitCommitKeydown(host, event)}
|
|
8534
|
+
/>
|
|
8535
|
+
<button
|
|
8536
|
+
type="button"
|
|
8537
|
+
class="doorstop-git-commit-button"
|
|
8538
|
+
title="Commit the staged index with this message"
|
|
8539
|
+
?disabled=${host.runInProgress !== void 0 || host.gitCommitMessage.trim() === ""}
|
|
8540
|
+
@click=${() => onGitCommitClick(host)}
|
|
8541
|
+
>${gitCommitIconSvg}Commit</button>
|
|
8542
|
+
${host.gitActionError === void 0 ? A : b2`<span class="doorstop-op-error" role="alert">${host.gitActionError}</span>`}
|
|
8543
|
+
</div>
|
|
8544
|
+
`;
|
|
8545
|
+
}
|
|
8546
|
+
function renderGitStatus(host) {
|
|
8547
|
+
const view = host.gitStatusView;
|
|
8548
|
+
if (view === void 0) return A;
|
|
8549
|
+
const refresh = () => {
|
|
8550
|
+
void host.controller?.requestGitStatus();
|
|
8551
|
+
};
|
|
8552
|
+
switch (view.state) {
|
|
8553
|
+
case "loading":
|
|
8554
|
+
return b2`<button
|
|
8555
|
+
type="button"
|
|
8556
|
+
class="doorstop-git-status"
|
|
8557
|
+
title="Refreshing git status…"
|
|
8558
|
+
aria-busy=${host.gitStatusInFlight}
|
|
8559
|
+
@click=${refresh}
|
|
8560
|
+
><span class="doorstop-git-status-text">⎇ …</span></button>`;
|
|
8561
|
+
case "no-git":
|
|
8562
|
+
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>`;
|
|
8563
|
+
case "error":
|
|
8564
|
+
return b2`<button
|
|
8565
|
+
type="button"
|
|
8566
|
+
class="doorstop-git-status doorstop-git-status-error"
|
|
8567
|
+
title=${`Git status unavailable \u2014 ${view.errorMessage ?? "request failed"} \u2014 click to retry`}
|
|
8568
|
+
aria-busy=${host.gitStatusInFlight}
|
|
8569
|
+
@click=${refresh}
|
|
8570
|
+
><span class="doorstop-git-status-text">git status error — retry</span></button>`;
|
|
8571
|
+
case "ready":
|
|
8572
|
+
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>`;
|
|
8573
|
+
}
|
|
8574
|
+
}
|
|
8575
|
+
function onRefreshClick(host) {
|
|
8576
|
+
void host.controller?.invalidate();
|
|
8577
|
+
}
|
|
8578
|
+
function onValidateClick(host) {
|
|
8579
|
+
host.runDoorstop("validate", "Doorstop: validate", { op: "validate" }, "doorstop", true);
|
|
8580
|
+
}
|
|
8581
|
+
function onPublishClick(host) {
|
|
8582
|
+
const target = doorstopPublishTarget(host.result);
|
|
8583
|
+
if (typeof window.confirm === "function") {
|
|
8584
|
+
if (!window.confirm(`Publish the Doorstop tree as HTML to ${target} in the workspace terminal?`)) return;
|
|
8585
|
+
host.confirmSkipped = false;
|
|
8586
|
+
} else {
|
|
8587
|
+
host.confirmSkipped = true;
|
|
8588
|
+
}
|
|
8589
|
+
host.runDoorstop(
|
|
8590
|
+
"publish",
|
|
8591
|
+
"Doorstop: publish",
|
|
8592
|
+
{ op: "publish", target },
|
|
8593
|
+
doorstopPublishCommand(host.result, target),
|
|
8594
|
+
false
|
|
8595
|
+
);
|
|
8596
|
+
}
|
|
8597
|
+
function onGitStageClick(host) {
|
|
8598
|
+
const controller = host.controller;
|
|
8599
|
+
if (controller === void 0) return;
|
|
8600
|
+
if (controller.runInProgress !== void 0) return;
|
|
8601
|
+
const result = host.result;
|
|
8602
|
+
if (result === void 0) {
|
|
8603
|
+
host.gitActionError = "The workspace is not loaded \u2014 refresh first";
|
|
8604
|
+
return;
|
|
8605
|
+
}
|
|
8606
|
+
const paths = doorstopPaths(result);
|
|
8607
|
+
if (paths.length === 0) {
|
|
8608
|
+
host.gitActionError = "Nothing to stage \u2014 the workspace has no Doorstop-managed files";
|
|
8609
|
+
return;
|
|
8610
|
+
}
|
|
8611
|
+
host.gitActionError = void 0;
|
|
8612
|
+
void controller.runGitStage(paths);
|
|
8613
|
+
}
|
|
8614
|
+
function onGitCommitInput(host, event) {
|
|
8615
|
+
host.gitCommitMessage = event.target.value;
|
|
8616
|
+
}
|
|
8617
|
+
function onGitCommitClick(host) {
|
|
8618
|
+
gitCommitSubmit(host);
|
|
8619
|
+
}
|
|
8620
|
+
function onGitCommitKeydown(host, event) {
|
|
8621
|
+
const input = host.gitCommitInputRef.value;
|
|
8622
|
+
if (input === void 0) return;
|
|
8623
|
+
if (event.key === "Enter") {
|
|
8624
|
+
event.preventDefault();
|
|
8625
|
+
gitCommitSubmit(host);
|
|
8626
|
+
} else if (event.key === "Escape") {
|
|
8627
|
+
input.value = "";
|
|
8628
|
+
host.gitCommitMessage = "";
|
|
8629
|
+
host.gitActionError = void 0;
|
|
8630
|
+
}
|
|
8631
|
+
}
|
|
8632
|
+
function gitCommitSubmit(host) {
|
|
8633
|
+
const controller = host.controller;
|
|
8634
|
+
if (controller === void 0) return;
|
|
8635
|
+
if (controller.runInProgress !== void 0) return;
|
|
8636
|
+
const input = host.gitCommitInputRef.value;
|
|
8637
|
+
const message = input?.value.trim() ?? "";
|
|
8638
|
+
if (message === "") {
|
|
8639
|
+
host.gitActionError = "Enter a commit message";
|
|
8640
|
+
input?.focus();
|
|
8641
|
+
return;
|
|
8642
|
+
}
|
|
8643
|
+
host.gitActionError = void 0;
|
|
8644
|
+
void controller.runGitCommit(message).then(() => {
|
|
8645
|
+
const lastRun = controller.lastRun;
|
|
8646
|
+
if (lastRun?.op === "git-commit" && lastRun.status === "ok" && input !== void 0) {
|
|
8647
|
+
input.value = "";
|
|
8648
|
+
host.gitCommitMessage = "";
|
|
8649
|
+
}
|
|
8650
|
+
});
|
|
8651
|
+
}
|
|
8652
|
+
|
|
8653
|
+
// node_modules/lit-html/directives/repeat.js
|
|
8654
|
+
var u4 = (e8, s6, t5) => {
|
|
8655
|
+
const r8 = /* @__PURE__ */ new Map();
|
|
8656
|
+
for (let l3 = s6; l3 <= t5; l3++) r8.set(e8[l3], l3);
|
|
8657
|
+
return r8;
|
|
8658
|
+
};
|
|
8659
|
+
var c5 = e4(class extends i6 {
|
|
8660
|
+
constructor(e8) {
|
|
8661
|
+
if (super(e8), e8.type !== t4.CHILD) throw Error("repeat() can only be used in text expressions");
|
|
8662
|
+
}
|
|
8663
|
+
dt(e8, s6, t5) {
|
|
8664
|
+
let r8;
|
|
8665
|
+
void 0 === t5 ? t5 = s6 : void 0 !== s6 && (r8 = s6);
|
|
8666
|
+
const l3 = [], o8 = [];
|
|
8667
|
+
let i8 = 0;
|
|
8668
|
+
for (const s7 of e8) l3[i8] = r8 ? r8(s7, i8) : i8, o8[i8] = t5(s7, i8), i8++;
|
|
8669
|
+
return { values: o8, keys: l3 };
|
|
8670
|
+
}
|
|
8671
|
+
render(e8, s6, t5) {
|
|
8672
|
+
return this.dt(e8, s6, t5).values;
|
|
8673
|
+
}
|
|
8674
|
+
update(s6, [t5, r8, c6]) {
|
|
8675
|
+
const d3 = M2(s6), { values: p4, keys: a3 } = this.dt(t5, r8, c6);
|
|
8676
|
+
if (!Array.isArray(d3)) return this.ut = a3, p4;
|
|
8677
|
+
const h6 = this.ut ??= [], v3 = [];
|
|
8678
|
+
let m3, y3, x2 = 0, j2 = d3.length - 1, k2 = 0, w2 = p4.length - 1;
|
|
8679
|
+
for (; x2 <= j2 && k2 <= w2; ) if (null === d3[x2]) x2++;
|
|
8680
|
+
else if (null === d3[j2]) j2--;
|
|
8681
|
+
else if (h6[x2] === a3[k2]) v3[k2] = u3(d3[x2], p4[k2]), x2++, k2++;
|
|
8682
|
+
else if (h6[j2] === a3[w2]) v3[w2] = u3(d3[j2], p4[w2]), j2--, w2--;
|
|
8683
|
+
else if (h6[x2] === a3[w2]) v3[w2] = u3(d3[x2], p4[w2]), v2(s6, v3[w2 + 1], d3[x2]), x2++, w2--;
|
|
8684
|
+
else if (h6[j2] === a3[k2]) v3[k2] = u3(d3[j2], p4[k2]), v2(s6, d3[x2], d3[j2]), j2--, k2++;
|
|
8685
|
+
else if (void 0 === m3 && (m3 = u4(a3, k2, w2), y3 = u4(h6, x2, j2)), m3.has(h6[x2])) if (m3.has(h6[j2])) {
|
|
8686
|
+
const e8 = y3.get(a3[k2]), t6 = void 0 !== e8 ? d3[e8] : null;
|
|
8687
|
+
if (null === t6) {
|
|
8688
|
+
const e9 = v2(s6, d3[x2]);
|
|
8689
|
+
u3(e9, p4[k2]), v3[k2] = e9;
|
|
8690
|
+
} else v3[k2] = u3(t6, p4[k2]), v2(s6, d3[x2], t6), d3[e8] = null;
|
|
8691
|
+
k2++;
|
|
8692
|
+
} else h3(d3[j2]), j2--;
|
|
8693
|
+
else h3(d3[x2]), x2++;
|
|
8694
|
+
for (; k2 <= w2; ) {
|
|
8695
|
+
const e8 = v2(s6, v3[w2 + 1]);
|
|
8696
|
+
u3(e8, p4[k2]), v3[k2++] = e8;
|
|
8697
|
+
}
|
|
8698
|
+
for (; x2 <= j2; ) {
|
|
8699
|
+
const e8 = d3[x2++];
|
|
8700
|
+
null !== e8 && h3(e8);
|
|
8701
|
+
}
|
|
8702
|
+
return this.ut = a3, p3(s6, v3), E;
|
|
8703
|
+
}
|
|
8704
|
+
});
|
|
8705
|
+
|
|
8706
|
+
// src/browser/doorstop-prompts.ts
|
|
7972
8707
|
function andJoin(values) {
|
|
7973
8708
|
if (values.length === 0) return "";
|
|
7974
8709
|
if (values.length === 1) return values[0] ?? "";
|
|
@@ -8013,28 +8748,788 @@ function draftChildRequirementPrompt(parent, childPrefix) {
|
|
|
8013
8748
|
"draftChildRequirementPrompt requires a non-empty child document prefix: `doorstop add` names its document by prefix"
|
|
8014
8749
|
);
|
|
8015
8750
|
}
|
|
8016
|
-
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>\`.`;
|
|
8751
|
+
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>\`.`;
|
|
8752
|
+
}
|
|
8753
|
+
function referencePaths(item) {
|
|
8754
|
+
const paths = item.references?.map((reference) => reference.path) ?? [];
|
|
8755
|
+
return item.ref === "" ? paths : [item.ref, ...paths];
|
|
8756
|
+
}
|
|
8757
|
+
function reviewReadinessPrompt(item, children = []) {
|
|
8758
|
+
const parents = andJoin(item.links.map((link) => link.uid));
|
|
8759
|
+
const references = referencePaths(item);
|
|
8760
|
+
const childLinks = andJoin(
|
|
8761
|
+
children.map((child) => `${child.uid} (\`${child.path}\`)`)
|
|
8762
|
+
);
|
|
8763
|
+
const facts = [
|
|
8764
|
+
item.text.trim() === "" ? "its text is empty (that alone blocks review)" : "its text is filled in",
|
|
8765
|
+
item.links.length === 0 ? "it declares no parent links" : `its recorded parent links (${parents}) are not suspect or missing a fingerprint`,
|
|
8766
|
+
children.length === 0 ? "its child links are left to the `doorstop` run below" : `its child links are declared (${childLinks})`,
|
|
8767
|
+
references.length === 0 ? "it has no file references to verify" : `its file references exist (${references.map((path) => `\`${path}\``).join(", ")})`
|
|
8768
|
+
];
|
|
8769
|
+
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.`;
|
|
8770
|
+
}
|
|
8771
|
+
function composeReviewCommand(item) {
|
|
8772
|
+
return `/opendoor-review ${item.uid}
|
|
8773
|
+
Review ${item.uid} (\`${item.path}\`) against its child links and references; run \`doorstop\` validation and, when it is ready, \`doorstop review ${item.uid}\`.`;
|
|
8774
|
+
}
|
|
8775
|
+
function setupProjectPrompt() {
|
|
8776
|
+
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.`;
|
|
8777
|
+
}
|
|
8778
|
+
|
|
8779
|
+
// node_modules/lit-html/directives/keyed.js
|
|
8780
|
+
var i7 = e4(class extends i6 {
|
|
8781
|
+
constructor() {
|
|
8782
|
+
super(...arguments), this.key = A;
|
|
8783
|
+
}
|
|
8784
|
+
render(r8, t5) {
|
|
8785
|
+
return this.key = r8, t5;
|
|
8786
|
+
}
|
|
8787
|
+
update(r8, [t5, e8]) {
|
|
8788
|
+
return t5 !== this.key && (p3(r8), this.key = t5), e8;
|
|
8789
|
+
}
|
|
8790
|
+
});
|
|
8791
|
+
|
|
8792
|
+
// src/browser/sections/detail-pane.ts
|
|
8793
|
+
function renderDetail(host, result) {
|
|
8794
|
+
const selectedUid = host.selectedUid;
|
|
8795
|
+
if (selectedUid === void 0) {
|
|
8796
|
+
return b2`<p class="doorstop-muted doorstop-standalone">Select an item in the list to inspect its details.</p>`;
|
|
8797
|
+
}
|
|
8798
|
+
const item = result.index.byUid.get(selectedUid);
|
|
8799
|
+
if (item === void 0) {
|
|
8800
|
+
return b2`<p class="doorstop-muted doorstop-standalone">This item is no longer available — refresh the panel.</p>`;
|
|
8801
|
+
}
|
|
8802
|
+
const children = result.index.childrenByUid.get(item.uid) ?? [];
|
|
8803
|
+
const findings = result.index.findings.filter((finding) => finding.uid === item.uid);
|
|
8804
|
+
return b2`
|
|
8805
|
+
<section class="doorstop-detail" aria-label="Item details">
|
|
8806
|
+
<div class="doorstop-detail-head">
|
|
8807
|
+
<code class="doorstop-detail-uid">${item.uid}</code>
|
|
8808
|
+
<span class="doorstop-level">level ${item.level}</span>
|
|
8809
|
+
${item.header === void 0 ? A : b2`<h3>${item.header}</h3>`}
|
|
8810
|
+
</div>
|
|
8811
|
+
${renderFlags(host, item)}
|
|
8812
|
+
<div class="doorstop-state-chip-row">
|
|
8813
|
+
${item.stateKeys.map((key) => host.renderStateChip(key))}
|
|
8814
|
+
</div>
|
|
8815
|
+
<h4 class="doorstop-section-title">Text</h4>
|
|
8816
|
+
${renderItemText(host, item)}
|
|
8817
|
+
${renderChangesSinceReview(host, item)}
|
|
8818
|
+
<h4 class="doorstop-section-title">Parent links</h4>
|
|
8819
|
+
${renderLinksOut(host, item, result.index)}
|
|
8820
|
+
<h4 class="doorstop-section-title">Child links</h4>
|
|
8821
|
+
${renderLinksIn(host, item, result.index, children)}
|
|
8822
|
+
<h4 class="doorstop-section-title">References</h4>
|
|
8823
|
+
${renderReferences(host, item, result.index)}
|
|
8824
|
+
${Object.entries(item.attributes).length === 0 ? A : b2`
|
|
8825
|
+
<h4 class="doorstop-section-title">Extended attributes</h4>
|
|
8826
|
+
<dl class="doorstop-attributes">
|
|
8827
|
+
${Object.entries(item.attributes).map(([key, value]) => renderAttribute(host, key, value))}
|
|
8828
|
+
</dl>
|
|
8829
|
+
`}
|
|
8830
|
+
<h4 class="doorstop-section-title">Findings</h4>
|
|
8831
|
+
${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">
|
|
8832
|
+
${findings.map((finding) => {
|
|
8833
|
+
const kind = finding.severity === "error" ? "doorstop-error" : finding.severity === "warning" ? "doorstop-warning" : "doorstop-info";
|
|
8834
|
+
return b2`<div class=${`doorstop-finding ${kind}`}><span class="doorstop-finding-severity">${finding.severity}</span><span>${finding.message}</span></div>`;
|
|
8835
|
+
})}
|
|
8836
|
+
</div>`}
|
|
8837
|
+
</section>
|
|
8838
|
+
`;
|
|
8839
|
+
}
|
|
8840
|
+
function renderItemText(host, item) {
|
|
8841
|
+
if (item.text === "") {
|
|
8842
|
+
return b2`<p class="doorstop-text doorstop-text-empty">empty</p>`;
|
|
8843
|
+
}
|
|
8844
|
+
const renderer = host.markdownRenderer;
|
|
8845
|
+
const machineId = host.context?.machine.id;
|
|
8846
|
+
if (renderer === void 0 || machineId === void 0) {
|
|
8847
|
+
return b2`<p class="doorstop-text">${item.text}</p>`;
|
|
8848
|
+
}
|
|
8849
|
+
return b2`<div class="doorstop-text doorstop-text-markdown">${renderer.render({
|
|
8850
|
+
machineId,
|
|
8851
|
+
text: item.text,
|
|
8852
|
+
allowManualPreview: true
|
|
8853
|
+
})}</div>`;
|
|
8854
|
+
}
|
|
8855
|
+
function renderAttribute(host, key, value) {
|
|
8856
|
+
if (!isMarkdownAttribute(key, value)) {
|
|
8857
|
+
return b2`<div class="doorstop-attribute"><dt>${key}</dt><dd><code>${jsonishText(value)}</code></dd></div>`;
|
|
8858
|
+
}
|
|
8859
|
+
const text = value.trim();
|
|
8860
|
+
const renderer = host.markdownRenderer;
|
|
8861
|
+
const machineId = host.context?.machine.id;
|
|
8862
|
+
const body = text === "" ? b2`<p class="doorstop-text doorstop-text-empty">empty</p>` : renderer === void 0 || machineId === void 0 ? text : b2`<div class="doorstop-text-markdown">${renderer.render({ machineId, text, allowManualPreview: true })}</div>`;
|
|
8863
|
+
return b2`<div class="doorstop-attribute doorstop-attribute-markdown"><dt>${key}</dt><dd>${body}</dd></div>`;
|
|
8864
|
+
}
|
|
8865
|
+
function renderFlags(host, item) {
|
|
8866
|
+
return b2`
|
|
8867
|
+
<div class="doorstop-flags" aria-label="Item flags">
|
|
8868
|
+
<span class=${e7({ "doorstop-flag": true, "is-inactive": !item.active })}>
|
|
8869
|
+
${item.active ? "active" : "inactive"}
|
|
8870
|
+
</span>
|
|
8871
|
+
<span class="doorstop-flag">${item.normative ? "normative" : "non-normative"}</span>
|
|
8872
|
+
<span class="doorstop-flag">${item.derived ? "derived" : "non-derived"}</span>
|
|
8873
|
+
</div>
|
|
8874
|
+
`;
|
|
8875
|
+
}
|
|
8876
|
+
function renderLinksOut(host, item, index) {
|
|
8877
|
+
if (item.links.length === 0) return b2`<p class="doorstop-muted">No parent links.</p>`;
|
|
8878
|
+
return b2`
|
|
8879
|
+
<div class="doorstop-links" aria-label="Parent links">
|
|
8880
|
+
${item.links.map((link) => renderLinkOut(host, item, link, index))}
|
|
8881
|
+
</div>
|
|
8882
|
+
`;
|
|
8883
|
+
}
|
|
8884
|
+
function renderLinkOut(host, item, link, index) {
|
|
8885
|
+
const remove = b2`
|
|
8886
|
+
<span
|
|
8887
|
+
class="doorstop-link-remove"
|
|
8888
|
+
aria-hidden="true"
|
|
8889
|
+
title=${`doorstop unlink ${item.uid} ${link.uid}`}
|
|
8890
|
+
@click=${(event) => {
|
|
8891
|
+
event.stopPropagation();
|
|
8892
|
+
unlinkLink(host, item, link.uid);
|
|
8893
|
+
}}
|
|
8894
|
+
>${trashIconSvg}</span>
|
|
8895
|
+
`;
|
|
8896
|
+
const target = index.byUid.get(link.uid);
|
|
8897
|
+
if (target === void 0) {
|
|
8898
|
+
return b2`
|
|
8899
|
+
<div class="doorstop-link-row">
|
|
8900
|
+
<code>${link.uid}</code>
|
|
8901
|
+
<span class="doorstop-chip doorstop-chip-danger">unknown</span>
|
|
8902
|
+
<span class="doorstop-fingerprint">not in the index — ${link.fingerprint === null ? "no recorded fingerprint" : `recorded ${shortFingerprint(link.fingerprint)}`}</span>
|
|
8903
|
+
${remove}
|
|
8904
|
+
</div>
|
|
8905
|
+
`;
|
|
8906
|
+
}
|
|
8907
|
+
const current = computeItemStamp(target, documentConfigFor(index, target), false);
|
|
8908
|
+
const suspect = link.fingerprint !== null && link.fingerprint !== current;
|
|
8909
|
+
return b2`
|
|
8910
|
+
<button
|
|
8911
|
+
type="button"
|
|
8912
|
+
class="doorstop-link-row"
|
|
8913
|
+
data-uid=${link.uid}
|
|
8914
|
+
title=${`Open ${link.uid}`}
|
|
8915
|
+
@click=${() => {
|
|
8916
|
+
host.controller?.selectUid(link.uid);
|
|
8917
|
+
}}
|
|
8918
|
+
>
|
|
8919
|
+
<code>${link.uid}</code>
|
|
8920
|
+
<span class=${suspect ? "doorstop-chip doorstop-chip-danger" : "doorstop-chip doorstop-chip-ok"}>${suspect ? "suspect" : "ok"}</span>
|
|
8921
|
+
<span class="doorstop-fingerprint">recorded ${shortFingerprint(link.fingerprint)} · current ${shortFingerprint(current)}</span>
|
|
8922
|
+
${remove}
|
|
8923
|
+
</button>
|
|
8924
|
+
`;
|
|
8925
|
+
}
|
|
8926
|
+
function renderLinksIn(host, item, index, children) {
|
|
8927
|
+
if (children.length === 0) {
|
|
8928
|
+
return b2`<p class="doorstop-muted">No child links.</p>`;
|
|
8929
|
+
}
|
|
8930
|
+
return b2`
|
|
8931
|
+
<div class="doorstop-links" aria-label="Child links">
|
|
8932
|
+
${children.map(
|
|
8933
|
+
(child) => b2`<button type="button" class="doorstop-link-row" data-uid=${child.uid} title=${`Open ${child.uid}`} @click=${() => {
|
|
8934
|
+
host.controller?.selectUid(child.uid);
|
|
8935
|
+
}}>
|
|
8936
|
+
<code>${child.uid}</code>
|
|
8937
|
+
<span class="doorstop-muted">${child.level}</span>
|
|
8938
|
+
</button>`
|
|
8939
|
+
)}
|
|
8940
|
+
</div>
|
|
8941
|
+
`;
|
|
8942
|
+
}
|
|
8943
|
+
function renderReferences(host, item, index) {
|
|
8944
|
+
const paths = [];
|
|
8945
|
+
if (item.ref !== "") paths.push(item.ref);
|
|
8946
|
+
if (item.references !== void 0) {
|
|
8947
|
+
for (const reference of item.references) paths.push(reference.path);
|
|
8948
|
+
}
|
|
8949
|
+
if (paths.length === 0) return b2`<p class="doorstop-muted">No file references.</p>`;
|
|
8950
|
+
return b2`
|
|
8951
|
+
<div class="doorstop-links" aria-label="File references">
|
|
8952
|
+
${paths.map((path) => {
|
|
8953
|
+
const found = index.knownFilePaths.has(path);
|
|
8954
|
+
return b2`
|
|
8955
|
+
<div class="doorstop-link-row">
|
|
8956
|
+
<code>${path}</code>
|
|
8957
|
+
${found ? A : b2`<span class="doorstop-chip doorstop-chip-danger">not found</span>`}
|
|
8958
|
+
</div>
|
|
8959
|
+
`;
|
|
8960
|
+
})}
|
|
8961
|
+
</div>
|
|
8962
|
+
`;
|
|
8963
|
+
}
|
|
8964
|
+
function renderChangesSinceReview(host, item) {
|
|
8965
|
+
if (!item.stateKeys.includes("unreviewed") || item.reviewed === null) return A;
|
|
8966
|
+
if (!host.backendActive()) return A;
|
|
8967
|
+
const view = host.controller?.baselineViewFor(item);
|
|
8968
|
+
return i7(
|
|
8969
|
+
item.uid,
|
|
8970
|
+
b2`
|
|
8971
|
+
<details class="doorstop-changes" @toggle=${(event) => onChangesToggle(host, event)}>
|
|
8972
|
+
<summary class="doorstop-changes-summary">Changes since review</summary>
|
|
8973
|
+
${view === void 0 || view.state === "loading" ? b2`<p class="doorstop-muted doorstop-changes-notice">Loading baseline…</p>` : renderBaselineView(host, view)}
|
|
8974
|
+
</details>
|
|
8975
|
+
`
|
|
8976
|
+
);
|
|
8977
|
+
}
|
|
8978
|
+
function renderBaselineView(host, view) {
|
|
8979
|
+
switch (view.state) {
|
|
8980
|
+
case "loading":
|
|
8981
|
+
return b2`<p class="doorstop-muted doorstop-changes-notice">Loading baseline…</p>`;
|
|
8982
|
+
case "no-git":
|
|
8983
|
+
return b2`<p class="doorstop-muted doorstop-changes-notice">No git history — previous version unavailable</p>`;
|
|
8984
|
+
case "no-match":
|
|
8985
|
+
return b2`<p class="doorstop-muted doorstop-changes-notice">Could not locate the reviewed version (history may have been rewritten)</p>`;
|
|
8986
|
+
case "error":
|
|
8987
|
+
return b2`<p class="doorstop-muted doorstop-changes-notice">Baseline unavailable — ${view.errorMessage ?? "request failed"}</p>`;
|
|
8988
|
+
case "ready": {
|
|
8989
|
+
const diff = view.diff;
|
|
8990
|
+
if (diff === void 0) return b2`<p class="doorstop-muted doorstop-changes-notice">No changes found.</p>`;
|
|
8991
|
+
return renderBaselineDiff(host, view, diff);
|
|
8992
|
+
}
|
|
8993
|
+
}
|
|
8994
|
+
}
|
|
8995
|
+
function renderBaselineDiff(host, view, diff) {
|
|
8996
|
+
return b2`
|
|
8997
|
+
${view.source === void 0 ? A : b2`<p class="doorstop-muted doorstop-changes-source">matched via ${view.source === "review-commit" ? "review commit" : "history walk"}</p>`}
|
|
8998
|
+
${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">
|
|
8999
|
+
${diff.text.map((line) => renderDiffLine(host, line))}
|
|
9000
|
+
</div>`}
|
|
9001
|
+
${diff.ref === void 0 ? A : renderFieldChange(host, "ref", diff.ref.before, diff.ref.after)}
|
|
9002
|
+
${diff.references === void 0 ? A : renderFieldChange(
|
|
9003
|
+
host,
|
|
9004
|
+
"references",
|
|
9005
|
+
referenceListText(diff.references.before),
|
|
9006
|
+
referenceListText(diff.references.after)
|
|
9007
|
+
)}
|
|
9008
|
+
${diff.linksAdded.length === 0 && diff.linksRemoved.length === 0 ? A : b2`<div class="doorstop-field-change">
|
|
9009
|
+
<span class="doorstop-field-name">links</span>
|
|
9010
|
+
${diff.linksAdded.map((uid) => b2`<code class="doorstop-field-chip doorstop-field-chip-after">+ ${uid}</code>`)}
|
|
9011
|
+
${diff.linksRemoved.map((uid) => b2`<code class="doorstop-field-chip doorstop-field-chip-before">− ${uid}</code>`)}
|
|
9012
|
+
</div>`}
|
|
9013
|
+
${diff.extended.map((change) => renderFieldChange(host, change.name, change.before, change.after))}
|
|
9014
|
+
`;
|
|
9015
|
+
}
|
|
9016
|
+
function renderDiffLine(host, line) {
|
|
9017
|
+
const mark = line.kind === "added" ? "+" : line.kind === "removed" ? "\u2212" : "";
|
|
9018
|
+
return b2`
|
|
9019
|
+
<div class=${`doorstop-diff-line is-${line.kind}`} role="listitem">
|
|
9020
|
+
<span class="doorstop-diff-mark">${mark}</span>
|
|
9021
|
+
<span class="doorstop-diff-text">${line.text === "" ? "\xA0" : line.text}</span>
|
|
9022
|
+
</div>
|
|
9023
|
+
`;
|
|
9024
|
+
}
|
|
9025
|
+
function renderFieldChange(host, label, before, after) {
|
|
9026
|
+
return b2`
|
|
9027
|
+
<div class="doorstop-field-change">
|
|
9028
|
+
<span class="doorstop-field-name">${label}</span>
|
|
9029
|
+
<code class="doorstop-field-chip doorstop-field-chip-before">${diffValueText(before)}</code>
|
|
9030
|
+
<span class="doorstop-field-arrow">→</span>
|
|
9031
|
+
<code class="doorstop-field-chip doorstop-field-chip-after">${diffValueText(after)}</code>
|
|
9032
|
+
</div>
|
|
9033
|
+
`;
|
|
9034
|
+
}
|
|
9035
|
+
function onChangesToggle(host, event) {
|
|
9036
|
+
const details = event.currentTarget;
|
|
9037
|
+
if (!(details instanceof HTMLDetailsElement) || !details.open) return;
|
|
9038
|
+
const item = host.selectedItem();
|
|
9039
|
+
if (item === void 0) return;
|
|
9040
|
+
void host.controller?.requestBaseline(item);
|
|
9041
|
+
}
|
|
9042
|
+
function unlinkLink(host, item, target) {
|
|
9043
|
+
const controller = host.controller;
|
|
9044
|
+
if (controller === void 0 || controller.runInProgress !== void 0) return;
|
|
9045
|
+
host.runDoorstop(
|
|
9046
|
+
"unlink",
|
|
9047
|
+
`Doorstop: unlink ${item.uid}`,
|
|
9048
|
+
{ op: "unlink", uid: item.uid, target },
|
|
9049
|
+
`doorstop unlink ${item.uid} ${target}`,
|
|
9050
|
+
false
|
|
9051
|
+
);
|
|
9052
|
+
}
|
|
9053
|
+
|
|
9054
|
+
// src/browser/sections/item-list.ts
|
|
9055
|
+
var DEFAULT_STATE_KEY = "normative";
|
|
9056
|
+
function renderListFilters(host, result) {
|
|
9057
|
+
return b2`
|
|
9058
|
+
<section class="doorstop-list-filters">
|
|
9059
|
+
<div class="doorstop-docs" role="list" aria-label="Doorstop documents">
|
|
9060
|
+
${renderDocumentChip(host, void 0, result)}
|
|
9061
|
+
${result.index.documents.map((document2) => renderDocumentChip(host, document2, result))}
|
|
9062
|
+
</div>
|
|
9063
|
+
<select class="doorstop-state-filter" aria-label="Filter by state" @change=${(event) => onStateFilterChange(host, event)}>
|
|
9064
|
+
<option value="" .selected=${host.stateFilter === void 0}>All states</option>
|
|
9065
|
+
${Object.entries(STATE_CHIP_LABELS).map(
|
|
9066
|
+
([key, label]) => b2`<option value=${key} .selected=${host.stateFilter === key}>${label}</option>`
|
|
9067
|
+
)}
|
|
9068
|
+
</select>
|
|
9069
|
+
<input
|
|
9070
|
+
class="doorstop-search"
|
|
9071
|
+
type="search"
|
|
9072
|
+
aria-label="Search items"
|
|
9073
|
+
placeholder="Search UID or text"
|
|
9074
|
+
.value=${host.search}
|
|
9075
|
+
@input=${(event) => onSearchInput(host, event)}
|
|
9076
|
+
/>
|
|
9077
|
+
</section>
|
|
9078
|
+
`;
|
|
9079
|
+
}
|
|
9080
|
+
function renderDocumentChip(host, document2, result) {
|
|
9081
|
+
const prefix = document2?.prefix ?? ALL_DOCUMENTS_LABEL;
|
|
9082
|
+
const count = document2 === void 0 ? result.index.items.length : result.index.items.filter((item) => item.documentPrefix === document2.prefix).length;
|
|
9083
|
+
const dots = document2 === void 0 ? [] : documentStateDots(document2, result.index.items);
|
|
9084
|
+
const selected = document2 === void 0 ? host.selectedDocumentPrefix === void 0 || host.selectedDocumentPrefix === "" : host.selectedDocumentPrefix === document2.prefix;
|
|
9085
|
+
return b2`
|
|
9086
|
+
<button
|
|
9087
|
+
type="button"
|
|
9088
|
+
role="listitem"
|
|
9089
|
+
class=${e7({ "doorstop-doc-chip": true, "is-selected": selected })}
|
|
9090
|
+
data-prefix=${document2?.prefix ?? ""}
|
|
9091
|
+
@click=${() => {
|
|
9092
|
+
host.controller?.selectDocument(document2?.prefix ?? "");
|
|
9093
|
+
}}
|
|
9094
|
+
>
|
|
9095
|
+
${document2?.parentPrefix === void 0 ? A : b2`<span class="doorstop-doc-arrow">← ${document2.parentPrefix}</span>`}
|
|
9096
|
+
<span class="doorstop-doc-prefix">${prefix}</span>
|
|
9097
|
+
<span class="doorstop-doc-count">${String(count)}</span>
|
|
9098
|
+
${dots.map((dot) => b2`<span class=${`doorstop-dot doorstop-dot-${dot}`} title=${dotTitle(dot)}></span>`)}
|
|
9099
|
+
</button>
|
|
9100
|
+
`;
|
|
9101
|
+
}
|
|
9102
|
+
function renderViewer(host) {
|
|
9103
|
+
const result = host.result;
|
|
9104
|
+
if (result === void 0) {
|
|
9105
|
+
return b2`<p class="doorstop-muted doorstop-standalone">${host.loading ? "Loading workspace\u2026" : "Run Refresh to scan for Doorstop documents."}</p>`;
|
|
9106
|
+
}
|
|
9107
|
+
if (host.view === "findings") {
|
|
9108
|
+
return renderFindingsView(host, result);
|
|
9109
|
+
}
|
|
9110
|
+
return b2`
|
|
9111
|
+
${renderListFilters(host, result)}
|
|
9112
|
+
${result.index.documents.length === 0 ? b2`
|
|
9113
|
+
${renderDiagnostics(host, result)}
|
|
9114
|
+
<section class="doorstop-empty">
|
|
9115
|
+
<p>${EMPTY_WORKSPACE_MESSAGE}</p>
|
|
9116
|
+
${host.context === void 0 ? A : b2`
|
|
9117
|
+
<button
|
|
9118
|
+
type="button"
|
|
9119
|
+
class="doorstop-setup"
|
|
9120
|
+
@click=${() => {
|
|
9121
|
+
host.insertPrompt(setupProjectPrompt());
|
|
9122
|
+
}}
|
|
9123
|
+
>Ask agent to set up the project</button>
|
|
9124
|
+
`}
|
|
9125
|
+
</section>
|
|
9126
|
+
` : b2`
|
|
9127
|
+
<section class="doorstop-split">
|
|
9128
|
+
<section class="doorstop-list">
|
|
9129
|
+
${renderDiagnostics(host, result)}
|
|
9130
|
+
${renderItemList(host, result)}
|
|
9131
|
+
</section>
|
|
9132
|
+
<section class="doorstop-detail-pane">${renderDetail(host, result)}</section>
|
|
9133
|
+
</section>
|
|
9134
|
+
`}
|
|
9135
|
+
`;
|
|
9136
|
+
}
|
|
9137
|
+
function renderItemList(host, result) {
|
|
9138
|
+
if (result.index.items.length === 0) {
|
|
9139
|
+
return b2`<p class="doorstop-muted doorstop-standalone">No Doorstop items found (item files may be binary or truncated — see the diagnostics above).</p>`;
|
|
9140
|
+
}
|
|
9141
|
+
const items = filteredItems(result.index, host.selectedDocumentPrefix, host.stateFilter, host.search);
|
|
9142
|
+
if (items.length === 0) {
|
|
9143
|
+
return b2`<p class="doorstop-muted doorstop-standalone">No items match the current document, state, or search filters.</p>`;
|
|
9144
|
+
}
|
|
9145
|
+
const gitFiles = host.readyGitFiles();
|
|
9146
|
+
const attributeKeys = doorstopShowAdditionalAttribute(result);
|
|
9147
|
+
return b2`
|
|
9148
|
+
<div
|
|
9149
|
+
class=${e7({ "doorstop-items": true, "has-item-attrs": attributeKeys.length > 0 })}
|
|
9150
|
+
role="list"
|
|
9151
|
+
aria-label="Doorstop items"
|
|
9152
|
+
>
|
|
9153
|
+
${c5(items, (item) => item.uid, (item) => renderItemRow(host, item, gitFiles, attributeKeys))}
|
|
9154
|
+
</div>
|
|
9155
|
+
`;
|
|
9156
|
+
}
|
|
9157
|
+
function renderItemRow(host, item, gitFiles, attributeKeys) {
|
|
9158
|
+
const selected = host.selectedUid === item.uid;
|
|
9159
|
+
const gitState = itemGitState(gitFiles?.get(item.path));
|
|
9160
|
+
const shownAttributes = shownAttributeRows(item, attributeKeys);
|
|
9161
|
+
const textBody = attributeKeys.includes("text") ? itemTextRow(item) : void 0;
|
|
9162
|
+
const stageable = itemStageable(gitFiles?.get(item.path));
|
|
9163
|
+
const unstageable = itemUnstageable(gitFiles?.get(item.path));
|
|
9164
|
+
return b2`
|
|
9165
|
+
<button
|
|
9166
|
+
type="button"
|
|
9167
|
+
role="listitem"
|
|
9168
|
+
class=${e7({ "doorstop-item-row": true, "is-selected": selected })}
|
|
9169
|
+
data-uid=${item.uid}
|
|
9170
|
+
@click=${() => {
|
|
9171
|
+
host.controller?.selectUid(item.uid);
|
|
9172
|
+
}}
|
|
9173
|
+
>
|
|
9174
|
+
<span class="doorstop-item-level">${item.level}</span>
|
|
9175
|
+
<code class="doorstop-item-uid">${item.uid}</code>
|
|
9176
|
+
<span class="doorstop-item-summary">${itemExcerpt(item)}</span>
|
|
9177
|
+
${shownAttributes.length === 0 ? A : b2`<span class="doorstop-item-attrs">${shownAttributes.map((row) => `${row.key}: ${row.text}`).join(" \xB7 ")}</span>`}
|
|
9178
|
+
<span class="doorstop-item-chips">
|
|
9179
|
+
${item.stateKeys.filter((key) => key !== DEFAULT_STATE_KEY).map((key) => renderStateChip(key))}
|
|
9180
|
+
${gitState === "clean" ? A : b2`<span class=${`doorstop-chip doorstop-chip-${gitChipKind(gitState)}`}>${GIT_CHIP_LABELS[gitState]}</span>`}
|
|
9181
|
+
${unstageable && host.runInProgress === void 0 ? b2`<span
|
|
9182
|
+
class="doorstop-item-add"
|
|
9183
|
+
aria-hidden="true"
|
|
9184
|
+
title=${`git reset ${item.path}`}
|
|
9185
|
+
@click=${(event) => {
|
|
9186
|
+
event.stopPropagation();
|
|
9187
|
+
host.unstageItem(item);
|
|
9188
|
+
}}
|
|
9189
|
+
>${gitUnstageIconSvg}</span>` : stageable && host.runInProgress === void 0 ? b2`<span
|
|
9190
|
+
class="doorstop-item-add"
|
|
9191
|
+
aria-hidden="true"
|
|
9192
|
+
title=${`git add ${item.path}`}
|
|
9193
|
+
@click=${(event) => {
|
|
9194
|
+
event.stopPropagation();
|
|
9195
|
+
host.stageItem(item);
|
|
9196
|
+
}}
|
|
9197
|
+
>${gitStageIconSvg}</span>` : A}
|
|
9198
|
+
${host.context === void 0 ? A : b2`<span
|
|
9199
|
+
class="doorstop-item-insert"
|
|
9200
|
+
aria-hidden="true"
|
|
9201
|
+
title=${`Insert ${item.uid} into the prompt`}
|
|
9202
|
+
@click=${(event) => {
|
|
9203
|
+
event.stopPropagation();
|
|
9204
|
+
host.insertPrompt(item.uid);
|
|
9205
|
+
}}
|
|
9206
|
+
>${insertPromptIconSvg}</span>`}
|
|
9207
|
+
</span>
|
|
9208
|
+
${textBody === void 0 ? A : b2`<span class="doorstop-item-text">${textBody}</span>`}
|
|
9209
|
+
</button>
|
|
9210
|
+
`;
|
|
9211
|
+
}
|
|
9212
|
+
function renderStateChip(key) {
|
|
9213
|
+
const kind = stateChipKind(key);
|
|
9214
|
+
return b2`<span class=${`doorstop-chip doorstop-chip-${kind}`}>${STATE_CHIP_LABELS[key]}</span>`;
|
|
9215
|
+
}
|
|
9216
|
+
function renderDiagnostics(host, result) {
|
|
9217
|
+
if (result.index.diagnostics.length === 0) return A;
|
|
9218
|
+
return b2`
|
|
9219
|
+
<section class="doorstop-diagnostics" aria-label="Workspace diagnostics">
|
|
9220
|
+
${result.index.diagnostics.map((diagnostic) => {
|
|
9221
|
+
const kind = diagnostic.severity === "error" ? "doorstop-error" : "doorstop-warning";
|
|
9222
|
+
return b2`
|
|
9223
|
+
<div class=${`doorstop-diagnostic ${kind}`}>
|
|
9224
|
+
<span class="doorstop-severity">${diagnostic.severity}</span>
|
|
9225
|
+
<span class="doorstop-diagnostic-copy">
|
|
9226
|
+
${diagnostic.path === void 0 ? A : b2`<code>${diagnostic.path}</code>`}
|
|
9227
|
+
${diagnostic.message}
|
|
9228
|
+
</span>
|
|
9229
|
+
</div>
|
|
9230
|
+
`;
|
|
9231
|
+
})}
|
|
9232
|
+
</section>
|
|
9233
|
+
`;
|
|
9234
|
+
}
|
|
9235
|
+
function renderFindingsView(host, result) {
|
|
9236
|
+
const rows = findingsViewRows(result.index);
|
|
9237
|
+
const counts = findingsViewCounts(rows);
|
|
9238
|
+
return b2`
|
|
9239
|
+
<section class="doorstop-findings-view" aria-label="Doorstop findings">
|
|
9240
|
+
<header class="doorstop-findings-head">
|
|
9241
|
+
<span class="doorstop-findings-counts" role="status" aria-label="Finding counts">${findingsCountText(counts)}</span>
|
|
9242
|
+
<span class="doorstop-findings-note doorstop-muted">${FINDINGS_PLUGIN_LOCAL_NOTE}</span>
|
|
9243
|
+
</header>
|
|
9244
|
+
${rows.length === 0 ? b2`
|
|
9245
|
+
<section class="doorstop-empty">
|
|
9246
|
+
<p>${FINDINGS_EMPTY_MESSAGE}</p>
|
|
9247
|
+
<p class="doorstop-muted">${FINDINGS_EMPTY_HINT}</p>
|
|
9248
|
+
</section>
|
|
9249
|
+
` : b2`<div class="doorstop-findings-list" role="list" aria-label="Validation findings">
|
|
9250
|
+
${rows.map((row) => renderFindingRow(host, row, result.index))}
|
|
9251
|
+
</div>`}
|
|
9252
|
+
</section>
|
|
9253
|
+
`;
|
|
9254
|
+
}
|
|
9255
|
+
function renderFindingRow(host, row, index) {
|
|
9256
|
+
const kind = row.severity === "error" ? "doorstop-error" : row.severity === "warning" ? "doorstop-warning" : "doorstop-info";
|
|
9257
|
+
const uid = row.uid;
|
|
9258
|
+
const navigable = uid !== void 0 && index.byUid.has(uid);
|
|
9259
|
+
return b2`
|
|
9260
|
+
<div class=${`doorstop-finding-row ${kind}`} role="listitem">
|
|
9261
|
+
<span class="doorstop-severity">${row.severity}</span>
|
|
9262
|
+
${uid === void 0 ? A : navigable ? b2`<button type="button" class="doorstop-finding-uid" data-uid=${uid} title=${`Show ${uid} in the item list`} @click=${() => {
|
|
9263
|
+
selectFindingTarget(host, uid);
|
|
9264
|
+
}}><code>${uid}</code></button>` : b2`<code class="doorstop-finding-uid">${uid}</code>`}
|
|
9265
|
+
${row.path === void 0 ? A : b2`<code class="doorstop-finding-path">${row.path}</code>`}
|
|
9266
|
+
<span class="doorstop-finding-message">${row.message}</span>
|
|
9267
|
+
</div>
|
|
9268
|
+
`;
|
|
9269
|
+
}
|
|
9270
|
+
function selectFindingTarget(host, uid) {
|
|
9271
|
+
const controller = host.controller;
|
|
9272
|
+
if (controller === void 0) return;
|
|
9273
|
+
controller.selectDocument("");
|
|
9274
|
+
controller.setStateFilter(void 0);
|
|
9275
|
+
controller.setSearch("");
|
|
9276
|
+
controller.selectUid(uid);
|
|
9277
|
+
host.view = "items";
|
|
9278
|
+
}
|
|
9279
|
+
function onStateFilterChange(host, event) {
|
|
9280
|
+
const value = event.target.value;
|
|
9281
|
+
host.controller?.setStateFilter(value === "" ? void 0 : value);
|
|
9282
|
+
}
|
|
9283
|
+
function onSearchInput(host, event) {
|
|
9284
|
+
host.controller?.setSearch(event.target.value);
|
|
9285
|
+
}
|
|
9286
|
+
|
|
9287
|
+
// src/browser/sections/item-action-palette.ts
|
|
9288
|
+
function renderItemActionPalette(host) {
|
|
9289
|
+
if (host.view !== "items") return A;
|
|
9290
|
+
const result = host.result;
|
|
9291
|
+
if (result === void 0 || result.index.documents.length === 0) return A;
|
|
9292
|
+
const item = host.selectedItem();
|
|
9293
|
+
if (item === void 0) {
|
|
9294
|
+
return b2`
|
|
9295
|
+
<section class="doorstop-action-palette" aria-label="Item actions">
|
|
9296
|
+
<span class="doorstop-muted doorstop-palette-placeholder">Select an item…</span>
|
|
9297
|
+
</section>
|
|
9298
|
+
`;
|
|
9299
|
+
}
|
|
9300
|
+
const suspects = suspectParentItems(item, result.index);
|
|
9301
|
+
return b2`
|
|
9302
|
+
<section class="doorstop-action-palette" aria-label="Item actions">
|
|
9303
|
+
${renderActionRow(host, item, result.index, suspects)}
|
|
9304
|
+
</section>
|
|
9305
|
+
`;
|
|
9306
|
+
}
|
|
9307
|
+
function renderActionRow(host, item, index, suspects) {
|
|
9308
|
+
const reviewed = item.stateKeys.includes("reviewed");
|
|
9309
|
+
const suspectUids = suspects.length > 0 ? suspects.map((parent) => parent.uid) : [];
|
|
9310
|
+
const gitStatus = host.readyGitStatus();
|
|
9311
|
+
const gitFile = gitStatus === void 0 ? void 0 : gitStatusFileFor(gitStatus, item.path);
|
|
9312
|
+
const stageable = itemStageable(gitFile);
|
|
9313
|
+
const unstageable = itemUnstageable(gitFile);
|
|
9314
|
+
return b2`
|
|
9315
|
+
<button
|
|
9316
|
+
type="button"
|
|
9317
|
+
class="doorstop-review"
|
|
9318
|
+
?disabled=${reviewed || host.runInProgress !== void 0}
|
|
9319
|
+
title=${reviewed ? `${item.uid} is already reviewed against its current fingerprint` : `Mark ${item.uid} reviewed`}
|
|
9320
|
+
@click=${() => {
|
|
9321
|
+
reviewItem(host, item);
|
|
9322
|
+
}}
|
|
9323
|
+
>Approve</button>
|
|
9324
|
+
<button
|
|
9325
|
+
type="button"
|
|
9326
|
+
class="doorstop-clear"
|
|
9327
|
+
?disabled=${suspects.length === 0 || host.runInProgress !== void 0}
|
|
9328
|
+
title=${suspects.length === 0 ? `No suspect links to clear` : `Re-record the parent fingerprints of ${item.uid}`}
|
|
9329
|
+
@click=${() => {
|
|
9330
|
+
clearSuspects(host, item, suspectUids);
|
|
9331
|
+
}}
|
|
9332
|
+
>Clear suspect links</button>
|
|
9333
|
+
${gitStatus === void 0 ? A : b2`<button
|
|
9334
|
+
type="button"
|
|
9335
|
+
class="doorstop-item-stage"
|
|
9336
|
+
?disabled=${host.runInProgress !== void 0 || !stageable}
|
|
9337
|
+
title=${stageable ? `Stage ${item.path}` : `${item.uid} has no unstaged changes \u2014 commit next`}
|
|
9338
|
+
@click=${() => {
|
|
9339
|
+
host.stageItem(item);
|
|
9340
|
+
}}
|
|
9341
|
+
>${gitStageIconSvg}Stage</button>
|
|
9342
|
+
<button
|
|
9343
|
+
type="button"
|
|
9344
|
+
class="doorstop-item-unstage"
|
|
9345
|
+
?disabled=${host.runInProgress !== void 0 || !unstageable}
|
|
9346
|
+
title=${unstageable ? `Unstage ${item.path} (git reset)` : `${item.uid} has nothing staged`}
|
|
9347
|
+
@click=${() => {
|
|
9348
|
+
host.unstageItem(item);
|
|
9349
|
+
}}
|
|
9350
|
+
>${gitUnstageIconSvg}Unstage</button>`}
|
|
9351
|
+
<div class="doorstop-op">
|
|
9352
|
+
<input
|
|
9353
|
+
type="text"
|
|
9354
|
+
class="doorstop-target-input"
|
|
9355
|
+
data-op="link"
|
|
9356
|
+
placeholder="parent UID"
|
|
9357
|
+
${n5(host.linkInputRef)}
|
|
9358
|
+
@keydown=${(event) => onTargetKeydown(host, event)}
|
|
9359
|
+
/>
|
|
9360
|
+
<button type="button" class="doorstop-link" title=${`doorstop link ${item.uid} <target>`} ?disabled=${host.runInProgress !== void 0} @click=${() => {
|
|
9361
|
+
runTargetOp(host, "link", host.linkInputRef, item);
|
|
9362
|
+
}}>Link</button>
|
|
9363
|
+
</div>
|
|
9364
|
+
${host.targetError === void 0 ? A : b2`<span class="doorstop-op-error" role="alert">${host.targetError}</span>`}
|
|
9365
|
+
${renderAskMenu(host, item, index, suspects)}
|
|
9366
|
+
`;
|
|
9367
|
+
}
|
|
9368
|
+
function renderAskMenu(host, item, index, suspects) {
|
|
9369
|
+
const children = index.childrenByUid.get(item.uid) ?? [];
|
|
9370
|
+
const childPrefix = firstChildDocumentPrefix(index, item);
|
|
9371
|
+
return b2`
|
|
9372
|
+
<div class="doorstop-menu">
|
|
9373
|
+
<button
|
|
9374
|
+
type="button"
|
|
9375
|
+
class="doorstop-menu-toggle"
|
|
9376
|
+
aria-expanded=${host.askMenuOpen ? "true" : "false"}
|
|
9377
|
+
@click=${() => onAskMenuToggle(host)}
|
|
9378
|
+
>Ask agent</button>
|
|
9379
|
+
${host.askMenuOpen ? b2`
|
|
9380
|
+
<div class="doorstop-menu-items" role="menu" aria-label="Ask the agent about ${item.uid}">
|
|
9381
|
+
<button type="button" role="menuitem" class="doorstop-menu-item doorstop-explain" @click=${() => {
|
|
9382
|
+
host.insertPrompt(explainItemPrompt(item));
|
|
9383
|
+
}}>Explain</button>
|
|
9384
|
+
<button
|
|
9385
|
+
type="button"
|
|
9386
|
+
role="menuitem"
|
|
9387
|
+
class="doorstop-menu-item doorstop-fix-suspects"
|
|
9388
|
+
?disabled=${suspects.length === 0}
|
|
9389
|
+
title=${suspects.length === 0 ? "No suspect links \u2014 nothing to fix" : `Fix the suspect links of ${item.uid}`}
|
|
9390
|
+
@click=${() => {
|
|
9391
|
+
host.insertPrompt(fixSuspectLinksPrompt(item, suspects));
|
|
9392
|
+
}}
|
|
9393
|
+
>Fix suspect links</button>
|
|
9394
|
+
<button
|
|
9395
|
+
type="button"
|
|
9396
|
+
role="menuitem"
|
|
9397
|
+
class="doorstop-menu-item doorstop-draft-child"
|
|
9398
|
+
?disabled=${childPrefix === void 0}
|
|
9399
|
+
title=${childPrefix === void 0 ? "No child document to add to" : `Draft a child item in the ${childPrefix} document`}
|
|
9400
|
+
@click=${() => {
|
|
9401
|
+
if (childPrefix !== void 0) host.insertPrompt(draftChildRequirementPrompt(item, childPrefix));
|
|
9402
|
+
}}
|
|
9403
|
+
>Draft child requirement</button>
|
|
9404
|
+
<button type="button" role="menuitem" class="doorstop-menu-item doorstop-review-readiness" @click=${() => {
|
|
9405
|
+
host.insertPrompt(reviewReadinessPrompt(item, children));
|
|
9406
|
+
}}>Review readiness</button>
|
|
9407
|
+
<button
|
|
9408
|
+
type="button"
|
|
9409
|
+
role="menuitem"
|
|
9410
|
+
class="doorstop-menu-item doorstop-start-review-session"
|
|
9411
|
+
title=${`Start an independent review session for ${item.uid} (inserts /opendoor-review)`}
|
|
9412
|
+
@click=${() => {
|
|
9413
|
+
insertReviewCommand(host, item);
|
|
9414
|
+
}}
|
|
9415
|
+
>Start review session</button>
|
|
9416
|
+
</div>
|
|
9417
|
+
` : A}
|
|
9418
|
+
</div>
|
|
9419
|
+
`;
|
|
9420
|
+
}
|
|
9421
|
+
function insertReviewCommand(host, item) {
|
|
9422
|
+
if (host.context === void 0) return;
|
|
9423
|
+
host.insertPrompt(composeReviewCommand(item));
|
|
9424
|
+
host.controller?.commitAskReview(item.uid);
|
|
9425
|
+
}
|
|
9426
|
+
function reviewItem(host, item) {
|
|
9427
|
+
const commit = doorstopCommitAfterReview(host.result);
|
|
9428
|
+
host.runDoorstop(
|
|
9429
|
+
"review",
|
|
9430
|
+
`Doorstop: review ${item.uid}`,
|
|
9431
|
+
commit ? { op: "review", uid: item.uid, commit: true } : { op: "review", uid: item.uid },
|
|
9432
|
+
`doorstop review ${item.uid}`,
|
|
9433
|
+
false
|
|
9434
|
+
);
|
|
9435
|
+
}
|
|
9436
|
+
function clearSuspects(host, item, suspectUids) {
|
|
9437
|
+
if (suspectUids.length === 0) return;
|
|
9438
|
+
host.runDoorstop(
|
|
9439
|
+
"clear",
|
|
9440
|
+
"Doorstop: clear suspect links",
|
|
9441
|
+
{ op: "clear", uid: item.uid, parents: suspectUids },
|
|
9442
|
+
`doorstop clear ${item.uid} ${suspectUids.join(" ")}`,
|
|
9443
|
+
false
|
|
9444
|
+
);
|
|
9445
|
+
}
|
|
9446
|
+
function runTargetOp(host, op, inputRef, item) {
|
|
9447
|
+
const input = inputRef.value;
|
|
9448
|
+
const target = input?.value.trim() ?? "";
|
|
9449
|
+
if (target === "") {
|
|
9450
|
+
host.targetError = `Enter a link target UID (e.g. ${item.documentPrefix}0001)`;
|
|
9451
|
+
input?.focus();
|
|
9452
|
+
return;
|
|
9453
|
+
}
|
|
9454
|
+
if (!isValidTargetUid(target)) {
|
|
9455
|
+
host.targetError = `Invalid link target "${target}" \u2014 expected a Doorstop UID like ${item.documentPrefix}0001 (no spaces, quotes, or special characters)`;
|
|
9456
|
+
input?.focus();
|
|
9457
|
+
return;
|
|
9458
|
+
}
|
|
9459
|
+
host.targetError = void 0;
|
|
9460
|
+
if (input !== void 0) input.value = "";
|
|
9461
|
+
host.runDoorstop(
|
|
9462
|
+
op,
|
|
9463
|
+
`Doorstop: ${op} ${item.uid}`,
|
|
9464
|
+
{ op, uid: item.uid, target },
|
|
9465
|
+
`doorstop ${op} ${item.uid} ${target}`,
|
|
9466
|
+
false
|
|
9467
|
+
);
|
|
9468
|
+
}
|
|
9469
|
+
function onTargetKeydown(host, event) {
|
|
9470
|
+
const input = event.target;
|
|
9471
|
+
if (input.getAttribute("data-op") !== "link") return;
|
|
9472
|
+
if (event.key === "Enter") {
|
|
9473
|
+
event.preventDefault();
|
|
9474
|
+
const item = host.selectedItem();
|
|
9475
|
+
if (item !== void 0) runTargetOp(host, "link", host.linkInputRef, item);
|
|
9476
|
+
} else if (event.key === "Escape") {
|
|
9477
|
+
input.value = "";
|
|
9478
|
+
}
|
|
9479
|
+
}
|
|
9480
|
+
function onAskMenuToggle(host) {
|
|
9481
|
+
host.askMenuOpen = !host.askMenuOpen;
|
|
9482
|
+
}
|
|
9483
|
+
|
|
9484
|
+
// src/browser/sections/status-bar.ts
|
|
9485
|
+
function renderStatusBar(host) {
|
|
9486
|
+
const lastRun = host.lastRun;
|
|
9487
|
+
if (lastRun === void 0) return A;
|
|
9488
|
+
const statusLabel = lastRun.status === "killed" ? "killed (timeout)" : lastRun.status;
|
|
9489
|
+
const detailParts = [lastRun.title, `${String(lastRun.durationMs)} ms`];
|
|
9490
|
+
if (lastRun.exitCode !== null && (lastRun.status === "failed" || lastRun.status === "killed")) {
|
|
9491
|
+
detailParts.push(`exit ${String(lastRun.exitCode)}`);
|
|
9492
|
+
}
|
|
9493
|
+
if (lastRun.signal !== null) detailParts.push(lastRun.signal);
|
|
9494
|
+
return b2`
|
|
9495
|
+
<section class="doorstop-status-bar" aria-label="Last run">
|
|
9496
|
+
<div class="doorstop-status-bar-row">
|
|
9497
|
+
<span class=${`doorstop-last-run-status is-${lastRun.status}`}>${statusLabel}</span>
|
|
9498
|
+
<span class="doorstop-last-run-meta">${detailParts.join(" \xB7 ")}</span>
|
|
9499
|
+
<button
|
|
9500
|
+
type="button"
|
|
9501
|
+
class="doorstop-last-run-dismiss"
|
|
9502
|
+
title="Dismiss the last run output"
|
|
9503
|
+
@click=${() => onDismissRun(host)}
|
|
9504
|
+
>Dismiss</button>
|
|
9505
|
+
</div>
|
|
9506
|
+
${host.statusExpanded ? renderStatusBarBody(host, lastRun) : A}
|
|
9507
|
+
</section>
|
|
9508
|
+
`;
|
|
8017
9509
|
}
|
|
8018
|
-
function
|
|
8019
|
-
|
|
8020
|
-
|
|
9510
|
+
function renderStatusBarBody(host, lastRun) {
|
|
9511
|
+
return b2`
|
|
9512
|
+
<div class="doorstop-last-run">
|
|
9513
|
+
${lastRun.commit === void 0 ? A : b2`<p class="doorstop-last-run-commit doorstop-muted">${commitOutcomeText(lastRun.op, lastRun.commit)}</p>`}
|
|
9514
|
+
${lastRun.status === "error" ? b2`<pre class="doorstop-last-run-pre">${lastRun.errorMessage ?? ""}</pre>` : A}
|
|
9515
|
+
${lastRun.stdout === "" ? A : b2`
|
|
9516
|
+
<pre class="doorstop-last-run-pre">${lastRun.stdout}</pre>
|
|
9517
|
+
${lastRun.stdoutTruncated ? b2`<p class="doorstop-last-run-notice">stdout truncated by the host stream limit (2 MiB) — output not fully captured</p>` : A}
|
|
9518
|
+
`}
|
|
9519
|
+
${lastRun.stderr === "" ? A : b2`
|
|
9520
|
+
<pre class="doorstop-last-run-pre">${lastRun.stderr}</pre>
|
|
9521
|
+
${lastRun.stderrTruncated ? b2`<p class="doorstop-last-run-notice">stderr truncated by the host stream limit (2 MiB) — output not fully captured</p>` : A}
|
|
9522
|
+
`}
|
|
9523
|
+
${lastRun.stdout === "" && lastRun.stderr === "" && lastRun.status !== "error" ? b2`<p class="doorstop-last-run-notice">No output captured.</p>` : A}
|
|
9524
|
+
</div>
|
|
9525
|
+
`;
|
|
8021
9526
|
}
|
|
8022
|
-
function
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
const childLinks = andJoin(
|
|
8026
|
-
children.map((child) => `${child.uid} (\`${child.path}\`)`)
|
|
8027
|
-
);
|
|
8028
|
-
const facts = [
|
|
8029
|
-
item.text.trim() === "" ? "its text is empty (that alone blocks review)" : "its text is filled in",
|
|
8030
|
-
item.links.length === 0 ? "it declares no parent links" : `its recorded parent links (${parents}) are not suspect or missing a fingerprint`,
|
|
8031
|
-
children.length === 0 ? "its child links are left to the `doorstop` run below" : `its child links are declared (${childLinks})`,
|
|
8032
|
-
references.length === 0 ? "it has no file references to verify" : `its file references exist (${references.map((path) => `\`${path}\``).join(", ")})`
|
|
8033
|
-
];
|
|
8034
|
-
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.`;
|
|
9527
|
+
function onDismissRun(host) {
|
|
9528
|
+
host.statusExpanded = false;
|
|
9529
|
+
host.controller?.dismissRun();
|
|
8035
9530
|
}
|
|
8036
9531
|
|
|
8037
|
-
// src/doorstop-panel-element.ts
|
|
9532
|
+
// src/browser/doorstop-panel-element.ts
|
|
8038
9533
|
var bodyElementTag = "pi-web-opendoor-panel-body";
|
|
8039
9534
|
function defineDoorstopPanelElements() {
|
|
8040
9535
|
defineDoorstopPanelBodyElement();
|
|
@@ -8054,90 +9549,10 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8054
9549
|
this.confirmSkipped = false;
|
|
8055
9550
|
this.view = "items";
|
|
8056
9551
|
this.statusExpanded = false;
|
|
8057
|
-
|
|
8058
|
-
this.linkInputRef =
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
this.onDismissRun = () => {
|
|
8062
|
-
this.statusExpanded = false;
|
|
8063
|
-
this.controller?.dismissRun();
|
|
8064
|
-
};
|
|
8065
|
-
this.onRefreshClick = () => {
|
|
8066
|
-
void this.controller?.invalidate();
|
|
8067
|
-
};
|
|
8068
|
-
this.onValidateClick = () => {
|
|
8069
|
-
this.runDoorstop("validate", "Doorstop: validate", { op: "validate" }, "doorstop", true);
|
|
8070
|
-
};
|
|
8071
|
-
this.onPublishClick = () => {
|
|
8072
|
-
const target = doorstopPublishTarget(this.result);
|
|
8073
|
-
if (typeof window.confirm === "function") {
|
|
8074
|
-
if (!window.confirm(`Publish the Doorstop tree as HTML to ${target} in the workspace terminal?`)) return;
|
|
8075
|
-
this.confirmSkipped = false;
|
|
8076
|
-
} else {
|
|
8077
|
-
this.confirmSkipped = true;
|
|
8078
|
-
}
|
|
8079
|
-
this.runDoorstop(
|
|
8080
|
-
"publish",
|
|
8081
|
-
"Doorstop: publish",
|
|
8082
|
-
{ op: "publish", target },
|
|
8083
|
-
doorstopPublishCommand(this.result, target),
|
|
8084
|
-
false
|
|
8085
|
-
);
|
|
8086
|
-
};
|
|
8087
|
-
this.onGitCommitInput = (event) => {
|
|
8088
|
-
this.gitCommitMessage = event.target.value;
|
|
8089
|
-
};
|
|
8090
|
-
this.onGitStageClick = () => {
|
|
8091
|
-
const controller = this.controller;
|
|
8092
|
-
if (controller === void 0) return;
|
|
8093
|
-
if (controller.runInProgress !== void 0) return;
|
|
8094
|
-
const result = this.result;
|
|
8095
|
-
if (result === void 0) {
|
|
8096
|
-
this.gitActionError = "The workspace is not loaded \u2014 refresh first";
|
|
8097
|
-
return;
|
|
8098
|
-
}
|
|
8099
|
-
const paths = doorstopPaths(result);
|
|
8100
|
-
if (paths.length === 0) {
|
|
8101
|
-
this.gitActionError = "Nothing to stage \u2014 the workspace has no Doorstop-managed files";
|
|
8102
|
-
return;
|
|
8103
|
-
}
|
|
8104
|
-
this.gitActionError = void 0;
|
|
8105
|
-
void controller.runGitStage(paths);
|
|
8106
|
-
};
|
|
8107
|
-
this.onGitCommitClick = () => {
|
|
8108
|
-
this.gitCommitSubmit();
|
|
8109
|
-
};
|
|
8110
|
-
this.onGitCommitKeydown = (event) => {
|
|
8111
|
-
const input = this.gitCommitInputRef.value;
|
|
8112
|
-
if (input === void 0) return;
|
|
8113
|
-
if (event.key === "Enter") {
|
|
8114
|
-
event.preventDefault();
|
|
8115
|
-
this.gitCommitSubmit();
|
|
8116
|
-
} else if (event.key === "Escape") {
|
|
8117
|
-
input.value = "";
|
|
8118
|
-
this.gitCommitMessage = "";
|
|
8119
|
-
this.gitActionError = void 0;
|
|
8120
|
-
}
|
|
8121
|
-
};
|
|
8122
|
-
this.onStateFilterChange = (event) => {
|
|
8123
|
-
const value = event.target.value;
|
|
8124
|
-
this.controller?.setStateFilter(value === "" ? void 0 : value);
|
|
8125
|
-
};
|
|
8126
|
-
this.onSearchInput = (event) => {
|
|
8127
|
-
this.controller?.setSearch(event.target.value);
|
|
8128
|
-
};
|
|
8129
|
-
this.onTargetKeydown = (event) => {
|
|
8130
|
-
const input = event.target;
|
|
8131
|
-
const op = input.getAttribute("data-op");
|
|
8132
|
-
if (op !== "unlink" && op !== "link") return;
|
|
8133
|
-
if (event.key === "Enter") {
|
|
8134
|
-
event.preventDefault();
|
|
8135
|
-
const item = this.selectedItem();
|
|
8136
|
-
if (item !== void 0) this.runTargetOp(op, op === "unlink" ? this.unlinkInputRef : this.linkInputRef, item);
|
|
8137
|
-
} else if (event.key === "Escape") {
|
|
8138
|
-
input.value = "";
|
|
8139
|
-
}
|
|
8140
|
-
};
|
|
9552
|
+
/** section-API: the palette's free-text Link target input. */
|
|
9553
|
+
this.linkInputRef = e5();
|
|
9554
|
+
/** section-API: the toolbar's commit-message input. */
|
|
9555
|
+
this.gitCommitInputRef = e5();
|
|
8141
9556
|
this.onDocumentClick = (event) => {
|
|
8142
9557
|
if (!this.askMenuOpen) return;
|
|
8143
9558
|
const menuRoot = this.shadowRoot?.querySelector(".doorstop-menu");
|
|
@@ -8156,16 +9571,6 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8156
9571
|
this.askMenuOpen = false;
|
|
8157
9572
|
}
|
|
8158
9573
|
};
|
|
8159
|
-
this.onAskMenuToggle = () => {
|
|
8160
|
-
this.askMenuOpen = !this.askMenuOpen;
|
|
8161
|
-
};
|
|
8162
|
-
this.onChangesToggle = (event) => {
|
|
8163
|
-
const details = event.currentTarget;
|
|
8164
|
-
if (!(details instanceof HTMLDetailsElement) || !details.open) return;
|
|
8165
|
-
const item = this.selectedItem();
|
|
8166
|
-
if (item === void 0) return;
|
|
8167
|
-
void this.controller?.requestBaseline(item);
|
|
8168
|
-
};
|
|
8169
9574
|
}
|
|
8170
9575
|
static {
|
|
8171
9576
|
this.styles = panelStyles;
|
|
@@ -8211,9 +9616,8 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8211
9616
|
* expand handler alone would strand the section on "Loading baseline…"
|
|
8212
9617
|
* with no fetch in flight. `requestBaseline` installs its "loading"
|
|
8213
9618
|
* view synchronously, so this cannot loop. (A selection switch needs
|
|
8214
|
-
* no help here —
|
|
8215
|
-
*
|
|
8216
|
-
* `toggle`.)
|
|
9619
|
+
* no help here — the section keys the node by UID, so Lit recreates it
|
|
9620
|
+
* closed and the user's expand fires a real `toggle`.)
|
|
8217
9621
|
*/
|
|
8218
9622
|
updated() {
|
|
8219
9623
|
this.ensureGitStatus();
|
|
@@ -8241,318 +9645,29 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8241
9645
|
}
|
|
8242
9646
|
render() {
|
|
8243
9647
|
return b2`
|
|
8244
|
-
${
|
|
9648
|
+
${renderProjectActions(this)}
|
|
8245
9649
|
${this.error === void 0 ? A : b2`<div class="doorstop-error" role="alert">${this.error}</div>`}
|
|
8246
|
-
<section class="doorstop-viewer">${
|
|
8247
|
-
${
|
|
8248
|
-
${
|
|
8249
|
-
`;
|
|
8250
|
-
}
|
|
8251
|
-
// --- project actions (region 1) -----------------------------------------------
|
|
8252
|
-
renderProjectActions() {
|
|
8253
|
-
const publishTarget = doorstopPublishTarget(this.result);
|
|
8254
|
-
const projectPath = this.context?.workspace.path ?? "";
|
|
8255
|
-
const projectLabel = projectPath === "" ? "Doorstop" : projectPath.split("/").filter(Boolean).pop() ?? projectPath;
|
|
8256
|
-
return b2`
|
|
8257
|
-
<section class="doorstop-project-actions">
|
|
8258
|
-
<strong class="doorstop-title" title=${projectPath === "" ? A : projectPath}>${doorstopIconSvg}${projectLabel}</strong>
|
|
8259
|
-
${this.renderViewToggle()}
|
|
8260
|
-
<div class="doorstop-toolbar-actions">
|
|
8261
|
-
${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}
|
|
8262
|
-
${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}
|
|
8263
|
-
<button type="button" class="doorstop-refresh" title="Re-read the workspace" @click=${this.onRefreshClick}>${refreshIconSvg}Refresh</button>
|
|
8264
|
-
<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>
|
|
8265
|
-
<button type="button" class="doorstop-publish" title=${`Publish the tree to ${publishTarget}`} ?disabled=${this.runInProgress !== void 0} @click=${this.onPublishClick}>${publishIconSvg}Publish HTML</button>
|
|
8266
|
-
${this.renderGitActions()}
|
|
8267
|
-
</div>
|
|
8268
|
-
</section>
|
|
8269
|
-
`;
|
|
8270
|
-
}
|
|
8271
|
-
/**
|
|
8272
|
-
* The commit INPUT is disabled while a run is in flight (like the
|
|
8273
|
-
* buttons) so a typed-but-never-submitted message cannot be lost when
|
|
8274
|
-
* the run's invalidate lands mid-typing.
|
|
8275
|
-
*/
|
|
8276
|
-
renderGitActions() {
|
|
8277
|
-
if (!this.backendActive()) return A;
|
|
8278
|
-
const hasDocuments = (this.result?.index.documents.length ?? 0) > 0;
|
|
8279
|
-
return b2`
|
|
8280
|
-
${this.renderGitStatus()}
|
|
8281
|
-
<button
|
|
8282
|
-
type="button"
|
|
8283
|
-
class="doorstop-git-stage"
|
|
8284
|
-
title="Stage all Doorstop-managed files (requirements, documents, configs)"
|
|
8285
|
-
?disabled=${this.runInProgress !== void 0 || !hasDocuments}
|
|
8286
|
-
@click=${this.onGitStageClick}
|
|
8287
|
-
>${gitStageIconSvg}Stage all</button>
|
|
8288
|
-
<div class="doorstop-git-commit">
|
|
8289
|
-
<input
|
|
8290
|
-
type="text"
|
|
8291
|
-
class="doorstop-git-commit-input"
|
|
8292
|
-
placeholder="Commit message"
|
|
8293
|
-
aria-label="Git commit message"
|
|
8294
|
-
?disabled=${this.runInProgress !== void 0}
|
|
8295
|
-
${n5(this.gitCommitInputRef)}
|
|
8296
|
-
@input=${this.onGitCommitInput}
|
|
8297
|
-
@keydown=${this.onGitCommitKeydown}
|
|
8298
|
-
/>
|
|
8299
|
-
<button
|
|
8300
|
-
type="button"
|
|
8301
|
-
class="doorstop-git-commit-button"
|
|
8302
|
-
title="Commit the staged index with this message"
|
|
8303
|
-
?disabled=${this.runInProgress !== void 0 || this.gitCommitMessage.trim() === ""}
|
|
8304
|
-
@click=${this.onGitCommitClick}
|
|
8305
|
-
>${gitCommitIconSvg}Commit</button>
|
|
8306
|
-
${this.gitActionError === void 0 ? A : b2`<span class="doorstop-op-error" role="alert">${this.gitActionError}</span>`}
|
|
8307
|
-
</div>
|
|
8308
|
-
`;
|
|
8309
|
-
}
|
|
8310
|
-
renderGitStatus() {
|
|
8311
|
-
const view = this.gitStatusView;
|
|
8312
|
-
if (view === void 0) return A;
|
|
8313
|
-
const refresh = () => {
|
|
8314
|
-
void this.controller?.requestGitStatus();
|
|
8315
|
-
};
|
|
8316
|
-
switch (view.state) {
|
|
8317
|
-
case "loading":
|
|
8318
|
-
return b2`<button
|
|
8319
|
-
type="button"
|
|
8320
|
-
class="doorstop-git-status"
|
|
8321
|
-
title="Refreshing git status…"
|
|
8322
|
-
aria-busy=${this.gitStatusInFlight}
|
|
8323
|
-
@click=${refresh}
|
|
8324
|
-
><span class="doorstop-git-status-text">⎇ …</span></button>`;
|
|
8325
|
-
case "no-git":
|
|
8326
|
-
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>`;
|
|
8327
|
-
case "error":
|
|
8328
|
-
return b2`<button
|
|
8329
|
-
type="button"
|
|
8330
|
-
class="doorstop-git-status doorstop-git-status-error"
|
|
8331
|
-
title=${`Git status unavailable \u2014 ${view.errorMessage ?? "request failed"} \u2014 click to retry`}
|
|
8332
|
-
aria-busy=${this.gitStatusInFlight}
|
|
8333
|
-
@click=${refresh}
|
|
8334
|
-
><span class="doorstop-git-status-text">git status error — retry</span></button>`;
|
|
8335
|
-
case "ready":
|
|
8336
|
-
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>`;
|
|
8337
|
-
}
|
|
8338
|
-
}
|
|
8339
|
-
// --- list filters (above the item list) ---------------------------------------
|
|
8340
|
-
renderListFilters(result) {
|
|
8341
|
-
return b2`
|
|
8342
|
-
<section class="doorstop-list-filters">
|
|
8343
|
-
<div class="doorstop-docs" role="list" aria-label="Doorstop documents">
|
|
8344
|
-
${this.renderDocumentChip(void 0, result)}
|
|
8345
|
-
${result.index.documents.map((document2) => this.renderDocumentChip(document2, result))}
|
|
8346
|
-
</div>
|
|
8347
|
-
<select class="doorstop-state-filter" aria-label="Filter by state" @change=${this.onStateFilterChange}>
|
|
8348
|
-
<option value="" .selected=${this.stateFilter === void 0}>All states</option>
|
|
8349
|
-
${Object.entries(STATE_CHIP_LABELS).map(
|
|
8350
|
-
([key, label]) => b2`<option value=${key} .selected=${this.stateFilter === key}>${label}</option>`
|
|
8351
|
-
)}
|
|
8352
|
-
</select>
|
|
8353
|
-
<input
|
|
8354
|
-
class="doorstop-search"
|
|
8355
|
-
type="search"
|
|
8356
|
-
aria-label="Search items"
|
|
8357
|
-
placeholder="Search UID or text"
|
|
8358
|
-
.value=${this.search}
|
|
8359
|
-
@input=${this.onSearchInput}
|
|
8360
|
-
/>
|
|
8361
|
-
</section>
|
|
8362
|
-
`;
|
|
8363
|
-
}
|
|
8364
|
-
/**
|
|
8365
|
-
* Expansion is CONTENT-DRIVEN (see `willUpdate`); there is no manual
|
|
8366
|
-
* expand/collapse toggle.
|
|
8367
|
-
*/
|
|
8368
|
-
renderStatusBar() {
|
|
8369
|
-
const lastRun = this.lastRun;
|
|
8370
|
-
if (lastRun === void 0) return A;
|
|
8371
|
-
const statusLabel = lastRun.status === "killed" ? "killed (timeout)" : lastRun.status;
|
|
8372
|
-
const detailParts = [lastRun.title, `${String(lastRun.durationMs)} ms`];
|
|
8373
|
-
if (lastRun.exitCode !== null && (lastRun.status === "failed" || lastRun.status === "killed")) {
|
|
8374
|
-
detailParts.push(`exit ${String(lastRun.exitCode)}`);
|
|
8375
|
-
}
|
|
8376
|
-
if (lastRun.signal !== null) detailParts.push(lastRun.signal);
|
|
8377
|
-
return b2`
|
|
8378
|
-
<section class="doorstop-status-bar" aria-label="Last run">
|
|
8379
|
-
<div class="doorstop-status-bar-row">
|
|
8380
|
-
<span class=${`doorstop-last-run-status is-${lastRun.status}`}>${statusLabel}</span>
|
|
8381
|
-
<span class="doorstop-last-run-meta">${detailParts.join(" \xB7 ")}</span>
|
|
8382
|
-
<button
|
|
8383
|
-
type="button"
|
|
8384
|
-
class="doorstop-last-run-dismiss"
|
|
8385
|
-
title="Dismiss the last run output"
|
|
8386
|
-
@click=${this.onDismissRun}
|
|
8387
|
-
>Dismiss</button>
|
|
8388
|
-
</div>
|
|
8389
|
-
${this.statusExpanded ? this.renderStatusBarBody(lastRun) : A}
|
|
8390
|
-
</section>
|
|
8391
|
-
`;
|
|
8392
|
-
}
|
|
8393
|
-
renderStatusBarBody(lastRun) {
|
|
8394
|
-
return b2`
|
|
8395
|
-
<div class="doorstop-last-run">
|
|
8396
|
-
${lastRun.commit === void 0 ? A : b2`<p class="doorstop-last-run-commit doorstop-muted">${commitOutcomeText(lastRun.op, lastRun.commit)}</p>`}
|
|
8397
|
-
${lastRun.status === "error" ? b2`<pre class="doorstop-last-run-pre">${lastRun.errorMessage ?? ""}</pre>` : A}
|
|
8398
|
-
${lastRun.stdout === "" ? A : b2`
|
|
8399
|
-
<pre class="doorstop-last-run-pre">${lastRun.stdout}</pre>
|
|
8400
|
-
${lastRun.stdoutTruncated ? b2`<p class="doorstop-last-run-notice">stdout truncated by the host stream limit (2 MiB) — output not fully captured</p>` : A}
|
|
8401
|
-
`}
|
|
8402
|
-
${lastRun.stderr === "" ? A : b2`
|
|
8403
|
-
<pre class="doorstop-last-run-pre">${lastRun.stderr}</pre>
|
|
8404
|
-
${lastRun.stderrTruncated ? b2`<p class="doorstop-last-run-notice">stderr truncated by the host stream limit (2 MiB) — output not fully captured</p>` : A}
|
|
8405
|
-
`}
|
|
8406
|
-
${lastRun.stdout === "" && lastRun.stderr === "" && lastRun.status !== "error" ? b2`<p class="doorstop-last-run-notice">No output captured.</p>` : A}
|
|
8407
|
-
</div>
|
|
8408
|
-
`;
|
|
8409
|
-
}
|
|
8410
|
-
renderViewToggle() {
|
|
8411
|
-
return b2`
|
|
8412
|
-
<div class="doorstop-view-toggle" role="tablist" aria-label="Requirements panel view">
|
|
8413
|
-
<button
|
|
8414
|
-
type="button"
|
|
8415
|
-
role="tab"
|
|
8416
|
-
class=${e5({
|
|
8417
|
-
"doorstop-view-tab": true,
|
|
8418
|
-
"doorstop-view-items": true,
|
|
8419
|
-
"is-selected": this.view === "items"
|
|
8420
|
-
})}
|
|
8421
|
-
aria-selected=${this.view === "items" ? "true" : "false"}
|
|
8422
|
-
title="Item list and detail"
|
|
8423
|
-
@click=${() => {
|
|
8424
|
-
this.view = "items";
|
|
8425
|
-
}}
|
|
8426
|
-
>Items</button>
|
|
8427
|
-
<button
|
|
8428
|
-
type="button"
|
|
8429
|
-
role="tab"
|
|
8430
|
-
class=${e5({
|
|
8431
|
-
"doorstop-view-tab": true,
|
|
8432
|
-
"doorstop-view-findings": true,
|
|
8433
|
-
"is-selected": this.view === "findings"
|
|
8434
|
-
})}
|
|
8435
|
-
aria-selected=${this.view === "findings" ? "true" : "false"}
|
|
8436
|
-
title="Validation findings"
|
|
8437
|
-
@click=${() => {
|
|
8438
|
-
this.view = "findings";
|
|
8439
|
-
}}
|
|
8440
|
-
>Findings</button>
|
|
8441
|
-
</div>
|
|
8442
|
-
`;
|
|
8443
|
-
}
|
|
8444
|
-
renderDocumentChip(document2, result) {
|
|
8445
|
-
const prefix = document2?.prefix ?? ALL_DOCUMENTS_LABEL;
|
|
8446
|
-
const count = document2 === void 0 ? result.index.items.length : result.index.items.filter((item) => item.documentPrefix === document2.prefix).length;
|
|
8447
|
-
const dots = document2 === void 0 ? [] : documentStateDots(document2, result.index.items);
|
|
8448
|
-
const selected = document2 === void 0 ? this.selectedDocumentPrefix === void 0 || this.selectedDocumentPrefix === "" : this.selectedDocumentPrefix === document2.prefix;
|
|
8449
|
-
return b2`
|
|
8450
|
-
<button
|
|
8451
|
-
type="button"
|
|
8452
|
-
role="listitem"
|
|
8453
|
-
class=${e5({ "doorstop-doc-chip": true, "is-selected": selected })}
|
|
8454
|
-
data-prefix=${document2?.prefix ?? ""}
|
|
8455
|
-
@click=${() => {
|
|
8456
|
-
this.controller?.selectDocument(document2?.prefix ?? "");
|
|
8457
|
-
}}
|
|
8458
|
-
>
|
|
8459
|
-
${document2?.parentPrefix === void 0 ? A : b2`<span class="doorstop-doc-arrow">← ${document2.parentPrefix}</span>`}
|
|
8460
|
-
<span class="doorstop-doc-prefix">${prefix}</span>
|
|
8461
|
-
<span class="doorstop-doc-count">${String(count)}</span>
|
|
8462
|
-
${dots.map((dot) => b2`<span class=${`doorstop-dot doorstop-dot-${dot}`} title=${dotTitle(dot)}></span>`)}
|
|
8463
|
-
</button>
|
|
8464
|
-
`;
|
|
8465
|
-
}
|
|
8466
|
-
// --- item list ------------------------------------------------------------------
|
|
8467
|
-
renderViewer() {
|
|
8468
|
-
const result = this.result;
|
|
8469
|
-
if (result === void 0) {
|
|
8470
|
-
return b2`<p class="doorstop-muted doorstop-standalone">${this.loading ? "Loading workspace\u2026" : "Run Refresh to scan for Doorstop documents."}</p>`;
|
|
8471
|
-
}
|
|
8472
|
-
if (this.view === "findings") {
|
|
8473
|
-
return this.renderFindingsView(result);
|
|
8474
|
-
}
|
|
8475
|
-
return b2`
|
|
8476
|
-
${this.renderListFilters(result)}
|
|
8477
|
-
${result.index.documents.length === 0 ? b2`
|
|
8478
|
-
${this.renderDiagnostics(result)}
|
|
8479
|
-
<section class="doorstop-empty"><p>${EMPTY_WORKSPACE_MESSAGE}</p></section>
|
|
8480
|
-
` : b2`
|
|
8481
|
-
<section class="doorstop-split">
|
|
8482
|
-
<section class="doorstop-list">
|
|
8483
|
-
${this.renderDiagnostics(result)}
|
|
8484
|
-
${this.renderItemList(result)}
|
|
8485
|
-
</section>
|
|
8486
|
-
<section class="doorstop-detail-pane">${this.renderDetail(result)}</section>
|
|
8487
|
-
</section>
|
|
8488
|
-
`}
|
|
8489
|
-
`;
|
|
8490
|
-
}
|
|
8491
|
-
renderFindingsView(result) {
|
|
8492
|
-
const rows = findingsViewRows(result.index);
|
|
8493
|
-
const counts = findingsViewCounts(rows);
|
|
8494
|
-
return b2`
|
|
8495
|
-
<section class="doorstop-findings-view" aria-label="Doorstop findings">
|
|
8496
|
-
<header class="doorstop-findings-head">
|
|
8497
|
-
<span class="doorstop-findings-counts" role="status" aria-label="Finding counts">${findingsCountText(counts)}</span>
|
|
8498
|
-
<span class="doorstop-findings-note doorstop-muted">${FINDINGS_PLUGIN_LOCAL_NOTE}</span>
|
|
8499
|
-
</header>
|
|
8500
|
-
${rows.length === 0 ? b2`
|
|
8501
|
-
<section class="doorstop-empty">
|
|
8502
|
-
<p>${FINDINGS_EMPTY_MESSAGE}</p>
|
|
8503
|
-
<p class="doorstop-muted">${FINDINGS_EMPTY_HINT}</p>
|
|
8504
|
-
</section>
|
|
8505
|
-
` : b2`<div class="doorstop-findings-list" role="list" aria-label="Validation findings">
|
|
8506
|
-
${rows.map((row) => this.renderFindingRow(row, result.index))}
|
|
8507
|
-
</div>`}
|
|
8508
|
-
</section>
|
|
8509
|
-
`;
|
|
8510
|
-
}
|
|
8511
|
-
renderFindingRow(row, index) {
|
|
8512
|
-
const kind = row.severity === "error" ? "doorstop-error" : row.severity === "warning" ? "doorstop-warning" : "doorstop-info";
|
|
8513
|
-
const uid = row.uid;
|
|
8514
|
-
const navigable = uid !== void 0 && index.byUid.has(uid);
|
|
8515
|
-
return b2`
|
|
8516
|
-
<div class=${`doorstop-finding-row ${kind}`} role="listitem">
|
|
8517
|
-
<span class="doorstop-severity">${row.severity}</span>
|
|
8518
|
-
${uid === void 0 ? A : navigable ? b2`<button type="button" class="doorstop-finding-uid" data-uid=${uid} title=${`Show ${uid} in the item list`} @click=${() => {
|
|
8519
|
-
this.selectFindingTarget(uid);
|
|
8520
|
-
}}><code>${uid}</code></button>` : b2`<code class="doorstop-finding-uid">${uid}</code>`}
|
|
8521
|
-
${row.path === void 0 ? A : b2`<code class="doorstop-finding-path">${row.path}</code>`}
|
|
8522
|
-
<span class="doorstop-finding-message">${row.message}</span>
|
|
8523
|
-
</div>
|
|
9650
|
+
<section class="doorstop-viewer">${renderViewer(this)}</section>
|
|
9651
|
+
${renderItemActionPalette(this)}
|
|
9652
|
+
${renderStatusBar(this)}
|
|
8524
9653
|
`;
|
|
8525
9654
|
}
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
${result.index.diagnostics.map((diagnostic) => {
|
|
8540
|
-
const kind = diagnostic.severity === "error" ? "doorstop-error" : "doorstop-warning";
|
|
8541
|
-
return b2`
|
|
8542
|
-
<div class=${`doorstop-diagnostic ${kind}`}>
|
|
8543
|
-
<span class="doorstop-severity">${diagnostic.severity}</span>
|
|
8544
|
-
<span class="doorstop-diagnostic-copy">
|
|
8545
|
-
${diagnostic.path === void 0 ? A : b2`<code>${diagnostic.path}</code>`}
|
|
8546
|
-
${diagnostic.message}
|
|
8547
|
-
</span>
|
|
8548
|
-
</div>
|
|
8549
|
-
`;
|
|
8550
|
-
})}
|
|
8551
|
-
</section>
|
|
8552
|
-
`;
|
|
9655
|
+
/** section-API: the baseline fetch and the review→commit pipeline live
|
|
9656
|
+
* server-side, so this gates the sections that need them. The peer
|
|
9657
|
+
* (`context.peer.request`) is the exact revision-paired path to this
|
|
9658
|
+
* package's own active server entry — unchanged by the v5 migration
|
|
9659
|
+
* (the v5 scoped `backend` is not consulted here), so `peer.request`
|
|
9660
|
+
* presence stays the paired-backend gate (a legal openChannel-only
|
|
9661
|
+
* peer is NOT paired for these request-shaped sections). The opendoor
|
|
9662
|
+
* provider ownership check is kept for the ownership-based
|
|
9663
|
+
* information display (behavior-preserving Phase A; Phase C revisits
|
|
9664
|
+
* it). */
|
|
9665
|
+
backendActive() {
|
|
9666
|
+
const context = this.context;
|
|
9667
|
+
return context?.peer?.request !== void 0 && context.workspace.provider?.pluginId === OPENDOOR_PLUGIN_ID;
|
|
8553
9668
|
}
|
|
8554
|
-
/**
|
|
8555
|
-
* ready view, and an UNTRUNCATED `files` list. `undefined` when
|
|
9669
|
+
/** section-API: the status response usable for PER-ITEM git UI: a paired
|
|
9670
|
+
* install, a ready view, and an UNTRUNCATED `files` list. `undefined` when
|
|
8556
9671
|
* unpaired / loading / no-git / error / truncated, so the item list and
|
|
8557
9672
|
* action row render no chips, no per-row add, and no svg inside the row
|
|
8558
9673
|
* (the XSS assertion's structural dependency). The strip reads
|
|
@@ -8565,448 +9680,24 @@ function defineDoorstopPanelBodyElement() {
|
|
|
8565
9680
|
if (gitStatusFilesTruncated(view.response)) return void 0;
|
|
8566
9681
|
return view.response;
|
|
8567
9682
|
}
|
|
8568
|
-
/**
|
|
8569
|
-
* git UI is unavailable (see {@link readyGitStatus}). */
|
|
9683
|
+
/** section-API: the path→git-file map for one list render; `undefined`
|
|
9684
|
+
* when per-item git UI is unavailable (see {@link readyGitStatus}). */
|
|
8570
9685
|
readyGitFiles() {
|
|
8571
9686
|
const response = this.readyGitStatus();
|
|
8572
9687
|
return response === void 0 ? void 0 : gitStatusFilesByPath(response);
|
|
8573
9688
|
}
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
}
|
|
8578
|
-
const items = filteredItems(result.index, this.selectedDocumentPrefix, this.stateFilter, this.search);
|
|
8579
|
-
if (items.length === 0) {
|
|
8580
|
-
return b2`<p class="doorstop-muted doorstop-standalone">No items match the current document, state, or search filters.</p>`;
|
|
8581
|
-
}
|
|
8582
|
-
const gitFiles = this.readyGitFiles();
|
|
8583
|
-
return b2`
|
|
8584
|
-
<div class="doorstop-items" role="list" aria-label="Doorstop items">
|
|
8585
|
-
${c5(items, (item) => item.uid, (item) => this.renderItemRow(item, gitFiles))}
|
|
8586
|
-
</div>
|
|
8587
|
-
`;
|
|
8588
|
-
}
|
|
8589
|
-
renderItemRow(item, gitFiles) {
|
|
8590
|
-
const selected = this.selectedUid === item.uid;
|
|
8591
|
-
const gitState = itemGitState(gitFiles?.get(item.path));
|
|
8592
|
-
const stageable = itemStageable(gitFiles?.get(item.path));
|
|
8593
|
-
const unstageable = itemUnstageable(gitFiles?.get(item.path));
|
|
8594
|
-
return b2`
|
|
8595
|
-
<button
|
|
8596
|
-
type="button"
|
|
8597
|
-
role="listitem"
|
|
8598
|
-
class=${e5({ "doorstop-item-row": true, "is-selected": selected })}
|
|
8599
|
-
data-uid=${item.uid}
|
|
8600
|
-
@click=${() => {
|
|
8601
|
-
this.controller?.selectUid(item.uid);
|
|
8602
|
-
}}
|
|
8603
|
-
>
|
|
8604
|
-
<span class="doorstop-item-level">${item.level}</span>
|
|
8605
|
-
<code class="doorstop-item-uid">${item.uid}</code>
|
|
8606
|
-
<span class="doorstop-item-summary">${itemExcerpt(item)}</span>
|
|
8607
|
-
<span class="doorstop-item-chips">
|
|
8608
|
-
${item.stateKeys.map((key) => this.renderStateChip(key))}
|
|
8609
|
-
${gitState === "clean" ? A : b2`<span class=${`doorstop-chip doorstop-chip-${gitChipKind(gitState)}`}>${GIT_CHIP_LABELS[gitState]}</span>`}
|
|
8610
|
-
${unstageable && this.runInProgress === void 0 ? b2`<span
|
|
8611
|
-
class="doorstop-item-add"
|
|
8612
|
-
aria-hidden="true"
|
|
8613
|
-
title=${`git reset ${item.path}`}
|
|
8614
|
-
@click=${(event) => {
|
|
8615
|
-
event.stopPropagation();
|
|
8616
|
-
this.unstageItem(item);
|
|
8617
|
-
}}
|
|
8618
|
-
>${gitUnstageIconSvg}</span>` : stageable && this.runInProgress === void 0 ? b2`<span
|
|
8619
|
-
class="doorstop-item-add"
|
|
8620
|
-
aria-hidden="true"
|
|
8621
|
-
title=${`git add ${item.path}`}
|
|
8622
|
-
@click=${(event) => {
|
|
8623
|
-
event.stopPropagation();
|
|
8624
|
-
this.stageItem(item);
|
|
8625
|
-
}}
|
|
8626
|
-
>${gitStageIconSvg}</span>` : A}
|
|
8627
|
-
</span>
|
|
8628
|
-
</button>
|
|
8629
|
-
`;
|
|
8630
|
-
}
|
|
8631
|
-
renderStateChip(key) {
|
|
8632
|
-
const kind = stateChipKind(key);
|
|
8633
|
-
return b2`<span class=${`doorstop-chip doorstop-chip-${kind}`}>${STATE_CHIP_LABELS[key]}</span>`;
|
|
8634
|
-
}
|
|
8635
|
-
/** The baseline fetch and the review→commit pipeline live server-side,
|
|
8636
|
-
* so this gates the sections that need them. */
|
|
8637
|
-
backendActive() {
|
|
8638
|
-
const context = this.context;
|
|
8639
|
-
return context?.backend !== void 0 && context.workspace.provider?.pluginId === OPENDOOR_PLUGIN_ID && context.workspace.provider?.capabilities.request !== false;
|
|
8640
|
-
}
|
|
8641
|
-
/**
|
|
8642
|
-
* The `<details>` is wrapped in `keyed(item.uid, …)` so a selection
|
|
8643
|
-
* switch RE-CREATES the node (fresh, collapsed, with a real `toggle` on
|
|
8644
|
-
* expand) instead of Lit reusing the old item's open node — a reused
|
|
8645
|
-
* node fires no `toggle`, which would strand the new item on
|
|
8646
|
-
* "Loading baseline…". {@link updated} covers the remaining reuse
|
|
8647
|
-
* hole (same item re-rendered after its cache key changed).
|
|
8648
|
-
*/
|
|
8649
|
-
renderChangesSinceReview(item) {
|
|
8650
|
-
if (!item.stateKeys.includes("unreviewed") || item.reviewed === null) return A;
|
|
8651
|
-
if (!this.backendActive()) return A;
|
|
8652
|
-
const view = this.controller?.baselineViewFor(item);
|
|
8653
|
-
return i7(
|
|
8654
|
-
item.uid,
|
|
8655
|
-
b2`
|
|
8656
|
-
<details class="doorstop-changes" @toggle=${this.onChangesToggle}>
|
|
8657
|
-
<summary class="doorstop-changes-summary">Changes since review</summary>
|
|
8658
|
-
${view === void 0 || view.state === "loading" ? b2`<p class="doorstop-muted doorstop-changes-notice">Loading baseline…</p>` : this.renderBaselineView(view)}
|
|
8659
|
-
</details>
|
|
8660
|
-
`
|
|
8661
|
-
);
|
|
8662
|
-
}
|
|
8663
|
-
renderBaselineView(view) {
|
|
8664
|
-
switch (view.state) {
|
|
8665
|
-
case "loading":
|
|
8666
|
-
return b2`<p class="doorstop-muted doorstop-changes-notice">Loading baseline…</p>`;
|
|
8667
|
-
case "no-git":
|
|
8668
|
-
return b2`<p class="doorstop-muted doorstop-changes-notice">No git history — previous version unavailable</p>`;
|
|
8669
|
-
case "no-match":
|
|
8670
|
-
return b2`<p class="doorstop-muted doorstop-changes-notice">Could not locate the reviewed version (history may have been rewritten)</p>`;
|
|
8671
|
-
case "error":
|
|
8672
|
-
return b2`<p class="doorstop-muted doorstop-changes-notice">Baseline unavailable — ${view.errorMessage ?? "request failed"}</p>`;
|
|
8673
|
-
case "ready": {
|
|
8674
|
-
const diff = view.diff;
|
|
8675
|
-
if (diff === void 0) return b2`<p class="doorstop-muted doorstop-changes-notice">No changes found.</p>`;
|
|
8676
|
-
return this.renderBaselineDiff(view, diff);
|
|
8677
|
-
}
|
|
8678
|
-
}
|
|
8679
|
-
}
|
|
8680
|
-
renderBaselineDiff(view, diff) {
|
|
8681
|
-
return b2`
|
|
8682
|
-
${view.source === void 0 ? A : b2`<p class="doorstop-muted doorstop-changes-source">matched via ${view.source === "review-commit" ? "review commit" : "history walk"}</p>`}
|
|
8683
|
-
${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">
|
|
8684
|
-
${diff.text.map((line) => this.renderDiffLine(line))}
|
|
8685
|
-
</div>`}
|
|
8686
|
-
${diff.ref === void 0 ? A : this.renderFieldChange("ref", diff.ref.before, diff.ref.after)}
|
|
8687
|
-
${diff.references === void 0 ? A : this.renderFieldChange(
|
|
8688
|
-
"references",
|
|
8689
|
-
referenceListText(diff.references.before),
|
|
8690
|
-
referenceListText(diff.references.after)
|
|
8691
|
-
)}
|
|
8692
|
-
${diff.linksAdded.length === 0 && diff.linksRemoved.length === 0 ? A : b2`<div class="doorstop-field-change">
|
|
8693
|
-
<span class="doorstop-field-name">links</span>
|
|
8694
|
-
${diff.linksAdded.map((uid) => b2`<code class="doorstop-field-chip doorstop-field-chip-after">+ ${uid}</code>`)}
|
|
8695
|
-
${diff.linksRemoved.map((uid) => b2`<code class="doorstop-field-chip doorstop-field-chip-before">− ${uid}</code>`)}
|
|
8696
|
-
</div>`}
|
|
8697
|
-
${diff.extended.map((change) => this.renderFieldChange(change.name, change.before, change.after))}
|
|
8698
|
-
`;
|
|
8699
|
-
}
|
|
8700
|
-
renderDiffLine(line) {
|
|
8701
|
-
const mark = line.kind === "added" ? "+" : line.kind === "removed" ? "\u2212" : "";
|
|
8702
|
-
return b2`
|
|
8703
|
-
<div class=${`doorstop-diff-line is-${line.kind}`} role="listitem">
|
|
8704
|
-
<span class="doorstop-diff-mark">${mark}</span>
|
|
8705
|
-
<span class="doorstop-diff-text">${line.text === "" ? "\xA0" : line.text}</span>
|
|
8706
|
-
</div>
|
|
8707
|
-
`;
|
|
8708
|
-
}
|
|
8709
|
-
renderFieldChange(label, before, after) {
|
|
8710
|
-
return b2`
|
|
8711
|
-
<div class="doorstop-field-change">
|
|
8712
|
-
<span class="doorstop-field-name">${label}</span>
|
|
8713
|
-
<code class="doorstop-field-chip doorstop-field-chip-before">${diffValueText(before)}</code>
|
|
8714
|
-
<span class="doorstop-field-arrow">→</span>
|
|
8715
|
-
<code class="doorstop-field-chip doorstop-field-chip-after">${diffValueText(after)}</code>
|
|
8716
|
-
</div>
|
|
8717
|
-
`;
|
|
8718
|
-
}
|
|
8719
|
-
// --- detail pane ------------------------------------------------------------------
|
|
8720
|
-
// v1 deliberately renders the item text as escaped text — no markdown,
|
|
8721
|
-
// injection-safe by construction.
|
|
8722
|
-
renderDetail(result) {
|
|
8723
|
-
const selectedUid = this.selectedUid;
|
|
8724
|
-
if (selectedUid === void 0) {
|
|
8725
|
-
return b2`<p class="doorstop-muted doorstop-standalone">Select an item in the list to inspect its details.</p>`;
|
|
8726
|
-
}
|
|
8727
|
-
const item = result.index.byUid.get(selectedUid);
|
|
8728
|
-
if (item === void 0) {
|
|
8729
|
-
return b2`<p class="doorstop-muted doorstop-standalone">This item is no longer available — refresh the panel.</p>`;
|
|
8730
|
-
}
|
|
8731
|
-
const children = result.index.childrenByUid.get(item.uid) ?? [];
|
|
8732
|
-
const findings = result.index.findings.filter((finding) => finding.uid === item.uid);
|
|
8733
|
-
return b2`
|
|
8734
|
-
<section class="doorstop-detail" aria-label="Item details">
|
|
8735
|
-
<div class="doorstop-detail-head">
|
|
8736
|
-
<code class="doorstop-detail-uid">${item.uid}</code>
|
|
8737
|
-
<span class="doorstop-level">level ${item.level}</span>
|
|
8738
|
-
${item.header === void 0 ? A : b2`<h3>${item.header}</h3>`}
|
|
8739
|
-
</div>
|
|
8740
|
-
${this.renderFlags(item)}
|
|
8741
|
-
<div class="doorstop-state-chip-row">
|
|
8742
|
-
${item.stateKeys.map((key) => this.renderStateChip(key))}
|
|
8743
|
-
</div>
|
|
8744
|
-
<h4 class="doorstop-section-title">Text</h4>
|
|
8745
|
-
<p class=${item.text === "" ? "doorstop-text doorstop-text-empty" : "doorstop-text"}>${item.text === "" ? "empty" : item.text}</p>
|
|
8746
|
-
${this.renderChangesSinceReview(item)}
|
|
8747
|
-
<h4 class="doorstop-section-title">Parent links</h4>
|
|
8748
|
-
${this.renderLinksOut(item, result.index)}
|
|
8749
|
-
<h4 class="doorstop-section-title">Child links</h4>
|
|
8750
|
-
${this.renderLinksIn(item, result.index, children)}
|
|
8751
|
-
<h4 class="doorstop-section-title">References</h4>
|
|
8752
|
-
${this.renderReferences(item, result.index)}
|
|
8753
|
-
${Object.entries(item.attributes).length === 0 ? A : b2`
|
|
8754
|
-
<h4 class="doorstop-section-title">Extended attributes</h4>
|
|
8755
|
-
<dl class="doorstop-attributes">
|
|
8756
|
-
${Object.entries(item.attributes).map(
|
|
8757
|
-
([key, value]) => b2`<div class="doorstop-attribute"><dt>${key}</dt><dd><code>${jsonishText(value)}</code></dd></div>`
|
|
8758
|
-
)}
|
|
8759
|
-
</dl>
|
|
8760
|
-
`}
|
|
8761
|
-
<h4 class="doorstop-section-title">Findings</h4>
|
|
8762
|
-
${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">
|
|
8763
|
-
${findings.map((finding) => {
|
|
8764
|
-
const kind = finding.severity === "error" ? "doorstop-error" : finding.severity === "warning" ? "doorstop-warning" : "doorstop-info";
|
|
8765
|
-
return b2`<div class=${`doorstop-finding ${kind}`}><span class="doorstop-finding-severity">${finding.severity}</span><span>${finding.message}</span></div>`;
|
|
8766
|
-
})}
|
|
8767
|
-
</div>`}
|
|
8768
|
-
</section>
|
|
8769
|
-
`;
|
|
8770
|
-
}
|
|
8771
|
-
// --- item action palette (region 4) --------------------------------------------
|
|
8772
|
-
renderItemActionPalette() {
|
|
8773
|
-
if (this.view !== "items") return A;
|
|
8774
|
-
const result = this.result;
|
|
8775
|
-
if (result === void 0 || result.index.documents.length === 0) return A;
|
|
8776
|
-
const item = this.selectedItem();
|
|
8777
|
-
if (item === void 0) {
|
|
8778
|
-
return b2`
|
|
8779
|
-
<section class="doorstop-action-palette" aria-label="Item actions">
|
|
8780
|
-
<span class="doorstop-muted doorstop-palette-placeholder">Select an item…</span>
|
|
8781
|
-
</section>
|
|
8782
|
-
`;
|
|
8783
|
-
}
|
|
8784
|
-
const suspects = suspectParentItems(item, result.index);
|
|
8785
|
-
return b2`
|
|
8786
|
-
<section class="doorstop-action-palette" aria-label="Item actions">
|
|
8787
|
-
${this.renderActionRow(item, result.index, suspects)}
|
|
8788
|
-
</section>
|
|
8789
|
-
`;
|
|
8790
|
-
}
|
|
8791
|
-
renderFlags(item) {
|
|
8792
|
-
return b2`
|
|
8793
|
-
<div class="doorstop-flags" aria-label="Item flags">
|
|
8794
|
-
<span class=${e5({ "doorstop-flag": true, "is-inactive": !item.active })}>
|
|
8795
|
-
${item.active ? "active" : "inactive"}
|
|
8796
|
-
</span>
|
|
8797
|
-
<span class="doorstop-flag">${item.normative ? "normative" : "non-normative"}</span>
|
|
8798
|
-
<span class="doorstop-flag">${item.derived ? "derived" : "non-derived"}</span>
|
|
8799
|
-
</div>
|
|
8800
|
-
`;
|
|
8801
|
-
}
|
|
8802
|
-
/** `computeItemStamp(parent, config, false)` is exactly the
|
|
8803
|
-
* link-record stamp the state chain compares against. */
|
|
8804
|
-
renderLinksOut(item, index) {
|
|
8805
|
-
if (item.links.length === 0) return b2`<p class="doorstop-muted">No parent links.</p>`;
|
|
8806
|
-
return b2`
|
|
8807
|
-
<div class="doorstop-links" aria-label="Parent links">
|
|
8808
|
-
${item.links.map((link) => this.renderLinkOut(item, link, index))}
|
|
8809
|
-
</div>
|
|
8810
|
-
`;
|
|
8811
|
-
}
|
|
8812
|
-
renderLinkOut(item, link, index) {
|
|
8813
|
-
const remove = b2`
|
|
8814
|
-
<span
|
|
8815
|
-
class="doorstop-link-remove"
|
|
8816
|
-
aria-hidden="true"
|
|
8817
|
-
title=${`doorstop unlink ${item.uid} ${link.uid}`}
|
|
8818
|
-
@click=${(event) => {
|
|
8819
|
-
event.stopPropagation();
|
|
8820
|
-
this.unlinkLink(item, link.uid);
|
|
8821
|
-
}}
|
|
8822
|
-
>${trashIconSvg}</span>
|
|
8823
|
-
`;
|
|
8824
|
-
const target = index.byUid.get(link.uid);
|
|
8825
|
-
if (target === void 0) {
|
|
8826
|
-
return b2`
|
|
8827
|
-
<div class="doorstop-link-row">
|
|
8828
|
-
<code>${link.uid}</code>
|
|
8829
|
-
<span class="doorstop-chip doorstop-chip-danger">unknown</span>
|
|
8830
|
-
<span class="doorstop-fingerprint">not in the index — ${link.fingerprint === null ? "no recorded fingerprint" : `recorded ${shortFingerprint(link.fingerprint)}`}</span>
|
|
8831
|
-
${remove}
|
|
8832
|
-
</div>
|
|
8833
|
-
`;
|
|
8834
|
-
}
|
|
8835
|
-
const current = computeItemStamp(target, documentConfigFor(index, target), false);
|
|
8836
|
-
const suspect = link.fingerprint !== null && link.fingerprint !== current;
|
|
8837
|
-
return b2`
|
|
8838
|
-
<button
|
|
8839
|
-
type="button"
|
|
8840
|
-
class="doorstop-link-row"
|
|
8841
|
-
data-uid=${link.uid}
|
|
8842
|
-
title=${`Open ${link.uid}`}
|
|
8843
|
-
@click=${() => {
|
|
8844
|
-
this.controller?.selectUid(link.uid);
|
|
8845
|
-
}}
|
|
8846
|
-
>
|
|
8847
|
-
<code>${link.uid}</code>
|
|
8848
|
-
<span class=${suspect ? "doorstop-chip doorstop-chip-danger" : "doorstop-chip doorstop-chip-ok"}>${suspect ? "suspect" : "ok"}</span>
|
|
8849
|
-
<span class="doorstop-fingerprint">recorded ${shortFingerprint(link.fingerprint)} · current ${shortFingerprint(current)}</span>
|
|
8850
|
-
${remove}
|
|
8851
|
-
</button>
|
|
8852
|
-
`;
|
|
8853
|
-
}
|
|
8854
|
-
renderLinksIn(item, index, children) {
|
|
8855
|
-
if (children.length === 0) {
|
|
8856
|
-
return b2`<p class="doorstop-muted">No child links.</p>`;
|
|
8857
|
-
}
|
|
8858
|
-
return b2`
|
|
8859
|
-
<div class="doorstop-links" aria-label="Child links">
|
|
8860
|
-
${children.map(
|
|
8861
|
-
(child) => b2`<button type="button" class="doorstop-link-row" data-uid=${child.uid} title=${`Open ${child.uid}`} @click=${() => {
|
|
8862
|
-
this.controller?.selectUid(child.uid);
|
|
8863
|
-
}}>
|
|
8864
|
-
<code>${child.uid}</code>
|
|
8865
|
-
<span class="doorstop-muted">${child.level}</span>
|
|
8866
|
-
</button>`
|
|
8867
|
-
)}
|
|
8868
|
-
</div>
|
|
8869
|
-
`;
|
|
8870
|
-
}
|
|
8871
|
-
renderReferences(item, index) {
|
|
8872
|
-
const paths = [];
|
|
8873
|
-
if (item.ref !== "") paths.push(item.ref);
|
|
8874
|
-
if (item.references !== void 0) {
|
|
8875
|
-
for (const reference of item.references) paths.push(reference.path);
|
|
8876
|
-
}
|
|
8877
|
-
if (paths.length === 0) return b2`<p class="doorstop-muted">No file references.</p>`;
|
|
8878
|
-
return b2`
|
|
8879
|
-
<div class="doorstop-links" aria-label="File references">
|
|
8880
|
-
${paths.map((path) => {
|
|
8881
|
-
const found = index.knownFilePaths.has(path);
|
|
8882
|
-
return b2`
|
|
8883
|
-
<div class="doorstop-link-row">
|
|
8884
|
-
<code>${path}</code>
|
|
8885
|
-
${found ? A : b2`<span class="doorstop-chip doorstop-chip-danger">not found</span>`}
|
|
8886
|
-
</div>
|
|
8887
|
-
`;
|
|
8888
|
-
})}
|
|
8889
|
-
</div>
|
|
8890
|
-
`;
|
|
8891
|
-
}
|
|
8892
|
-
// --- action row ------------------------------------------------------------------
|
|
8893
|
-
renderActionRow(item, index, suspects) {
|
|
8894
|
-
const reviewed = item.stateKeys.includes("reviewed");
|
|
8895
|
-
const suspectUids = suspects.length > 0 ? suspects.map((parent) => parent.uid) : [];
|
|
8896
|
-
const gitStatus = this.readyGitStatus();
|
|
8897
|
-
const gitFile = gitStatus === void 0 ? void 0 : gitStatusFileFor(gitStatus, item.path);
|
|
8898
|
-
const stageable = itemStageable(gitFile);
|
|
8899
|
-
const unstageable = itemUnstageable(gitFile);
|
|
8900
|
-
return b2`
|
|
8901
|
-
<button
|
|
8902
|
-
type="button"
|
|
8903
|
-
class="doorstop-review"
|
|
8904
|
-
?disabled=${reviewed || this.runInProgress !== void 0}
|
|
8905
|
-
title=${reviewed ? `${item.uid} is already reviewed against its current fingerprint` : `Mark ${item.uid} reviewed`}
|
|
8906
|
-
@click=${() => {
|
|
8907
|
-
this.reviewItem(item);
|
|
8908
|
-
}}
|
|
8909
|
-
>Approve</button>
|
|
8910
|
-
<button
|
|
8911
|
-
type="button"
|
|
8912
|
-
class="doorstop-clear"
|
|
8913
|
-
?disabled=${suspects.length === 0 || this.runInProgress !== void 0}
|
|
8914
|
-
title=${suspects.length === 0 ? `No suspect links to clear` : `Re-record the parent fingerprints of ${item.uid}`}
|
|
8915
|
-
@click=${() => {
|
|
8916
|
-
this.clearSuspects(item, suspectUids);
|
|
8917
|
-
}}
|
|
8918
|
-
>Clear suspect links</button>
|
|
8919
|
-
${gitStatus === void 0 ? A : b2`<button
|
|
8920
|
-
type="button"
|
|
8921
|
-
class="doorstop-item-stage"
|
|
8922
|
-
?disabled=${this.runInProgress !== void 0 || !stageable}
|
|
8923
|
-
title=${stageable ? `Stage ${item.path}` : `${item.uid} has no unstaged changes \u2014 commit next`}
|
|
8924
|
-
@click=${() => {
|
|
8925
|
-
this.stageItem(item);
|
|
8926
|
-
}}
|
|
8927
|
-
>${gitStageIconSvg}Stage</button>
|
|
8928
|
-
<button
|
|
8929
|
-
type="button"
|
|
8930
|
-
class="doorstop-item-unstage"
|
|
8931
|
-
?disabled=${this.runInProgress !== void 0 || !unstageable}
|
|
8932
|
-
title=${unstageable ? `Unstage ${item.path} (git reset)` : `${item.uid} has nothing staged`}
|
|
8933
|
-
@click=${() => {
|
|
8934
|
-
this.unstageItem(item);
|
|
8935
|
-
}}
|
|
8936
|
-
>${gitUnstageIconSvg}Unstage</button>`}
|
|
8937
|
-
<div class="doorstop-op">
|
|
8938
|
-
<input
|
|
8939
|
-
type="text"
|
|
8940
|
-
class="doorstop-target-input"
|
|
8941
|
-
data-op="link"
|
|
8942
|
-
placeholder="parent UID"
|
|
8943
|
-
${n5(this.linkInputRef)}
|
|
8944
|
-
@keydown=${this.onTargetKeydown}
|
|
8945
|
-
/>
|
|
8946
|
-
<button type="button" class="doorstop-link" title=${`doorstop link ${item.uid} <target>`} ?disabled=${this.runInProgress !== void 0} @click=${() => {
|
|
8947
|
-
this.runTargetOp("link", this.linkInputRef, item);
|
|
8948
|
-
}}>Link</button>
|
|
8949
|
-
</div>
|
|
8950
|
-
${this.targetError === void 0 ? A : b2`<span class="doorstop-op-error" role="alert">${this.targetError}</span>`}
|
|
8951
|
-
${this.renderAskMenu(item, index, suspects)}
|
|
8952
|
-
`;
|
|
8953
|
-
}
|
|
8954
|
-
renderAskMenu(item, index, suspects) {
|
|
8955
|
-
const children = index.childrenByUid.get(item.uid) ?? [];
|
|
8956
|
-
const childPrefix = firstChildDocumentPrefix(index, item);
|
|
8957
|
-
return b2`
|
|
8958
|
-
<div class="doorstop-menu">
|
|
8959
|
-
<button
|
|
8960
|
-
type="button"
|
|
8961
|
-
class="doorstop-menu-toggle"
|
|
8962
|
-
aria-expanded=${this.askMenuOpen ? "true" : "false"}
|
|
8963
|
-
@click=${this.onAskMenuToggle}
|
|
8964
|
-
>Ask agent</button>
|
|
8965
|
-
${this.askMenuOpen ? b2`
|
|
8966
|
-
<div class="doorstop-menu-items" role="menu" aria-label="Ask the agent about ${item.uid}">
|
|
8967
|
-
<button type="button" role="menuitem" class="doorstop-menu-item doorstop-explain" @click=${() => {
|
|
8968
|
-
this.insertPrompt(explainItemPrompt(item));
|
|
8969
|
-
}}>Explain</button>
|
|
8970
|
-
<button
|
|
8971
|
-
type="button"
|
|
8972
|
-
role="menuitem"
|
|
8973
|
-
class="doorstop-menu-item doorstop-fix-suspects"
|
|
8974
|
-
?disabled=${suspects.length === 0}
|
|
8975
|
-
title=${suspects.length === 0 ? "No suspect links \u2014 nothing to fix" : `Fix the suspect links of ${item.uid}`}
|
|
8976
|
-
@click=${() => {
|
|
8977
|
-
this.insertPrompt(fixSuspectLinksPrompt(item, suspects));
|
|
8978
|
-
}}
|
|
8979
|
-
>Fix suspect links</button>
|
|
8980
|
-
<button
|
|
8981
|
-
type="button"
|
|
8982
|
-
role="menuitem"
|
|
8983
|
-
class="doorstop-menu-item doorstop-draft-child"
|
|
8984
|
-
?disabled=${childPrefix === void 0}
|
|
8985
|
-
title=${childPrefix === void 0 ? "No child document to add to" : `Draft a child item in the ${childPrefix} document`}
|
|
8986
|
-
@click=${() => {
|
|
8987
|
-
if (childPrefix !== void 0) this.insertPrompt(draftChildRequirementPrompt(item, childPrefix));
|
|
8988
|
-
}}
|
|
8989
|
-
>Draft child requirement</button>
|
|
8990
|
-
<button type="button" role="menuitem" class="doorstop-menu-item doorstop-review-readiness" @click=${() => {
|
|
8991
|
-
this.insertPrompt(reviewReadinessPrompt(item, children));
|
|
8992
|
-
}}>Review readiness</button>
|
|
8993
|
-
</div>
|
|
8994
|
-
` : A}
|
|
8995
|
-
</div>
|
|
8996
|
-
`;
|
|
8997
|
-
}
|
|
8998
|
-
/** Stage one item's own file — the same `doorstop.git-stage` operation
|
|
8999
|
-
* with `[item.path]`; the shared run dispatch narrates the uid. Shared
|
|
9000
|
-
* by the row's `git add` span and the palette's Stage button. */
|
|
9689
|
+
/** section-API: stage one item's own file — the same `doorstop.git-stage`
|
|
9690
|
+
* operation with `[item.path]`; the shared run dispatch narrates the uid.
|
|
9691
|
+
* Shared by the row's `git add` span and the palette's Stage button. */
|
|
9001
9692
|
stageItem(item) {
|
|
9002
9693
|
const controller = this.controller;
|
|
9003
9694
|
if (controller === void 0 || controller.runInProgress !== void 0) return;
|
|
9004
9695
|
void controller.runGitStage([item.path], `Git: stage ${item.uid}`);
|
|
9005
9696
|
}
|
|
9006
|
-
/**
|
|
9007
|
-
* `[item.path]`; the shared run dispatch narrates the uid.
|
|
9008
|
-
* the row's minus span and the palette's Unstage button. The
|
|
9009
|
-
* mirrors {@link stageItem}: a run already in flight must never be
|
|
9697
|
+
/** section-API: unstage one item's own file — the `doorstop.git-unstage`
|
|
9698
|
+
* inverse with `[item.path]`; the shared run dispatch narrates the uid.
|
|
9699
|
+
* Shared by the row's minus span and the palette's Unstage button. The
|
|
9700
|
+
* guard mirrors {@link stageItem}: a run already in flight must never be
|
|
9010
9701
|
* overlapped (the disabled states cover pointer clicks; this covers
|
|
9011
9702
|
* every call path). */
|
|
9012
9703
|
unstageItem(item) {
|
|
@@ -9014,103 +9705,7 @@ function defineDoorstopPanelBodyElement() {
|
|
|
9014
9705
|
if (controller === void 0 || controller.runInProgress !== void 0) return;
|
|
9015
9706
|
void controller.runGitUnstage([item.path], `Git: unstage ${item.uid}`);
|
|
9016
9707
|
}
|
|
9017
|
-
/**
|
|
9018
|
-
* free-text Unlink button dispatches, but with the target sourced from
|
|
9019
|
-
* the index (`link.uid`) instead of an input. The guard mirrors
|
|
9020
|
-
* {@link stageItem}: a run already in flight must never be overlapped.
|
|
9021
|
-
* There is no `targetError` interaction: an index-sourced UID cannot
|
|
9022
|
-
* fail the free-text validation, and `parseDoorstopRunRequest`
|
|
9023
|
-
* re-validates server-side. */
|
|
9024
|
-
unlinkLink(item, target) {
|
|
9025
|
-
const controller = this.controller;
|
|
9026
|
-
if (controller === void 0 || controller.runInProgress !== void 0) return;
|
|
9027
|
-
this.runDoorstop(
|
|
9028
|
-
"unlink",
|
|
9029
|
-
`Doorstop: unlink ${item.uid}`,
|
|
9030
|
-
{ op: "unlink", uid: item.uid, target },
|
|
9031
|
-
`doorstop unlink ${item.uid} ${target}`,
|
|
9032
|
-
false
|
|
9033
|
-
);
|
|
9034
|
-
}
|
|
9035
|
-
/**
|
|
9036
|
-
* The browser must never send an empty commit message: the server
|
|
9037
|
-
* rejects it, and git with an empty `-m` would hang the exec until the
|
|
9038
|
-
* deadline. The input is cleared only on SUCCESS so a `failed` commit
|
|
9039
|
-
* (hook stderr, missing identity) keeps the message for a corrected
|
|
9040
|
-
* retry.
|
|
9041
|
-
*/
|
|
9042
|
-
gitCommitSubmit() {
|
|
9043
|
-
const controller = this.controller;
|
|
9044
|
-
if (controller === void 0) return;
|
|
9045
|
-
if (controller.runInProgress !== void 0) return;
|
|
9046
|
-
const input = this.gitCommitInputRef.value;
|
|
9047
|
-
const message = input?.value.trim() ?? "";
|
|
9048
|
-
if (message === "") {
|
|
9049
|
-
this.gitActionError = "Enter a commit message";
|
|
9050
|
-
input?.focus();
|
|
9051
|
-
return;
|
|
9052
|
-
}
|
|
9053
|
-
this.gitActionError = void 0;
|
|
9054
|
-
void controller.runGitCommit(message).then(() => {
|
|
9055
|
-
const lastRun = controller.lastRun;
|
|
9056
|
-
if (lastRun?.op === "git-commit" && lastRun.status === "ok" && input !== void 0) {
|
|
9057
|
-
input.value = "";
|
|
9058
|
-
this.gitCommitMessage = "";
|
|
9059
|
-
}
|
|
9060
|
-
});
|
|
9061
|
-
}
|
|
9062
|
-
reviewItem(item) {
|
|
9063
|
-
const commit = doorstopCommitAfterReview(this.result);
|
|
9064
|
-
this.runDoorstop(
|
|
9065
|
-
"review",
|
|
9066
|
-
`Doorstop: review ${item.uid}`,
|
|
9067
|
-
commit ? { op: "review", uid: item.uid, commit: true } : { op: "review", uid: item.uid },
|
|
9068
|
-
`doorstop review ${item.uid}`,
|
|
9069
|
-
false
|
|
9070
|
-
);
|
|
9071
|
-
}
|
|
9072
|
-
clearSuspects(item, suspectUids) {
|
|
9073
|
-
if (suspectUids.length === 0) return;
|
|
9074
|
-
this.runDoorstop(
|
|
9075
|
-
"clear",
|
|
9076
|
-
"Doorstop: clear suspect links",
|
|
9077
|
-
{ op: "clear", uid: item.uid, parents: suspectUids },
|
|
9078
|
-
`doorstop clear ${item.uid} ${suspectUids.join(" ")}`,
|
|
9079
|
-
false
|
|
9080
|
-
);
|
|
9081
|
-
}
|
|
9082
|
-
editItem(item) {
|
|
9083
|
-
this.runDoorstop(
|
|
9084
|
-
"edit",
|
|
9085
|
-
`Doorstop: edit ${item.uid}`,
|
|
9086
|
-
{ op: "edit", uid: item.uid },
|
|
9087
|
-
`doorstop edit ${item.uid}`,
|
|
9088
|
-
false
|
|
9089
|
-
);
|
|
9090
|
-
}
|
|
9091
|
-
runTargetOp(op, inputRef, item) {
|
|
9092
|
-
const input = inputRef.value;
|
|
9093
|
-
const target = input?.value.trim() ?? "";
|
|
9094
|
-
if (target === "") {
|
|
9095
|
-
this.targetError = `Enter a ${op} target UID (e.g. ${item.documentPrefix}0001)`;
|
|
9096
|
-
input?.focus();
|
|
9097
|
-
return;
|
|
9098
|
-
}
|
|
9099
|
-
if (!isValidTargetUid(target)) {
|
|
9100
|
-
this.targetError = `Invalid ${op} target "${target}" \u2014 expected a Doorstop UID like ${item.documentPrefix}0001 (no spaces, quotes, or special characters)`;
|
|
9101
|
-
input?.focus();
|
|
9102
|
-
return;
|
|
9103
|
-
}
|
|
9104
|
-
this.targetError = void 0;
|
|
9105
|
-
if (input !== void 0) input.value = "";
|
|
9106
|
-
this.runDoorstop(
|
|
9107
|
-
op,
|
|
9108
|
-
`Doorstop: ${op} ${item.uid}`,
|
|
9109
|
-
{ op, uid: item.uid, target },
|
|
9110
|
-
`doorstop ${op} ${item.uid} ${target}`,
|
|
9111
|
-
false
|
|
9112
|
-
);
|
|
9113
|
-
}
|
|
9708
|
+
/** section-API: insert an Ask-agent prompt and close the menu. */
|
|
9114
9709
|
insertPrompt(text) {
|
|
9115
9710
|
const context = this.context;
|
|
9116
9711
|
if (context === void 0) return;
|
|
@@ -9121,9 +9716,9 @@ function defineDoorstopPanelBodyElement() {
|
|
|
9121
9716
|
/**
|
|
9122
9717
|
* The single dispatch surface of every panel action, with TWO paths:
|
|
9123
9718
|
*
|
|
9124
|
-
* -
|
|
9125
|
-
* with an active
|
|
9126
|
-
* `context.
|
|
9719
|
+
* - PEER path when the workspace is owned by the opendoor provider
|
|
9720
|
+
* with an active peer: the STRUCTURED `input` goes through
|
|
9721
|
+
* `context.peer.request("doorstop.run", …)` — argv is built
|
|
9127
9722
|
* server-side, so the browser never shell-quotes on this path. A
|
|
9128
9723
|
* rejected request commits `status: "error"` and does NOT
|
|
9129
9724
|
* invalidate (the run wrote nothing to the workspace).
|
|
@@ -9139,8 +9734,8 @@ function defineDoorstopPanelBodyElement() {
|
|
|
9139
9734
|
const controller = this.controller;
|
|
9140
9735
|
if (context === void 0 || controller === void 0) return;
|
|
9141
9736
|
if (controller.runInProgress !== void 0) return;
|
|
9142
|
-
if (context.
|
|
9143
|
-
void this.runDoorstopBackend(op, title, input, context.
|
|
9737
|
+
if (context.peer?.request !== void 0 && context.workspace.provider?.pluginId === OPENDOOR_PLUGIN_ID) {
|
|
9738
|
+
void this.runDoorstopBackend(op, title, input, context.peer);
|
|
9144
9739
|
return;
|
|
9145
9740
|
}
|
|
9146
9741
|
const terminal = context.terminal;
|
|
@@ -9154,13 +9749,15 @@ function defineDoorstopPanelBodyElement() {
|
|
|
9154
9749
|
}).catch(() => {
|
|
9155
9750
|
});
|
|
9156
9751
|
}
|
|
9157
|
-
async runDoorstopBackend(op, title, input,
|
|
9752
|
+
async runDoorstopBackend(op, title, input, peer) {
|
|
9158
9753
|
const controller = this.controller;
|
|
9159
9754
|
if (controller === void 0) return;
|
|
9755
|
+
const request = peer.request;
|
|
9756
|
+
if (request === void 0) return;
|
|
9160
9757
|
controller.beginRun(title);
|
|
9161
9758
|
const startedAt = Date.now();
|
|
9162
9759
|
try {
|
|
9163
|
-
const response = await
|
|
9760
|
+
const response = await request(DOORSTOP_RUN_OPERATION, input);
|
|
9164
9761
|
const parsed = parseDoorstopRunResponse(response);
|
|
9165
9762
|
const status = parsed.signal !== null ? "killed" : parsed.exitCode === 0 ? "ok" : "failed";
|
|
9166
9763
|
controller.commitRun({
|
|
@@ -9200,12 +9797,21 @@ function defineDoorstopPanelBodyElement() {
|
|
|
9200
9797
|
controller.endRun();
|
|
9201
9798
|
}
|
|
9202
9799
|
}
|
|
9800
|
+
/** section-API: the currently selected item, resolved against the
|
|
9801
|
+
* loaded index. */
|
|
9203
9802
|
selectedItem() {
|
|
9204
9803
|
const result = this.result;
|
|
9205
9804
|
const uid = this.selectedUid;
|
|
9206
9805
|
if (result === void 0 || uid === void 0) return void 0;
|
|
9207
9806
|
return result.index.byUid.get(uid);
|
|
9208
9807
|
}
|
|
9808
|
+
/** section-API: the shared state chip (item rows + detail head). The
|
|
9809
|
+
* renderer is a free function in `sections/item-list.ts`; this delegate
|
|
9810
|
+
* lets detail-pane reach it through the host, keeping the runtime import
|
|
9811
|
+
* graph one-way (coordinator → sections). */
|
|
9812
|
+
renderStateChip(key) {
|
|
9813
|
+
return renderStateChip(key);
|
|
9814
|
+
}
|
|
9209
9815
|
}
|
|
9210
9816
|
__decorateClass([
|
|
9211
9817
|
n6({ attribute: false })
|
|
@@ -9213,6 +9819,9 @@ function defineDoorstopPanelBodyElement() {
|
|
|
9213
9819
|
__decorateClass([
|
|
9214
9820
|
n6({ attribute: false })
|
|
9215
9821
|
], DoorstopPanelBodyElement.prototype, "context", 2);
|
|
9822
|
+
__decorateClass([
|
|
9823
|
+
n6({ attribute: false })
|
|
9824
|
+
], DoorstopPanelBodyElement.prototype, "markdownRenderer", 2);
|
|
9216
9825
|
__decorateClass([
|
|
9217
9826
|
n6({ attribute: false })
|
|
9218
9827
|
], DoorstopPanelBodyElement.prototype, "result", 2);
|
|
@@ -9285,18 +9894,19 @@ function defineCustomElementOnce(tag, define) {
|
|
|
9285
9894
|
define();
|
|
9286
9895
|
}
|
|
9287
9896
|
|
|
9288
|
-
// src/doorstop-contributions.ts
|
|
9289
|
-
var
|
|
9897
|
+
// src/browser/doorstop-contributions.ts
|
|
9898
|
+
var DOORSTOP_PANEL_LOCAL_ID2 = "workspace.doorstop";
|
|
9290
9899
|
var DOORSTOP_LABEL_STATE_LIMIT = 8;
|
|
9291
|
-
function createOpendoorBrowserContributions(runtimePluginId, _html, _svg) {
|
|
9292
|
-
const panelId = `${runtimePluginId}:${
|
|
9900
|
+
function createOpendoorBrowserContributions(runtimePluginId, _html, _svg, markdown) {
|
|
9901
|
+
const panelId = `${runtimePluginId}:${DOORSTOP_PANEL_LOCAL_ID2}`;
|
|
9293
9902
|
const registry = new DoorstopWorkspaceRegistry();
|
|
9294
9903
|
const labelCache = new DoorstopLabelCache();
|
|
9295
9904
|
defineDoorstopPanelElements();
|
|
9296
9905
|
return {
|
|
9297
9906
|
actions: createDoorstopActions(panelId),
|
|
9298
|
-
workspacePanels: [createDoorstopPanel(registry)],
|
|
9299
|
-
workspaceLabels: [createDoorstopLabel(labelCache)]
|
|
9907
|
+
workspacePanels: [createDoorstopPanel(registry, markdown)],
|
|
9908
|
+
workspaceLabels: [createDoorstopLabel(labelCache)],
|
|
9909
|
+
sessionLabels: [createDoorstopSessionLabel(runtimePluginId)]
|
|
9300
9910
|
};
|
|
9301
9911
|
}
|
|
9302
9912
|
function createDoorstopActions(panelId) {
|
|
@@ -9315,7 +9925,7 @@ function createDoorstopActions(panelId) {
|
|
|
9315
9925
|
// Enabled unconditionally: the Requirements panel is useful for every
|
|
9316
9926
|
// workspace (paired or unpaired — it needs no owned provider).
|
|
9317
9927
|
run: (context) => {
|
|
9318
|
-
context.
|
|
9928
|
+
context.selectWorkspaceTool(panelId);
|
|
9319
9929
|
}
|
|
9320
9930
|
},
|
|
9321
9931
|
{
|
|
@@ -9334,9 +9944,9 @@ function createDoorstopActions(panelId) {
|
|
|
9334
9944
|
}
|
|
9335
9945
|
];
|
|
9336
9946
|
}
|
|
9337
|
-
function createDoorstopPanel(registry) {
|
|
9947
|
+
function createDoorstopPanel(registry, markdown) {
|
|
9338
9948
|
return {
|
|
9339
|
-
id:
|
|
9949
|
+
id: DOORSTOP_PANEL_LOCAL_ID2,
|
|
9340
9950
|
title: "Requirements",
|
|
9341
9951
|
// Door/archive icon (currentColor): a filing cabinet with the doorstop
|
|
9342
9952
|
// checklist mark — the panel title icon, created with this module's own
|
|
@@ -9348,22 +9958,58 @@ function createDoorstopPanel(registry) {
|
|
|
9348
9958
|
<path d="m9 12 2 2 4-4"></path>
|
|
9349
9959
|
</svg>
|
|
9350
9960
|
`,
|
|
9351
|
-
order:
|
|
9961
|
+
// Plan Phase C step C3 follow-up — order 5: the host's chat-file-link
|
|
9962
|
+
// dispatch (resolveWorkspaceFileOpen) opens the FIRST ACCEPTING panel,
|
|
9963
|
+
// iterating panels by `order` ascending, and the bundled Files panel is
|
|
9964
|
+
// order 10 and answers `{ file: path }` for EVERY path unconditionally —
|
|
9965
|
+
// at order 60 this panel's `fileOpenQuery` was never consulted at all.
|
|
9966
|
+
// Sitting below 10 is the only host-supported way to claim doorstop item
|
|
9967
|
+
// files for the Requirements panel; the accepted consequence is that the
|
|
9968
|
+
// Requirements tab precedes Files in the workspace tab bar.
|
|
9969
|
+
order: 5,
|
|
9352
9970
|
// The panel is always visible (paired or unpaired): when the workspace
|
|
9353
9971
|
// is opendoor-owned the run actions use the backend, otherwise the
|
|
9354
9972
|
// terminal fallback keeps every action working; the discovery-based
|
|
9355
9973
|
// empty state carries the "no documents" story (feature spec §7.1).
|
|
9356
9974
|
visible: () => true,
|
|
9357
|
-
|
|
9358
|
-
|
|
9975
|
+
// Plan Phase C step C1 — typed invalidation (browser API v4, unchanged
|
|
9976
|
+
// on v5): declare the fixed workspace resources whose automatic invalidations this panel
|
|
9977
|
+
// receives. With `workspace.files` declared, the host calls onInvalidate
|
|
9978
|
+
// not only for the manual Refresh action (refreshWorkspacePanels) but
|
|
9979
|
+
// also when workspace files change (`reason: "mutation"` — e.g. the
|
|
9980
|
+
// review-commit pipeline's commits landing, or any other plugin's file
|
|
9981
|
+
// writes) and when agent work finishes in the workspace (`reason:
|
|
9982
|
+
// "agent-activity"` — an agent editing doorstop documents refreshes the
|
|
9983
|
+
// panel without a manual Refresh). The reason is forwarded to the
|
|
9984
|
+
// registry, which rescans unconditionally (see
|
|
9985
|
+
// DoorstopWorkspaceRegistry.invalidate).
|
|
9986
|
+
invalidationResources: ["workspace.files"],
|
|
9987
|
+
onInvalidate: (context, invalidation) => registry.invalidate(context, invalidation),
|
|
9988
|
+
// Plan Phase C step C3 — chat file links: when the host resolves a
|
|
9989
|
+
// Markdown link to a workspace-relative path, this hook answers with the
|
|
9990
|
+
// panel's navigation query IF the panel owns the path (its last landed
|
|
9991
|
+
// discovery indexed it as an item file), so the chat opens the item IN
|
|
9992
|
+
// the Requirements panel instead of the Files fallback. Synchronous and
|
|
9993
|
+
// side-effect-free: it answers purely from the registry's recorded index
|
|
9994
|
+
// (never loads, never renders); unknown paths and pre-first-load panels
|
|
9995
|
+
// return undefined (the host asks the next panel / serves Files).
|
|
9996
|
+
fileOpenQuery: (context, path) => registry.fileOpenQuery(context, path),
|
|
9997
|
+
render: (context) => renderDoorstopPanel(registry, context, markdown)
|
|
9359
9998
|
};
|
|
9360
9999
|
}
|
|
9361
|
-
function renderDoorstopPanel(registry, context) {
|
|
10000
|
+
function renderDoorstopPanel(registry, context, markdown) {
|
|
9362
10001
|
const controller = registry.for(context);
|
|
10002
|
+
if (context.navigation !== void 0) {
|
|
10003
|
+
controller.applyNavigationQuery(readPanelNavigationQuery(context.navigation));
|
|
10004
|
+
}
|
|
10005
|
+
if (controller.result !== void 0) {
|
|
10006
|
+
registry.recordFileOpenIndex(context, controller.result);
|
|
10007
|
+
}
|
|
9363
10008
|
return b2`
|
|
9364
10009
|
<pi-web-opendoor-panel-body
|
|
9365
10010
|
.controller=${controller}
|
|
9366
10011
|
.context=${context}
|
|
10012
|
+
.markdownRenderer=${markdown.renderer}
|
|
9367
10013
|
.result=${controller.result}
|
|
9368
10014
|
.loading=${controller.loading}
|
|
9369
10015
|
.stale=${controller.stale}
|
|
@@ -9458,13 +10104,36 @@ function doorstopLabelKey(context) {
|
|
|
9458
10104
|
return JSON.stringify([context.machine.id, context.workspace.projectId, context.workspace.id]);
|
|
9459
10105
|
}
|
|
9460
10106
|
|
|
9461
|
-
// src/
|
|
10107
|
+
// src/browser/doorstop-markdown.ts
|
|
10108
|
+
var DOORSTOP_MARKDOWN_CAPABILITY = {
|
|
10109
|
+
pluginId: "pi-web",
|
|
10110
|
+
id: "markdown-rendering",
|
|
10111
|
+
version: 1,
|
|
10112
|
+
parse(value) {
|
|
10113
|
+
if (typeof value !== "object" || value === null || !("render" in value) || typeof value.render !== "function") {
|
|
10114
|
+
throw new Error("Markdown rendering capability v1 is unavailable");
|
|
10115
|
+
}
|
|
10116
|
+
return value;
|
|
10117
|
+
}
|
|
10118
|
+
};
|
|
10119
|
+
function createDoorstopMarkdownHolder() {
|
|
10120
|
+
return { renderer: void 0 };
|
|
10121
|
+
}
|
|
10122
|
+
|
|
10123
|
+
// src/browser/pi-web-plugin.ts
|
|
9462
10124
|
var plugin = {
|
|
9463
|
-
apiVersion:
|
|
10125
|
+
apiVersion: 5,
|
|
9464
10126
|
name: "Opendoor",
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
10127
|
+
requires: [DOORSTOP_MARKDOWN_CAPABILITY],
|
|
10128
|
+
activate: ({ runtimePluginId, html, svg }) => {
|
|
10129
|
+
const markdown = createDoorstopMarkdownHolder();
|
|
10130
|
+
return {
|
|
10131
|
+
contributions: createOpendoorBrowserContributions(runtimePluginId, html, svg, markdown),
|
|
10132
|
+
start: ({ capabilities }) => {
|
|
10133
|
+
markdown.renderer = capabilities.resolve(DOORSTOP_MARKDOWN_CAPABILITY);
|
|
10134
|
+
}
|
|
10135
|
+
};
|
|
10136
|
+
}
|
|
9468
10137
|
};
|
|
9469
10138
|
var pi_web_plugin_default = plugin;
|
|
9470
10139
|
export {
|
|
@@ -9484,7 +10153,6 @@ lit-html/lit-html.js:
|
|
|
9484
10153
|
lit-element/lit-element.js:
|
|
9485
10154
|
lit-html/directive.js:
|
|
9486
10155
|
lit-html/async-directive.js:
|
|
9487
|
-
lit-html/directives/repeat.js:
|
|
9488
10156
|
@lit/reactive-element/decorators/custom-element.js:
|
|
9489
10157
|
@lit/reactive-element/decorators/property.js:
|
|
9490
10158
|
@lit/reactive-element/decorators/state.js:
|
|
@@ -9494,6 +10162,7 @@ lit-html/directives/repeat.js:
|
|
|
9494
10162
|
@lit/reactive-element/decorators/query-all.js:
|
|
9495
10163
|
@lit/reactive-element/decorators/query-async.js:
|
|
9496
10164
|
@lit/reactive-element/decorators/query-assigned-nodes.js:
|
|
10165
|
+
lit-html/directives/repeat.js:
|
|
9497
10166
|
(**
|
|
9498
10167
|
* @license
|
|
9499
10168
|
* Copyright 2017 Google LLC
|
|
@@ -9507,13 +10176,6 @@ lit-html/is-server.js:
|
|
|
9507
10176
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
9508
10177
|
*)
|
|
9509
10178
|
|
|
9510
|
-
lit-html/directives/class-map.js:
|
|
9511
|
-
(**
|
|
9512
|
-
* @license
|
|
9513
|
-
* Copyright 2018 Google LLC
|
|
9514
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
9515
|
-
*)
|
|
9516
|
-
|
|
9517
10179
|
lit-html/directive-helpers.js:
|
|
9518
10180
|
lit-html/directives/ref.js:
|
|
9519
10181
|
(**
|
|
@@ -9522,11 +10184,18 @@ lit-html/directives/ref.js:
|
|
|
9522
10184
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
9523
10185
|
*)
|
|
9524
10186
|
|
|
9525
|
-
lit-html/directives/keyed.js:
|
|
9526
10187
|
@lit/reactive-element/decorators/query-assigned-elements.js:
|
|
10188
|
+
lit-html/directives/keyed.js:
|
|
9527
10189
|
(**
|
|
9528
10190
|
* @license
|
|
9529
10191
|
* Copyright 2021 Google LLC
|
|
9530
10192
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
9531
10193
|
*)
|
|
10194
|
+
|
|
10195
|
+
lit-html/directives/class-map.js:
|
|
10196
|
+
(**
|
|
10197
|
+
* @license
|
|
10198
|
+
* Copyright 2018 Google LLC
|
|
10199
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
10200
|
+
*)
|
|
9532
10201
|
*/
|