reviw 0.10.7 → 0.11.2
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 +56 -0
- package/cli.cjs +892 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ A lightweight browser-based tool for reviewing and annotating tabular data, text
|
|
|
20
20
|
### Media Fullscreen
|
|
21
21
|
- Click images in Markdown preview to open fullscreen viewer
|
|
22
22
|
- Click videos to open fullscreen playback with native controls
|
|
23
|
+
- Click anywhere (including the image/video itself) to close the fullscreen overlay
|
|
24
|
+
- Clicking media automatically highlights the corresponding source line in the Markdown panel
|
|
23
25
|
|
|
24
26
|
### UI Features
|
|
25
27
|
- **Theme toggle**: Switch between light and dark modes
|
|
@@ -103,10 +105,64 @@ comments:
|
|
|
103
105
|
summary: Overall the data looks good, minor issues noted above.
|
|
104
106
|
```
|
|
105
107
|
|
|
108
|
+
## Claude Code Plugin
|
|
109
|
+
|
|
110
|
+
This repository also serves as a Claude Code plugin marketplace. The plugin integrates reviw into Claude Code workflows with task management and review automation.
|
|
111
|
+
|
|
112
|
+
### Installation
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# In Claude Code
|
|
116
|
+
/plugin marketplace add kazuph/reviw
|
|
117
|
+
/plugin install reviw-plugin@reviw-marketplace
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Plugin Features
|
|
121
|
+
|
|
122
|
+
| Component | Name | Description |
|
|
123
|
+
|-----------|------|-------------|
|
|
124
|
+
| **Command** | `/reviw:do` | Start a task - create worktree, plan, register todos |
|
|
125
|
+
| **Command** | `/reviw:done` | Complete checklist - collect evidence, start review with reviw |
|
|
126
|
+
| **Agent** | `report-builder` | Prepare reports and evidence for user review |
|
|
127
|
+
| **Skill** | `artifact-proof` | Collect evidence (screenshots, videos, logs) + reviw review workflow |
|
|
128
|
+
| **Hook** | PreToolUse | Remind to review before git commit/push |
|
|
129
|
+
| **Hook** | Stop | Warn if task is still in progress |
|
|
130
|
+
|
|
131
|
+
### Workflow
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
/reviw:do <task description>
|
|
135
|
+
↓
|
|
136
|
+
Create worktree + Plan
|
|
137
|
+
↓
|
|
138
|
+
Implementation
|
|
139
|
+
↓
|
|
140
|
+
/reviw:done
|
|
141
|
+
↓
|
|
142
|
+
Collect evidence + Create report
|
|
143
|
+
↓
|
|
144
|
+
npx reviw opens report (foreground)
|
|
145
|
+
↓
|
|
146
|
+
User comments → Submit & Exit
|
|
147
|
+
↓
|
|
148
|
+
Register feedback to Todo
|
|
149
|
+
↓
|
|
150
|
+
Fix → Re-review until approved
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Completion Criteria
|
|
154
|
+
|
|
155
|
+
| Stage | Content |
|
|
156
|
+
|-------|---------|
|
|
157
|
+
| 1/3 | Implementation complete |
|
|
158
|
+
| 2/3 | Build, start, verification complete |
|
|
159
|
+
| 3/3 | Review with reviw → User approval |
|
|
160
|
+
|
|
106
161
|
## Development
|
|
107
162
|
|
|
108
163
|
- Main source: `cli.cjs`
|
|
109
164
|
- Tests: `npm test` (vitest + playwright)
|
|
165
|
+
- Plugin: `plugin/` directory
|
|
110
166
|
|
|
111
167
|
## License
|
|
112
168
|
|
package/cli.cjs
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
const fs = require("fs");
|
|
15
15
|
const http = require("http");
|
|
16
|
+
const os = require("os");
|
|
16
17
|
const path = require("path");
|
|
17
18
|
const { spawn } = require("child_process");
|
|
18
19
|
const chardet = require("chardet");
|
|
@@ -465,7 +466,8 @@ function loadDiff(diffText) {
|
|
|
465
466
|
return {
|
|
466
467
|
rows,
|
|
467
468
|
files: sortedFiles,
|
|
468
|
-
|
|
469
|
+
projectRoot: "",
|
|
470
|
+
relativePath: "Git Diff",
|
|
469
471
|
mode: "diff",
|
|
470
472
|
};
|
|
471
473
|
}
|
|
@@ -574,7 +576,7 @@ function loadCsv(filePath) {
|
|
|
574
576
|
return {
|
|
575
577
|
rows,
|
|
576
578
|
cols: Math.max(1, maxCols),
|
|
577
|
-
|
|
579
|
+
...formatTitlePaths(filePath),
|
|
578
580
|
};
|
|
579
581
|
}
|
|
580
582
|
|
|
@@ -585,7 +587,7 @@ function loadText(filePath) {
|
|
|
585
587
|
return {
|
|
586
588
|
rows: lines.map((line) => [line]),
|
|
587
589
|
cols: 1,
|
|
588
|
-
|
|
590
|
+
...formatTitlePaths(filePath),
|
|
589
591
|
preview: null,
|
|
590
592
|
};
|
|
591
593
|
}
|
|
@@ -598,6 +600,7 @@ function loadMarkdown(filePath) {
|
|
|
598
600
|
// Parse YAML frontmatter
|
|
599
601
|
let frontmatterHtml = "";
|
|
600
602
|
let contentStart = 0;
|
|
603
|
+
let reviwQuestions = []; // Extract reviw questions for modal
|
|
601
604
|
|
|
602
605
|
if (lines[0] && lines[0].trim() === "---") {
|
|
603
606
|
let frontmatterEnd = -1;
|
|
@@ -615,30 +618,91 @@ function loadMarkdown(filePath) {
|
|
|
615
618
|
try {
|
|
616
619
|
const frontmatter = yaml.load(frontmatterText);
|
|
617
620
|
if (frontmatter && typeof frontmatter === "object") {
|
|
618
|
-
//
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
621
|
+
// Extract reviw questions if present
|
|
622
|
+
if (frontmatter.reviw && Array.isArray(frontmatter.reviw.questions)) {
|
|
623
|
+
reviwQuestions = frontmatter.reviw.questions.map((q, idx) => ({
|
|
624
|
+
id: q.id || "q" + (idx + 1),
|
|
625
|
+
question: q.question || "",
|
|
626
|
+
resolved: q.resolved === true,
|
|
627
|
+
answer: q.answer || "",
|
|
628
|
+
options: Array.isArray(q.options) ? q.options : [],
|
|
629
|
+
}));
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Create HTML table for frontmatter (show reviw questions in detail for 1:1 correspondence with YAML source)
|
|
633
|
+
const displayFrontmatter = { ...frontmatter };
|
|
634
|
+
// Keep reviw as-is for detailed rendering
|
|
635
|
+
|
|
636
|
+
if (Object.keys(displayFrontmatter).length > 0) {
|
|
637
|
+
frontmatterHtml = '<div class="frontmatter-table"><table>';
|
|
638
|
+
frontmatterHtml += '<colgroup><col style="width:12%"><col style="width:88%"></colgroup>';
|
|
639
|
+
frontmatterHtml += '<thead><tr><th colspan="2">Document Metadata</th></tr></thead>';
|
|
640
|
+
frontmatterHtml += "<tbody>";
|
|
641
|
+
|
|
642
|
+
// Render reviw.questions as detailed cards
|
|
643
|
+
function renderReviwQuestions(questions) {
|
|
644
|
+
if (!Array.isArray(questions) || questions.length === 0) {
|
|
645
|
+
return '<span class="fm-tag">質問なし</span>';
|
|
646
|
+
}
|
|
647
|
+
let html = '<div class="reviw-questions-preview">';
|
|
648
|
+
questions.forEach((q, idx) => {
|
|
649
|
+
const statusIcon = q.resolved ? '✅' : '⏳';
|
|
650
|
+
const statusClass = q.resolved ? 'resolved' : 'pending';
|
|
651
|
+
html += '<div class="reviw-q-card ' + statusClass + '">';
|
|
652
|
+
html += '<div class="reviw-q-header">' + statusIcon + ' <strong>' + escapeHtmlChars(q.id || 'Q' + (idx + 1)) + '</strong></div>';
|
|
653
|
+
html += '<div class="reviw-q-question">' + escapeHtmlChars(q.question || '') + '</div>';
|
|
654
|
+
if (q.options && Array.isArray(q.options) && q.options.length > 0) {
|
|
655
|
+
html += '<div class="reviw-q-options">';
|
|
656
|
+
q.options.forEach(opt => {
|
|
657
|
+
html += '<span class="fm-tag">' + escapeHtmlChars(opt) + '</span>';
|
|
658
|
+
});
|
|
659
|
+
html += '</div>';
|
|
660
|
+
}
|
|
661
|
+
if (q.answer) {
|
|
662
|
+
html += '<div class="reviw-q-answer">💬 ' + escapeHtmlChars(q.answer) + '</div>';
|
|
663
|
+
}
|
|
664
|
+
html += '</div>';
|
|
665
|
+
});
|
|
666
|
+
html += '</div>';
|
|
667
|
+
return html;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function renderValue(val, key) {
|
|
671
|
+
// Special handling for reviw object
|
|
672
|
+
if (key === 'reviw' && typeof val === 'object' && val !== null) {
|
|
673
|
+
let html = '';
|
|
674
|
+
if (val.questions && Array.isArray(val.questions)) {
|
|
675
|
+
html += renderReviwQuestions(val.questions);
|
|
676
|
+
}
|
|
677
|
+
// Render other reviw properties
|
|
678
|
+
const { questions, ...rest } = val;
|
|
679
|
+
if (Object.keys(rest).length > 0) {
|
|
680
|
+
html += '<div style="margin-top: 8px;">';
|
|
681
|
+
for (const [k, v] of Object.entries(rest)) {
|
|
682
|
+
html += '<div><strong>' + escapeHtmlChars(k) + ':</strong> ' + escapeHtmlChars(String(v)) + '</div>';
|
|
683
|
+
}
|
|
684
|
+
html += '</div>';
|
|
685
|
+
}
|
|
686
|
+
return html || '<span class="fm-tag">-</span>';
|
|
687
|
+
}
|
|
688
|
+
if (Array.isArray(val)) {
|
|
689
|
+
return val
|
|
690
|
+
.map((v) => '<span class="fm-tag">' + escapeHtmlChars(String(v)) + "</span>")
|
|
691
|
+
.join(" ");
|
|
692
|
+
}
|
|
693
|
+
if (typeof val === "object" && val !== null) {
|
|
694
|
+
return "<pre>" + escapeHtmlChars(JSON.stringify(val, null, 2)) + "</pre>";
|
|
695
|
+
}
|
|
696
|
+
return escapeHtmlChars(String(val));
|
|
629
697
|
}
|
|
630
|
-
|
|
631
|
-
|
|
698
|
+
|
|
699
|
+
for (const [key, val] of Object.entries(displayFrontmatter)) {
|
|
700
|
+
frontmatterHtml +=
|
|
701
|
+
"<tr><th>" + escapeHtmlChars(key) + "</th><td>" + renderValue(val, key) + "</td></tr>";
|
|
632
702
|
}
|
|
633
|
-
return escapeHtmlChars(String(val));
|
|
634
|
-
}
|
|
635
703
|
|
|
636
|
-
|
|
637
|
-
frontmatterHtml +=
|
|
638
|
-
"<tr><th>" + escapeHtmlChars(key) + "</th><td>" + renderValue(val) + "</td></tr>";
|
|
704
|
+
frontmatterHtml += "</tbody></table></div>";
|
|
639
705
|
}
|
|
640
|
-
|
|
641
|
-
frontmatterHtml += "</tbody></table></div>";
|
|
642
706
|
contentStart = frontmatterEnd + 1;
|
|
643
707
|
}
|
|
644
708
|
} catch (e) {
|
|
@@ -654,8 +718,9 @@ function loadMarkdown(filePath) {
|
|
|
654
718
|
return {
|
|
655
719
|
rows: lines.map((line) => [line]),
|
|
656
720
|
cols: 1,
|
|
657
|
-
|
|
721
|
+
...formatTitlePaths(filePath),
|
|
658
722
|
preview,
|
|
723
|
+
reviwQuestions, // Pass questions to UI
|
|
659
724
|
};
|
|
660
725
|
}
|
|
661
726
|
|
|
@@ -667,6 +732,20 @@ function escapeHtmlChars(str) {
|
|
|
667
732
|
.replace(/"/g, """);
|
|
668
733
|
}
|
|
669
734
|
|
|
735
|
+
function formatTitlePaths(filePath) {
|
|
736
|
+
const cwd = process.cwd();
|
|
737
|
+
const home = os.homedir();
|
|
738
|
+
const relativePath = path.relative(cwd, filePath) || path.basename(filePath);
|
|
739
|
+
let projectRoot = cwd;
|
|
740
|
+
if (projectRoot.startsWith(home)) {
|
|
741
|
+
projectRoot = "~" + projectRoot.slice(home.length);
|
|
742
|
+
}
|
|
743
|
+
if (!projectRoot.endsWith("/")) {
|
|
744
|
+
projectRoot += "/";
|
|
745
|
+
}
|
|
746
|
+
return { projectRoot, relativePath };
|
|
747
|
+
}
|
|
748
|
+
|
|
670
749
|
function loadData(filePath) {
|
|
671
750
|
// Check if path exists
|
|
672
751
|
if (!fs.existsSync(filePath)) {
|
|
@@ -714,7 +793,7 @@ function serializeForScript(value) {
|
|
|
714
793
|
}
|
|
715
794
|
|
|
716
795
|
function diffHtmlTemplate(diffData) {
|
|
717
|
-
const { rows,
|
|
796
|
+
const { rows, projectRoot, relativePath } = diffData;
|
|
718
797
|
const serialized = serializeForScript(rows);
|
|
719
798
|
const fileCount = rows.filter((r) => r.type === "file").length;
|
|
720
799
|
|
|
@@ -726,7 +805,7 @@ function diffHtmlTemplate(diffData) {
|
|
|
726
805
|
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
|
|
727
806
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
728
807
|
<meta http-equiv="Expires" content="0" />
|
|
729
|
-
<title>${
|
|
808
|
+
<title>${relativePath} | reviw</title>
|
|
730
809
|
<style>
|
|
731
810
|
:root {
|
|
732
811
|
color-scheme: dark;
|
|
@@ -795,7 +874,9 @@ function diffHtmlTemplate(diffData) {
|
|
|
795
874
|
}
|
|
796
875
|
header .meta { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
|
|
797
876
|
header .actions { display: flex; gap: 8px; align-items: center; }
|
|
798
|
-
header h1 {
|
|
877
|
+
header h1 { display: flex; flex-direction: column; margin: 0; line-height: 1.3; }
|
|
878
|
+
header h1 .title-path { font-size: 11px; font-weight: 400; color: var(--muted); }
|
|
879
|
+
header h1 .title-file { font-size: 16px; font-weight: 600; }
|
|
799
880
|
header .badge {
|
|
800
881
|
background: var(--selected-bg);
|
|
801
882
|
color: var(--text);
|
|
@@ -958,11 +1039,14 @@ function diffHtmlTemplate(diffData) {
|
|
|
958
1039
|
display: none;
|
|
959
1040
|
}
|
|
960
1041
|
.floating header {
|
|
961
|
-
position:
|
|
1042
|
+
position: static;
|
|
962
1043
|
background: transparent;
|
|
1044
|
+
backdrop-filter: none;
|
|
963
1045
|
border: none;
|
|
964
1046
|
padding: 0 0 10px 0;
|
|
1047
|
+
display: flex;
|
|
965
1048
|
justify-content: space-between;
|
|
1049
|
+
align-items: center;
|
|
966
1050
|
}
|
|
967
1051
|
.floating h2 { font-size: 14px; margin: 0; font-weight: 600; }
|
|
968
1052
|
.floating button {
|
|
@@ -987,6 +1071,10 @@ function diffHtmlTemplate(diffData) {
|
|
|
987
1071
|
font-size: 13px;
|
|
988
1072
|
font-family: inherit;
|
|
989
1073
|
}
|
|
1074
|
+
.floating textarea:focus {
|
|
1075
|
+
outline: none;
|
|
1076
|
+
border-color: var(--accent);
|
|
1077
|
+
}
|
|
990
1078
|
.floating .actions {
|
|
991
1079
|
display: flex;
|
|
992
1080
|
gap: 8px;
|
|
@@ -1092,6 +1180,21 @@ function diffHtmlTemplate(diffData) {
|
|
|
1092
1180
|
.modal-actions button:hover { background: var(--border); }
|
|
1093
1181
|
.modal-actions button.primary { background: var(--accent); color: var(--text-inverse); border-color: var(--accent); }
|
|
1094
1182
|
|
|
1183
|
+
.modal-checkboxes { margin: 12px 0; }
|
|
1184
|
+
.modal-checkboxes label {
|
|
1185
|
+
display: flex;
|
|
1186
|
+
align-items: flex-start;
|
|
1187
|
+
gap: 8px;
|
|
1188
|
+
font-size: 12px;
|
|
1189
|
+
color: var(--text);
|
|
1190
|
+
margin-bottom: 8px;
|
|
1191
|
+
cursor: pointer;
|
|
1192
|
+
}
|
|
1193
|
+
.modal-checkboxes input[type="checkbox"] {
|
|
1194
|
+
margin-top: 2px;
|
|
1195
|
+
accent-color: var(--accent);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1095
1198
|
.no-diff {
|
|
1096
1199
|
text-align: center;
|
|
1097
1200
|
padding: 60px 20px;
|
|
@@ -1104,7 +1207,7 @@ function diffHtmlTemplate(diffData) {
|
|
|
1104
1207
|
<body>
|
|
1105
1208
|
<header>
|
|
1106
1209
|
<div class="meta">
|
|
1107
|
-
<h1>${title}</h1>
|
|
1210
|
+
<h1>${projectRoot ? `<span class="title-path">${projectRoot}</span>` : ""}<span class="title-file">${relativePath}</span></h1>
|
|
1108
1211
|
<span class="badge">${fileCount} file${fileCount !== 1 ? "s" : ""} changed</span>
|
|
1109
1212
|
<span class="pill">Comments <strong id="comment-count">0</strong></span>
|
|
1110
1213
|
</div>
|
|
@@ -1146,6 +1249,11 @@ function diffHtmlTemplate(diffData) {
|
|
|
1146
1249
|
<p class="modal-summary" id="modal-summary"></p>
|
|
1147
1250
|
<label for="global-comment">Overall comment (optional)</label>
|
|
1148
1251
|
<textarea id="global-comment" placeholder="Add a summary or overall feedback..."></textarea>
|
|
1252
|
+
<div class="modal-checkboxes">
|
|
1253
|
+
<label><input type="checkbox" id="prompt-subagents" checked /> All implementation, verification, and report creation will be done by the sub-agents.</label>
|
|
1254
|
+
<label><input type="checkbox" id="prompt-reviw" checked /> Open in REVIW next time.</label>
|
|
1255
|
+
<label><input type="checkbox" id="prompt-screenshots" checked /> Update all screenshots and videos.</label>
|
|
1256
|
+
</div>
|
|
1149
1257
|
<div class="modal-actions">
|
|
1150
1258
|
<button id="modal-cancel">Cancel</button>
|
|
1151
1259
|
<button class="primary" id="modal-submit">Submit</button>
|
|
@@ -1514,9 +1622,61 @@ function diffHtmlTemplate(diffData) {
|
|
|
1514
1622
|
const modalSummary = document.getElementById('modal-summary');
|
|
1515
1623
|
const globalCommentInput = document.getElementById('global-comment');
|
|
1516
1624
|
|
|
1625
|
+
// Prompt checkboxes
|
|
1626
|
+
const promptCheckboxes = [
|
|
1627
|
+
{ id: 'prompt-subagents', text: 'All implementation, verification, and report creation will be done by the sub-agents.' },
|
|
1628
|
+
{ id: 'prompt-reviw', text: 'Open in REVIW next time.' },
|
|
1629
|
+
{ id: 'prompt-screenshots', text: 'Update all screenshots and videos.' }
|
|
1630
|
+
];
|
|
1631
|
+
const PROMPT_STORAGE_KEY = 'reviw-prompt-prefs';
|
|
1632
|
+
|
|
1633
|
+
// Load saved preferences
|
|
1634
|
+
function loadPromptPrefs() {
|
|
1635
|
+
try {
|
|
1636
|
+
const saved = localStorage.getItem(PROMPT_STORAGE_KEY);
|
|
1637
|
+
if (saved) {
|
|
1638
|
+
const prefs = JSON.parse(saved);
|
|
1639
|
+
promptCheckboxes.forEach(p => {
|
|
1640
|
+
const el = document.getElementById(p.id);
|
|
1641
|
+
if (el && typeof prefs[p.id] === 'boolean') el.checked = prefs[p.id];
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
} catch (e) {}
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// Save preferences
|
|
1648
|
+
function savePromptPrefs() {
|
|
1649
|
+
try {
|
|
1650
|
+
const prefs = {};
|
|
1651
|
+
promptCheckboxes.forEach(p => {
|
|
1652
|
+
const el = document.getElementById(p.id);
|
|
1653
|
+
if (el) prefs[p.id] = el.checked;
|
|
1654
|
+
});
|
|
1655
|
+
localStorage.setItem(PROMPT_STORAGE_KEY, JSON.stringify(prefs));
|
|
1656
|
+
} catch (e) {}
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
// Initialize checkbox listeners
|
|
1660
|
+
promptCheckboxes.forEach(p => {
|
|
1661
|
+
const el = document.getElementById(p.id);
|
|
1662
|
+
if (el) el.addEventListener('change', savePromptPrefs);
|
|
1663
|
+
});
|
|
1664
|
+
loadPromptPrefs();
|
|
1665
|
+
|
|
1666
|
+
function getSelectedPrompts() {
|
|
1667
|
+
const prompts = [];
|
|
1668
|
+
promptCheckboxes.forEach(p => {
|
|
1669
|
+
const el = document.getElementById(p.id);
|
|
1670
|
+
if (el && el.checked) prompts.push(p.text);
|
|
1671
|
+
});
|
|
1672
|
+
return prompts;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1517
1675
|
function payload(reason) {
|
|
1518
1676
|
const data = { file: FILE_NAME, mode: MODE, reason, at: new Date().toISOString(), comments: Object.values(comments) };
|
|
1519
1677
|
if (globalComment.trim()) data.summary = globalComment.trim();
|
|
1678
|
+
const prompts = getSelectedPrompts();
|
|
1679
|
+
if (prompts.length > 0) data.prompts = prompts;
|
|
1520
1680
|
return data;
|
|
1521
1681
|
}
|
|
1522
1682
|
function sendAndExit(reason = 'button') {
|
|
@@ -1537,6 +1697,7 @@ function diffHtmlTemplate(diffData) {
|
|
|
1537
1697
|
document.getElementById('modal-cancel').addEventListener('click', hideSubmitModal);
|
|
1538
1698
|
function doSubmit() {
|
|
1539
1699
|
globalComment = globalCommentInput.value;
|
|
1700
|
+
savePromptPrefs();
|
|
1540
1701
|
hideSubmitModal();
|
|
1541
1702
|
sendAndExit('button');
|
|
1542
1703
|
// Try to close window; if it fails (browser security), show completion message
|
|
@@ -1594,10 +1755,11 @@ function diffHtmlTemplate(diffData) {
|
|
|
1594
1755
|
}
|
|
1595
1756
|
|
|
1596
1757
|
// --- HTML template ---------------------------------------------------------
|
|
1597
|
-
function htmlTemplate(dataRows, cols,
|
|
1758
|
+
function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHtml, reviwQuestions = []) {
|
|
1598
1759
|
const serialized = serializeForScript(dataRows);
|
|
1599
1760
|
const modeJson = serializeForScript(mode);
|
|
1600
|
-
const titleJson = serializeForScript(
|
|
1761
|
+
const titleJson = serializeForScript(relativePath); // Use relativePath as file identifier
|
|
1762
|
+
const questionsJson = serializeForScript(reviwQuestions || []);
|
|
1601
1763
|
const hasPreview = !!previewHtml;
|
|
1602
1764
|
return `<!doctype html>
|
|
1603
1765
|
<html lang="ja">
|
|
@@ -1607,7 +1769,7 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
1607
1769
|
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
|
|
1608
1770
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
1609
1771
|
<meta http-equiv="Expires" content="0" />
|
|
1610
|
-
<title>${
|
|
1772
|
+
<title>${relativePath} | reviw</title>
|
|
1611
1773
|
<style>
|
|
1612
1774
|
/* Dark theme (default) */
|
|
1613
1775
|
:root {
|
|
@@ -1688,7 +1850,9 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
1688
1850
|
}
|
|
1689
1851
|
header .meta { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
|
|
1690
1852
|
header .actions { display: flex; gap: 8px; align-items: center; }
|
|
1691
|
-
header h1 {
|
|
1853
|
+
header h1 { display: flex; flex-direction: column; margin: 0; line-height: 1.3; }
|
|
1854
|
+
header h1 .title-path { font-size: 11px; font-weight: 400; color: var(--muted); }
|
|
1855
|
+
header h1 .title-file { font-size: 16px; font-weight: 700; }
|
|
1692
1856
|
header .badge {
|
|
1693
1857
|
background: var(--selected-bg);
|
|
1694
1858
|
color: var(--text);
|
|
@@ -1909,12 +2073,14 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
1909
2073
|
transition: background 200ms ease, border-color 200ms ease;
|
|
1910
2074
|
}
|
|
1911
2075
|
.floating header {
|
|
1912
|
-
position:
|
|
1913
|
-
top: 0;
|
|
2076
|
+
position: static;
|
|
1914
2077
|
background: transparent;
|
|
2078
|
+
backdrop-filter: none;
|
|
1915
2079
|
border: none;
|
|
1916
2080
|
padding: 0 0 8px 0;
|
|
2081
|
+
display: flex;
|
|
1917
2082
|
justify-content: space-between;
|
|
2083
|
+
align-items: center;
|
|
1918
2084
|
}
|
|
1919
2085
|
.floating h2 { font-size: 14px; margin: 0; color: var(--text); }
|
|
1920
2086
|
.floating button {
|
|
@@ -1943,6 +2109,10 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
1943
2109
|
line-height: 1.4;
|
|
1944
2110
|
transition: background 200ms ease, border-color 200ms ease;
|
|
1945
2111
|
}
|
|
2112
|
+
.floating textarea:focus {
|
|
2113
|
+
outline: none;
|
|
2114
|
+
border-color: var(--accent);
|
|
2115
|
+
}
|
|
1946
2116
|
.floating .actions {
|
|
1947
2117
|
display: flex;
|
|
1948
2118
|
gap: 8px;
|
|
@@ -2120,6 +2290,50 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2120
2290
|
border-radius: 4px;
|
|
2121
2291
|
font-size: 11px;
|
|
2122
2292
|
}
|
|
2293
|
+
/* Reviw questions preview cards */
|
|
2294
|
+
.reviw-questions-preview {
|
|
2295
|
+
display: flex;
|
|
2296
|
+
flex-direction: column;
|
|
2297
|
+
gap: 8px;
|
|
2298
|
+
}
|
|
2299
|
+
.reviw-q-card {
|
|
2300
|
+
background: var(--code-bg);
|
|
2301
|
+
border: 1px solid var(--border);
|
|
2302
|
+
border-radius: 8px;
|
|
2303
|
+
padding: 10px 12px;
|
|
2304
|
+
}
|
|
2305
|
+
.reviw-q-card.resolved {
|
|
2306
|
+
border-left: 3px solid #22c55e;
|
|
2307
|
+
}
|
|
2308
|
+
.reviw-q-card.pending {
|
|
2309
|
+
border-left: 3px solid #f59e0b;
|
|
2310
|
+
}
|
|
2311
|
+
.reviw-q-header {
|
|
2312
|
+
font-size: 12px;
|
|
2313
|
+
color: var(--text-dim);
|
|
2314
|
+
margin-bottom: 4px;
|
|
2315
|
+
}
|
|
2316
|
+
.reviw-q-header strong {
|
|
2317
|
+
color: var(--accent);
|
|
2318
|
+
}
|
|
2319
|
+
.reviw-q-question {
|
|
2320
|
+
font-size: 13px;
|
|
2321
|
+
color: var(--text);
|
|
2322
|
+
margin-bottom: 6px;
|
|
2323
|
+
}
|
|
2324
|
+
.reviw-q-options {
|
|
2325
|
+
display: flex;
|
|
2326
|
+
flex-wrap: wrap;
|
|
2327
|
+
gap: 4px;
|
|
2328
|
+
margin-bottom: 6px;
|
|
2329
|
+
}
|
|
2330
|
+
.reviw-q-answer {
|
|
2331
|
+
font-size: 12px;
|
|
2332
|
+
color: #22c55e;
|
|
2333
|
+
background: rgba(34, 197, 94, 0.1);
|
|
2334
|
+
padding: 4px 8px;
|
|
2335
|
+
border-radius: 4px;
|
|
2336
|
+
}
|
|
2123
2337
|
[data-theme="light"] .frontmatter-table tbody th {
|
|
2124
2338
|
color: #7c3aed;
|
|
2125
2339
|
}
|
|
@@ -2267,6 +2481,267 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2267
2481
|
border-radius: 8px;
|
|
2268
2482
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
|
2269
2483
|
}
|
|
2484
|
+
/* Reviw Questions Modal */
|
|
2485
|
+
.reviw-questions-overlay {
|
|
2486
|
+
display: none;
|
|
2487
|
+
position: fixed;
|
|
2488
|
+
inset: 0;
|
|
2489
|
+
background: rgba(0, 0, 0, 0.8);
|
|
2490
|
+
z-index: 1100;
|
|
2491
|
+
justify-content: center;
|
|
2492
|
+
align-items: center;
|
|
2493
|
+
}
|
|
2494
|
+
.reviw-questions-overlay.visible {
|
|
2495
|
+
display: flex;
|
|
2496
|
+
}
|
|
2497
|
+
.reviw-questions-modal {
|
|
2498
|
+
background: var(--card-bg);
|
|
2499
|
+
border: 1px solid var(--border);
|
|
2500
|
+
border-radius: 16px;
|
|
2501
|
+
width: 90%;
|
|
2502
|
+
max-width: 600px;
|
|
2503
|
+
max-height: 80vh;
|
|
2504
|
+
display: flex;
|
|
2505
|
+
flex-direction: column;
|
|
2506
|
+
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.5);
|
|
2507
|
+
}
|
|
2508
|
+
.reviw-questions-header {
|
|
2509
|
+
display: flex;
|
|
2510
|
+
justify-content: space-between;
|
|
2511
|
+
align-items: center;
|
|
2512
|
+
padding: 16px 20px;
|
|
2513
|
+
border-bottom: 1px solid var(--border);
|
|
2514
|
+
}
|
|
2515
|
+
.reviw-questions-header h2 {
|
|
2516
|
+
margin: 0;
|
|
2517
|
+
font-size: 16px;
|
|
2518
|
+
font-weight: 600;
|
|
2519
|
+
color: var(--text);
|
|
2520
|
+
}
|
|
2521
|
+
.reviw-questions-header h2 span {
|
|
2522
|
+
font-size: 14px;
|
|
2523
|
+
color: var(--text-dim);
|
|
2524
|
+
font-weight: 400;
|
|
2525
|
+
}
|
|
2526
|
+
.reviw-questions-close {
|
|
2527
|
+
width: 32px;
|
|
2528
|
+
height: 32px;
|
|
2529
|
+
border: none;
|
|
2530
|
+
background: transparent;
|
|
2531
|
+
color: var(--text-dim);
|
|
2532
|
+
font-size: 18px;
|
|
2533
|
+
cursor: pointer;
|
|
2534
|
+
border-radius: 8px;
|
|
2535
|
+
transition: all 150ms ease;
|
|
2536
|
+
}
|
|
2537
|
+
.reviw-questions-close:hover {
|
|
2538
|
+
background: var(--border);
|
|
2539
|
+
color: var(--text);
|
|
2540
|
+
}
|
|
2541
|
+
.reviw-questions-body {
|
|
2542
|
+
flex: 1;
|
|
2543
|
+
overflow-y: auto;
|
|
2544
|
+
padding: 16px 20px;
|
|
2545
|
+
}
|
|
2546
|
+
.reviw-questions-footer {
|
|
2547
|
+
padding: 12px 20px;
|
|
2548
|
+
border-top: 1px solid var(--border);
|
|
2549
|
+
display: flex;
|
|
2550
|
+
justify-content: flex-end;
|
|
2551
|
+
}
|
|
2552
|
+
.reviw-questions-later {
|
|
2553
|
+
padding: 8px 16px;
|
|
2554
|
+
border: 1px solid var(--border);
|
|
2555
|
+
background: transparent;
|
|
2556
|
+
color: var(--text-dim);
|
|
2557
|
+
border-radius: 8px;
|
|
2558
|
+
cursor: pointer;
|
|
2559
|
+
font-size: 13px;
|
|
2560
|
+
transition: all 150ms ease;
|
|
2561
|
+
}
|
|
2562
|
+
.reviw-questions-later:hover {
|
|
2563
|
+
background: var(--border);
|
|
2564
|
+
color: var(--text);
|
|
2565
|
+
}
|
|
2566
|
+
/* Question Item */
|
|
2567
|
+
.reviw-question-item {
|
|
2568
|
+
margin-bottom: 20px;
|
|
2569
|
+
padding-bottom: 20px;
|
|
2570
|
+
border-bottom: 1px solid var(--border);
|
|
2571
|
+
}
|
|
2572
|
+
.reviw-question-item:last-child {
|
|
2573
|
+
margin-bottom: 0;
|
|
2574
|
+
padding-bottom: 0;
|
|
2575
|
+
border-bottom: none;
|
|
2576
|
+
}
|
|
2577
|
+
.reviw-question-text {
|
|
2578
|
+
font-size: 14px;
|
|
2579
|
+
color: var(--text);
|
|
2580
|
+
margin-bottom: 12px;
|
|
2581
|
+
line-height: 1.5;
|
|
2582
|
+
}
|
|
2583
|
+
.reviw-question-options {
|
|
2584
|
+
display: flex;
|
|
2585
|
+
flex-wrap: wrap;
|
|
2586
|
+
gap: 8px;
|
|
2587
|
+
margin-bottom: 12px;
|
|
2588
|
+
}
|
|
2589
|
+
.reviw-question-option {
|
|
2590
|
+
padding: 8px 14px;
|
|
2591
|
+
border: 1px solid var(--border);
|
|
2592
|
+
background: transparent;
|
|
2593
|
+
color: var(--text);
|
|
2594
|
+
border-radius: 8px;
|
|
2595
|
+
cursor: pointer;
|
|
2596
|
+
font-size: 13px;
|
|
2597
|
+
transition: all 150ms ease;
|
|
2598
|
+
}
|
|
2599
|
+
.reviw-question-option:hover {
|
|
2600
|
+
border-color: var(--accent);
|
|
2601
|
+
background: rgba(96, 165, 250, 0.1);
|
|
2602
|
+
}
|
|
2603
|
+
.reviw-question-option.selected {
|
|
2604
|
+
border-color: var(--accent);
|
|
2605
|
+
background: var(--accent);
|
|
2606
|
+
color: var(--text-inverse);
|
|
2607
|
+
}
|
|
2608
|
+
.reviw-question-input {
|
|
2609
|
+
width: 100%;
|
|
2610
|
+
padding: 10px 12px;
|
|
2611
|
+
border: 1px solid var(--border);
|
|
2612
|
+
background: var(--input-bg);
|
|
2613
|
+
color: var(--text);
|
|
2614
|
+
border-radius: 8px;
|
|
2615
|
+
font-size: 13px;
|
|
2616
|
+
resize: vertical;
|
|
2617
|
+
min-height: 60px;
|
|
2618
|
+
}
|
|
2619
|
+
.reviw-question-input:focus {
|
|
2620
|
+
outline: none;
|
|
2621
|
+
border-color: var(--accent);
|
|
2622
|
+
}
|
|
2623
|
+
.reviw-question-input::placeholder {
|
|
2624
|
+
color: var(--text-dim);
|
|
2625
|
+
}
|
|
2626
|
+
.reviw-check-mark {
|
|
2627
|
+
color: #22c55e;
|
|
2628
|
+
font-weight: bold;
|
|
2629
|
+
}
|
|
2630
|
+
.reviw-question-item.answered {
|
|
2631
|
+
border-color: #22c55e;
|
|
2632
|
+
background: rgba(34, 197, 94, 0.05);
|
|
2633
|
+
}
|
|
2634
|
+
.reviw-question-submit {
|
|
2635
|
+
margin-top: 10px;
|
|
2636
|
+
padding: 8px 16px;
|
|
2637
|
+
border: none;
|
|
2638
|
+
background: var(--accent);
|
|
2639
|
+
color: var(--text-inverse);
|
|
2640
|
+
border-radius: 8px;
|
|
2641
|
+
cursor: pointer;
|
|
2642
|
+
font-size: 13px;
|
|
2643
|
+
font-weight: 500;
|
|
2644
|
+
transition: all 150ms ease;
|
|
2645
|
+
}
|
|
2646
|
+
.reviw-question-submit:hover {
|
|
2647
|
+
filter: brightness(1.1);
|
|
2648
|
+
}
|
|
2649
|
+
.reviw-question-submit:disabled {
|
|
2650
|
+
opacity: 0.5;
|
|
2651
|
+
cursor: not-allowed;
|
|
2652
|
+
}
|
|
2653
|
+
/* Resolved Section */
|
|
2654
|
+
.reviw-resolved-section {
|
|
2655
|
+
margin-top: 16px;
|
|
2656
|
+
border-top: 1px solid var(--border);
|
|
2657
|
+
padding-top: 12px;
|
|
2658
|
+
}
|
|
2659
|
+
.reviw-resolved-toggle {
|
|
2660
|
+
display: flex;
|
|
2661
|
+
align-items: center;
|
|
2662
|
+
gap: 8px;
|
|
2663
|
+
background: none;
|
|
2664
|
+
border: none;
|
|
2665
|
+
color: var(--text-dim);
|
|
2666
|
+
font-size: 13px;
|
|
2667
|
+
cursor: pointer;
|
|
2668
|
+
padding: 4px 0;
|
|
2669
|
+
}
|
|
2670
|
+
.reviw-resolved-toggle:hover {
|
|
2671
|
+
color: var(--text);
|
|
2672
|
+
}
|
|
2673
|
+
.reviw-resolved-toggle .arrow {
|
|
2674
|
+
transition: transform 150ms ease;
|
|
2675
|
+
}
|
|
2676
|
+
.reviw-resolved-toggle.open .arrow {
|
|
2677
|
+
transform: rotate(90deg);
|
|
2678
|
+
}
|
|
2679
|
+
.reviw-resolved-list {
|
|
2680
|
+
display: none;
|
|
2681
|
+
margin-top: 12px;
|
|
2682
|
+
}
|
|
2683
|
+
.reviw-resolved-list.visible {
|
|
2684
|
+
display: block;
|
|
2685
|
+
}
|
|
2686
|
+
.reviw-resolved-item {
|
|
2687
|
+
padding: 10px 12px;
|
|
2688
|
+
background: var(--input-bg);
|
|
2689
|
+
border-radius: 8px;
|
|
2690
|
+
margin-bottom: 8px;
|
|
2691
|
+
opacity: 0.7;
|
|
2692
|
+
}
|
|
2693
|
+
.reviw-resolved-item:last-child {
|
|
2694
|
+
margin-bottom: 0;
|
|
2695
|
+
}
|
|
2696
|
+
.reviw-resolved-q {
|
|
2697
|
+
font-size: 12px;
|
|
2698
|
+
color: var(--text-dim);
|
|
2699
|
+
margin-bottom: 4px;
|
|
2700
|
+
}
|
|
2701
|
+
.reviw-resolved-a {
|
|
2702
|
+
font-size: 13px;
|
|
2703
|
+
color: var(--text);
|
|
2704
|
+
}
|
|
2705
|
+
/* Notice Bar */
|
|
2706
|
+
.reviw-questions-bar {
|
|
2707
|
+
display: none;
|
|
2708
|
+
position: fixed;
|
|
2709
|
+
top: 0;
|
|
2710
|
+
left: 0;
|
|
2711
|
+
right: 0;
|
|
2712
|
+
background: var(--accent);
|
|
2713
|
+
color: var(--text-inverse);
|
|
2714
|
+
padding: 8px 16px;
|
|
2715
|
+
font-size: 13px;
|
|
2716
|
+
z-index: 1050;
|
|
2717
|
+
justify-content: center;
|
|
2718
|
+
align-items: center;
|
|
2719
|
+
gap: 12px;
|
|
2720
|
+
}
|
|
2721
|
+
.reviw-questions-bar.visible {
|
|
2722
|
+
display: flex;
|
|
2723
|
+
}
|
|
2724
|
+
.reviw-questions-bar button {
|
|
2725
|
+
padding: 4px 12px;
|
|
2726
|
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
2727
|
+
background: rgba(255, 255, 255, 0.1);
|
|
2728
|
+
color: var(--text-inverse);
|
|
2729
|
+
border-radius: 6px;
|
|
2730
|
+
cursor: pointer;
|
|
2731
|
+
font-size: 12px;
|
|
2732
|
+
transition: all 150ms ease;
|
|
2733
|
+
}
|
|
2734
|
+
.reviw-questions-bar button:hover {
|
|
2735
|
+
background: rgba(255, 255, 255, 0.2);
|
|
2736
|
+
}
|
|
2737
|
+
/* Adjust layout when bar is visible */
|
|
2738
|
+
body.has-questions-bar header {
|
|
2739
|
+
top: 36px;
|
|
2740
|
+
}
|
|
2741
|
+
body.has-questions-bar .toolbar,
|
|
2742
|
+
body.has-questions-bar .table-wrap {
|
|
2743
|
+
margin-top: 36px;
|
|
2744
|
+
}
|
|
2270
2745
|
/* Copy notification toast */
|
|
2271
2746
|
.copy-toast {
|
|
2272
2747
|
position: fixed;
|
|
@@ -2379,6 +2854,22 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2379
2854
|
.modal-actions button:hover { background: var(--hover-bg); }
|
|
2380
2855
|
.modal-actions button.primary { background: var(--accent); color: var(--text-inverse); border-color: var(--accent); }
|
|
2381
2856
|
.modal-actions button.primary:hover { background: #7dd3fc; }
|
|
2857
|
+
|
|
2858
|
+
.modal-checkboxes { margin: 12px 0; }
|
|
2859
|
+
.modal-checkboxes label {
|
|
2860
|
+
display: flex;
|
|
2861
|
+
align-items: flex-start;
|
|
2862
|
+
gap: 8px;
|
|
2863
|
+
font-size: 12px;
|
|
2864
|
+
color: var(--text);
|
|
2865
|
+
margin-bottom: 8px;
|
|
2866
|
+
cursor: pointer;
|
|
2867
|
+
}
|
|
2868
|
+
.modal-checkboxes input[type="checkbox"] {
|
|
2869
|
+
margin-top: 2px;
|
|
2870
|
+
accent-color: var(--accent);
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2382
2873
|
body.dragging { user-select: none; cursor: crosshair; }
|
|
2383
2874
|
body.dragging .diff-line { cursor: crosshair; }
|
|
2384
2875
|
@media (max-width: 840px) {
|
|
@@ -2529,7 +3020,7 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2529
3020
|
<body>
|
|
2530
3021
|
<header>
|
|
2531
3022
|
<div class="meta">
|
|
2532
|
-
<h1>${title}</h1>
|
|
3023
|
+
<h1><span class="title-path">${projectRoot}</span><span class="title-file">${relativePath}</span></h1>
|
|
2533
3024
|
<span class="badge">Click to comment / ESC to cancel</span>
|
|
2534
3025
|
<span class="pill">Comments <strong id="comment-count">0</strong></span>
|
|
2535
3026
|
</div>
|
|
@@ -2645,6 +3136,11 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2645
3136
|
<p class="modal-summary" id="modal-summary"></p>
|
|
2646
3137
|
<label for="global-comment">Overall comment (optional)</label>
|
|
2647
3138
|
<textarea id="global-comment" placeholder="Add a summary or overall feedback..."></textarea>
|
|
3139
|
+
<div class="modal-checkboxes">
|
|
3140
|
+
<label><input type="checkbox" id="prompt-subagents" checked /> All implementation, verification, and report creation will be done by the sub-agents.</label>
|
|
3141
|
+
<label><input type="checkbox" id="prompt-reviw" checked /> Open in REVIW next time.</label>
|
|
3142
|
+
<label><input type="checkbox" id="prompt-screenshots" checked /> Update all screenshots and videos.</label>
|
|
3143
|
+
</div>
|
|
2648
3144
|
<div class="modal-actions">
|
|
2649
3145
|
<button id="modal-cancel">Cancel</button>
|
|
2650
3146
|
<button class="primary" id="modal-submit">Submit</button>
|
|
@@ -2682,11 +3178,32 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2682
3178
|
<div class="video-container" id="video-container"></div>
|
|
2683
3179
|
</div>
|
|
2684
3180
|
|
|
3181
|
+
<!-- Reviw Questions Modal -->
|
|
3182
|
+
<div class="reviw-questions-overlay" id="reviw-questions-overlay">
|
|
3183
|
+
<div class="reviw-questions-modal" id="reviw-questions-modal">
|
|
3184
|
+
<div class="reviw-questions-header">
|
|
3185
|
+
<h2>📋 AIからの質問 <span id="reviw-questions-count"></span></h2>
|
|
3186
|
+
<button class="reviw-questions-close" id="reviw-questions-close" aria-label="Close">✕</button>
|
|
3187
|
+
</div>
|
|
3188
|
+
<div class="reviw-questions-body" id="reviw-questions-body"></div>
|
|
3189
|
+
<div class="reviw-questions-footer">
|
|
3190
|
+
<button class="reviw-questions-later" id="reviw-questions-later">後で回答する</button>
|
|
3191
|
+
</div>
|
|
3192
|
+
</div>
|
|
3193
|
+
</div>
|
|
3194
|
+
|
|
3195
|
+
<!-- Reviw Questions Notice Bar -->
|
|
3196
|
+
<div class="reviw-questions-bar" id="reviw-questions-bar">
|
|
3197
|
+
<span id="reviw-questions-bar-message">\ud83d\udccb \u672a\u56de\u7b54\u306e\u8cea\u554f\u304c<span id="reviw-questions-bar-count">0</span>\u4ef6\u3042\u308a\u307e\u3059</span>
|
|
3198
|
+
<button id="reviw-questions-bar-open">\u8cea\u554f\u3092\u898b\u308b</button>
|
|
3199
|
+
</div>
|
|
3200
|
+
|
|
2685
3201
|
<script>
|
|
2686
3202
|
const DATA = ${serialized};
|
|
2687
3203
|
const MAX_COLS = ${cols};
|
|
2688
3204
|
const FILE_NAME = ${titleJson};
|
|
2689
3205
|
const MODE = ${modeJson};
|
|
3206
|
+
const REVIW_QUESTIONS = ${questionsJson};
|
|
2690
3207
|
|
|
2691
3208
|
// --- Theme Management ---
|
|
2692
3209
|
(function initTheme() {
|
|
@@ -3045,6 +3562,12 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3045
3562
|
|
|
3046
3563
|
function openCardForSelection() {
|
|
3047
3564
|
if (!selection) return;
|
|
3565
|
+
// Don't open card while image/video modal is visible
|
|
3566
|
+
const imageOverlay = document.getElementById('image-fullscreen');
|
|
3567
|
+
const videoOverlay = document.getElementById('video-fullscreen');
|
|
3568
|
+
if (imageOverlay?.classList.contains('visible') || videoOverlay?.classList.contains('visible')) {
|
|
3569
|
+
return;
|
|
3570
|
+
}
|
|
3048
3571
|
const { startRow, endRow, startCol, endCol } = selection;
|
|
3049
3572
|
const isSingleCell = startRow === endRow && startCol === endCol;
|
|
3050
3573
|
|
|
@@ -3550,6 +4073,56 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3550
4073
|
const modalCancel = document.getElementById('modal-cancel');
|
|
3551
4074
|
const modalSubmit = document.getElementById('modal-submit');
|
|
3552
4075
|
|
|
4076
|
+
// Prompt checkboxes
|
|
4077
|
+
const promptCheckboxes = [
|
|
4078
|
+
{ id: 'prompt-subagents', text: 'All implementation, verification, and report creation will be done by the sub-agents.' },
|
|
4079
|
+
{ id: 'prompt-reviw', text: 'Open in REVIW next time.' },
|
|
4080
|
+
{ id: 'prompt-screenshots', text: 'Update all screenshots and videos.' }
|
|
4081
|
+
];
|
|
4082
|
+
const PROMPT_STORAGE_KEY = 'reviw-prompt-prefs';
|
|
4083
|
+
|
|
4084
|
+
// Load saved preferences
|
|
4085
|
+
function loadPromptPrefs() {
|
|
4086
|
+
try {
|
|
4087
|
+
const saved = localStorage.getItem(PROMPT_STORAGE_KEY);
|
|
4088
|
+
if (saved) {
|
|
4089
|
+
const prefs = JSON.parse(saved);
|
|
4090
|
+
promptCheckboxes.forEach(p => {
|
|
4091
|
+
const el = document.getElementById(p.id);
|
|
4092
|
+
if (el && typeof prefs[p.id] === 'boolean') el.checked = prefs[p.id];
|
|
4093
|
+
});
|
|
4094
|
+
}
|
|
4095
|
+
} catch (e) {}
|
|
4096
|
+
}
|
|
4097
|
+
|
|
4098
|
+
// Save preferences
|
|
4099
|
+
function savePromptPrefs() {
|
|
4100
|
+
try {
|
|
4101
|
+
const prefs = {};
|
|
4102
|
+
promptCheckboxes.forEach(p => {
|
|
4103
|
+
const el = document.getElementById(p.id);
|
|
4104
|
+
if (el) prefs[p.id] = el.checked;
|
|
4105
|
+
});
|
|
4106
|
+
localStorage.setItem(PROMPT_STORAGE_KEY, JSON.stringify(prefs));
|
|
4107
|
+
} catch (e) {}
|
|
4108
|
+
}
|
|
4109
|
+
|
|
4110
|
+
// Initialize checkbox listeners
|
|
4111
|
+
promptCheckboxes.forEach(p => {
|
|
4112
|
+
const el = document.getElementById(p.id);
|
|
4113
|
+
if (el) el.addEventListener('change', savePromptPrefs);
|
|
4114
|
+
});
|
|
4115
|
+
loadPromptPrefs();
|
|
4116
|
+
|
|
4117
|
+
function getSelectedPrompts() {
|
|
4118
|
+
const prompts = [];
|
|
4119
|
+
promptCheckboxes.forEach(p => {
|
|
4120
|
+
const el = document.getElementById(p.id);
|
|
4121
|
+
if (el && el.checked) prompts.push(p.text);
|
|
4122
|
+
});
|
|
4123
|
+
return prompts;
|
|
4124
|
+
}
|
|
4125
|
+
|
|
3553
4126
|
function payload(reason) {
|
|
3554
4127
|
const data = {
|
|
3555
4128
|
file: FILE_NAME,
|
|
@@ -3561,6 +4134,24 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3561
4134
|
if (globalComment.trim()) {
|
|
3562
4135
|
data.summary = globalComment.trim();
|
|
3563
4136
|
}
|
|
4137
|
+
const prompts = getSelectedPrompts();
|
|
4138
|
+
if (prompts.length > 0) data.prompts = prompts;
|
|
4139
|
+
// Include answered questions
|
|
4140
|
+
if (window.REVIW_ANSWERS) {
|
|
4141
|
+
const answeredQuestions = [];
|
|
4142
|
+
for (const [id, answer] of Object.entries(window.REVIW_ANSWERS)) {
|
|
4143
|
+
if (answer.selected || answer.text.trim()) {
|
|
4144
|
+
answeredQuestions.push({
|
|
4145
|
+
id,
|
|
4146
|
+
selected: answer.selected,
|
|
4147
|
+
text: answer.text.trim()
|
|
4148
|
+
});
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
if (answeredQuestions.length > 0) {
|
|
4152
|
+
data.reviwAnswers = answeredQuestions;
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
3564
4155
|
return data;
|
|
3565
4156
|
}
|
|
3566
4157
|
function sendAndExit(reason = 'pagehide') {
|
|
@@ -3586,6 +4177,7 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3586
4177
|
modalCancel.addEventListener('click', hideSubmitModal);
|
|
3587
4178
|
function doSubmit() {
|
|
3588
4179
|
globalComment = globalCommentInput.value;
|
|
4180
|
+
savePromptPrefs();
|
|
3589
4181
|
hideSubmitModal();
|
|
3590
4182
|
sendAndExit('button');
|
|
3591
4183
|
// Try to close window; if it fails (browser security), show completion message
|
|
@@ -4042,8 +4634,9 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
4042
4634
|
}
|
|
4043
4635
|
|
|
4044
4636
|
if (imageOverlay) {
|
|
4637
|
+
// Close on any click (including image itself)
|
|
4045
4638
|
imageOverlay.addEventListener('click', (e) => {
|
|
4046
|
-
|
|
4639
|
+
closeImageOverlay();
|
|
4047
4640
|
});
|
|
4048
4641
|
}
|
|
4049
4642
|
|
|
@@ -4058,7 +4651,8 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
4058
4651
|
img.title = 'Click to view fullscreen';
|
|
4059
4652
|
|
|
4060
4653
|
img.addEventListener('click', (e) => {
|
|
4061
|
-
|
|
4654
|
+
// Don't stop propagation - allow select to work
|
|
4655
|
+
e.preventDefault();
|
|
4062
4656
|
|
|
4063
4657
|
imageContainer.innerHTML = '';
|
|
4064
4658
|
const clonedImg = img.cloneNode(true);
|
|
@@ -4103,8 +4697,9 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
4103
4697
|
}
|
|
4104
4698
|
|
|
4105
4699
|
if (videoOverlay) {
|
|
4700
|
+
// Close on any click (including video itself)
|
|
4106
4701
|
videoOverlay.addEventListener('click', (e) => {
|
|
4107
|
-
|
|
4702
|
+
closeVideoOverlay();
|
|
4108
4703
|
});
|
|
4109
4704
|
}
|
|
4110
4705
|
|
|
@@ -4123,7 +4718,7 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
4123
4718
|
|
|
4124
4719
|
link.addEventListener('click', (e) => {
|
|
4125
4720
|
e.preventDefault();
|
|
4126
|
-
|
|
4721
|
+
// Don't stop propagation - allow select to work
|
|
4127
4722
|
|
|
4128
4723
|
// Remove existing video if any
|
|
4129
4724
|
const existingVideo = videoContainer.querySelector('video');
|
|
@@ -4347,15 +4942,11 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
4347
4942
|
|
|
4348
4943
|
// Click on block elements
|
|
4349
4944
|
preview.addEventListener('click', (e) => {
|
|
4350
|
-
// Handle image clicks
|
|
4945
|
+
// Handle image clicks - always select, even if modal is showing
|
|
4351
4946
|
if (e.target.tagName === 'IMG') {
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
e.preventDefault();
|
|
4356
|
-
e.stopPropagation();
|
|
4357
|
-
selectSourceRange(line);
|
|
4358
|
-
}
|
|
4947
|
+
const line = findImageSourceLine(e.target.src);
|
|
4948
|
+
if (line > 0) {
|
|
4949
|
+
selectSourceRange(line);
|
|
4359
4950
|
}
|
|
4360
4951
|
return;
|
|
4361
4952
|
}
|
|
@@ -4425,6 +5016,227 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
4425
5016
|
}, 10);
|
|
4426
5017
|
});
|
|
4427
5018
|
})();
|
|
5019
|
+
|
|
5020
|
+
// --- Reviw Questions Modal ---
|
|
5021
|
+
(function initReviwQuestions() {
|
|
5022
|
+
if (MODE !== 'markdown') return;
|
|
5023
|
+
if (!REVIW_QUESTIONS || REVIW_QUESTIONS.length === 0) return;
|
|
5024
|
+
|
|
5025
|
+
const overlay = document.getElementById('reviw-questions-overlay');
|
|
5026
|
+
const modal = document.getElementById('reviw-questions-modal');
|
|
5027
|
+
const body = document.getElementById('reviw-questions-body');
|
|
5028
|
+
const closeBtn = document.getElementById('reviw-questions-close');
|
|
5029
|
+
const laterBtn = document.getElementById('reviw-questions-later');
|
|
5030
|
+
const countSpan = document.getElementById('reviw-questions-count');
|
|
5031
|
+
const bar = document.getElementById('reviw-questions-bar');
|
|
5032
|
+
const barMessage = document.getElementById('reviw-questions-bar-message');
|
|
5033
|
+
const barCount = document.getElementById('reviw-questions-bar-count');
|
|
5034
|
+
const barOpenBtn = document.getElementById('reviw-questions-bar-open');
|
|
5035
|
+
|
|
5036
|
+
if (!overlay || !modal || !body) return;
|
|
5037
|
+
|
|
5038
|
+
// Store answers locally
|
|
5039
|
+
const answers = {};
|
|
5040
|
+
REVIW_QUESTIONS.forEach(q => {
|
|
5041
|
+
answers[q.id] = { selected: '', text: '' };
|
|
5042
|
+
});
|
|
5043
|
+
|
|
5044
|
+
// Count unresolved questions
|
|
5045
|
+
const unresolvedQuestions = REVIW_QUESTIONS.filter(q => !q.resolved);
|
|
5046
|
+
const resolvedQuestions = REVIW_QUESTIONS.filter(q => q.resolved);
|
|
5047
|
+
|
|
5048
|
+
function getUnansweredCount() {
|
|
5049
|
+
// Count questions that have no answer (no selection and no text)
|
|
5050
|
+
return unresolvedQuestions.filter(q => {
|
|
5051
|
+
const a = answers[q.id];
|
|
5052
|
+
return !a.selected && !a.text.trim();
|
|
5053
|
+
}).length;
|
|
5054
|
+
}
|
|
5055
|
+
|
|
5056
|
+
function updateCounts() {
|
|
5057
|
+
const unansweredCount = getUnansweredCount();
|
|
5058
|
+
if (unansweredCount > 0) {
|
|
5059
|
+
countSpan.textContent = '(' + unansweredCount + '\u4ef6\u672a\u56de\u7b54)';
|
|
5060
|
+
barMessage.innerHTML = '\ud83d\udccb \u672a\u56de\u7b54\u306e\u8cea\u554f\u304c<span id="reviw-questions-bar-count">' + unansweredCount + '</span>\u4ef6\u3042\u308a\u307e\u3059';
|
|
5061
|
+
laterBtn.textContent = '\u5f8c\u3067\u56de\u7b54\u3059\u308b';
|
|
5062
|
+
} else {
|
|
5063
|
+
countSpan.textContent = '(\u5168\u3066\u56de\u7b54\u6e08\u307f)';
|
|
5064
|
+
barMessage.innerHTML = '\u2705 \u5168\u3066\u306e\u8cea\u554f\u306b\u56de\u7b54\u3057\u307e\u3057\u305f';
|
|
5065
|
+
laterBtn.textContent = '\u9589\u3058\u308b';
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5068
|
+
|
|
5069
|
+
function checkAllAnswered() {
|
|
5070
|
+
if (getUnansweredCount() === 0) {
|
|
5071
|
+
// All answered - close modal but keep bar visible
|
|
5072
|
+
setTimeout(() => {
|
|
5073
|
+
overlay.classList.remove('visible');
|
|
5074
|
+
// Keep bar visible with different message
|
|
5075
|
+
bar.classList.add('visible');
|
|
5076
|
+
document.body.classList.add('has-questions-bar');
|
|
5077
|
+
}, 500);
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
|
|
5081
|
+
function renderQuestions() {
|
|
5082
|
+
body.innerHTML = '';
|
|
5083
|
+
|
|
5084
|
+
// Render unresolved questions
|
|
5085
|
+
unresolvedQuestions.forEach((q, idx) => {
|
|
5086
|
+
const item = document.createElement('div');
|
|
5087
|
+
item.className = 'reviw-question-item';
|
|
5088
|
+
item.dataset.id = q.id;
|
|
5089
|
+
|
|
5090
|
+
let optionsHtml = '';
|
|
5091
|
+
if (q.options && q.options.length > 0) {
|
|
5092
|
+
optionsHtml = '<div class="reviw-question-options">' +
|
|
5093
|
+
q.options.map(opt =>
|
|
5094
|
+
'<button class="reviw-question-option" data-value="' + escapeAttr(opt) + '">' + escapeHtml(opt) + '</button>'
|
|
5095
|
+
).join('') +
|
|
5096
|
+
'</div>';
|
|
5097
|
+
}
|
|
5098
|
+
|
|
5099
|
+
const isOkOnly = q.options && q.options.length === 1 && q.options[0] === 'OK';
|
|
5100
|
+
|
|
5101
|
+
item.innerHTML =
|
|
5102
|
+
'<div class="reviw-question-text">Q' + (idx + 1) + '. ' + escapeHtml(q.question) + '<span class="reviw-check-mark"></span></div>' +
|
|
5103
|
+
optionsHtml +
|
|
5104
|
+
(isOkOnly ? '' : '<textarea class="reviw-question-input" placeholder="\u30c6\u30ad\u30b9\u30c8\u3067\u56de\u7b54\u30fb\u88dc\u8db3..."></textarea>');
|
|
5105
|
+
|
|
5106
|
+
body.appendChild(item);
|
|
5107
|
+
|
|
5108
|
+
// Check mark element
|
|
5109
|
+
const checkMark = item.querySelector('.reviw-check-mark');
|
|
5110
|
+
|
|
5111
|
+
function updateCheckMark() {
|
|
5112
|
+
const answer = answers[q.id];
|
|
5113
|
+
const hasAnswer = answer.selected || answer.text.trim();
|
|
5114
|
+
if (hasAnswer) {
|
|
5115
|
+
checkMark.textContent = ' \u2713';
|
|
5116
|
+
item.classList.add('answered');
|
|
5117
|
+
} else {
|
|
5118
|
+
checkMark.textContent = '';
|
|
5119
|
+
item.classList.remove('answered');
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
5122
|
+
|
|
5123
|
+
// Option click handlers - always toggle
|
|
5124
|
+
const optionBtns = item.querySelectorAll('.reviw-question-option');
|
|
5125
|
+
optionBtns.forEach(btn => {
|
|
5126
|
+
btn.addEventListener('click', () => {
|
|
5127
|
+
const wasSelected = btn.classList.contains('selected');
|
|
5128
|
+
optionBtns.forEach(b => b.classList.remove('selected'));
|
|
5129
|
+
if (!wasSelected) {
|
|
5130
|
+
btn.classList.add('selected');
|
|
5131
|
+
answers[q.id].selected = btn.dataset.value;
|
|
5132
|
+
} else {
|
|
5133
|
+
answers[q.id].selected = '';
|
|
5134
|
+
}
|
|
5135
|
+
updateCheckMark();
|
|
5136
|
+
updateCounts();
|
|
5137
|
+
checkAllAnswered();
|
|
5138
|
+
});
|
|
5139
|
+
});
|
|
5140
|
+
|
|
5141
|
+
// Text input handler
|
|
5142
|
+
const textarea = item.querySelector('.reviw-question-input');
|
|
5143
|
+
if (textarea) {
|
|
5144
|
+
textarea.addEventListener('input', () => {
|
|
5145
|
+
answers[q.id].text = textarea.value;
|
|
5146
|
+
updateCheckMark();
|
|
5147
|
+
updateCounts();
|
|
5148
|
+
checkAllAnswered();
|
|
5149
|
+
});
|
|
5150
|
+
}
|
|
5151
|
+
|
|
5152
|
+
updateCheckMark();
|
|
5153
|
+
});
|
|
5154
|
+
|
|
5155
|
+
// Render resolved questions (collapsed)
|
|
5156
|
+
if (resolvedQuestions.length > 0) {
|
|
5157
|
+
const section = document.createElement('div');
|
|
5158
|
+
section.className = 'reviw-resolved-section';
|
|
5159
|
+
section.innerHTML =
|
|
5160
|
+
'<button class="reviw-resolved-toggle">' +
|
|
5161
|
+
'<span class="arrow">\u25b6</span> \u89e3\u6c7a\u6e08\u307f (' + resolvedQuestions.length + '\u4ef6)' +
|
|
5162
|
+
'</button>' +
|
|
5163
|
+
'<div class="reviw-resolved-list">' +
|
|
5164
|
+
resolvedQuestions.map(q =>
|
|
5165
|
+
'<div class="reviw-resolved-item">' +
|
|
5166
|
+
'<div class="reviw-resolved-q">' + escapeHtml(q.question) + '</div>' +
|
|
5167
|
+
'<div class="reviw-resolved-a">\u2192 ' + escapeHtml(q.answer || '(no answer)') + '</div>' +
|
|
5168
|
+
'</div>'
|
|
5169
|
+
).join('') +
|
|
5170
|
+
'</div>';
|
|
5171
|
+
body.appendChild(section);
|
|
5172
|
+
|
|
5173
|
+
const toggle = section.querySelector('.reviw-resolved-toggle');
|
|
5174
|
+
const list = section.querySelector('.reviw-resolved-list');
|
|
5175
|
+
toggle.addEventListener('click', () => {
|
|
5176
|
+
toggle.classList.toggle('open');
|
|
5177
|
+
list.classList.toggle('visible');
|
|
5178
|
+
});
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
|
|
5182
|
+
function escapeHtml(str) {
|
|
5183
|
+
return String(str)
|
|
5184
|
+
.replace(/&/g, '&')
|
|
5185
|
+
.replace(/</g, '<')
|
|
5186
|
+
.replace(/>/g, '>');
|
|
5187
|
+
}
|
|
5188
|
+
|
|
5189
|
+
function escapeAttr(str) {
|
|
5190
|
+
return String(str)
|
|
5191
|
+
.replace(/&/g, '&')
|
|
5192
|
+
.replace(/"/g, '"');
|
|
5193
|
+
}
|
|
5194
|
+
|
|
5195
|
+
function openModal() {
|
|
5196
|
+
overlay.classList.add('visible');
|
|
5197
|
+
bar.classList.remove('visible');
|
|
5198
|
+
document.body.classList.remove('has-questions-bar');
|
|
5199
|
+
}
|
|
5200
|
+
|
|
5201
|
+
function closeModal(allAnswered) {
|
|
5202
|
+
overlay.classList.remove('visible');
|
|
5203
|
+
const unansweredCount = getUnansweredCount();
|
|
5204
|
+
if (unansweredCount > 0 && !allAnswered) {
|
|
5205
|
+
bar.classList.add('visible');
|
|
5206
|
+
document.body.classList.add('has-questions-bar');
|
|
5207
|
+
} else {
|
|
5208
|
+
bar.classList.remove('visible');
|
|
5209
|
+
document.body.classList.remove('has-questions-bar');
|
|
5210
|
+
}
|
|
5211
|
+
}
|
|
5212
|
+
|
|
5213
|
+
// Event listeners
|
|
5214
|
+
closeBtn.addEventListener('click', () => closeModal(false));
|
|
5215
|
+
laterBtn.addEventListener('click', () => closeModal(false));
|
|
5216
|
+
barOpenBtn.addEventListener('click', openModal);
|
|
5217
|
+
|
|
5218
|
+
overlay.addEventListener('click', (e) => {
|
|
5219
|
+
if (e.target === overlay) closeModal(false);
|
|
5220
|
+
});
|
|
5221
|
+
|
|
5222
|
+
document.addEventListener('keydown', (e) => {
|
|
5223
|
+
if (e.key === 'Escape' && overlay.classList.contains('visible')) {
|
|
5224
|
+
closeModal(false);
|
|
5225
|
+
}
|
|
5226
|
+
});
|
|
5227
|
+
|
|
5228
|
+
// Expose answers for submit (only answered ones)
|
|
5229
|
+
window.REVIW_ANSWERS = answers;
|
|
5230
|
+
|
|
5231
|
+
// Initialize
|
|
5232
|
+
updateCounts();
|
|
5233
|
+
renderQuestions();
|
|
5234
|
+
|
|
5235
|
+
// Show modal on load if there are unresolved questions
|
|
5236
|
+
if (unresolvedQuestions.length > 0) {
|
|
5237
|
+
setTimeout(() => openModal(), 300);
|
|
5238
|
+
}
|
|
5239
|
+
})();
|
|
4428
5240
|
</script>
|
|
4429
5241
|
</body>
|
|
4430
5242
|
</html>`;
|
|
@@ -4435,8 +5247,8 @@ function buildHtml(filePath) {
|
|
|
4435
5247
|
if (data.mode === "diff") {
|
|
4436
5248
|
return diffHtmlTemplate(data);
|
|
4437
5249
|
}
|
|
4438
|
-
const { rows, cols,
|
|
4439
|
-
return htmlTemplate(rows, cols,
|
|
5250
|
+
const { rows, cols, projectRoot, relativePath, mode, preview, reviwQuestions } = data;
|
|
5251
|
+
return htmlTemplate(rows, cols, projectRoot, relativePath, mode, preview, reviwQuestions);
|
|
4440
5252
|
}
|
|
4441
5253
|
|
|
4442
5254
|
// --- HTTP Server -----------------------------------------------------------
|
|
@@ -4468,6 +5280,20 @@ function outputAllResults() {
|
|
|
4468
5280
|
const yamlOut = yaml.dump(combined, { noRefs: true, lineWidth: 120 });
|
|
4469
5281
|
console.log(yamlOut.trim());
|
|
4470
5282
|
}
|
|
5283
|
+
|
|
5284
|
+
// Output answered questions if any
|
|
5285
|
+
const allAnswers = [];
|
|
5286
|
+
for (const result of allResults) {
|
|
5287
|
+
if (result.reviwAnswers && result.reviwAnswers.length > 0) {
|
|
5288
|
+
allAnswers.push(...result.reviwAnswers);
|
|
5289
|
+
}
|
|
5290
|
+
}
|
|
5291
|
+
if (allAnswers.length > 0) {
|
|
5292
|
+
console.log("\n[REVIW_ANSWERS]");
|
|
5293
|
+
const answersYaml = yaml.dump(allAnswers, { noRefs: true, lineWidth: 120 });
|
|
5294
|
+
console.log(answersYaml.trim());
|
|
5295
|
+
console.log("[/REVIW_ANSWERS]");
|
|
5296
|
+
}
|
|
4471
5297
|
}
|
|
4472
5298
|
|
|
4473
5299
|
function checkAllDone() {
|
|
@@ -4714,7 +5540,14 @@ function createFileServer(filePath, fileIndex = 0) {
|
|
|
4714
5540
|
const delay = fileIndex * 300;
|
|
4715
5541
|
setTimeout(() => {
|
|
4716
5542
|
try {
|
|
4717
|
-
spawn(opener, [url], { stdio: "ignore", detached: true });
|
|
5543
|
+
const child = spawn(opener, [url], { stdio: "ignore", detached: true });
|
|
5544
|
+
child.on('error', (err) => {
|
|
5545
|
+
console.warn(
|
|
5546
|
+
"Failed to open browser automatically. Please open this URL manually:",
|
|
5547
|
+
url,
|
|
5548
|
+
);
|
|
5549
|
+
});
|
|
5550
|
+
child.unref();
|
|
4718
5551
|
} catch (err) {
|
|
4719
5552
|
console.warn(
|
|
4720
5553
|
"Failed to open browser automatically. Please open this URL manually:",
|
|
@@ -4882,7 +5715,14 @@ function createDiffServer(diffContent) {
|
|
|
4882
5715
|
? "start"
|
|
4883
5716
|
: "xdg-open";
|
|
4884
5717
|
try {
|
|
4885
|
-
spawn(opener, [url], { stdio: "ignore", detached: true });
|
|
5718
|
+
const child = spawn(opener, [url], { stdio: "ignore", detached: true });
|
|
5719
|
+
child.on('error', (err) => {
|
|
5720
|
+
console.warn(
|
|
5721
|
+
"Failed to open browser automatically. Please open this URL manually:",
|
|
5722
|
+
url,
|
|
5723
|
+
);
|
|
5724
|
+
});
|
|
5725
|
+
child.unref();
|
|
4886
5726
|
} catch (err) {
|
|
4887
5727
|
console.warn(
|
|
4888
5728
|
"Failed to open browser automatically. Please open this URL manually:",
|