opendoor-plugin 0.1.0 → 0.2.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 CHANGED
@@ -271,6 +271,7 @@ bottom:
271
271
  ```sh
272
272
  npm install # install dependencies
273
273
  npm test # vitest (unit tests under src/**/*.test.ts)
274
+ npm run test:coverage # v8 coverage report
274
275
  npm run typecheck # tsc --noEmit (strict)
275
276
  npm run build # esbuild: dist/browser/pi-web-plugin.js + dist/server-plugin.js
276
277
  # (+ verbatim package.json manifest copy into dist/)
@@ -7568,9 +7568,32 @@ var panelStyles = i`
7568
7568
  background: var(--pi-selection-bg);
7569
7569
  }
7570
7570
 
7571
+ /* The per-link trash can (pointer-only, see {@link renderLinkOut}):
7572
+ right-aligned in the flex row, mirroring .doorstop-item-add. */
7573
+ .doorstop-link-remove {
7574
+ display: inline-flex;
7575
+ align-items: center;
7576
+ align-self: center;
7577
+ flex: 0 0 auto;
7578
+ margin-left: auto;
7579
+ color: var(--pi-muted);
7580
+ cursor: pointer;
7581
+ }
7582
+
7583
+ .doorstop-link-remove:hover {
7584
+ color: var(--pi-text);
7585
+ }
7586
+
7571
7587
  .doorstop-fingerprint {
7572
7588
  color: var(--pi-muted);
7573
7589
  font-size: 11px;
7590
+ /* The link row wraps, which could push the trash can onto a second
7591
+ line on narrow panes. Let the fingerprint shrink and ellipsize
7592
+ instead (min-width: 0 lifts the flex min-content floor). */
7593
+ min-width: 0;
7594
+ overflow: hidden;
7595
+ text-overflow: ellipsis;
7596
+ white-space: nowrap;
7574
7597
  }
7575
7598
 
