Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9dcb60d
feat(annotation): add three-way merge core
AliceJump Oct 4, 2026
035653f
feat(annotation): merge external edits before save
AliceJump Oct 4, 2026
87e01c1
fix(annotation): keep conflicts pending for editor UI
AliceJump Oct 4, 2026
2adedaa
test(annotation): run merge regression suite
AliceJump Oct 4, 2026
5f4653d
fix(test): preserve VS Code type dependency
AliceJump Oct 4, 2026
61e6c60
fix(assets): preserve source cards across annotation refreshes
AliceJump Oct 4, 2026
5e27712
test(assets): match card-first annotation editing
AliceJump Oct 4, 2026
3c4e45a
feat(annotation): render conflict candidates in editor
AliceJump Oct 4, 2026
06e9f99
style(annotation): add conflict resolution panel
AliceJump Oct 4, 2026
cdd777e
feat(annotation): wire conflict overlay into editor
AliceJump Oct 4, 2026
f91392f
feat(annotation): resolve external conflicts without data loss
AliceJump Oct 4, 2026
f6eb580
test(assets): align swap picker with persistent source cards
AliceJump Oct 4, 2026
032e5bd
refactor(annotation): retire Generate Box UI
AliceJump Oct 4, 2026
7a1a6d3
fix(annotation): keep hidden Generate Box compatibility stubs
AliceJump Oct 4, 2026
5375b06
chore(jetbrains): test annotation merge parity head
AliceJump Oct 4, 2026
a6bfa89
chore(jetbrains): verify annotation merge compile fix
AliceJump Oct 4, 2026
29b7c01
chore(jetbrains): advance annotation sync submodule
AliceJump Oct 4, 2026
a0ed950
chore(jetbrains): sync external annotation conflict UI
AliceJump Oct 4, 2026
e60fb39
refactor(annotation): remove Generate Box backend
AliceJump Oct 4, 2026
6e466a8
chore(jetbrains): sync localized conflict UI
AliceJump Oct 4, 2026
f82ac8b
refactor(annotation): remove Generate Box webview flow
AliceJump Oct 5, 2026
bce7550
refactor(annotation): remove Generate Box markup
AliceJump Oct 5, 2026
7f1fdae
feat(annotation): report safe external reload points
AliceJump Oct 5, 2026
9b6e131
feat(annotation): load external sync bridge
AliceJump Oct 5, 2026
d287acd
feat(annotation): reload clean sessions on external changes
AliceJump Oct 5, 2026
91ff6e8
test(annotation): cover safe external reload handshake
AliceJump Oct 5, 2026
8b602b2
feat(annotation): add unified annotation editor strings
AliceJump Oct 5, 2026
2eb0707
feat(annotation): inject annotation-specific localization
AliceJump Oct 5, 2026
2aa6582
refactor(annotation): use injected conflict translations
AliceJump Oct 5, 2026
c503efe
feat(annotation): localize dynamic editor labels
AliceJump Oct 5, 2026
fb7e62b
refactor(annotation): parameterize localized tool labels
AliceJump Oct 5, 2026
5d18b9d
refactor(annotation): expose localization hooks
AliceJump Oct 5, 2026
f5ba35f
feat(annotation): localize editor runtime text
AliceJump Oct 5, 2026
24b24b5
fix(annotation): preserve coordinate resize direction
AliceJump Oct 5, 2026
e67e3d9
chore(jetbrains): update annotation UI submodule
AliceJump Oct 5, 2026
267cd0b
test(annotation): assert localized clipboard status
AliceJump Oct 5, 2026
8cc58c3
fix(annotation): address conflict review findings
AliceJump Oct 5, 2026
04487c0
chore(jetbrains): update reviewed annotation sync submodule
AliceJump Oct 5, 2026
a764cac
fix(annotation): bind conflicts to merged targets
AliceJump Oct 5, 2026
b09e141
fix(annotation): commit merge state only after save
AliceJump Oct 5, 2026
63bf814
chore(jetbrains): update reviewed conflict target fix
AliceJump Oct 5, 2026
41dce8a
fix(annotation): preserve local state during external sync
AliceJump Oct 5, 2026
8e6a6a1
fix(annotation): address final external sync review findings
AliceJump Oct 5, 2026
3443c70
fix(annotation): guard load before editor handlers
AliceJump Oct 5, 2026
f852d24
fix(annotation): version editor loads against pending saves
AliceJump Oct 5, 2026
a590568
fix(annotation): coordinate cross-panel reloads
AliceJump Oct 5, 2026
7d07615
test(annotation): guard coordinated cross-panel reload
AliceJump Oct 5, 2026
2efdd6e
fix(annotation): add revision-guarded atomic writes
AliceJump Oct 5, 2026
e1b4e43
fix(annotation): guard point writes by revision
AliceJump Oct 5, 2026
89a8530
fix(annotation): bind prepared saves to source revision
AliceJump Oct 5, 2026
9866cab
test(annotation): cover guarded point writes
AliceJump Oct 5, 2026
1745925
test(annotation): cover compare-and-write race
AliceJump Oct 5, 2026
27063ef
fix(annotation): preserve guarded write recovery
AliceJump Oct 5, 2026
af9c292
test(annotation): cover guarded write recovery
AliceJump Oct 5, 2026
756bdbb
fix(annotation): complete editor load handshake
AliceJump Oct 5, 2026
7faa728
fix(annotation): bind editor state to image loads
AliceJump Oct 5, 2026
06bb152
test(annotation): cover image-bound load handshake
AliceJump Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jetbrains
Submodule jetbrains updated 18 files
+20 −0 src/main/kotlin/com/alicejump/okscripttoolkit/AnnotationUiBundle.kt
+67 −0 src/main/kotlin/com/alicejump/okscripttoolkit/LocaleBundles.kt
+3 −54 src/main/kotlin/com/alicejump/okscripttoolkit/OkScriptToolkitBundle.kt
+235 −0 src/main/kotlin/com/alicejump/okscripttoolkit/core/AnnotationMerge.kt
+101 −0 src/main/kotlin/com/alicejump/okscripttoolkit/core/AnnotationSessionSync.kt
+0 −18 src/main/kotlin/com/alicejump/okscripttoolkit/core/BoxResource.kt
+181 −0 src/main/kotlin/com/alicejump/okscripttoolkit/ui/AnnotationConflictPanel.kt
+316 −60 src/main/kotlin/com/alicejump/okscripttoolkit/ui/UnifiedAnnotationUi.kt
+46 −0 src/main/resources/messages/OkScriptToolkitAnnotationBundle.properties
+46 −0 src/main/resources/messages/OkScriptToolkitAnnotationBundle_es.properties
+46 −0 src/main/resources/messages/OkScriptToolkitAnnotationBundle_ja.properties
+46 −0 src/main/resources/messages/OkScriptToolkitAnnotationBundle_ko.properties
+46 −0 src/main/resources/messages/OkScriptToolkitAnnotationBundle_zh_CN.properties
+46 −0 src/main/resources/messages/OkScriptToolkitAnnotationBundle_zh_TW.properties
+25 −0 src/test/kotlin/com/alicejump/okscripttoolkit/LocaleBundlesTest.kt
+182 −0 src/test/kotlin/com/alicejump/okscripttoolkit/core/AnnotationMergeTest.kt
+144 −0 src/test/kotlin/com/alicejump/okscripttoolkit/core/AnnotationSessionSyncTest.kt
+0 −27 src/test/kotlin/com/alicejump/okscripttoolkit/core/BoxResourceTest.kt
80 changes: 23 additions & 57 deletions media/annotationPanel/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@
if (problem.code === 'segment') return t('boxPathBadSegment', { segment: problem.bad || '' });
if (problem.code === 'duplicate') return t('boxPathExists');
if (problem.code === 'rule') return t('boxPathRuleMissing');
return t('generateBoxFailed');
return t('positionPathFailed');
}

