@vintasoftware/pr-review-canvas 0.1.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 (157) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +192 -0
  3. package/bin/pr-review.mjs +5 -0
  4. package/docs/reference.md +340 -0
  5. package/package.json +74 -0
  6. package/pr-review.config.example.yml +68 -0
  7. package/prompts/chat-seed.md +64 -0
  8. package/prompts/generation-format.md +255 -0
  9. package/prompts/generation-strict.md +34 -0
  10. package/prompts/generation-surfacing.md +67 -0
  11. package/prompts/layers-default.md +13 -0
  12. package/prompts/quality-standards.md +32 -0
  13. package/skills/pr-review-canvas/SKILL.md +177 -0
  14. package/src/acpx/acpx.ts +530 -0
  15. package/src/acpx/agents.ts +85 -0
  16. package/src/acpx/events.ts +216 -0
  17. package/src/acpx/ndjson.ts +69 -0
  18. package/src/acpx/preflight.ts +44 -0
  19. package/src/canvas/export.ts +95 -0
  20. package/src/canvas/import.ts +138 -0
  21. package/src/canvas/name.ts +55 -0
  22. package/src/canvas/zip.ts +123 -0
  23. package/src/chat/chat-manager.ts +389 -0
  24. package/src/chat/context.ts +160 -0
  25. package/src/chat/seed.ts +71 -0
  26. package/src/chat/threads.ts +114 -0
  27. package/src/cli.ts +199 -0
  28. package/src/commands.ts +424 -0
  29. package/src/config.ts +142 -0
  30. package/src/contract/api.ts +190 -0
  31. package/src/contract/canvas-manifest.ts +29 -0
  32. package/src/contract/chat.ts +76 -0
  33. package/src/contract/comments.ts +96 -0
  34. package/src/contract/discovery.ts +20 -0
  35. package/src/contract/generation-context.ts +77 -0
  36. package/src/contract/keys.ts +14 -0
  37. package/src/contract/links.ts +5 -0
  38. package/src/contract/mermaid-fences.ts +4 -0
  39. package/src/contract/review-artifact.ts +324 -0
  40. package/src/contract/settings.ts +144 -0
  41. package/src/contract/state.ts +46 -0
  42. package/src/contract/validation.ts +43 -0
  43. package/src/git/diff-collector.ts +151 -0
  44. package/src/git/git.ts +115 -0
  45. package/src/git/lang.ts +1 -0
  46. package/src/git/materialize.ts +79 -0
  47. package/src/git/patch-lines.ts +60 -0
  48. package/src/github/attachments.ts +288 -0
  49. package/src/github/capabilities.ts +112 -0
  50. package/src/github/comments.ts +132 -0
  51. package/src/github/gh.ts +196 -0
  52. package/src/github/post-comment.ts +104 -0
  53. package/src/github/post-review.ts +44 -0
  54. package/src/github/pr.ts +133 -0
  55. package/src/github/review-body.ts +72 -0
  56. package/src/github/threads.ts +63 -0
  57. package/src/paths.ts +10 -0
  58. package/src/project-config.ts +219 -0
  59. package/src/prompt-files.ts +26 -0
  60. package/src/review/diagram-nodes.ts +227 -0
  61. package/src/review/doctor.ts +139 -0
  62. package/src/review/glob.ts +33 -0
  63. package/src/review/install-skill.ts +107 -0
  64. package/src/review/normalize.ts +209 -0
  65. package/src/review/prepare.ts +165 -0
  66. package/src/review/prompt.ts +233 -0
  67. package/src/review/publish.ts +209 -0
  68. package/src/review/skill-command.ts +4 -0
  69. package/src/review/test-paths.ts +32 -0
  70. package/src/review/text-length.ts +15 -0
  71. package/src/review/trim-caps.ts +114 -0
  72. package/src/review/validate-folds.ts +110 -0
  73. package/src/review/validate.ts +520 -0
  74. package/src/server/app.ts +46 -0
  75. package/src/server/bundle.ts +266 -0
  76. package/src/server/capped-body.ts +62 -0
  77. package/src/server/context.ts +174 -0
  78. package/src/server/env.ts +7 -0
  79. package/src/server/errors.ts +65 -0
  80. package/src/server/html.ts +140 -0
  81. package/src/server/node-server.ts +42 -0
  82. package/src/server/routes/api.ts +256 -0
  83. package/src/server/routes/chat-routes.ts +221 -0
  84. package/src/server/routes/pages.ts +64 -0
  85. package/src/server/routes/review-routes.ts +245 -0
  86. package/src/server/routes/static.ts +114 -0
  87. package/src/server/security.ts +104 -0
  88. package/src/server/sse.ts +67 -0
  89. package/src/store/atomic-json.ts +68 -0
  90. package/src/store/canvas-store.ts +120 -0
  91. package/src/store/data-dir.ts +29 -0
  92. package/src/store/derived-store.ts +93 -0
  93. package/src/store/pr-store.ts +69 -0
  94. package/src/store/settings-store.ts +152 -0
  95. package/src/store/state-store.ts +121 -0
  96. package/static/js/anchors.js +141 -0
  97. package/static/js/api.js +542 -0
  98. package/static/js/app.js +418 -0
  99. package/static/js/ask.js +35 -0
  100. package/static/js/chat-context.js +137 -0
  101. package/static/js/chat-scroll.js +114 -0
  102. package/static/js/chat.js +843 -0
  103. package/static/js/code-folds.js +200 -0
  104. package/static/js/commands.js +110 -0
  105. package/static/js/comment-link.js +37 -0
  106. package/static/js/composer.js +241 -0
  107. package/static/js/contract-types.d.ts +59 -0
  108. package/static/js/deep-link.js +160 -0
  109. package/static/js/diagram.js +582 -0
  110. package/static/js/diff-decorations.js +204 -0
  111. package/static/js/diff-renderer.js +860 -0
  112. package/static/js/dom.js +145 -0
  113. package/static/js/download.js +52 -0
  114. package/static/js/empty-state.js +161 -0
  115. package/static/js/errors.js +135 -0
  116. package/static/js/fences.js +90 -0
  117. package/static/js/header.js +134 -0
  118. package/static/js/hunks.js +62 -0
  119. package/static/js/import-zone.js +95 -0
  120. package/static/js/interactions.js +952 -0
  121. package/static/js/keyboard.js +131 -0
  122. package/static/js/keys.js +97 -0
  123. package/static/js/lang.js +54 -0
  124. package/static/js/layers.js +596 -0
  125. package/static/js/links.js +150 -0
  126. package/static/js/markdown.js +232 -0
  127. package/static/js/mermaid-fences.js +55 -0
  128. package/static/js/nav.js +91 -0
  129. package/static/js/overview.js +85 -0
  130. package/static/js/points.js +247 -0
  131. package/static/js/progress.js +49 -0
  132. package/static/js/proposed-comment.js +133 -0
  133. package/static/js/quick-questions.js +216 -0
  134. package/static/js/regenerate.js +69 -0
  135. package/static/js/review-session.js +257 -0
  136. package/static/js/scroll-spy.js +66 -0
  137. package/static/js/selection.js +193 -0
  138. package/static/js/settings.js +206 -0
  139. package/static/js/signoff.js +171 -0
  140. package/static/js/skin.js +56 -0
  141. package/static/js/store.js +35 -0
  142. package/static/js/theme.js +56 -0
  143. package/static/js/threads.js +78 -0
  144. package/static/js/vendor.d.ts +15 -0
  145. package/static/styles/base.css +223 -0
  146. package/static/styles/chat-tools.css +130 -0
  147. package/static/styles/chat.css +140 -0
  148. package/static/styles/commands.css +156 -0
  149. package/static/styles/diff.css +258 -0
  150. package/static/styles/header.css +114 -0
  151. package/static/styles/layout.css +123 -0
  152. package/static/styles/panels.css +152 -0
  153. package/static/styles/responsive.css +80 -0
  154. package/static/styles/review-actions.css +124 -0
  155. package/static/styles/review.css +473 -0
  156. package/static/styles/skin-github.css +356 -0
  157. package/static/styles.css +14 -0
