@vintasoftware/pr-review-canvas 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +61 -26
  2. package/docs/reference.md +231 -121
  3. package/package.json +18 -4
  4. package/pr-review.config.example.yml +10 -4
  5. package/prompts/chat-seed.md +3 -0
  6. package/prompts/generation-format.md +3 -0
  7. package/skills/pr-review-canvas/SKILL.md +77 -40
  8. package/src/acpx/acpx.ts +21 -4
  9. package/src/acpx/events.ts +5 -1
  10. package/src/acpx/ndjson.ts +3 -1
  11. package/src/acpx/preflight.ts +5 -1
  12. package/src/canvas/comment.ts +24 -0
  13. package/src/canvas/export.ts +11 -2
  14. package/src/canvas/import.ts +30 -9
  15. package/src/canvas/name.ts +1 -0
  16. package/src/canvas/zip.ts +25 -2
  17. package/src/chat/chat-manager.ts +55 -41
  18. package/src/chat/context.ts +10 -3
  19. package/src/chat/seed.ts +3 -1
  20. package/src/chat/threads.ts +27 -26
  21. package/src/cli.ts +24 -9
  22. package/src/commands.ts +86 -25
  23. package/src/config.ts +24 -24
  24. package/src/contract/api.ts +32 -1
  25. package/src/contract/canvas-manifest.ts +2 -0
  26. package/src/contract/comments.ts +8 -1
  27. package/src/contract/discovery.ts +5 -2
  28. package/src/contract/generation-context.ts +27 -2
  29. package/src/contract/review-artifact.ts +14 -8
  30. package/src/contract/review-key.ts +51 -0
  31. package/src/contract/reviews.ts +17 -0
  32. package/src/contract/settings.ts +2 -0
  33. package/src/contract/state.ts +41 -19
  34. package/src/git/diff-collector.ts +2 -1
  35. package/src/git/environment.mjs +27 -0
  36. package/src/git/git.ts +117 -12
  37. package/src/git/local-target.ts +138 -0
  38. package/src/git/patch-lines.ts +34 -2
  39. package/src/git/pr-refs.ts +36 -0
  40. package/src/github/attachments.ts +9 -257
  41. package/src/github/canvas-comment.ts +22 -0
  42. package/src/github/capabilities.ts +3 -37
  43. package/src/github/comments.ts +9 -27
  44. package/src/github/post-comment.ts +7 -37
  45. package/src/github/post-review.ts +4 -19
  46. package/src/github/pr.ts +6 -84
  47. package/src/github/threads.ts +6 -2
  48. package/src/gitlab/attachments.ts +40 -0
  49. package/src/gitlab/canvas-comment.ts +26 -0
  50. package/src/gitlab/capabilities.ts +64 -0
  51. package/src/gitlab/comments.ts +164 -0
  52. package/src/gitlab/mr.ts +115 -0
  53. package/src/gitlab/post-comment.ts +111 -0
  54. package/src/gitlab/post-review.ts +54 -0
  55. package/src/gitlab/project.ts +13 -0
  56. package/src/host/attachments.ts +293 -0
  57. package/src/host/capabilities.ts +38 -0
  58. package/src/host/client.ts +245 -0
  59. package/src/host/host.ts +136 -0
  60. package/src/host/pr.ts +51 -0
  61. package/src/host/remote.ts +42 -0
  62. package/src/project-config.ts +25 -9
  63. package/src/prompt-files.ts +4 -3
  64. package/src/review/carry-over.ts +79 -0
  65. package/src/review/doctor.ts +31 -14
  66. package/src/review/normalize.ts +3 -1
  67. package/src/review/prepare.ts +87 -17
  68. package/src/review/prompt.ts +8 -2
  69. package/src/review/publish.ts +76 -13
  70. package/src/{github → review}/review-body.ts +17 -5
  71. package/src/review/skill-command.ts +5 -3
  72. package/src/review/trim-caps.ts +10 -6
  73. package/src/review/validate-folds.ts +2 -2
  74. package/src/review/validate.ts +67 -15
  75. package/src/server/app.ts +12 -4
  76. package/src/server/bundle.ts +320 -106
  77. package/src/server/context.ts +24 -10
  78. package/src/server/errors.ts +56 -10
  79. package/src/server/html.ts +36 -12
  80. package/src/server/node-server.ts +4 -2
  81. package/src/server/routes/api.ts +92 -31
  82. package/src/server/routes/chat-routes.ts +105 -46
  83. package/src/server/routes/pages.ts +25 -9
  84. package/src/server/routes/review-routes.ts +101 -35
  85. package/src/server/sse.ts +3 -1
  86. package/src/store/atomic-json.ts +5 -1
  87. package/src/store/canvas-store.ts +95 -46
  88. package/src/store/data-dir.ts +2 -1
  89. package/src/store/derived-store.ts +41 -23
  90. package/src/store/pr-store.ts +25 -15
  91. package/src/store/state-store.ts +42 -37
  92. package/static/brand.svg +19 -0
  93. package/static/js/api.js +47 -27
  94. package/static/js/app.js +27 -7
  95. package/static/js/chat-panel.js +93 -0
  96. package/static/js/chat.js +41 -17
  97. package/static/js/composer.js +30 -17
  98. package/static/js/contract-types.d.ts +3 -0
  99. package/static/js/diagram.js +2 -1
  100. package/static/js/diff-decorations.js +5 -3
  101. package/static/js/diff-renderer.js +7 -2
  102. package/static/js/dom.js +5 -7
  103. package/static/js/download.js +1 -1
  104. package/static/js/empty-state.js +85 -16
  105. package/static/js/errors.js +22 -6
  106. package/static/js/header.js +32 -9
  107. package/static/js/host.js +40 -0
  108. package/static/js/import-zone.js +1 -1
  109. package/static/js/interactions.js +56 -17
  110. package/static/js/keyboard.js +7 -2
  111. package/static/js/layers.js +26 -14
  112. package/static/js/links.js +9 -3
  113. package/static/js/markdown.js +28 -1
  114. package/static/js/nav.js +5 -2
  115. package/static/js/overview.js +32 -4
  116. package/static/js/points.js +5 -3
  117. package/static/js/progress.js +2 -1
  118. package/static/js/proposed-comment.js +4 -1
  119. package/static/js/quick-questions.js +2 -1
  120. package/static/js/regenerate.js +4 -1
  121. package/static/js/review-session.js +7 -2
  122. package/static/js/settings.js +2 -1
  123. package/static/js/signoff.js +9 -6
  124. package/static/styles/base.css +16 -6
  125. package/static/styles/chat-panel.css +81 -0
  126. package/static/styles/chat-tools.css +28 -0
  127. package/static/styles/chat.css +1 -1
  128. package/static/styles/commands.css +10 -4
  129. package/static/styles/diff.css +1 -1
  130. package/static/styles/header.css +18 -4
  131. package/static/styles/layout.css +4 -4
  132. package/static/styles/panels.css +4 -0
  133. package/static/styles/responsive.css +1 -15
  134. package/static/styles/review-actions.css +1 -0
  135. package/static/styles/review.css +24 -3
  136. package/static/styles/skin-github.css +99 -100
  137. package/static/styles.css +13 -12
  138. package/src/github/gh.ts +0 -196