function syncAnnotationList() {
Expand Down Expand Up @@ -546,8 +546,9 @@
document.getElementById('templateModeBtn').classList.toggle('active', annotationMode === 'template');
document.getElementById('rectModeBtn').classList.toggle('active', annotationMode === 'rect');
document.getElementById('pointModeBtn').classList.toggle('active', annotationMode === 'point');
document.getElementById('generateBoxBtn').style.display = annotationMode === 'template' ? '' : 'none';
document.getElementById('drawBtn').textContent = pointMode ? 'Point (' + keybindings.drawBbox.toUpperCase() + ')' : t('drawBbox');
document.getElementById('drawBtn').textContent = pointMode
? t('pointTool', { key: keybindings.drawBbox.toUpperCase() })
: t('drawBbox');
document.getElementById('bboxWRow').style.display = pointMode ? 'none' : '';
document.getElementById('bboxHRow').style.display = pointMode ? 'none' : '';
updateUndoRedoButtons();
Expand All @@ -562,7 +563,10 @@
const wInput = document.getElementById('bboxW');
const hInput = document.getElementById('bboxH');
const ok = document.getElementById('bboxOk');
document.getElementById('bboxTitle').textContent = category ? (pointMode ? 'Edit point' : t('editBboxTitle')) : (pointMode ? 'New point' : t('newBboxTitle'));
document.getElementById('bboxTitle').textContent = category
? (pointMode ? t('editPointTitle') : t('editBboxTitle'))
: (pointMode ? t('newPointTitle') : t('newBboxTitle'));
document.getElementById('bboxCatLabel').textContent = isPositionMode() ? t('generatePath') + ':' : t('categoryLabel');
catInput.value = category;
catInput.placeholder = isPositionMode() ? t('generatePathPlaceholder') : t('categoryLabel');
xInput.value = x;
Expand Down Expand Up @@ -779,10 +783,10 @@
function pasteClipboardText(text) {
if (!img || toolMode !== 'none' || modeLoading) return;
const payload = parseClipboardPayload(text);
if (!payload) { pasteStatus('Clipboard is not a valid normalized coordinate tuple.'); return; }
if (!payload) { pasteStatus(t('clipboardInvalid')); return; }
const zeroSize = payload.box.w === 0 && payload.box.h === 0;
if (zeroSize && annotationMode !== 'point') {
pasteStatus('Zero-size coordinates can only be pasted in Point mode.');
pasteStatus(t('clipboardPointOnly'));
return;
}
let box = payload.box;
Expand Down Expand Up @@ -1012,30 +1016,6 @@
offsetX = px - ix * scale; offsetY = py - iy * scale; recalcOffset(); e.preventDefault(); paint();
}, { passive:false });