@@ -0,0 +1,114 @@
1
+ // @ts-check
2
+ // Where the transcript stands: following the answer, or left where the reader put it. Pure, so
3
+ // the rules are testable without a scrolling element; chat.js applies the result to the DOM.
4
+
5
+ /** Within this many pixels of the bottom counts as "at the bottom". */
6
+ export const PIN_THRESHOLD_PX = 48
7
+
8
+ /**
9
+ * @typedef {{ pinned: boolean, unseen: number, firstUnseenId: string | null }} ChatScrollState
10
+ */
11
+
12
+ /**
13
+ * @typedef {{ type: 'scroll', atBottom: boolean }
14
+ * | { type: 'append', id: string, belowFold: boolean }
15
+ * | { type: 'send' }
16
+ * | { type: 'jump' }
17
+ * | { type: 'thread-switch', pinned: boolean }} ChatScrollAction
18
+ */
19
+
20
+ /** @type {ChatScrollState} */
21
+ export const INITIAL_SCROLL_STATE = { pinned: true, unseen: 0, firstUnseenId: null }
22
+
23
+ /**
24
+ * True when the element is scrolled to within the threshold of its bottom.
25
+ * @param {{ scrollTop: number, scrollHeight: number, clientHeight: number }} el
26
+ */
27
+ export function isAtBottom(el) {
28
+ return el.scrollHeight - el.scrollTop - el.clientHeight <= PIN_THRESHOLD_PX
29
+ }
30
+
31
+ /**
32
+ * @param {ChatScrollState} state
33
+ * @param {ChatScrollAction} action
34
+ * @returns {ChatScrollState}
35
+ */
36
+ export function chatScrollReducer(state, action) {
37
+ switch (action.type) {
38
+ case 'scroll':
39
+ // Scrolling back to the bottom by hand counts as catching up.
40
+ return action.atBottom ? { pinned: true, unseen: 0, firstUnseenId: null } : { ...state, pinned: false }
41
+ case 'append':
42
+ // While pinned the new content is being scrolled into view, so nothing is unseen; content
43
+ // that lands inside the part already on screen is not unseen either.
44
+ if (state.pinned || !action.belowFold) {
45
+ return state
46
+ }
47
+ return {
48
+ pinned: false,
49
+ unseen: state.unseen + 1,
50
+ firstUnseenId: state.firstUnseenId ?? action.id,
51
+ }
52
+ case 'send':
53
+ return { pinned: true, unseen: 0, firstUnseenId: null }
54
+ case 'jump':
55
+ // The jump scrolls to the first unseen message, which is not the bottom; the scroll event
56
+ // that follows decides whether to re-pin.
57
+ return { pinned: false, unseen: 0, firstUnseenId: null }
58
+ case 'thread-switch':
59
+ return { pinned: action.pinned, unseen: 0, firstUnseenId: null }
60
+ }
61
+ }
62
+
63
+ /**
64
+ * The bubble's text, or null when there is nothing to catch up on.
65
+ * @param {ChatScrollState} state
66
+ */
67
+ export function unseenLabel(state) {
68
+ if (state.unseen === 0) {
69
+ return null
70
+ }
71
+ return state.unseen === 1 ? '1 new message ↓' : `${state.unseen} new messages ↓`
72
+ }
73
+
74
+ /**
75
+ * How to scroll: instantly while following an answer, smoothly when the reader jumps, and never
76
+ * smoothly for a reader who asked for less motion.
77
+ * @param {'follow' | 'jump'} reason
78
+ * @param {boolean} reducedMotion
79
+ * @returns {ScrollBehavior}
80
+ */
81
+ export function scrollBehavior(reason, reducedMotion) {
82
+ return reason === 'follow' || reducedMotion ? 'auto' : 'smooth'
83
+ }
84
+
85
+ /**
86
+ * Whether a wheel turn over the chat pane should move the transcript instead of the page.
87
+ *
88
+ * The pane is pinned to the viewport, so a wheel over its header, its controls, or its composer
89
+ * would otherwise scroll the page behind it and leave the transcript where it was. Redirecting
90
+ * keeps one pane under one pointer. A turn that starts inside the transcript already lands there,
91
+ * and a turn inside a control that scrolls its own text belongs to that control.
92
+ *
93
+ * @param {{ pinned: boolean, insideTranscript: boolean, zooming: boolean, insideOwnScroller: boolean }} at
94
+ * @returns {boolean}
95
+ */
96
+ export function redirectsWheelToTranscript(at) {
97
+ if (!at.pinned || at.zooming || at.insideTranscript || at.insideOwnScroller) {
98
+ return false
99
+ }
100
+ return true
101
+ }
102
+
103
+ /**
104
+ * True when an element can still scroll in the direction a wheel turn asks for. Used to leave a
105
+ * composer that has its own overflow in charge of its own text.
106
+ * @param {{ scrollTop: number, scrollHeight: number, clientHeight: number }} el
107
+ * @param {number} deltaY
108
+ */
109
+ export function canScrollBy(el, deltaY) {
110
+ if (deltaY < 0) {
111
+ return el.scrollTop > 0
112
+ }
113
+ return el.scrollTop + el.clientHeight < el.scrollHeight - 1
114
+ }