@@ -22,8 +22,10 @@
22
22
  --s4: var(--v4);
23
23
  --s5: var(--v5);
24
24
  --s6: var(--v6);
25
- --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
26
- --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
25
+ --sans:
26
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif,
27
+ 'Apple Color Emoji', 'Segoe UI Emoji';
28
+ --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
27
29
 
28
30
  --v1: light-dark(#ffd43b, #ffe066);
29
31
  --v2: light-dark(#ffa94d, #ffc078);
@@ -31,7 +33,15 @@
31
33
  --v4: light-dark(#da77f2, #e599f7);
32
34
  --v5: light-dark(#9775fa, #b197fc);
33
35
  --v6: light-dark(#748ffc, #91a7ff);
34
- --stripe-y: linear-gradient(180deg, var(--v1) 0 1px, var(--v2) 1px 2px, var(--v3) 2px 3px, var(--v4) 3px 4px, var(--v5) 4px 5px, var(--v6) 5px 6px);
36
+ --stripe-y: linear-gradient(
37
+ 180deg,
38
+ var(--v1) 0 1px,
39
+ var(--v2) 1px 2px,
40
+ var(--v3) 2px 3px,
41
+ var(--v4) 3px 4px,
42
+ var(--v5) 4px 5px,
43
+ var(--v6) 5px 6px
44
+ );
35
45
 
36
46
  --bg: light-dark(#ffffff, #1e1f2b);
37
47
  --panel: light-dark(#ffffff, #282a3a);
@@ -80,10 +90,10 @@
80
90
 
81
91
  /* The server writes `data-theme` for every load. `auto` matches neither rule, which leaves the
82
92
  `color-scheme: light dark` above to follow the operating system. */
83
- :root[data-theme="dark"] {
93
+ :root[data-theme='dark'] {
84
94
  color-scheme: dark;
85
95
  }
86
- :root[data-theme="light"] {
96
+ :root[data-theme='light'] {
87
97
  color-scheme: light;
88
98
  }
89
99
 
@@ -218,6 +228,6 @@ strong {
218
228
  }
219
229
 
220
230
  /* The hidden attribute takes priority over component layout styles. */
221
- [hidden]:not([hidden="until-found"]) {
231
+ [hidden]:not([hidden='until-found']) {
222
232
  display: none !important;
223
233
  }
@@ -0,0 +1,81 @@
1
+ .chat-launcher,
2
+ .chat-minimize {
3
+ display: none;
4
+ }
5
+ @media (max-width: 1360px) {
6
+ .chat-launcher {
7
+ display: block;
8
+ position: fixed;
9
+ right: 20px;
10
+ bottom: 20px;
11
+ z-index: 10;
12
+ padding: 14px 20px;
13
+ border: 1px solid var(--ink);
14
+ border-radius: 999px;
15
+ background: var(--accent);
16
+ color: var(--bg);
17
+ box-shadow: 0 4px 20px #0003;
18
+ font: inherit;
19
+ font-weight: 600;
20
+ cursor: pointer;
21
+ }
22
+ .chat-launcher[hidden] {
23
+ display: none;
24
+ }
25
+ .chat-minimize {
26
+ display: inline-block;
27
+ }
28
+ dialog.chat-dialog {
29
+ position: fixed;
30
+ inset: auto 20px 20px auto;
31
+ width: min(420px, calc(100vw - 40px));
32
+ height: min(680px, calc(100dvh - 40px));
33
+ max-width: none;
34
+ max-height: none;
35
+ margin: 0;
36
+ padding: 0;
37
+ border: 1px solid var(--ink);
38
+ border-radius: 12px;
39
+ background: var(--panel);
40
+ color: var(--fg);
41
+ box-shadow: 0 8px 32px #0004;
42
+ overflow: hidden;
43
+ z-index: 11;
44
+ }
45
+ .chat-dialog .chat {
46
+ position: relative;
47
+ top: auto;
48
+ height: 100%;
49
+ padding: 0;
50
+ }
51
+ .chat-dialog .chat-h,
52
+ .chat-dialog .ctx-line,
53
+ .chat-dialog .chat-composer {
54
+ flex-shrink: 0;
55
+ }
56
+ .chat-dialog .chat-h h2 {
57
+ margin-bottom: 0;
58
+ }
59
+ .chat-dialog .chat-composer {
60
+ margin: 0;
61
+ padding: 12px 14px;
62
+ gap: 8px;
63
+ }
64
+ .chat-dialog textarea {
65
+ max-height: 25dvh;
66
+ }
67
+ }
68
+ @media (max-width: 600px) {
69
+ dialog.chat-dialog {
70
+ inset: auto 8px max(8px, env(safe-area-inset-bottom)) 8px;
71
+ width: auto;
72
+ height: calc(100dvh - 24px - env(safe-area-inset-bottom));
73
+ }
74
+ .chat-launcher {
75
+ right: 16px;
76
+ bottom: max(16px, env(safe-area-inset-bottom));
77
+ }
78
+ html:has(.chat-dialog:modal) {
79
+ overflow: hidden;
80
+ }
81
+ }
@@ -1,4 +1,32 @@
1
1
  /* Chat context, proposed comments, quick questions, and settings. */
2
+ .chat-tool-calls > summary {
3
+ padding: 4px 0;
4
+ gap: 8px;
5
+ align-items: baseline;
6
+ overflow-wrap: anywhere;
7
+ cursor: pointer;
8
+ }
9
+ .chat-tool-calls > summary::before {
10
+ content: '';
11
+ width: 6px;
12
+ height: 6px;
13
+ flex: 0 0 6px;
14
+ border-right: 1.5px solid currentColor;
15
+ border-bottom: 1.5px solid currentColor;
16
+ transform: rotate(-45deg);
17
+ }
18
+ .chat-tool-calls[open] > summary::before {
19
+ transform: rotate(45deg);
20
+ }
21
+ .chat-tool-calls > ul {
22
+ margin: 4px 0 8px;
23
+ padding-left: 14px;
24
+ list-style: none;
25
+ }
26
+ .chat-tool-calls > ul > li {
27
+ margin: 4px 0;
28
+ overflow-wrap: anywhere;
29
+ }
2
30
  .ctx-line {
3
31
  display: flex;
4
32
  align-items: baseline;
@@ -25,7 +25,7 @@
25
25
  z-index: 3;
26
26
  }
27
27
  .handle::before {
28
- content: "";
28
+ content: '';
29
29
  width: 4px;
30
30
  height: 36px;
31
31
  border-left: 1px solid currentColor;
@@ -14,10 +14,10 @@
14
14
  line-height: inherit;
15
15
  }
16
16
  .cmd::before {
17
- content: "[ ";
17
+ content: '[ ';
18
18
  }
19
19
  .cmd::after {
20
- content: " ]";
20
+ content: ' ]';
21
21
  }
22
22
  .cmd:hover {
23
23
  text-decoration: underline;
@@ -25,12 +25,12 @@
25
25
  text-decoration-thickness: 2px;
26
26
  }
27
27
  .cmd:disabled,
28
- .cmd[aria-disabled="true"] {
28
+ .cmd[aria-disabled='true'] {
29
29
  color: var(--fg-dim);
30
30
  cursor: not-allowed;
31
31
  text-decoration: none;
32
32
  }
33
- .cmd[aria-busy="true"] {
33
+ .cmd[aria-busy='true'] {
34
34
  color: var(--fg-muted);
35
35
  cursor: progress;
36
36
  }
@@ -116,6 +116,12 @@
116
116
  color: var(--purple);
117
117
  border-color: currentColor;
118
118
  }
119
+ /* The two local reviews: work with no pull request, so neither open nor closed. */
120
+ .pill.branch,
121
+ .pill.uncommitted {
122
+ color: var(--accent);
123
+ border-color: currentColor;
124
+ }
119
125
  .pill.agent {
120
126
  color: var(--fg);
121
127
  border-color: var(--ink);
@@ -127,7 +127,7 @@ tr.thread.hidden-thread .x {
127
127
  font-size: 12px;
128
128
  }
129
129
  tr.is-approx .x::before {
130
- content: "near line ";
130
+ content: 'near line ';
131
131
  color: var(--fg-muted);
132
132
  font-size: 11px;
133
133
  }
@@ -20,12 +20,26 @@
20
20
  text-transform: uppercase;
21
21
  font-size: 12px;
22
22
  }
23
- .brand .box {
24
- border: 2px solid var(--ink);
25
- padding: 2px 8px;
26
- box-shadow: 3px 3px 0 var(--shadow);
23
+ .brand-wordmark {
24
+ position: relative;
25
+ display: inline-flex;
26
+ align-items: center;
27
+ min-height: 32px;
28
+ padding-left: 40px;
29
+ white-space: nowrap;
27
30
  color: var(--fg);
28
31
  }
32
+ .brand-icon {
33
+ position: absolute;
34
+ left: 0;
35
+ top: 50%;
36
+ transform: translateY(-50%);
37
+ width: 32px;
38
+ height: 32px;
39
+ }
40
+ .brand {
41
+ flex-wrap: wrap;
42
+ }
29
43
  .hdr-actions {
30
44
  display: flex;
31
45
  flex-wrap: wrap;
@@ -31,8 +31,8 @@
31
31
  display: grid;
32
32
  grid-template-columns: auto auto minmax(0, 1fr);
33
33
  grid-template-areas:
34
- "d n t"
35
- "d n m";
34
+ 'd n t'
35
+ 'd n m';
36
36
  column-gap: 8px;
37
37
  padding: 6px 14px 6px 12px;
38
38
  color: var(--fg);
@@ -44,8 +44,8 @@
44
44
  .tree a:not(:has(.dot)) {
45
45
  grid-template-columns: minmax(0, 1fr);
46
46
  grid-template-areas:
47
- "t"
48
- "m";
47
+ 't'
48
+ 'm';
49
49
  }
50
50
  .tree a:hover {
51
51
  background: var(--panel-2);
@@ -88,6 +88,10 @@
88
88
  box-shadow: 2px 2px 0 var(--shadow);
89
89
  font-size: 13px;
90
90
  }
91
+ /* The same bar for a canvas that still applies: a note, not a warning. */
92
+ .stale-bar.carried-over-bar {
93
+ background: var(--bg);
94
+ }
91
95
  .notice {
92
96
  margin: 10px 0 0;
93
97
  padding: 6px 10px;
@@ -4,25 +4,11 @@
4
4
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
5
5
  background: linear-gradient(var(--ink), var(--ink)) calc(var(--rail-w) - 1px) 0 / 1px 100% no-repeat;
6
6
  }
7
- .chat {
8
- grid-column: 2;
9
- position: static;
10
- align-self: auto;
11
- height: auto;
12
- border-top: 2px solid var(--ink);
13
- padding: 0;
14
- }
15
- .transcript {
16
- max-height: 50vh;
17
- }
18
7
  .handle {
19
8
  display: none;
20
9
  }
21
10
  }
22
11
  @media (max-width: 1000px) {
23
- .chat {
24
- grid-column: 1 / -1;
25
- }
26
12
  .layout,
27
13
  .layout.no-chat {
28
14
  grid-template-columns: minmax(0, 1fr);
@@ -53,7 +39,7 @@
53
39
  }
54
40
  .tree a,
55
41
  .tree a:not(:has(.dot)) {
56
- grid-template-areas: "d n t";
42
+ grid-template-areas: 'd n t';
57
43
  grid-template-columns: auto auto minmax(0, 1fr);
58
44
  align-items: center;
59
45
  border: 1px solid var(--ink);
@@ -35,6 +35,7 @@ tr.sel-bar .lbl {
35
35
  outline: 2px solid var(--focus);
36
36
  outline-offset: 1px;
37
37
  }
38
+ .preview-controls,
38
39
  .composer-actions {
39
40
  display: flex;
40
41
  gap: 12px;
@@ -52,7 +52,7 @@
52
52
  cursor: pointer;
53
53
  transition: transform 0.12s ease-out;
54
54
  }
55
- .chev[aria-expanded="true"] {
55
+ .chev[aria-expanded='true'] {
56
56
  transform: rotate(90deg);
57
57
  }
58
58
  .body {
@@ -323,6 +323,21 @@
323
323
  .pr-desc {
324
324
  border-top: 1px solid var(--ink);
325
325
  }
326
+ .review-history {
327
+ border-top: 1px solid var(--line);
328
+ }
329
+ .review-entry > .pill {
330
+ margin-bottom: 10px;
331
+ }
332
+ .review-entry .cmt {
333
+ row-gap: 8px;
334
+ }
335
+ .review-entry .who {
336
+ display: flex;
337
+ flex-wrap: wrap;
338
+ align-items: baseline;
339
+ gap: 4px 10px;
340
+ }
326
341
  /* A <details> summary reads like a card header: the chevron before the title is its toggle. The row
327
342
  itself keeps the default cursor and no hover background; the native <details> handles the click. */
328
343
  summary {
@@ -469,5 +484,11 @@ details[open] > summary .chev {
469
484
  margin: 0;
470
485
  }
471
486
 
472
- .prose img { max-width: 100%; height: auto; }
473
- img.av { object-fit: cover; border-radius: 50%; }
487
+ .prose img {
488
+ max-width: 100%;
489
+ height: auto;
490
+ }
491
+ img.av {
492
+ object-fit: cover;
493
+ border-radius: 50%;
494
+ }