@gallop.software/studio 0.1.25 → 0.1.27

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.
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
2
2
 
3
3
 
4
4
 
@@ -242,33 +242,42 @@ var progressStyles = {
242
242
  function ProgressModal({
243
243
  title,
244
244
  progress,
245
- onClose
245
+ onClose,
246
+ onStop
246
247
  }) {
247
248
  const isComplete = progress.status === "complete";
248
249
  const isError = progress.status === "error";
249
- const canClose = isComplete || isError;
250
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.overlay, onClick: canClose ? onClose : void 0, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles.modal, onClick: (e) => e.stopPropagation(), children: [
250
+ const isStopped = progress.status === "stopped";
251
+ const canClose = isComplete || isError || isStopped;
252
+ const isRunning = !canClose;
253
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.overlay, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles.modal, onClick: (e) => e.stopPropagation(), children: [
251
254
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.header, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css: styles.title, children: title }) }),
252
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.body, children: isError ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles.message, children: progress.message || "An error occurred" }) : isComplete ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { css: styles.message, children: [
255
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.body, children: isError ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles.message, children: progress.message || "An error occurred" }) : isStopped ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { css: styles.message, children: [
256
+ "Processing stopped. Processed ",
257
+ _nullishCoalesce(progress.processed, () => ( progress.current)),
258
+ " image",
259
+ (_nullishCoalesce(progress.processed, () => ( progress.current))) !== 1 ? "s" : "",
260
+ " before stopping."
261
+ ] }) : isComplete ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { css: styles.message, children: [
253
262
  "Processed ",
254
263
  progress.processed,
255
264
  " image",
256
265
  progress.processed !== 1 ? "s" : "",
257
266
  ".",
258
- progress.orphansRemoved && progress.orphansRemoved > 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
267
+ progress.orphansRemoved !== void 0 && progress.orphansRemoved > 0 ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
259
268
  " Removed ",
260
269
  progress.orphansRemoved,
261
270
  " orphaned thumbnail",
262
271
  progress.orphansRemoved !== 1 ? "s" : "",
263
272
  "."
264
- ] }),
265
- progress.errors && progress.errors > 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
273
+ ] }) : null,
274
+ progress.errors !== void 0 && progress.errors > 0 ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
266
275
  " ",
267
276
  progress.errors,
268
277
  " error",
269
278
  progress.errors !== 1 ? "s" : "",
270
279
  " occurred."
271
- ] })
280
+ ] }) : null
272
281
  ] }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
273
282
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles.message, children: progress.status === "cleanup" ? "Cleaning up orphaned files..." : `Processing images...` }),
274
283
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: progressStyles.progressContainer, children: [
@@ -293,7 +302,10 @@ function ProgressModal({
293
302
  progress.currentFile && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: progressStyles.currentFile, title: progress.currentFile, children: progress.currentFile })
294
303
  ] })
295
304
  ] }) }),
296
- canClose && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.footer, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: [styles.btn, styles.btnConfirm], onClick: onClose, children: "Done" }) })
305
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles.footer, children: [
306
+ isRunning && onStop && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: [styles.btn, styles.btnDanger], onClick: onStop, children: "Stop" }),
307
+ canClose && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: [styles.btn, styles.btnConfirm], onClick: onClose, children: "Done" })
308
+ ] })
297
309
  ] }) });
298
310
  }
299
311
 
@@ -438,6 +450,7 @@ var styles2 = {
438
450
  function StudioToolbar() {
439
451
  const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh, focusedItem } = useStudio();
440
452
  const fileInputRef = _react.useRef.call(void 0, null);
453
+ const abortControllerRef = _react.useRef.call(void 0, null);
441
454
  const [uploading, setUploading] = _react.useState.call(void 0, false);
442
455
  const [refreshing, setRefreshing] = _react.useState.call(void 0, false);
443
456
  const [processing, setProcessing] = _react.useState.call(void 0, false);
@@ -452,6 +465,7 @@ function StudioToolbar() {
452
465
  });