function openGenerateBox() {
if (annotationMode !== 'template') return;
const choices = document.getElementById('generateChoices'); choices.replaceChildren();
const selected = new Set(selectedList());
annotations.forEach((ann, index) => {
const label = document.createElement('label'); label.className = 'annotation-row';
const input = document.createElement('input'); input.type = 'checkbox'; input.checked = selected.size === 0 || selected.has(index); input.dataset.index = String(index);
const text = document.createElement('span'); text.textContent = ann.category; label.append(input, text); choices.append(label);
});
const seedAnn = annotations[selectedIdx] || annotations[0];
const seed = seedAnn?.category || 'region';
document.getElementById('generatePath').value = 'screen.' + String(seed).replace(/[^A-Za-z0-9_]/g, '_');
document.getElementById('generateError').textContent = '';
document.getElementById('generateModal').classList.add('visible');
}

function refreshGeneratePathState() {
const input = document.getElementById('generatePath');
const problem = pathProblem(input.value) || pathOccupied(input.value, null);
document.getElementById('generateError').textContent = problem ? pathMessage(problem) : '';
document.getElementById('generateOk').disabled = !!problem;
return problem;
}

function navigate(delta) {
if (!imageData) return;
const target = imageData.currentIndex + delta;
Expand All @@ -1049,10 +1029,6 @@

document.addEventListener('keydown', (e) => {
if (document.getElementById('bboxModal').classList.contains('visible')) return;
if (document.getElementById('generateModal').classList.contains('visible')) {
if (e.key === 'Enter') { e.preventDefault(); document.getElementById('generateOk').click(); }
return;
}
if (matchKeybinding(e, keybindings.modeTemplate)) { e.preventDefault(); requestAnnotationMode('template'); return; }
if (matchKeybinding(e, keybindings.modeRect)) { e.preventDefault(); requestAnnotationMode('rect'); return; }
if (matchKeybinding(e, keybindings.modePoint)) { e.preventDefault(); requestAnnotationMode('point'); return; }
Expand Down Expand Up @@ -1107,31 +1083,26 @@
annotations.forEach(a => { if (!categories.has(a.category)) hidden.add(a.category); });
listSignature=''; paint();
};
document.getElementById('generateBoxBtn').onclick = openGenerateBox;
document.getElementById('generateCancel').onclick = () => document.getElementById('generateModal').classList.remove('visible');
document.getElementById('generatePath').oninput = refreshGeneratePathState;
document.getElementById('generateOk').onclick = () => {
if (refreshGeneratePathState()) return;
const chosen = [];
document.querySelectorAll('#generateChoices input').forEach(input => { if (input.checked) chosen.push(annotations[Number(input.dataset.index)]); });
if (!chosen.length) { document.getElementById('generateError').textContent = t('generateNeedSelection'); return; }
vscode.postMessage({ type:'generateBox', path:document.getElementById('generatePath').value.trim(), boxes:chosen.map(a=>({x:a.x,y:a.y,w:a.w,h:a.h})) });
};

function updateStaticText() {
document.getElementById('bboxCatLabel').textContent = t('categoryLabel');
document.getElementById('annotationModeGroup').setAttribute('aria-label', t('annotationModeLabel'));
document.getElementById('templateModeBtn').textContent = t('modeTemplate');
document.getElementById('rectModeBtn').textContent = t('modeRect');
document.getElementById('pointModeBtn').textContent = t('modePoint');
document.getElementById('sharedHistoryLabel').textContent = t('sharedHistory');
document.getElementById('sharedHistoryWrap').title = t('sharedHistoryTooltip');
document.getElementById('coordPreferenceWrap').title = t('coordPreferenceTooltip');
document.getElementById('bboxCatLabel').textContent = isPositionMode() ? t('generatePath') + ':' : t('categoryLabel');
document.getElementById('bboxWLabel').textContent = t('widthLabel');
document.getElementById('bboxHLabel').textContent = t('heightLabel');
document.getElementById('bboxCancel').textContent = t('cancel');
document.getElementById('bboxOk').textContent = t('confirm');
document.getElementById('coordBtn').textContent = t('copyCoords');
document.getElementById('deleteBtn').textContent = t('deleteMode');
document.getElementById('annotationListTitle').textContent = t('annotationListTitle');
document.getElementById('showAllBtn').textContent = t('showAllAnnotations');
document.getElementById('hideAllBtn').textContent = t('hideAllAnnotations');
document.getElementById('onlyCurrentBtn').textContent = t('showOnlyCurrent');
document.getElementById('generateBoxBtn').textContent = t('generateBox');
document.getElementById('generateTitle').textContent = t('generateBoxTitle');
document.getElementById('generatePathLabel').textContent = t('generatePath');
document.getElementById('generateCancel').textContent = t('cancel');
document.getElementById('generateOk').textContent = t('ok');
document.getElementById('prevBtn').title = t('prevImage');
document.getElementById('nextBtn').title = t('nextImage');
document.getElementById('emptyMsg').textContent = t('noImageLoaded');
Expand All @@ -1158,11 +1129,6 @@
pasteClipboardText(msg.text || '');
return;
}
if (msg.type === 'generateBoxResult') {
if (msg.ok) document.getElementById('generateModal').classList.remove('visible');
else document.getElementById('generateError').textContent = pathMessage(msg.error === 'duplicate' ? {code:'duplicate'} : {code:msg.error || 'unknown'});
return;
}
if (msg.type !== 'load') return;

const incomingMode = msg.annotationMode || annotationMode;
Expand All @@ -1182,7 +1148,7 @@
clearSelection();
hoveredIdx = -1;
listSignature = '';
updateModeUi();
updateStaticText();

const needImageLoad = !img || imageChanged;
if (msg.imageBase64 && needImageLoad) {
Expand All @@ -1207,4 +1173,4 @@
resize();
updateStaticText();
vscode.postMessage({ type:'ready' });
})();
})();
74 changes: 74 additions & 0 deletions media/annotationPanel/conflict.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#conflictCanvas {
position: absolute;
inset: 0;
z-index: 4;
pointer-events: none;
}

.conflict-panel {
display: flex;
flex-direction: column;
gap: var(--space-sm);
max-height: 52%;
padding: var(--space-sm);
border: var(--border-width) solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-container-raised);
overflow: hidden;
}

.conflict-panel[hidden] {
display: none;
}

.conflict-summary {
white-space: pre-line;
color: var(--text-muted);
font-size: var(--font-xs);
}

.conflict-rows {
display: flex;
flex-direction: column;
gap: var(--space-sm);
overflow: auto;
min-height: 0;
}

.conflict-row {
display: flex;
flex-direction: column;
gap: var(--space-xs);
padding: var(--space-xs);
border: var(--border-width) solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-row);
}

.conflict-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: var(--font-sm);
font-weight: var(--weight-medium);
}

.conflict-fields {
color: var(--text-muted);
font-size: var(--font-xs);
}

.conflict-choice {
width: 100%;
height: auto;
min-height: var(--control-h-sm);
padding: var(--space-xs) var(--space-sm);
white-space: normal;
text-align: left;
line-height: 1.35;
}

.conflict-apply {
width: 100%;
flex-shrink: 0;
}
Loading
Loading