@cluesmith/codev 1.2.1 → 1.2.3
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.
|
@@ -52,6 +52,28 @@ const langMap = {
|
|
|
52
52
|
};
|
|
53
53
|
const lang = langMap[ext] || ext;
|
|
54
54
|
const isMarkdown = ext === 'md';
|
|
55
|
+
// Image detection
|
|
56
|
+
const imageExtensions = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'];
|
|
57
|
+
const isImage = imageExtensions.includes(ext);
|
|
58
|
+
// Video detection
|
|
59
|
+
const videoExtensions = ['webm', 'mp4', 'mov', 'avi'];
|
|
60
|
+
const isVideo = videoExtensions.includes(ext);
|
|
61
|
+
// MIME type mapping for images
|
|
62
|
+
const imageMimeTypes = {
|
|
63
|
+
png: 'image/png',
|
|
64
|
+
jpg: 'image/jpeg',
|
|
65
|
+
jpeg: 'image/jpeg',
|
|
66
|
+
gif: 'image/gif',
|
|
67
|
+
webp: 'image/webp',
|
|
68
|
+
svg: 'image/svg+xml'
|
|
69
|
+
};
|
|
70
|
+
// MIME type mapping for videos
|
|
71
|
+
const videoMimeTypes = {
|
|
72
|
+
webm: 'video/webm',
|
|
73
|
+
mp4: 'video/mp4',
|
|
74
|
+
mov: 'video/quicktime',
|
|
75
|
+
avi: 'video/x-msvideo'
|
|
76
|
+
};
|
|
55
77
|
// Create server
|
|
56
78
|
const server = http.createServer((req, res) => {
|
|
57
79
|
// CORS headers
|
|
@@ -67,18 +89,34 @@ const server = http.createServer((req, res) => {
|
|
|
67
89
|
if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {
|
|
68
90
|
try {
|
|
69
91
|
let template = fs.readFileSync(templatePath, 'utf-8');
|
|
70
|
-
|
|
92
|
+
// Get file stats for images
|
|
93
|
+
const fileStats = fs.statSync(fullFilePath);
|
|
94
|
+
const fileSize = fileStats.size;
|
|
71
95
|
// Replace placeholders
|
|
72
96
|
template = template.replace(/\{\{BUILDER_ID\}\}/g, '');
|
|
73
97
|
template = template.replace(/\{\{FILE_PATH\}\}/g, fullFilePath);
|
|
74
98
|
template = template.replace(/\{\{FILE\}\}/g, displayPath);
|
|
75
99
|
template = template.replace(/\{\{LANG\}\}/g, lang);
|
|
76
100
|
template = template.replace(/\{\{IS_MARKDOWN\}\}/g, String(isMarkdown));
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
101
|
+
template = template.replace(/\{\{IS_IMAGE\}\}/g, String(isImage));
|
|
102
|
+
template = template.replace(/\{\{IS_VIDEO\}\}/g, String(isVideo));
|
|
103
|
+
template = template.replace(/\{\{FILE_SIZE\}\}/g, String(fileSize));
|
|
104
|
+
if (isImage) {
|
|
105
|
+
// For images, don't inject file content - it will be loaded via /api/image
|
|
106
|
+
template = template.replace('// FILE_CONTENT will be injected by the server', `initImage(${fileSize});`);
|
|
107
|
+
}
|
|
108
|
+
else if (isVideo) {
|
|
109
|
+
// For videos, don't inject file content - it will be loaded via /api/video
|
|
110
|
+
template = template.replace('// FILE_CONTENT will be injected by the server', `initVideo(${fileSize});`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// For text files, inject content as before
|
|
114
|
+
const fileContent = fs.readFileSync(fullFilePath, 'utf-8');
|
|
115
|
+
// JSON.stringify escapes quotes but not </script> which would break HTML parsing
|
|
116
|
+
// Replace </script> with <\/script> (valid JS, doesn't match HTML closing tag)
|
|
117
|
+
const escapedContent = JSON.stringify(fileContent).replace(/<\/script>/gi, '<\\/script>');
|
|
118
|
+
template = template.replace('// FILE_CONTENT will be injected by the server', `init(${escapedContent});`);
|
|
119
|
+
}
|
|
82
120
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
83
121
|
res.end(template);
|
|
84
122
|
}
|
|
@@ -102,6 +140,54 @@ const server = http.createServer((req, res) => {
|
|
|
102
140
|
}
|
|
103
141
|
return;
|
|
104
142
|
}
|
|
143
|
+
// Handle image content (GET /api/image)
|
|
144
|
+
// Use startsWith to allow query params like ?t=123 for cache busting
|
|
145
|
+
if (req.method === 'GET' && req.url?.startsWith('/api/image')) {
|
|
146
|
+
if (!isImage) {
|
|
147
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
148
|
+
res.end('Not an image file');
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const imageData = fs.readFileSync(fullFilePath);
|
|
153
|
+
const mimeType = imageMimeTypes[ext] || 'application/octet-stream';
|
|
154
|
+
res.writeHead(200, {
|
|
155
|
+
'Content-Type': mimeType,
|
|
156
|
+
'Content-Length': imageData.length,
|
|
157
|
+
'Cache-Control': 'no-cache' // Don't cache, allow reload to work
|
|
158
|
+
});
|
|
159
|
+
res.end(imageData);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
163
|
+
res.end('Error reading image: ' + err.message);
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
// Handle video content (GET /api/video)
|
|
168
|
+
// Use startsWith to allow query params like ?t=123 for cache busting
|
|
169
|
+
if (req.method === 'GET' && req.url?.startsWith('/api/video')) {
|
|
170
|
+
if (!isVideo) {
|
|
171
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
172
|
+
res.end('Not a video file');
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const videoData = fs.readFileSync(fullFilePath);
|
|
177
|
+
const mimeType = videoMimeTypes[ext] || 'application/octet-stream';
|
|
178
|
+
res.writeHead(200, {
|
|
179
|
+
'Content-Type': mimeType,
|
|
180
|
+
'Content-Length': videoData.length,
|
|
181
|
+
'Cache-Control': 'no-cache' // Don't cache, allow reload to work
|
|
182
|
+
});
|
|
183
|
+
res.end(videoData);
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
187
|
+
res.end('Error reading video: ' + err.message);
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
105
191
|
// Handle file save
|
|
106
192
|
if (req.method === 'POST' && req.url === '/save') {
|
|
107
193
|
let body = '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"open-server.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/open-server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"open-server.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/open-server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/templates/open.html
CHANGED
|
@@ -284,12 +284,75 @@
|
|
|
284
284
|
.annotation-item:hover { background: #2a2a2a; }
|
|
285
285
|
.annotation-item .line { color: #3b82f6; font-size: 12px; }
|
|
286
286
|
.annotation-item .text { font-size: 13px; margin-top: 4px; }
|
|
287
|
+
|
|
288
|
+
/* Image Viewer */
|
|
289
|
+
#image-viewer {
|
|
290
|
+
display: none;
|
|
291
|
+
flex-direction: column;
|
|
292
|
+
height: calc(100vh - 80px);
|
|
293
|
+
padding: 15px;
|
|
294
|
+
}
|
|
295
|
+
.image-controls {
|
|
296
|
+
display: flex;
|
|
297
|
+
align-items: center;
|
|
298
|
+
gap: 8px;
|
|
299
|
+
padding: 10px 0;
|
|
300
|
+
flex-shrink: 0;
|
|
301
|
+
}
|
|
302
|
+
.image-controls .btn {
|
|
303
|
+
padding: 6px 12px;
|
|
304
|
+
border-radius: 4px;
|
|
305
|
+
border: none;
|
|
306
|
+
cursor: pointer;
|
|
307
|
+
font-size: 12px;
|
|
308
|
+
background: #444;
|
|
309
|
+
color: #fff;
|
|
310
|
+
}
|
|
311
|
+
.image-controls .btn:hover { opacity: 0.9; }
|
|
312
|
+
.image-controls .btn.active {
|
|
313
|
+
background: #3b82f6;
|
|
314
|
+
}
|
|
315
|
+
#image-info {
|
|
316
|
+
color: #888;
|
|
317
|
+
font-size: 12px;
|
|
318
|
+
margin-left: auto;
|
|
319
|
+
}
|
|
320
|
+
.image-container {
|
|
321
|
+
flex: 1;
|
|
322
|
+
overflow: auto;
|
|
323
|
+
display: flex;
|
|
324
|
+
align-items: center;
|
|
325
|
+
justify-content: center;
|
|
326
|
+
background: #222;
|
|
327
|
+
border-radius: 4px;
|
|
328
|
+
}
|
|
329
|
+
.image-container.zoom-fit img {
|
|
330
|
+
max-width: 100%;
|
|
331
|
+
max-height: 100%;
|
|
332
|
+
width: auto;
|
|
333
|
+
height: auto;
|
|
334
|
+
object-fit: contain;
|
|
335
|
+
}
|
|
336
|
+
.image-container.zoom-100 img,
|
|
337
|
+
.image-container.zoom-custom img {
|
|
338
|
+
max-width: none;
|
|
339
|
+
max-height: none;
|
|
340
|
+
}
|
|
341
|
+
#image-display {
|
|
342
|
+
transition: transform 0.1s ease;
|
|
343
|
+
}
|
|
344
|
+
#image-error {
|
|
345
|
+
color: #ef4444;
|
|
346
|
+
text-align: center;
|
|
347
|
+
padding: 40px;
|
|
348
|
+
}
|
|
287
349
|
</style>
|
|
288
350
|
</head>
|
|
289
|
-
<body data-builder="{{BUILDER_ID}}" data-file="{{FILE_PATH}}" data-lang="{{LANG}}">
|
|
351
|
+
<body data-builder="{{BUILDER_ID}}" data-file="{{FILE_PATH}}" data-lang="{{LANG}}" data-is-image="{{IS_IMAGE}}" data-is-video="{{IS_VIDEO}}" data-file-size="{{FILE_SIZE}}">
|
|
290
352
|
<div class="header">
|
|
291
353
|
<h1 class="path">{{FILE_PATH}}</h1>
|
|
292
|
-
<div class="subtitle">Click on a line number to leave an annotation.</div>
|
|
354
|
+
<div class="subtitle" id="subtitle">Click on a line number to leave an annotation.</div>
|
|
355
|
+
<div id="image-header-info" style="display: none; color: #888; font-size: 12px; margin-top: 4px;"></div>
|
|
293
356
|
<div class="actions">
|
|
294
357
|
<button id="reloadBtn" class="btn btn-secondary" onclick="reloadFile()" title="Reload from disk" aria-label="Reload file from disk">↻</button>
|
|
295
358
|
<button id="togglePreviewBtn" class="btn btn-secondary" style="display: none;" title="Toggle Preview (Cmd+Shift+P)">
|
|
@@ -309,6 +372,32 @@
|
|
|
309
372
|
<!-- Markdown preview mode -->
|
|
310
373
|
<div id="preview-container" style="display: none; padding: 20px; overflow: auto; height: calc(100vh - 80px);"></div>
|
|
311
374
|
|
|
375
|
+
<!-- Image viewer mode -->
|
|
376
|
+
<div id="image-viewer">
|
|
377
|
+
<div class="image-controls">
|
|
378
|
+
<button id="zoomFitBtn" class="btn active" onclick="zoomFit()">Fit</button>
|
|
379
|
+
<button id="zoom100Btn" class="btn" onclick="zoom100()">100%</button>
|
|
380
|
+
<button id="zoomInBtn" class="btn" onclick="zoomIn()">+</button>
|
|
381
|
+
<button id="zoomOutBtn" class="btn" onclick="zoomOut()">−</button>
|
|
382
|
+
<span id="zoom-level"></span>
|
|
383
|
+
<span id="image-info"></span>
|
|
384
|
+
</div>
|
|
385
|
+
<div class="image-container zoom-fit" id="image-container">
|
|
386
|
+
<img id="image-display" alt="Image preview" />
|
|
387
|
+
<div id="image-error" style="display: none;"></div>
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
|
|
391
|
+
<!-- Video viewer mode -->
|
|
392
|
+
<div id="video-viewer" style="display: none; padding: 15px; height: calc(100vh - 80px);">
|
|
393
|
+
<div style="display: flex; align-items: center; justify-content: center; height: 100%; background: #222; border-radius: 4px;">
|
|
394
|
+
<video id="video-display" controls style="max-width: 100%; max-height: 100%;">
|
|
395
|
+
Your browser does not support the video tag.
|
|
396
|
+
</video>
|
|
397
|
+
<div id="video-error" style="display: none; color: #ef4444; text-align: center; padding: 40px;"></div>
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
|
|
312
401
|
<!-- Editor mode -->
|
|
313
402
|
<textarea id="editor" spellcheck="false"></textarea>
|
|
314
403
|
|
|
@@ -362,6 +451,16 @@
|
|
|
362
451
|
const isMarkdownFile = {{IS_MARKDOWN}};
|
|
363
452
|
let isPreviewMode = false;
|
|
364
453
|
|
|
454
|
+
// Image viewer state
|
|
455
|
+
const isImageFile = {{IS_IMAGE}};
|
|
456
|
+
let currentZoomMode = 'fit'; // 'fit', '100', 'custom'
|
|
457
|
+
let currentZoomLevel = 1.0;
|
|
458
|
+
let imageNaturalWidth = 0;
|
|
459
|
+
let imageNaturalHeight = 0;
|
|
460
|
+
|
|
461
|
+
// Video viewer state
|
|
462
|
+
const isVideoFile = {{IS_VIDEO}};
|
|
463
|
+
|
|
365
464
|
// Comment patterns by file extension
|
|
366
465
|
const COMMENT_PATTERNS = {
|
|
367
466
|
js: { prefix: '// REVIEW', regex: /^(\s*)\/\/\s*REVIEW(\(@\w+\))?:\s*(.*)$/ },
|
|
@@ -403,6 +502,181 @@
|
|
|
403
502
|
}
|
|
404
503
|
}
|
|
405
504
|
|
|
505
|
+
// Initialize image viewer
|
|
506
|
+
function initImage(fileSize) {
|
|
507
|
+
// Hide code view elements
|
|
508
|
+
document.getElementById('viewMode').style.display = 'none';
|
|
509
|
+
document.getElementById('editBtn').style.display = 'none';
|
|
510
|
+
document.getElementById('togglePreviewBtn').style.display = 'none';
|
|
511
|
+
|
|
512
|
+
// Update subtitle
|
|
513
|
+
document.querySelector('.subtitle').textContent = 'Image viewer - use zoom controls to adjust view.';
|
|
514
|
+
|
|
515
|
+
// Show image viewer
|
|
516
|
+
const imageViewer = document.getElementById('image-viewer');
|
|
517
|
+
imageViewer.style.display = 'flex';
|
|
518
|
+
|
|
519
|
+
// Load image
|
|
520
|
+
const img = document.getElementById('image-display');
|
|
521
|
+
const imageError = document.getElementById('image-error');
|
|
522
|
+
|
|
523
|
+
img.onload = function() {
|
|
524
|
+
imageNaturalWidth = img.naturalWidth;
|
|
525
|
+
imageNaturalHeight = img.naturalHeight;
|
|
526
|
+
updateImageInfo(fileSize);
|
|
527
|
+
imageError.style.display = 'none';
|
|
528
|
+
img.style.display = 'block';
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
img.onerror = function() {
|
|
532
|
+
imageError.textContent = 'Failed to load image. The file may be corrupted.';
|
|
533
|
+
imageError.style.display = 'block';
|
|
534
|
+
img.style.display = 'none';
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
// Add cache-busting query param to allow reload
|
|
538
|
+
img.src = '/api/image?t=' + Date.now();
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Initialize video viewer
|
|
542
|
+
function initVideo(fileSize) {
|
|
543
|
+
// Hide code view elements
|
|
544
|
+
document.getElementById('viewMode').style.display = 'none';
|
|
545
|
+
document.getElementById('editBtn').style.display = 'none';
|
|
546
|
+
document.getElementById('togglePreviewBtn').style.display = 'none';
|
|
547
|
+
|
|
548
|
+
// Update subtitle
|
|
549
|
+
const sizeStr = formatFileSize(fileSize);
|
|
550
|
+
document.querySelector('.subtitle').textContent = 'Video player · ' + sizeStr;
|
|
551
|
+
|
|
552
|
+
// Show video viewer
|
|
553
|
+
const videoViewer = document.getElementById('video-viewer');
|
|
554
|
+
videoViewer.style.display = 'block';
|
|
555
|
+
|
|
556
|
+
// Load video
|
|
557
|
+
const video = document.getElementById('video-display');
|
|
558
|
+
const videoError = document.getElementById('video-error');
|
|
559
|
+
|
|
560
|
+
video.onloadedmetadata = function() {
|
|
561
|
+
videoError.style.display = 'none';
|
|
562
|
+
video.style.display = 'block';
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
video.onerror = function() {
|
|
566
|
+
videoError.textContent = 'Failed to load video. The file may be corrupted or in an unsupported format.';
|
|
567
|
+
videoError.style.display = 'block';
|
|
568
|
+
video.style.display = 'none';
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
// Add cache-busting query param to allow reload
|
|
572
|
+
video.src = '/api/video?t=' + Date.now();
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Update image info display (in both header and controls)
|
|
576
|
+
function updateImageInfo(fileSize) {
|
|
577
|
+
const sizeStr = formatFileSize(fileSize);
|
|
578
|
+
const infoText = `${imageNaturalWidth} × ${imageNaturalHeight} px · ${sizeStr}`;
|
|
579
|
+
|
|
580
|
+
// Update controls area
|
|
581
|
+
const info = document.getElementById('image-info');
|
|
582
|
+
info.textContent = infoText;
|
|
583
|
+
|
|
584
|
+
// Update header area
|
|
585
|
+
const headerInfo = document.getElementById('image-header-info');
|
|
586
|
+
headerInfo.textContent = infoText;
|
|
587
|
+
headerInfo.style.display = 'block';
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Format file size in human-readable form
|
|
591
|
+
function formatFileSize(bytes) {
|
|
592
|
+
if (bytes < 1024) return bytes + ' B';
|
|
593
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
|
594
|
+
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Update zoom level display
|
|
598
|
+
function updateZoomLevelDisplay() {
|
|
599
|
+
const zoomLevelEl = document.getElementById('zoom-level');
|
|
600
|
+
if (currentZoomMode === 'fit') {
|
|
601
|
+
zoomLevelEl.textContent = '';
|
|
602
|
+
} else {
|
|
603
|
+
zoomLevelEl.textContent = Math.round(currentZoomLevel * 100) + '%';
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Update active button state
|
|
608
|
+
function updateZoomButtons() {
|
|
609
|
+
document.getElementById('zoomFitBtn').classList.toggle('active', currentZoomMode === 'fit');
|
|
610
|
+
document.getElementById('zoom100Btn').classList.toggle('active', currentZoomMode === '100');
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Zoom fit - image fits within container
|
|
614
|
+
function zoomFit() {
|
|
615
|
+
const container = document.getElementById('image-container');
|
|
616
|
+
const img = document.getElementById('image-display');
|
|
617
|
+
|
|
618
|
+
container.className = 'image-container zoom-fit';
|
|
619
|
+
img.style.transform = '';
|
|
620
|
+
img.style.width = '';
|
|
621
|
+
img.style.height = '';
|
|
622
|
+
|
|
623
|
+
currentZoomMode = 'fit';
|
|
624
|
+
currentZoomLevel = 1.0;
|
|
625
|
+
updateZoomButtons();
|
|
626
|
+
updateZoomLevelDisplay();
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Zoom 100% - actual pixels
|
|
630
|
+
function zoom100() {
|
|
631
|
+
const container = document.getElementById('image-container');
|
|
632
|
+
const img = document.getElementById('image-display');
|
|
633
|
+
|
|
634
|
+
container.className = 'image-container zoom-100';
|
|
635
|
+
img.style.transform = '';
|
|
636
|
+
img.style.width = imageNaturalWidth + 'px';
|
|
637
|
+
img.style.height = imageNaturalHeight + 'px';
|
|
638
|
+
|
|
639
|
+
currentZoomMode = '100';
|
|
640
|
+
currentZoomLevel = 1.0;
|
|
641
|
+
updateZoomButtons();
|
|
642
|
+
updateZoomLevelDisplay();
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Zoom in by 25%
|
|
646
|
+
function zoomIn() {
|
|
647
|
+
if (currentZoomMode === 'fit') {
|
|
648
|
+
// Switch to custom zoom starting at 100%
|
|
649
|
+
currentZoomLevel = 1.0;
|
|
650
|
+
}
|
|
651
|
+
currentZoomLevel = Math.min(currentZoomLevel * 1.25, 10.0); // Max 1000%
|
|
652
|
+
applyCustomZoom();
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Zoom out by 20%
|
|
656
|
+
function zoomOut() {
|
|
657
|
+
if (currentZoomMode === 'fit') {
|
|
658
|
+
// Switch to custom zoom starting at 100%
|
|
659
|
+
currentZoomLevel = 1.0;
|
|
660
|
+
}
|
|
661
|
+
currentZoomLevel = Math.max(currentZoomLevel * 0.8, 0.1); // Min 10%
|
|
662
|
+
applyCustomZoom();
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Apply custom zoom level
|
|
666
|
+
function applyCustomZoom() {
|
|
667
|
+
const container = document.getElementById('image-container');
|
|
668
|
+
const img = document.getElementById('image-display');
|
|
669
|
+
|
|
670
|
+
container.className = 'image-container zoom-custom';
|
|
671
|
+
img.style.transform = '';
|
|
672
|
+
img.style.width = (imageNaturalWidth * currentZoomLevel) + 'px';
|
|
673
|
+
img.style.height = (imageNaturalHeight * currentZoomLevel) + 'px';
|
|
674
|
+
|
|
675
|
+
currentZoomMode = 'custom';
|
|
676
|
+
updateZoomButtons();
|
|
677
|
+
updateZoomLevelDisplay();
|
|
678
|
+
}
|
|
679
|
+
|
|
406
680
|
// Toggle between annotated view and preview mode
|
|
407
681
|
function togglePreviewMode() {
|
|
408
682
|
// Don't allow preview toggle while in textarea edit mode
|