453
466
  const [processCount, setProcessCount] = _react.useState.call(void 0, 0);
454
467
  const [processMode, setProcessMode] = _react.useState.call(void 0, "all");
468
+ const [imagesToProcess, setImagesToProcess] = _react.useState.call(void 0, []);
455
469
  const [alertMessage, setAlertMessage] = _react.useState.call(void 0, null);
456
470
  const isInImagesFolder = currentPath === "public/images" || currentPath.startsWith("public/images/");
457
471
  const handleUpload = _react.useCallback.call(void 0, () => {
@@ -508,18 +522,38 @@ function StudioToolbar() {
508
522
  const handleProcessImages = _react.useCallback.call(void 0, async () => {
509
523
  const hasSelection2 = selectedItems.size > 0;
510
524
  if (hasSelection2) {
511
- const selectedImagePaths = Array.from(selectedItems).filter((p) => {
525
+ const selectedPaths = Array.from(selectedItems);
526
+ const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
527
+ const selectedImagePaths = selectedPaths.filter((p) => {
512
528
  const ext = _optionalChain([p, 'access', _4 => _4.split, 'call', _5 => _5("."), 'access', _6 => _6.pop, 'call', _7 => _7(), 'optionalAccess', _8 => _8.toLowerCase, 'call', _9 => _9()]) || "";
513
- return ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"].includes(ext);
529
+ return imageExtensions.includes(ext);
514
530
  });
531
+ const selectedFolders = selectedPaths.filter((p) => !p.includes(".") || p.endsWith("/"));
532
+ if (selectedFolders.length > 0) {
533
+ try {
534
+ const response = await fetch(`/api/studio/folder-images?folders=${encodeURIComponent(selectedFolders.join(","))}`);
535
+ const data = await response.json();
536
+ if (data.images) {
537
+ for (const img of data.images) {
538
+ const fullPath = `public/${img}`;
539
+ if (!selectedImagePaths.includes(fullPath)) {
540
+ selectedImagePaths.push(fullPath);
541
+ }
542
+ }
543
+ }
544
+ } catch (error) {
545
+ console.error("Failed to get folder images:", error);
546
+ }
547
+ }
515
548
  if (selectedImagePaths.length === 0) {
516
549
  setAlertMessage({
517
- title: "No Images Selected",
518
- message: "Please select image files to process."
550
+ title: "No Images Found",
551
+ message: "No images found in the selected items."
519
552
  });
520
553
  return;
521
554
  }
522
555
  setProcessCount(selectedImagePaths.length);
556
+ setImagesToProcess(selectedImagePaths);
523
557
  setProcessMode("selected");
524
558
  setShowProcessConfirm(true);
525
559
  } else {
@@ -548,6 +582,8 @@ function StudioToolbar() {
548
582
  const handleProcessConfirm = _react.useCallback.call(void 0, async () => {
549
583
  setShowProcessConfirm(false);
550
584
  setProcessing(true);
585
+ abortControllerRef.current = new AbortController();
586
+ const signal = abortControllerRef.current.signal;
551
587
  try {
552
588
  if (processMode === "all") {
553
589
  setShowProgress(true);
@@ -558,61 +594,79 @@ function StudioToolbar() {
558
594
  status: "processing"
559
595
  });
560
596
  const response = await fetch("/api/studio/process-all", {
561
- method: "POST"
597
+ method: "POST",
598
+ signal
562
599
  });
563
600
  if (!response.body) {
564
601
  throw new Error("No response body");
565
602
  }
566
603
  const reader = response.body.getReader();
567
604
  const decoder = new TextDecoder();
568
- while (true) {
569
- const { done, value } = await reader.read();
570
- if (done) break;
571
- const text = decoder.decode(value);
572
- const lines = text.split("\n\n").filter((line) => line.startsWith("data: "));
573
- for (const line of lines) {
574
- try {
575
- const data = JSON.parse(line.replace("data: ", ""));
576
- if (data.type === "start") {
577
- setProgressState((prev) => ({
578
- ...prev,
579
- total: data.total
580
- }));
581
- } else if (data.type === "progress") {
582
- setProgressState({
583
- current: data.current,
584
- total: data.total,
585
- percent: data.percent,
586
- currentFile: data.currentFile,
587
- status: "processing"
588
- });
589
- } else if (data.type === "cleanup") {
590
- setProgressState((prev) => ({
591
- ...prev,
592
- status: "cleanup",
593
- currentFile: void 0
594
- }));
595
- } else if (data.type === "complete") {
596
- setProgressState({
597
- current: data.processed,
598
- total: data.processed,
599
- percent: 100,
600
- status: "complete",
601
- processed: data.processed,
602
- orphansRemoved: data.orphansRemoved,
603
- errors: data.errors
604
- });
605
- triggerRefresh();
606
- } else if (data.type === "error") {
607
- setProgressState((prev) => ({
608
- ...prev,
609
- status: "error",
610
- message: data.message
611
- }));
605
+ try {
606
+ while (true) {
607
+ const { done, value } = await reader.read();
608
+ if (done) break;
609
+ if (signal.aborted) {
610
+ reader.cancel();
611
+ break;
612
+ }
613
+ const text = decoder.decode(value);
614
+ const lines = text.split("\n\n").filter((line) => line.startsWith("data: "));
615
+ for (const line of lines) {
616
+ try {
617
+ const data = JSON.parse(line.replace("data: ", ""));
618
+ if (data.type === "start") {
619
+ setProgressState((prev) => ({
620
+ ...prev,
621
+ total: data.total
622
+ }));
623
+ } else if (data.type === "progress") {
624
+ setProgressState({
625
+ current: data.current,
626
+ total: data.total,
627
+ percent: data.percent,
628
+ currentFile: data.currentFile,
629
+ status: "processing"
630
+ });
631
+ } else if (data.type === "cleanup") {
632
+ setProgressState((prev) => ({
633
+ ...prev,
634
+ status: "cleanup",
635
+ currentFile: void 0
636
+ }));
637
+ } else if (data.type === "complete") {
638
+ setProgressState({
639
+ current: data.processed,
640
+ total: data.processed,
641
+ percent: 100,
642
+ status: "complete",
643
+ processed: data.processed,
644
+ orphansRemoved: data.orphansRemoved,
645
+ errors: data.errors
646
+ });
647
+ triggerRefresh();
648
+ } else if (data.type === "error") {
649
+ setProgressState((prev) => ({
650
+ ...prev,
651
+ status: "error",
652
+ message: data.message
653
+ }));
654
+ }
655
+ } catch (e2) {
612
656
  }
613
- } catch (e2) {
614
657
  }
615
658
  }
659
+ } catch (err) {
660
+ if (signal.aborted) {
661
+ setProgressState((prev) => ({
662
+ ...prev,
663
+ status: "stopped",
664
+ processed: prev.current
665
+ }));
666
+ triggerRefresh();
667
+ } else {
668
+ throw err;
669
+ }
616
670
  }
617
671
  } else {
618
672
  setShowProgress(true);
@@ -622,24 +676,22 @@ function StudioToolbar() {
622
676
  percent: 0,
623
677
  status: "processing"
624
678
  });
625
- const selectedImageKeys = Array.from(selectedItems).filter((p) => {
626
- const ext = _optionalChain([p, 'access', _10 => _10.split, 'call', _11 => _11("."), 'access', _12 => _12.pop, 'call', _13 => _13(), 'optionalAccess', _14 => _14.toLowerCase, 'call', _15 => _15()]) || "";
627
- return ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"].includes(ext);
628
- }).map((p) => p.replace(/^public\//, ""));
679
+ const selectedImageKeys = imagesToProcess.map((p) => p.replace(/^public\//, ""));
629
680
  const response = await fetch("/api/studio/reprocess", {
630
681
  method: "POST",
631
682
  headers: { "Content-Type": "application/json" },
632
- body: JSON.stringify({ imageKeys: selectedImageKeys })
683
+ body: JSON.stringify({ imageKeys: selectedImageKeys }),
684
+ signal
633
685
  });
634
686
  const data = await response.json();
635
687
  if (response.ok) {
636
688
  setProgressState({
637
- current: _optionalChain([data, 'access', _16 => _16.processed, 'optionalAccess', _17 => _17.length]) || 0,
638
- total: _optionalChain([data, 'access', _18 => _18.processed, 'optionalAccess', _19 => _19.length]) || 0,
689
+ current: _optionalChain([data, 'access', _10 => _10.processed, 'optionalAccess', _11 => _11.length]) || 0,
690
+ total: _optionalChain([data, 'access', _12 => _12.processed, 'optionalAccess', _13 => _13.length]) || 0,
639
691
  percent: 100,
640
692
  status: "complete",
641
- processed: _optionalChain([data, 'access', _20 => _20.processed, 'optionalAccess', _21 => _21.length]) || 0,
642
- errors: _optionalChain([data, 'access', _22 => _22.errors, 'optionalAccess', _23 => _23.length]) || 0
693
+ processed: _optionalChain([data, 'access', _14 => _14.processed, 'optionalAccess', _15 => _15.length]) || 0,
694
+ errors: _optionalChain([data, 'access', _16 => _16.errors, 'optionalAccess', _17 => _17.length]) || 0
643
695
  });
644
696
  clearSelection();
645
697
  triggerRefresh();
@@ -654,18 +706,33 @@ function StudioToolbar() {
654
706
  }
655
707
  }
656
708
  } catch (error) {
657
- console.error("Processing error:", error);
658
- setProgressState({
659
- current: 0,
660
- total: 0,
661
- percent: 0,
662
- status: "error",
663
- message: "Processing failed. Check console for details."
664
- });
709
+ if (signal.aborted) {
710
+ setProgressState((prev) => ({
711
+ ...prev,
712
+ status: "stopped",
713
+ processed: prev.current
714
+ }));
715
+ triggerRefresh();
716
+ } else {
717
+ console.error("Processing error:", error);
718
+ setProgressState({
719
+ current: 0,
720
+ total: 0,
721
+ percent: 0,
722
+ status: "error",
723
+ message: "Processing failed. Check console for details."
724
+ });
725
+ }
665
726
  } finally {
666
727
  setProcessing(false);
728
+ abortControllerRef.current = null;
729
+ }
730
+ }, [processMode, processCount, imagesToProcess, clearSelection, triggerRefresh]);
731
+ const handleStopProcessing = _react.useCallback.call(void 0, () => {
732
+ if (abortControllerRef.current) {
733
+ abortControllerRef.current.abort();
667
734
  }
668
- }, [processMode, processCount, selectedItems, clearSelection, triggerRefresh]);
735
+ }, []);
669
736
  const handleDeleteClick = _react.useCallback.call(void 0, () => {
670
737
  if (selectedItems.size === 0) return;
671
738
  setShowDeleteConfirm(true);
@@ -733,6 +800,7 @@ function StudioToolbar() {
733
800
  {
734
801
  title: "Processing Images",
735
802
  progress: progressState,
803
+ onStop: handleStopProcessing,
736
804
  onClose: () => {
737
805
  setShowProgress(false);
738
806
  setProgressState({
@@ -1242,7 +1310,7 @@ function GridItem({ item, isSelected, onClick, onOpen }) {
1242
1310
  ) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles3.fileIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" }) }) }),
1243
1311
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles3.label, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles3.labelRow, children: [
1244
1312
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles3.labelText, children: [
1245
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles3.name, title: item.name, children: item.name }),
1313
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles3.name, title: item.name, children: truncateMiddle(item.name) }),
1246
1314
  isFolder ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { css: styles3.size, children: [
1247
1315
  item.fileCount !== void 0 ? `${item.fileCount} files` : "",
1248
1316
  item.fileCount !== void 0 && item.totalSize !== void 0 ? " \xB7 " : "",
@@ -1270,6 +1338,19 @@ function formatFileSize(bytes) {
1270
1338
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1271
1339
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1272
1340
  }
1341
+ function truncateMiddle(str, maxLength = 24) {
1342
+ if (str.length <= maxLength) return str;
1343
+ const lastDot = str.lastIndexOf(".");
1344
+ const ext = lastDot > 0 ? str.substring(lastDot) : "";
1345
+ const name = lastDot > 0 ? str.substring(0, lastDot) : str;
1346
+ const availableLength = maxLength - ext.length - 3;
1347
+ if (availableLength < 6) {
1348
+ return str.substring(0, maxLength - 3) + "...";
1349
+ }
1350
+ const startLength = Math.ceil(availableLength / 2);
1351
+ const endLength = Math.floor(availableLength / 2);
1352
+ return name.substring(0, startLength) + "..." + name.substring(name.length - endLength) + ext;
1353
+ }
1273
1354
 
1274
1355
  // src/components/StudioFileList.tsx
1275
1356
 
@@ -1577,7 +1658,7 @@ function ListRow({ item, isSelected, onClick, onOpen }) {
1577
1658
  ),
1578
1659
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles4.td, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles4.nameCell, children: [
1579
1660
  isFolder ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles4.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }) : item.thumbnail ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "img", { css: styles4.thumbnail, src: item.thumbnail, alt: item.name, loading: "lazy" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles4.fileIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" }) }),
1580
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles4.name, children: item.name }),
1661
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles4.name, title: item.name, children: truncateMiddle2(item.name) }),
1581
1662
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1582
1663
  "button",
1583
1664
  {
@@ -1605,6 +1686,19 @@ function formatFileSize2(bytes) {
1605
1686
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1606
1687
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1607
1688
  }
1689
+ function truncateMiddle2(str, maxLength = 32) {
1690
+ if (str.length <= maxLength) return str;
1691
+ const lastDot = str.lastIndexOf(".");
1692
+ const ext = lastDot > 0 ? str.substring(lastDot) : "";
1693
+ const name = lastDot > 0 ? str.substring(0, lastDot) : str;
1694
+ const availableLength = maxLength - ext.length - 3;
1695
+ if (availableLength < 6) {
1696
+ return str.substring(0, maxLength - 3) + "...";
1697
+ }
1698
+ const startLength = Math.ceil(availableLength / 2);
1699
+ const endLength = Math.floor(availableLength / 2);
1700
+ return name.substring(0, startLength) + "..." + name.substring(name.length - endLength) + ext;
1701
+ }
1608
1702
 
1609
1703
  // src/components/StudioDetailView.tsx
1610
1704
 
@@ -1748,6 +1842,14 @@ var styles5 = {
1748
1842
  text-overflow: ellipsis;
1749
1843
  white-space: nowrap;
1750
1844
  `,
1845
+ infoValueWrap: _react3.css`
1846
+ color: ${_chunkAY2DAS6Wjs.colors.text};
1847
+ font-weight: 500;
1848
+ text-align: right;
1849
+ max-width: 160px;
1850
+ word-break: break-all;
1851
+ white-space: normal;
1852
+ `,
1751
1853
  actions: _react3.css`
1752
1854
  display: flex;
1753
1855
  flex-direction: column;
@@ -1881,7 +1983,7 @@ function StudioDetailView() {
1881
1983
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.info, children: [
1882
1984
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.infoRow, children: [
1883
1985
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles5.infoLabel, children: "Name" }),
1884
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles5.infoValue, title: focusedItem.name, children: focusedItem.name })
1986
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles5.infoValueWrap, children: focusedItem.name })
1885
1987
  ] }),
1886
1988
  focusedItem.size !== void 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.infoRow, children: [
1887
1989
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles5.infoLabel, children: "Size" }),
@@ -2397,4 +2499,4 @@ var StudioUI_default = StudioUI;
2397
2499
 
2398
2500
 
2399
2501
  exports.StudioUI = StudioUI; exports.default = StudioUI_default;
2400
- //# sourceMappingURL=StudioUI-4TDLHJCA.js.map
2502
+ //# sourceMappingURL=StudioUI-7DJ6CTZQ.js.map