7576
7599
  .doorstop-attributes {
@@ -7688,7 +7711,7 @@ var panelStyles = i`
7688
7711
  .doorstop-menu-items {
7689
7712
  position: absolute;
7690
7713
  right: 0;
7691
- top: calc(100% + 4px);
7714
+ bottom: calc(100% + 4px);
7692
7715
  z-index: 3;
7693
7716
  min-width: 180px;
7694
7717
  display: grid;
@@ -7943,6 +7966,7 @@ var publishIconSvg = w`<svg width="14" height="14" viewBox="0 0 24 24" fill="non
7943
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"><circle cx="12" cy="12" r="10"/><path d="M8 12h8"/><path d="M12 8v8"/></svg>`;
7944
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"><circle cx="12" cy="12" r="10"/><path d="M8 12h8"/></svg>`;
7945
7968
  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>`;
7969
+ 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>`;
7946
7970
 
7947
7971
  // src/doorstop-prompts.ts
7948
7972
  function andJoin(values) {
@@ -8717,9 +8741,9 @@ function defineDoorstopPanelBodyElement() {
8717
8741
  <div class="doorstop-state-chip-row">
8718
8742
  ${item.stateKeys.map((key) => this.renderStateChip(key))}
8719
8743
  </div>
8720
- ${this.renderChangesSinceReview(item)}
8721
8744
  <h4 class="doorstop-section-title">Text</h4>
8722
8745
  <p class=${item.text === "" ? "doorstop-text doorstop-text-empty" : "doorstop-text"}>${item.text === "" ? "empty" : item.text}</p>
8746
+ ${this.renderChangesSinceReview(item)}
8723
8747
  <h4 class="doorstop-section-title">Parent links</h4>
8724
8748
  ${this.renderLinksOut(item, result.index)}
8725
8749
  <h4 class="doorstop-section-title">Child links</h4>
@@ -8781,11 +8805,22 @@ function defineDoorstopPanelBodyElement() {
8781
8805
  if (item.links.length === 0) return b2`<p class="doorstop-muted">No parent links.</p>`;
8782
8806
  return b2`
8783
8807
  <div class="doorstop-links" aria-label="Parent links">
8784
- ${item.links.map((link) => this.renderLinkOut(link, index))}
8808
+ ${item.links.map((link) => this.renderLinkOut(item, link, index))}
8785
8809
  </div>
8786
8810
  `;
8787
8811
  }
8788
- renderLinkOut(link, index) {
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
+ `;
8789
8824
  const target = index.byUid.get(link.uid);
8790
8825
  if (target === void 0) {
8791
8826
  return b2`
@@ -8793,6 +8828,7 @@ function defineDoorstopPanelBodyElement() {
8793
8828
  <code>${link.uid}</code>
8794
8829
  <span class="doorstop-chip doorstop-chip-danger">unknown</span>
8795
8830
  <span class="doorstop-fingerprint">not in the index — ${link.fingerprint === null ? "no recorded fingerprint" : `recorded ${shortFingerprint(link.fingerprint)}`}</span>
8831
+ ${remove}
8796
8832
  </div>
8797
8833
  `;
8798
8834
  }
@@ -8811,6 +8847,7 @@ function defineDoorstopPanelBodyElement() {
8811
8847
  <code>${link.uid}</code>
8812
8848
  <span class=${suspect ? "doorstop-chip doorstop-chip-danger" : "doorstop-chip doorstop-chip-ok"}>${suspect ? "suspect" : "ok"}</span>
8813
8849
  <span class="doorstop-fingerprint">recorded ${shortFingerprint(link.fingerprint)} · current ${shortFingerprint(current)}</span>
8850
+ ${remove}
8814
8851
  </button>
8815
8852
  `;
8816
8853
  }
@@ -8869,7 +8906,7 @@ function defineDoorstopPanelBodyElement() {
8869
8906
  @click=${() => {
8870
8907
  this.reviewItem(item);
8871
8908
  }}
8872
- >Review</button>
8909
+ >Approve</button>
8873
8910
  <button
8874
8911
  type="button"
8875
8912
  class="doorstop-clear"
@@ -8879,15 +8916,6 @@ function defineDoorstopPanelBodyElement() {
8879
8916
  this.clearSuspects(item, suspectUids);
8880
8917
  }}
8881
8918
  >Clear suspect links</button>
8882
- <button
8883
- type="button"
8884
- class="doorstop-edit"
8885
- ?disabled=${this.runInProgress !== void 0}
8886
- title=${`Open ${item.uid} in the editor`}
8887
- @click=${() => {
8888
- this.editItem(item);
8889
- }}
8890
- >Edit</button>
8891
8919
  ${gitStatus === void 0 ? A : b2`<button
8892
8920
  type="button"
8893
8921
  class="doorstop-item-stage"
@@ -8906,19 +8934,6 @@ function defineDoorstopPanelBodyElement() {
8906
8934
  this.unstageItem(item);
8907
8935
  }}
8908
8936
  >${gitUnstageIconSvg}Unstage</button>`}
8909
- <div class="doorstop-op">
8910
- <input
8911
- type="text"
8912
- class="doorstop-target-input"
8913
- data-op="unlink"
8914
- placeholder="parent UID"
8915
- ${n5(this.unlinkInputRef)}
8916
- @keydown=${this.onTargetKeydown}
8917
- />
8918
- <button type="button" class="doorstop-unlink" title=${`doorstop unlink ${item.uid} <target>`} ?disabled=${this.runInProgress !== void 0} @click=${() => {
8919
- this.runTargetOp("unlink", this.unlinkInputRef, item);
8920
- }}>Unlink</button>
8921
- </div>
8922
8937
  <div class="doorstop-op">
8923
8938
  <input
8924
8939
  type="text"
@@ -8999,6 +9014,24 @@ function defineDoorstopPanelBodyElement() {
8999
9014
  if (controller === void 0 || controller.runInProgress !== void 0) return;
9000
9015
  void controller.runGitUnstage([item.path], `Git: unstage ${item.uid}`);
9001
9016
  }
9017
+ /** Unlink one parent from one item — the same `unlink` run the palette's
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
+ }
9002
9035
  /**
9003
9036
  * The browser must never send an empty commit message: the server
9004
9037
  * rejects it, and git with an empty `-m` would hang the exec until the
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opendoor-plugin",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Opendoor — a pi-web browser plugin that surfaces Doorstop requirements stored in the workspace: the document tree, per-item state (reviewed/unreviewed changes, suspect links), validation findings, and one-click doorstop CLI actions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -33,6 +33,7 @@
33
33
  "build": "node scripts/build-plugin.mjs",
34
34
  "dev": "node scripts/build-plugin.mjs --watch",
35
35
  "test": "vitest run",
36
+ "test:coverage": "vitest run --coverage",
36
37
  "typecheck": "tsc --noEmit"
37
38
  },
38
39
  "peerDependencies": {
@@ -42,10 +43,11 @@
42
43
  "@jmfederico/pi-web": "^1.202608.2",
43
44
  "@types/js-yaml": "^4.0.9",
44
45
  "@types/node": "^24.0.0",
46
+ "@vitest/coverage-v8": "^4.1.11",
45
47
  "esbuild": "^0.27.7",
48
+ "happy-dom": "^20.11.1",
46
49
  "js-yaml": "^4.3.2",
47
50
  "lit": "^3.3.3",
48
- "happy-dom": "^20.11.1",
49
51
  "typescript": "^6.0.3",
50
52
  "vitest": "^4.1.10"
51
53
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opendoor-plugin",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Opendoor — a pi-web browser plugin that surfaces Doorstop requirements stored in the workspace: the document tree, per-item state (reviewed/unreviewed changes, suspect links), validation findings, and one-click doorstop CLI actions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -33,6 +33,7 @@
33
33
  "build": "node scripts/build-plugin.mjs",
34
34
  "dev": "node scripts/build-plugin.mjs --watch",
35
35
  "test": "vitest run",
36
+ "test:coverage": "vitest run --coverage",
36
37
  "typecheck": "tsc --noEmit"
37
38
  },
38
39
  "peerDependencies": {
@@ -42,10 +43,11 @@
42
43
  "@jmfederico/pi-web": "^1.202608.2",
43
44
  "@types/js-yaml": "^4.0.9",
44
45
  "@types/node": "^24.0.0",
46
+ "@vitest/coverage-v8": "^4.1.11",
45
47
  "esbuild": "^0.27.7",
48
+ "happy-dom": "^20.11.1",
46
49
  "js-yaml": "^4.3.2",
47
50
  "lit": "^3.3.3",
48
- "happy-dom": "^20.11.1",
49
51
  "typescript": "^6.0.3",
50
52
  "vitest": "^4.1.10"
51
53
  }