reviw 0.22.1 → 0.23.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/cli.cjs +192 -19
- package/package.json +1 -1
package/cli.cjs
CHANGED
|
@@ -2591,7 +2591,7 @@ function diffHtmlTemplate(diffData, history = []) {
|
|
|
2591
2591
|
document.getElementById('modal-cancel').addEventListener('click', hideSubmitModal);
|
|
2592
2592
|
async function doSubmit() {
|
|
2593
2593
|
globalComment = globalCommentInput.value;
|
|
2594
|
-
savePromptPrefs();
|
|
2594
|
+
if (typeof savePromptPrefs === 'function') savePromptPrefs();
|
|
2595
2595
|
hideSubmitModal();
|
|
2596
2596
|
await sendAndExit('button');
|
|
2597
2597
|
// Try to close window; if it fails (browser security), show completion message
|
|
@@ -2956,6 +2956,7 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
2956
2956
|
td:hover:not(.selected) { background: var(--hover-bg); box-shadow: inset 0 0 0 1px rgba(96,165,250,0.25); }
|
|
2957
2957
|
td.has-comment { background: rgba(34,197,94,0.12); box-shadow: inset 0 0 0 1px rgba(34,197,94,0.35); }
|
|
2958
2958
|
td.selected, tbody th.selected { background: rgba(99,102,241,0.22) !important; box-shadow: inset 0 0 0 1px rgba(99,102,241,0.45); }
|
|
2959
|
+
.preview-highlight { background: rgba(167,139,250,0.18) !important; box-shadow: inset 0 0 0 2px rgba(139,92,246,0.35); border-radius: 4px; transition: background 150ms ease, box-shadow 150ms ease; padding-left: 8px; margin-left: -8px; }
|
|
2959
2960
|
thead th.selected { background: #c7d2fe !important; box-shadow: inset 0 0 0 1px rgba(99,102,241,0.45); }
|
|
2960
2961
|
[data-theme="dark"] thead th.selected { background: #3730a3 !important; }
|
|
2961
2962
|
body.dragging { user-select: none; cursor: crosshair; }
|
|
@@ -3525,6 +3526,18 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
3525
3526
|
border-radius: 8px;
|
|
3526
3527
|
line-height: 1.4;
|
|
3527
3528
|
}
|
|
3529
|
+
.view-toggle {
|
|
3530
|
+
background: var(--panel);
|
|
3531
|
+
border: 1px solid var(--border);
|
|
3532
|
+
border-radius: 8px;
|
|
3533
|
+
padding: 6px 10px;
|
|
3534
|
+
cursor: pointer;
|
|
3535
|
+
color: var(--text);
|
|
3536
|
+
font-size: 14px;
|
|
3537
|
+
transition: background 0.15s ease, border-color 0.15s ease;
|
|
3538
|
+
}
|
|
3539
|
+
.view-toggle:hover { background: rgba(96,165,250,0.2); }
|
|
3540
|
+
.view-toggle.active { background: var(--accent); color: #fff; }
|
|
3528
3541
|
@media (max-width: 1200px) {
|
|
3529
3542
|
.media-sidebar-viewer.open {
|
|
3530
3543
|
width: 40vw;
|
|
@@ -4431,10 +4444,13 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
4431
4444
|
}
|
|
4432
4445
|
@media (max-width: 960px) {
|
|
4433
4446
|
.md-layout { flex-direction: column; }
|
|
4434
|
-
.md-left { max-width: 100%; flex: 0 0
|
|
4447
|
+
.md-left { max-width: 100%; flex: 1 1 0; min-height: 0; }
|
|
4448
|
+
.md-right { display: none; }
|
|
4435
4449
|
.media-sidebar { display: none; }
|
|
4436
4450
|
.media-sidebar-toggle { display: none !important; }
|
|
4437
4451
|
}
|
|
4452
|
+
.md-layout.preview-only .md-right { display: none; }
|
|
4453
|
+
.md-layout.preview-only .md-left { flex: 1 1 0; min-height: 0; max-width: 100%; }
|
|
4438
4454
|
.filter-menu {
|
|
4439
4455
|
position: absolute;
|
|
4440
4456
|
background: var(--panel-solid);
|
|
@@ -4867,6 +4883,7 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
4867
4883
|
</div>
|
|
4868
4884
|
<div class="actions">
|
|
4869
4885
|
<button class="media-sidebar-toggle" id="media-sidebar-toggle" title="Media Gallery" aria-label="Toggle media gallery">🖼<span class="toggle-count" id="media-toggle-count"></span></button>
|
|
4886
|
+
<button class="view-toggle" id="view-toggle" title="Hide source panel" aria-label="Toggle source panel">📝</button>
|
|
4870
4887
|
<button class="history-toggle" id="history-toggle" title="Review History">☰</button>
|
|
4871
4888
|
<button class="theme-toggle" id="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
|
4872
4889
|
<span id="theme-icon">🌙</span>
|
|
@@ -6133,10 +6150,17 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
6133
6150
|
commentInput.value = existingComment?.text || '';
|
|
6134
6151
|
|
|
6135
6152
|
card.style.display = 'block';
|
|
6136
|
-
//
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6153
|
+
// Check if source panel is hidden (preview-only mode or narrow viewport)
|
|
6154
|
+
const mdRight = document.querySelector('.md-right');
|
|
6155
|
+
const isSourceHidden = mdRight && (mdRight.offsetParent === null || getComputedStyle(mdRight).display === 'none');
|
|
6156
|
+
|
|
6157
|
+
if (isSourceHidden && previewElement) {
|
|
6158
|
+
// In preview-only mode, position card below the clicked preview element
|
|
6159
|
+
positionCardBelowElement(previewElement);
|
|
6160
|
+
} else {
|
|
6161
|
+
// 常にソーステーブルの選択セル位置を基準にカードを配置
|
|
6162
|
+
positionCardForSelection(startRow, endRow, startCol, endCol);
|
|
6163
|
+
}
|
|
6140
6164
|
commentInput.focus();
|
|
6141
6165
|
}
|
|
6142
6166
|
|
|
@@ -6177,6 +6201,61 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
6177
6201
|
card.style.top = top + 'px';
|
|
6178
6202
|
}
|
|
6179
6203
|
|
|
6204
|
+
// Position card below a clicked element (used in preview-only / narrow mode)
|
|
6205
|
+
// Falls back to above the element if below doesn't fit
|
|
6206
|
+
function positionCardBelowElement(element) {
|
|
6207
|
+
const cardWidth = card.offsetWidth || 380;
|
|
6208
|
+
const cardHeight = card.offsetHeight || 220;
|
|
6209
|
+
const margin = 12;
|
|
6210
|
+
const vw = window.innerWidth;
|
|
6211
|
+
const vh = window.innerHeight;
|
|
6212
|
+
|
|
6213
|
+
const mdLeft = document.querySelector('.md-left');
|
|
6214
|
+
let rect = element.getBoundingClientRect();
|
|
6215
|
+
|
|
6216
|
+
// Try below first: scroll to make room if needed
|
|
6217
|
+
const spaceBelow = vh - rect.bottom - margin;
|
|
6218
|
+
if (spaceBelow < cardHeight && mdLeft) {
|
|
6219
|
+
const scrollNeeded = cardHeight - spaceBelow + margin;
|
|
6220
|
+
const scrollBefore = mdLeft.scrollTop;
|
|
6221
|
+
mdLeft.scrollBy({ top: scrollNeeded, behavior: 'instant' });
|
|
6222
|
+
// Recalculate after scroll
|
|
6223
|
+
rect = element.getBoundingClientRect();
|
|
6224
|
+
}
|
|
6225
|
+
|
|
6226
|
+
// Horizontal: ensure card fits
|
|
6227
|
+
let left = rect.left;
|
|
6228
|
+
if (left + cardWidth > vw - margin) {
|
|
6229
|
+
left = vw - cardWidth - margin;
|
|
6230
|
+
}
|
|
6231
|
+
left = Math.max(margin, left);
|
|
6232
|
+
|
|
6233
|
+
// Vertical: prefer below, fallback to above
|
|
6234
|
+
const spaceBelowAfterScroll = vh - rect.bottom - margin;
|
|
6235
|
+
const spaceAbove = rect.top - margin;
|
|
6236
|
+
let top;
|
|
6237
|
+
|
|
6238
|
+
if (spaceBelowAfterScroll >= cardHeight) {
|
|
6239
|
+
// Fits below
|
|
6240
|
+
top = rect.bottom + margin;
|
|
6241
|
+
} else if (spaceAbove >= cardHeight) {
|
|
6242
|
+
// Doesn't fit below, but fits above
|
|
6243
|
+
top = rect.top - cardHeight - margin;
|
|
6244
|
+
} else {
|
|
6245
|
+
// Doesn't fit either way: choose the side with more space
|
|
6246
|
+
if (spaceAbove > spaceBelowAfterScroll) {
|
|
6247
|
+
top = Math.max(margin, rect.top - cardHeight - margin);
|
|
6248
|
+
} else {
|
|
6249
|
+
top = rect.bottom + margin;
|
|
6250
|
+
top = Math.min(top, vh - cardHeight - margin);
|
|
6251
|
+
}
|
|
6252
|
+
}
|
|
6253
|
+
|
|
6254
|
+
top = Math.max(margin, top);
|
|
6255
|
+
card.style.left = left + 'px';
|
|
6256
|
+
card.style.top = top + 'px';
|
|
6257
|
+
}
|
|
6258
|
+
|
|
6180
6259
|
function positionCardForSelection(startRow, endRow, startCol, endCol) {
|
|
6181
6260
|
const cardWidth = card.offsetWidth || 380;
|
|
6182
6261
|
const cardHeight = card.offsetHeight || 220;
|
|
@@ -6255,6 +6334,8 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
6255
6334
|
card.style.display = 'none';
|
|
6256
6335
|
currentKey = null;
|
|
6257
6336
|
clearSelection();
|
|
6337
|
+
// Clear preview highlight
|
|
6338
|
+
document.querySelectorAll('.preview-highlight').forEach(el => el.classList.remove('preview-highlight'));
|
|
6258
6339
|
// Re-enable scroll sync when card is closed
|
|
6259
6340
|
window._disableScrollSync = false;
|
|
6260
6341
|
}
|
|
@@ -7145,6 +7226,52 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
7145
7226
|
}
|
|
7146
7227
|
}
|
|
7147
7228
|
|
|
7229
|
+
// --- Panel State (Preview-Only Toggle) ---
|
|
7230
|
+
const PANEL_STATE_KEY = 'reviw-panel-state';
|
|
7231
|
+
(function initPanelState() {
|
|
7232
|
+
const viewToggle = document.getElementById('view-toggle');
|
|
7233
|
+
const mdLayout = document.querySelector('.md-layout');
|
|
7234
|
+
if (!viewToggle || !mdLayout) return;
|
|
7235
|
+
|
|
7236
|
+
function applyPanelState(isPreviewOnly) {
|
|
7237
|
+
if (isPreviewOnly) {
|
|
7238
|
+
mdLayout.classList.add('preview-only');
|
|
7239
|
+
viewToggle.textContent = '\u{1F441}';
|
|
7240
|
+
viewToggle.title = 'Show source panel';
|
|
7241
|
+
viewToggle.classList.add('active');
|
|
7242
|
+
} else {
|
|
7243
|
+
mdLayout.classList.remove('preview-only');
|
|
7244
|
+
viewToggle.textContent = '\u{1F4DD}';
|
|
7245
|
+
viewToggle.title = 'Hide source panel';
|
|
7246
|
+
viewToggle.classList.remove('active');
|
|
7247
|
+
}
|
|
7248
|
+
}
|
|
7249
|
+
|
|
7250
|
+
// Load saved state
|
|
7251
|
+
const saved = localStorage.getItem(PANEL_STATE_KEY);
|
|
7252
|
+
if (saved === 'preview-only') {
|
|
7253
|
+
applyPanelState(true);
|
|
7254
|
+
}
|
|
7255
|
+
|
|
7256
|
+
// Also apply preview-only for narrow viewports (auto-detect)
|
|
7257
|
+
function checkNarrowMode() {
|
|
7258
|
+
if (window.innerWidth <= 960) {
|
|
7259
|
+
// In narrow mode, always act as preview-only (source is hidden by CSS)
|
|
7260
|
+
viewToggle.style.display = 'none';
|
|
7261
|
+
} else {
|
|
7262
|
+
viewToggle.style.display = '';
|
|
7263
|
+
}
|
|
7264
|
+
}
|
|
7265
|
+
checkNarrowMode();
|
|
7266
|
+
window.addEventListener('resize', checkNarrowMode);
|
|
7267
|
+
|
|
7268
|
+
viewToggle.addEventListener('click', () => {
|
|
7269
|
+
const isNowPreviewOnly = !mdLayout.classList.contains('preview-only');
|
|
7270
|
+
applyPanelState(isNowPreviewOnly);
|
|
7271
|
+
localStorage.setItem(PANEL_STATE_KEY, isNowPreviewOnly ? 'preview-only' : 'both');
|
|
7272
|
+
});
|
|
7273
|
+
})();
|
|
7274
|
+
|
|
7148
7275
|
// --- Mermaid Initialization ---
|
|
7149
7276
|
(function initMermaid() {
|
|
7150
7277
|
if (typeof mermaid === 'undefined') return;
|
|
@@ -8867,13 +8994,43 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
8867
8994
|
}
|
|
8868
8995
|
|
|
8869
8996
|
// Helper: find matching source line for table cell (prioritizes table rows)
|
|
8870
|
-
function findTableSourceLine(text, startFromLine) {
|
|
8997
|
+
function findTableSourceLine(text, startFromLine, element = null) {
|
|
8871
8998
|
if (!text) return -1;
|
|
8872
8999
|
startFromLine = startFromLine || 0;
|
|
8873
9000
|
// Remove toggle icon characters (▼, ▶) that may be included from heading toggles
|
|
8874
9001
|
const cleanText = text.replace(/[▼▶]/g, '').trim();
|
|
8875
9002
|
const normalized = cleanText.replace(/\\s+/g, ' ').slice(0, 100);
|
|
8876
9003
|
if (!normalized) return -1;
|
|
9004
|
+
const normalizedLower = normalized.toLowerCase();
|
|
9005
|
+
|
|
9006
|
+
function escapeRegExp(s) {
|
|
9007
|
+
return s.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
|
|
9008
|
+
}
|
|
9009
|
+
|
|
9010
|
+
function normalizeCellText(cellText) {
|
|
9011
|
+
return stripMarkdown(cellText)
|
|
9012
|
+
.replace(/\\s+/g, ' ')
|
|
9013
|
+
.trim()
|
|
9014
|
+
.slice(0, 100);
|
|
9015
|
+
}
|
|
9016
|
+
|
|
9017
|
+
// If this click comes from a linked table cell, prefer matching by href for stable mapping.
|
|
9018
|
+
if (element) {
|
|
9019
|
+
const linkEl = element.querySelector('a[href]') || (element.matches?.('a[href]') ? element : null);
|
|
9020
|
+
const href = linkEl ? linkEl.getAttribute('href') : '';
|
|
9021
|
+
if (href) {
|
|
9022
|
+
var hrefSearchPasses = startFromLine > 0 ? [startFromLine, 0] : [0];
|
|
9023
|
+
for (var hrefPass = 0; hrefPass < hrefSearchPasses.length; hrefPass++) {
|
|
9024
|
+
var hrefSearchStart = hrefSearchPasses[hrefPass];
|
|
9025
|
+
var hrefSearchEnd = hrefPass === 0 && startFromLine > 0 ? DATA.length : (startFromLine > 0 ? startFromLine : DATA.length);
|
|
9026
|
+
for (let i = hrefSearchStart; i < hrefSearchEnd; i++) {
|
|
9027
|
+
const lineText = (DATA[i][0] || '').trim();
|
|
9028
|
+
if (!lineText || !lineText.startsWith('|')) continue;
|
|
9029
|
+
if (lineText.includes(href)) return i + 1;
|
|
9030
|
+
}
|
|
9031
|
+
}
|
|
9032
|
+
}
|
|
9033
|
+
}
|
|
8877
9034
|
|
|
8878
9035
|
// Two-pass strategy: search from startFromLine first, then fallback to 0
|
|
8879
9036
|
var searchPasses = startFromLine > 0 ? [startFromLine, 0] : [0];
|
|
@@ -8886,27 +9043,37 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
8886
9043
|
const lineText = (DATA[i][0] || '').trim();
|
|
8887
9044
|
if (!lineText || !lineText.startsWith('|')) continue;
|
|
8888
9045
|
|
|
8889
|
-
// Split into cells and check for exact match
|
|
9046
|
+
// Split into cells and check for exact match, including markdown link display text.
|
|
8890
9047
|
const cells = lineText.split('|').map(c => c.trim());
|
|
8891
9048
|
for (const cell of cells) {
|
|
8892
|
-
|
|
8893
|
-
|
|
9049
|
+
if (!cell) continue;
|
|
9050
|
+
const plainCell = normalizeCellText(cell);
|
|
8894
9051
|
|
|
8895
|
-
//
|
|
9052
|
+
// Exact raw-cell or rendered-cell text match.
|
|
8896
9053
|
if (cell === normalized) return i + 1;
|
|
9054
|
+
if (plainCell === normalized) return i + 1;
|
|
9055
|
+
if (plainCell.toLowerCase() === normalizedLower) return i + 1;
|
|
8897
9056
|
|
|
8898
|
-
// For short
|
|
8899
|
-
if (normalized.length <= 5 &&
|
|
9057
|
+
// For short labels (e.g. Go/OK), allow whole-word match in rendered cell.
|
|
9058
|
+
if (normalized.length <= 5 && plainCell) {
|
|
9059
|
+
const wordPattern = new RegExp('(^|\\s)' + escapeRegExp(normalizedLower) + '(\\s|$)', 'i');
|
|
9060
|
+
if (wordPattern.test(plainCell.toLowerCase())) return i + 1;
|
|
9061
|
+
}
|
|
8900
9062
|
}
|
|
8901
9063
|
}
|
|
8902
9064
|
|
|
8903
|
-
// Second pass:
|
|
9065
|
+
// Second pass: partial rendered-cell matching.
|
|
8904
9066
|
for (let i = searchStart; i < searchEnd; i++) {
|
|
8905
9067
|
const lineText = (DATA[i][0] || '').trim();
|
|
8906
9068
|
if (!lineText || !lineText.startsWith('|')) continue;
|
|
8907
9069
|
|
|
8908
|
-
const
|
|
8909
|
-
|
|
9070
|
+
const cells = lineText.split('|').map(c => normalizeCellText(c));
|
|
9071
|
+
for (const plainCell of cells) {
|
|
9072
|
+
if (!plainCell) continue;
|
|
9073
|
+
const plainLower = plainCell.toLowerCase();
|
|
9074
|
+
if (normalized.length > 5 && plainLower.includes(normalizedLower.slice(0, 30))) return i + 1;
|
|
9075
|
+
if (normalized.length > 5 && normalizedLower.includes(plainLower.slice(0, 30)) && plainLower.length > 5) return i + 1;
|
|
9076
|
+
}
|
|
8910
9077
|
}
|
|
8911
9078
|
}
|
|
8912
9079
|
|
|
@@ -9077,6 +9244,12 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
9077
9244
|
selection = { startRow, endRow: endRow || startRow, startCol: 1, endCol: 1 };
|
|
9078
9245
|
updateSelectionVisual();
|
|
9079
9246
|
|
|
9247
|
+
// Highlight the clicked preview element
|
|
9248
|
+
document.querySelectorAll('.preview-highlight').forEach(el => el.classList.remove('preview-highlight'));
|
|
9249
|
+
if (clickedPreviewElement) {
|
|
9250
|
+
clickedPreviewElement.classList.add('preview-highlight');
|
|
9251
|
+
}
|
|
9252
|
+
|
|
9080
9253
|
// Clear header selection
|
|
9081
9254
|
document.querySelectorAll('thead th.selected').forEach(el => el.classList.remove('selected'));
|
|
9082
9255
|
|
|
@@ -9097,7 +9270,7 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
9097
9270
|
}
|
|
9098
9271
|
|
|
9099
9272
|
// Open the card (synchronously) - now target cell should be visible for positioning
|
|
9100
|
-
openCardForSelection();
|
|
9273
|
+
openCardForSelection(clickedPreviewElement);
|
|
9101
9274
|
|
|
9102
9275
|
// Re-add scroll handlers after a delay to allow scroll to settle
|
|
9103
9276
|
setTimeout(() => {
|
|
@@ -9172,7 +9345,7 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
9172
9345
|
const isTableCell = parentBlock.tagName === 'TD' || parentBlock.tagName === 'TH';
|
|
9173
9346
|
const closestH = findClosestHeadingAbove(parentBlock);
|
|
9174
9347
|
const hLine = getHeadingSourceLine(closestH);
|
|
9175
|
-
const line = isTableCell ? findTableSourceLine(parentBlock.textContent, hLine) : findSourceLine(parentBlock.textContent, null, hLine);
|
|
9348
|
+
const line = isTableCell ? findTableSourceLine(parentBlock.textContent, hLine, parentBlock) : findSourceLine(parentBlock.textContent, null, hLine);
|
|
9176
9349
|
if (line > 0) {
|
|
9177
9350
|
selectSourceRange(line, null, parentBlock);
|
|
9178
9351
|
}
|
|
@@ -9233,7 +9406,7 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
9233
9406
|
|
|
9234
9407
|
// Use table-specific search for table cells, otherwise use element-aware search
|
|
9235
9408
|
const isTableCell = target.tagName === 'TD' || target.tagName === 'TH';
|
|
9236
|
-
const line = isTableCell ? findTableSourceLine(searchText, headingLine) : findSourceLine(searchText, target, headingLine);
|
|
9409
|
+
const line = isTableCell ? findTableSourceLine(searchText, headingLine, target) : findSourceLine(searchText, target, headingLine);
|
|
9237
9410
|
if (line <= 0) return;
|
|
9238
9411
|
|
|
9239
9412
|
// Don't prevent default for summary elements - let native <details> toggle work
|