@vishu1301/script-writing 1.5.9 → 1.6.1
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/dist/index.cjs +936 -550
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +937 -551
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -77,12 +77,12 @@ var blockTypes = [
|
|
|
77
77
|
];
|
|
78
78
|
var uuid = () => Math.random().toString(36).slice(2, 9);
|
|
79
79
|
var icons = {
|
|
80
|
-
SCENE_HEADING: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
81
|
-
ACTION: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
82
|
-
CHARACTER: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-
|
|
83
|
-
PARENTHETICAL: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Parentheses, { className: "w-
|
|
84
|
-
DIALOGUE: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
85
|
-
TRANSITION: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
80
|
+
SCENE_HEADING: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mountain, { className: "w-[18px] h-[18px]", strokeWidth: 1.5 }),
|
|
81
|
+
ACTION: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clapperboard, { className: "w-[18px] h-[18px]", strokeWidth: 1.5 }),
|
|
82
|
+
CHARACTER: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-[18px] h-[18px]", strokeWidth: 1.5 }),
|
|
83
|
+
PARENTHETICAL: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Parentheses, { className: "w-[18px] h-[18px]", strokeWidth: 1.5 }),
|
|
84
|
+
DIALOGUE: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageCircle, { className: "w-[18px] h-[18px]", strokeWidth: 1.5 }),
|
|
85
|
+
TRANSITION: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "w-[18px] h-[18px]", strokeWidth: 1.5 })
|
|
86
86
|
};
|
|
87
87
|
var blockStyles = {
|
|
88
88
|
SCENE_HEADING: {
|
|
@@ -817,7 +817,7 @@ function PhoneticSuggestions({
|
|
|
817
817
|
] });
|
|
818
818
|
}
|
|
819
819
|
pdfjs__namespace.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs__namespace.version}/build/pdf.worker.min.mjs`;
|
|
820
|
-
function PdfImporter({ onScriptImported, disabled, children }) {
|
|
820
|
+
function PdfImporter({ onScriptImported, disabled, children, className }) {
|
|
821
821
|
const [isProcessing, setIsProcessing] = React4.useState(false);
|
|
822
822
|
const [error, setError] = React4.useState(null);
|
|
823
823
|
const fileInputRef = React4.useRef(null);
|
|
@@ -854,9 +854,9 @@ function PdfImporter({ onScriptImported, disabled, children }) {
|
|
|
854
854
|
let divText = ((_a2 = div.textContent) == null ? void 0 : _a2.trim()) || "";
|
|
855
855
|
if (!divText) return;
|
|
856
856
|
let type = "ACTION";
|
|
857
|
-
for (const
|
|
858
|
-
if (typeMap[
|
|
859
|
-
type = typeMap[
|
|
857
|
+
for (const className2 of Array.from(div.classList)) {
|
|
858
|
+
if (typeMap[className2]) {
|
|
859
|
+
type = typeMap[className2];
|
|
860
860
|
break;
|
|
861
861
|
}
|
|
862
862
|
}
|
|
@@ -960,7 +960,7 @@ function PdfImporter({ onScriptImported, disabled, children }) {
|
|
|
960
960
|
{
|
|
961
961
|
onClick: handleClick,
|
|
962
962
|
disabled: isProcessing || disabled,
|
|
963
|
-
className: `flex items-center justify-center transition-all duration-200 active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 ${isProcessing ? "w-auto px-3 sm:px-4 h-8 sm:h-10 rounded-full text-sm font-semibold bg-zinc-100 text-zinc-500" : "w-8 h-8 sm:w-10 sm:h-10 rounded-full text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900"}`,
|
|
963
|
+
className: className || `flex items-center justify-center transition-all duration-200 active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 ${isProcessing ? "w-auto px-3 sm:px-4 h-8 sm:h-10 rounded-full text-sm font-semibold bg-zinc-100 text-zinc-500" : "w-8 h-8 sm:w-10 sm:h-10 rounded-full text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900"}`,
|
|
964
964
|
"aria-label": "Import Script",
|
|
965
965
|
children: isProcessing ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-semibold", children: "Processing..." }) : children
|
|
966
966
|
}
|
|
@@ -1244,6 +1244,44 @@ function PhoneticGuide({
|
|
|
1244
1244
|
}
|
|
1245
1245
|
);
|
|
1246
1246
|
}
|
|
1247
|
+
function Tooltip({
|
|
1248
|
+
children,
|
|
1249
|
+
content,
|
|
1250
|
+
shortcut,
|
|
1251
|
+
align = "center"
|
|
1252
|
+
}) {
|
|
1253
|
+
const alignClasses = {
|
|
1254
|
+
center: "left-1/2 -translate-x-1/2 items-center",
|
|
1255
|
+
left: "left-0 items-start",
|
|
1256
|
+
right: "right-0 items-end"
|
|
1257
|
+
};
|
|
1258
|
+
const arrowClasses = {
|
|
1259
|
+
center: "",
|
|
1260
|
+
left: "left-3.5",
|
|
1261
|
+
right: "right-3.5"
|
|
1262
|
+
};
|
|
1263
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group relative flex items-center justify-center", children: [
|
|
1264
|
+
children,
|
|
1265
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1266
|
+
"div",
|
|
1267
|
+
{
|
|
1268
|
+
className: `absolute top-[calc(100%+8px)] scale-95 opacity-0 pointer-events-none group-hover:scale-100 group-hover:opacity-100 transition-all duration-150 ease-out z-[100] flex flex-col ${alignClasses[align]}`,
|
|
1269
|
+
children: [
|
|
1270
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1271
|
+
"div",
|
|
1272
|
+
{
|
|
1273
|
+
className: `w-2 h-2 rotate-45 bg-blumine-950 border-l border-t border-white/10 -mb-1 shadow-sm ${align === "center" ? "" : "relative"} ${arrowClasses[align]}`
|
|
1274
|
+
}
|
|
1275
|
+
),
|
|
1276
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-blumine-950/95 text-white text-[11px] font-medium px-2.5 py-1.5 rounded-lg shadow-xl border border-white/10 backdrop-blur-sm whitespace-nowrap flex items-center gap-1.5", children: [
|
|
1277
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: content }),
|
|
1278
|
+
shortcut && /* @__PURE__ */ jsxRuntime.jsx("kbd", { className: "bg-blumine-800 text-[10px] text-blumine-300 px-1.5 py-0.5 rounded border border-zinc-700 font-sans font-semibold", children: shortcut })
|
|
1279
|
+
] })
|
|
1280
|
+
]
|
|
1281
|
+
}
|
|
1282
|
+
)
|
|
1283
|
+
] });
|
|
1284
|
+
}
|
|
1247
1285
|
function ScreenplayEditorView({
|
|
1248
1286
|
blocks,
|
|
1249
1287
|
refs,
|
|
@@ -1285,7 +1323,8 @@ function ScreenplayEditorView({
|
|
|
1285
1323
|
currentLanguage,
|
|
1286
1324
|
setCurrentLanguage,
|
|
1287
1325
|
phoneticSuggestions,
|
|
1288
|
-
handleSelectPhoneticSuggestion
|
|
1326
|
+
handleSelectPhoneticSuggestion,
|
|
1327
|
+
autosaveStatus
|
|
1289
1328
|
}) {
|
|
1290
1329
|
const [isRulesOpen, setIsRulesOpen] = React4.useState(false);
|
|
1291
1330
|
const [isLanguageOpen, setIsLanguageOpen] = React4.useState(false);
|
|
@@ -1335,168 +1374,299 @@ function ScreenplayEditorView({
|
|
|
1335
1374
|
document.head.appendChild(style);
|
|
1336
1375
|
}
|
|
1337
1376
|
}, [COURIER_STACK]);
|
|
1338
|
-
if (isLoading) {
|
|
1339
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-white/80 transition-opacity", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-3", children: [
|
|
1340
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-8 h-8 text-zinc-400 animate-spin" }),
|
|
1341
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-zinc-500 text-sm font-medium", children: "Loading script..." })
|
|
1342
|
-
] }) });
|
|
1343
|
-
}
|
|
1344
1377
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1345
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
1346
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
1378
|
+
isLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-blumine-500/40 backdrop-blur-md p-6", children: [
|
|
1379
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
1380
|
+
@keyframes gpu-spin {
|
|
1381
|
+
0% { transform: rotate(0deg); }
|
|
1382
|
+
100% { transform: rotate(360deg); }
|
|
1383
|
+
}
|
|
1384
|
+
.gpu-spinner-container {
|
|
1385
|
+
transform: translate3d(0,0,0);
|
|
1386
|
+
will-change: transform;
|
|
1387
|
+
}
|
|
1388
|
+
.gpu-spinner {
|
|
1389
|
+
width: 32px;
|
|
1390
|
+
height: 32px;
|
|
1391
|
+
border: 3.5px solid rgba(255, 255, 255, 0.25);
|
|
1392
|
+
border-top-color: #ffffff;
|
|
1393
|
+
border-radius: 50%;
|
|
1394
|
+
animation: gpu-spin 0.85s linear infinite;
|
|
1395
|
+
will-change: transform;
|
|
1396
|
+
transform: translate3d(0,0,0);
|
|
1397
|
+
}
|
|
1398
|
+
` }),
|
|
1399
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full border-2 border-white border-dashed rounded-[2.5rem] flex items-center justify-center gpu-spinner-container", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
|
|
1400
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "gpu-spinner" }),
|
|
1401
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-lg font-semibold mt-4", children: "Preparing Script..." }),
|
|
1402
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-xs font-medium mt-2", children: "Please wait while we prepare your screenplay." }),
|
|
1403
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white/80 text-xs mt-2", children: "Status: Arranging data..." })
|
|
1404
|
+
] }) })
|
|
1405
|
+
] }),
|
|
1406
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sticky top-0 left-0 right-0 z-50 w-full bg-zinc-50/95 backdrop-blur-md border-b border-zinc-200/60 shadow-[0_1px_2px_rgba(0,0,0,0.01)] flex flex-wrap lg:flex-nowrap items-center justify-between px-3 sm:px-6 py-2 mb-8 select-none transition-all gap-y-2", children: [
|
|
1407
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 w-auto shrink-0 order-1", children: [
|
|
1408
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold tracking-wider text-blumine-400/80 uppercase", children: "Script Editor" }),
|
|
1409
|
+
autosaveStatus === "saved" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-2.5 py-0.5 rounded-full bg-emerald-50 text-emerald-600 border border-emerald-100/50 animate-in fade-in duration-300", children: [
|
|
1410
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse" }),
|
|
1411
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-semibold tracking-wide", children: "Script synced" })
|
|
1412
|
+
] }),
|
|
1413
|
+
autosaveStatus === "saving" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-2.5 py-0.5 rounded-full bg-blue-50 text-blue-600 border border-blue-100/50 animate-in fade-in duration-300", children: [
|
|
1414
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-2.5 h-2.5 animate-spin" }),
|
|
1415
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-semibold tracking-wide", children: "Saving..." })
|
|
1416
|
+
] }),
|
|
1417
|
+
autosaveStatus === "typing" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-2.5 py-0.5 rounded-full bg-amber-50 text-amber-600 border border-amber-100/50 animate-in fade-in duration-300", children: [
|
|
1418
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-amber-500 animate-pulse" }),
|
|
1419
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-semibold tracking-wide", children: "Drafting..." })
|
|
1420
|
+
] }),
|
|
1421
|
+
autosaveStatus === "error" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-2.5 py-0.5 rounded-full bg-rose-50 text-rose-600 border border-rose-100/50 animate-in fade-in duration-300", children: [
|
|
1422
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-rose-500 animate-pulse" }),
|
|
1423
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-semibold tracking-wide", children: "Error saving" })
|
|
1424
|
+
] })
|
|
1425
|
+
] }),
|
|
1426
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center gap-1 sm:gap-2 basis-full lg:basis-auto flex-initial min-w-0 py-1 lg:py-0.5 order-3 lg:order-2", children: blockTypes.map((type) => {
|
|
1347
1427
|
var _a;
|
|
1348
1428
|
const selected = ((_a = blocks.find((b) => b.id === focusedBlockId)) == null ? void 0 : _a.type) === type;
|
|
1349
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
1350
|
-
|
|
1429
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1430
|
+
Tooltip,
|
|
1351
1431
|
{
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1432
|
+
content: blockStyles[type].label,
|
|
1433
|
+
shortcut: "Ctrl + \u2191/\u2193",
|
|
1434
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1435
|
+
"button",
|
|
1436
|
+
{
|
|
1437
|
+
type: "button",
|
|
1438
|
+
disabled: isLocked,
|
|
1439
|
+
onClick: () => handleBlockTypeChange(type),
|
|
1440
|
+
className: `w-8 h-8 sm:w-9 sm:h-9 flex shrink-0 items-center justify-center rounded-lg transition-all duration-150 ease-out active:scale-95 whitespace-nowrap ${selected ? "bg-blumine-200/60 text-blumine-800 shadow-sm border border-blumine-200/50" : "text-blumine-400 hover:bg-blumine-100 hover:text-blumine-700"} ${isLocked ? "opacity-50 cursor-not-allowed" : ""}`,
|
|
1441
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1442
|
+
"div",
|
|
1443
|
+
{
|
|
1444
|
+
className: `flex items-center justify-center transition-all duration-150 ${selected ? "scale-105 opacity-100" : "opacity-80 hover:opacity-100"}`,
|
|
1445
|
+
children: icons[type]
|
|
1446
|
+
}
|
|
1447
|
+
)
|
|
1448
|
+
}
|
|
1449
|
+
)
|
|
1366
1450
|
},
|
|
1367
1451
|
type
|
|
1368
1452
|
);
|
|
1369
1453
|
}) }),
|
|
1370
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5
|
|
1371
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1372
|
-
showPdfImport && !isLocked && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1373
|
-
PdfImporter,
|
|
1374
|
-
{
|
|
1375
|
-
disabled: isLocked,
|
|
1376
|
-
onScriptImported: handleScriptImport,
|
|
1377
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center rounded-full text-blumine-500 hover:text-blumine-900 hover:bg-white/70 transition active:scale-95", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" }) })
|
|
1378
|
-
}
|
|
1379
|
-
),
|
|
1380
|
-
onToggleLock && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1381
|
-
"button",
|
|
1382
|
-
{
|
|
1383
|
-
onClick: onToggleLock,
|
|
1384
|
-
className: `w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center rounded-full transition-all duration-200 active:scale-95 ${isLocked ? "text-rose-500 bg-rose-50/60" : "text-blumine-500 hover:text-blumine-900 hover:bg-white/70"}`,
|
|
1385
|
-
children: isLocked ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Lock, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Unlock, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" })
|
|
1386
|
-
}
|
|
1387
|
-
),
|
|
1388
|
-
onSave && showSaveButton && !isLocked && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1454
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-1 sm:gap-1.5 w-auto lg:w-[220px] shrink-0 order-2 lg:order-3", children: [
|
|
1455
|
+
onSave && showSaveButton && !isLocked && /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: "Save changes", align: "right", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1389
1456
|
"button",
|
|
1390
1457
|
{
|
|
1391
1458
|
onClick: onSave,
|
|
1392
|
-
className: "w-
|
|
1393
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
className: "w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center rounded-full text-blumine-500 hover:text-blumine-900 hover:bg-white/70 transition active:scale-95",
|
|
1401
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileDown, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" })
|
|
1459
|
+
className: "w-8 h-8 sm:w-9 sm:h-9 flex items-center justify-center rounded-lg text-blumine-400 hover:bg-blumine-100 hover:text-blumine-700 transition active:scale-95",
|
|
1460
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1461
|
+
lucideReact.Save,
|
|
1462
|
+
{
|
|
1463
|
+
className: "w-4 h-4 sm:w-[18px] sm:h-[18px]",
|
|
1464
|
+
strokeWidth: 1.5
|
|
1465
|
+
}
|
|
1466
|
+
)
|
|
1402
1467
|
}
|
|
1403
|
-
),
|
|
1404
|
-
|
|
1405
|
-
|
|
1468
|
+
) }),
|
|
1469
|
+
onToggleLock && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1470
|
+
Tooltip,
|
|
1406
1471
|
{
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1472
|
+
content: isLocked ? "Unlock Screenplay" : "Lock Screenplay",
|
|
1473
|
+
align: "right",
|
|
1474
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1475
|
+
"button",
|
|
1476
|
+
{
|
|
1477
|
+
onClick: onToggleLock,
|
|
1478
|
+
className: `w-8 h-8 sm:w-9 sm:h-9 flex items-center justify-center rounded-lg transition active:scale-95 ${isLocked ? "text-rose-500 hover:bg-rose-50 hover:text-rose-700 bg-rose-50/50" : "text-blumine-400 hover:bg-blumine-100 hover:text-blumine-700"}`,
|
|
1479
|
+
children: isLocked ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1480
|
+
lucideReact.Lock,
|
|
1481
|
+
{
|
|
1482
|
+
className: "w-4 h-4 sm:w-[18px] sm:h-[18px]",
|
|
1483
|
+
strokeWidth: 1.5
|
|
1484
|
+
}
|
|
1485
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1486
|
+
lucideReact.Unlock,
|
|
1487
|
+
{
|
|
1488
|
+
className: "w-4 h-4 sm:w-[18px] sm:h-[18px]",
|
|
1489
|
+
strokeWidth: 1.5
|
|
1490
|
+
}
|
|
1491
|
+
)
|
|
1492
|
+
}
|
|
1493
|
+
)
|
|
1410
1494
|
}
|
|
1411
1495
|
),
|
|
1412
1496
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rulesRef, className: "relative flex items-center", children: [
|
|
1413
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1497
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: "Settings & Actions", align: "right", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1414
1498
|
"button",
|
|
1415
1499
|
{
|
|
1416
1500
|
onClick: () => setIsRulesOpen(!isRulesOpen),
|
|
1417
|
-
className: `w-
|
|
1418
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1501
|
+
className: `w-8 h-8 sm:w-9 sm:h-9 flex items-center justify-center rounded-lg transition-all duration-200 active:scale-95 ${isRulesOpen ? "bg-blumine-200/60 text-blumine-800 shadow-sm border border-blumine-200/50" : "text-blumine-400 hover:bg-blumine-100 hover:text-blumine-700"}`,
|
|
1502
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1503
|
+
lucideReact.Cog,
|
|
1504
|
+
{
|
|
1505
|
+
className: "w-4 h-4 sm:w-[18px] sm:h-[18px]",
|
|
1506
|
+
strokeWidth: 1.5
|
|
1507
|
+
}
|
|
1508
|
+
)
|
|
1419
1509
|
}
|
|
1420
|
-
),
|
|
1421
|
-
isRulesOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute
|
|
1422
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-
|
|
1423
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between mb-
|
|
1424
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 flex items-center justify-center rounded-xl bg-gradient-to-b from-white to-blumine-50 text-blumine-
|
|
1510
|
+
) }),
|
|
1511
|
+
isRulesOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 top-[calc(100%+14px)] w-[360px] max-w-[90vw] p-4 rounded-2xl bg-white border border-blumine-200 shadow-2xl backdrop-blur-xl origin-top-right animate-in fade-in zoom-in-95 z-50", children: [
|
|
1512
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-1.5 right-3.5 w-3 h-3 rotate-45 bg-white border-l border-t border-blumine-200" }),
|
|
1513
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between mb-4 px-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3", children: [
|
|
1514
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 flex items-center justify-center rounded-xl bg-gradient-to-b from-white to-blumine-50/50 text-blumine-600 border border-blumine-200 shadow-inner", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Cog, { className: "w-4 h-4", strokeWidth: 1.5 }) }),
|
|
1425
1515
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1426
|
-
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-[15px] font-semibold text-blumine-
|
|
1427
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-blumine-
|
|
1516
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-[15px] font-semibold text-blumine-800 leading-tight", children: "Settings & Actions" }),
|
|
1517
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-blumine-400 mt-1", children: "Screenplay controls & keyboard shortcuts" })
|
|
1428
1518
|
] })
|
|
1429
1519
|
] }) }),
|
|
1430
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1431
|
-
{
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1520
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 mb-4", children: [
|
|
1521
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1.5 mb-1", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold text-blumine-400 uppercase tracking-wider", children: "Document Actions" }) }),
|
|
1522
|
+
showPdfImport && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1523
|
+
PdfImporter,
|
|
1524
|
+
{
|
|
1525
|
+
disabled: isLocked,
|
|
1526
|
+
onScriptImported: (title, content, preParsedBlocks) => {
|
|
1527
|
+
handleScriptImport(title, content, preParsedBlocks);
|
|
1528
|
+
setIsRulesOpen(false);
|
|
1529
|
+
},
|
|
1530
|
+
className: "w-full text-left",
|
|
1531
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1532
|
+
"div",
|
|
1533
|
+
{
|
|
1534
|
+
className: `w-full flex items-center gap-3 p-3 rounded-xl border border-blumine-100 bg-gradient-to-b from-white to-blumine-50/10 hover:from-blumine-50/40 hover:to-blumine-50/60 hover:border-blumine-200 hover:shadow-sm active:scale-[0.98] transition-all duration-200 cursor-pointer ${isLocked ? "opacity-60 cursor-not-allowed pointer-events-none" : ""}`,
|
|
1535
|
+
children: [
|
|
1536
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 flex items-center justify-center rounded-lg bg-blumine-50 text-blumine-600 border border-blumine-100 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "w-4 h-4", strokeWidth: 1.5 }) }),
|
|
1537
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 text-left", children: [
|
|
1538
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-blumine-800 leading-none", children: "Import PDF" }),
|
|
1539
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] text-blumine-400 mt-1", children: isLocked ? "Unlock screenplay to import files" : "Load screenplay from external file" })
|
|
1540
|
+
] })
|
|
1541
|
+
]
|
|
1542
|
+
}
|
|
1543
|
+
)
|
|
1544
|
+
}
|
|
1545
|
+
),
|
|
1546
|
+
onToggleLock && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1547
|
+
"button",
|
|
1548
|
+
{
|
|
1549
|
+
onClick: () => {
|
|
1550
|
+
onToggleLock();
|
|
1551
|
+
},
|
|
1552
|
+
className: "w-full text-left flex items-center gap-3 p-3 rounded-xl border border-blumine-100 bg-gradient-to-b from-white to-blumine-50/10 hover:from-blumine-50/40 hover:to-blumine-50/60 hover:border-blumine-200 hover:shadow-sm active:scale-[0.98] transition-all duration-200",
|
|
1553
|
+
children: [
|
|
1554
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1555
|
+
"div",
|
|
1465
1556
|
{
|
|
1466
|
-
className:
|
|
1467
|
-
children:
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1557
|
+
className: `w-8 h-8 flex items-center justify-center rounded-lg shrink-0 border ${isLocked ? "bg-rose-50 text-rose-500 border-rose-100/50" : "bg-blumine-50 text-blumine-600 border-blumine-100/50"}`,
|
|
1558
|
+
children: isLocked ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Lock, { className: "w-4 h-4", strokeWidth: 1.5 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Unlock, { className: "w-4 h-4", strokeWidth: 1.5 })
|
|
1559
|
+
}
|
|
1560
|
+
),
|
|
1561
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1562
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-blumine-800 leading-none", children: isLocked ? "Unlock Screenplay" : "Lock Screenplay" }),
|
|
1563
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] text-blumine-400 mt-1", children: isLocked ? "Enable editing controls" : "Prevent accidental editing" })
|
|
1564
|
+
] })
|
|
1565
|
+
]
|
|
1566
|
+
}
|
|
1567
|
+
),
|
|
1568
|
+
onSaveAsPdf && showSaveButton && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1569
|
+
"button",
|
|
1570
|
+
{
|
|
1571
|
+
onClick: () => {
|
|
1572
|
+
onSaveAsPdf();
|
|
1573
|
+
setIsRulesOpen(false);
|
|
1574
|
+
},
|
|
1575
|
+
className: "w-full text-left flex items-center gap-3 p-3 rounded-xl border border-blumine-100 bg-gradient-to-b from-white to-blumine-50/10 hover:from-blumine-50/40 hover:to-blumine-50/60 hover:border-blumine-200 hover:shadow-sm active:scale-[0.98] transition-all duration-200",
|
|
1576
|
+
children: [
|
|
1577
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 flex items-center justify-center rounded-lg bg-blumine-50 text-blumine-600 border border-blumine-100 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileDown, { className: "w-4 h-4", strokeWidth: 1.5 }) }),
|
|
1578
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1579
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-blumine-800 leading-none", children: "Save as PDF" }),
|
|
1580
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] text-blumine-400 mt-1", children: "Export screenplay to standard print PDF" })
|
|
1581
|
+
] })
|
|
1582
|
+
]
|
|
1583
|
+
}
|
|
1584
|
+
)
|
|
1585
|
+
] }),
|
|
1586
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-blumine-100/60 my-4" }),
|
|
1587
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1588
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1.5 mb-1", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold text-blumine-400 uppercase tracking-wider", children: "Keyboard Shortcuts" }) }),
|
|
1589
|
+
[
|
|
1590
|
+
{
|
|
1591
|
+
title: "New Block",
|
|
1592
|
+
desc: "Insert the next section",
|
|
1593
|
+
key: ["Enter"]
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
title: "Delete Block",
|
|
1597
|
+
desc: "Remove selected section",
|
|
1598
|
+
key: ["Backspace \u{1F860}"]
|
|
1599
|
+
},
|
|
1600
|
+
{
|
|
1601
|
+
title: "Change Type",
|
|
1602
|
+
desc: "Cycle block styles",
|
|
1603
|
+
key: ["Ctrl", "+", "\u2191/\u2193"]
|
|
1604
|
+
}
|
|
1605
|
+
].map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1606
|
+
"div",
|
|
1607
|
+
{
|
|
1608
|
+
className: "flex items-center justify-between gap-4 p-2.5 rounded-xl bg-gradient-to-b from-white to-blumine-50/30 border border-blumine-100/80 shadow-[inset_0_1px_0_rgba(255,255,255,0.8)]",
|
|
1609
|
+
children: [
|
|
1610
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0 text-left", children: [
|
|
1611
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-blumine-800", children: item.title }),
|
|
1612
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-blumine-400 truncate mt-0.5", children: item.desc })
|
|
1613
|
+
] }),
|
|
1614
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1 shrink-0", children: item.key.map(
|
|
1615
|
+
(k, i) => k === "+" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1616
|
+
"span",
|
|
1617
|
+
{
|
|
1618
|
+
className: "text-blumine-400 text-xs font-semibold",
|
|
1619
|
+
children: "+"
|
|
1620
|
+
},
|
|
1621
|
+
i
|
|
1622
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1623
|
+
"kbd",
|
|
1624
|
+
{
|
|
1625
|
+
className: "min-w-[28px] px-2 py-1 text-[11px] font-bold text-blumine-700 bg-gradient-to-b from-white to-blumine-100 border border-blumine-200 rounded-lg text-center shadow-[inset_0_1px_0_rgba(255,255,255,0.9)]",
|
|
1626
|
+
children: k
|
|
1627
|
+
},
|
|
1628
|
+
i
|
|
1629
|
+
)
|
|
1630
|
+
) })
|
|
1631
|
+
]
|
|
1632
|
+
},
|
|
1633
|
+
item.title
|
|
1634
|
+
))
|
|
1635
|
+
] })
|
|
1476
1636
|
] })
|
|
1477
1637
|
] }),
|
|
1478
1638
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1479
1639
|
"div",
|
|
1480
1640
|
{
|
|
1481
1641
|
ref: languageRef,
|
|
1482
|
-
className: "flex items-center pl-1.5 border-l border-blumine-200
|
|
1642
|
+
className: "relative flex items-center pl-1.5 border-l border-blumine-200",
|
|
1483
1643
|
children: [
|
|
1484
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1485
|
-
|
|
1644
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1645
|
+
Tooltip,
|
|
1486
1646
|
{
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
children:
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1647
|
+
content: `Input Language: ${LANGUAGE_CONFIGS[currentLanguage].name}`,
|
|
1648
|
+
align: "right",
|
|
1649
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1650
|
+
"button",
|
|
1651
|
+
{
|
|
1652
|
+
onClick: () => setIsLanguageOpen(!isLanguageOpen),
|
|
1653
|
+
className: `w-8 h-8 sm:w-9 sm:h-9 flex items-center justify-center rounded-lg transition-all duration-200 active:scale-95 ${isLanguageOpen ? "bg-blumine-200/60 text-blumine-800 border border-blumine-200 shadow-sm" : "text-blumine-400 hover:bg-blumine-100 hover:text-blumine-700"}`,
|
|
1654
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1655
|
+
lucideReact.Languages,
|
|
1656
|
+
{
|
|
1657
|
+
className: "w-4 h-4 sm:w-[18px] sm:h-[18px]",
|
|
1658
|
+
strokeWidth: 1.5
|
|
1659
|
+
}
|
|
1660
|
+
)
|
|
1661
|
+
}
|
|
1662
|
+
)
|
|
1493
1663
|
}
|
|
1494
1664
|
),
|
|
1495
|
-
isLanguageOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 top-[calc(100%+14px)] w-[400px] max-w-[90vw] p-3 rounded-
|
|
1496
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-
|
|
1665
|
+
isLanguageOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 top-[calc(100%+14px)] w-[400px] max-w-[90vw] p-3 rounded-2xl bg-white border border-blumine-200 shadow-xl backdrop-blur-xl origin-top-right animate-in fade-in zoom-in-95 z-50", children: [
|
|
1666
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-1.5 right-3.5 w-3 h-3 rotate-45 bg-white border-l border-t border-blumine-200" }),
|
|
1497
1667
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-2 mb-3", children: [
|
|
1498
|
-
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-[13px] font-bold text-blumine-
|
|
1499
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-blumine-
|
|
1668
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-[13px] font-bold text-blumine-800 uppercase tracking-wider", children: "Select Input Language" }),
|
|
1669
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-blumine-400 mt-0.5", children: "Direct keyboard mapping (InScript logic)" })
|
|
1500
1670
|
] }),
|
|
1501
1671
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-1.5 max-h-[300px] overflow-y-auto pr-1 flex-1 custom-scrollbar", children: Object.keys(LANGUAGE_CONFIGS).map(
|
|
1502
1672
|
(lang) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -1506,16 +1676,16 @@ function ScreenplayEditorView({
|
|
|
1506
1676
|
setCurrentLanguage(lang);
|
|
1507
1677
|
setIsLanguageOpen(false);
|
|
1508
1678
|
},
|
|
1509
|
-
className: `group flex items-center justify-between px-3.5 py-2.5 rounded-
|
|
1679
|
+
className: `group flex items-center justify-between px-3.5 py-2.5 rounded-xl transition-all duration-200 ${currentLanguage === lang ? "bg-blumine-100/80 text-blumine-800 border border-blumine-200/50 shadow-sm" : "hover:bg-blumine-50 border border-transparent"}`,
|
|
1510
1680
|
children: [
|
|
1511
1681
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1512
1682
|
"span",
|
|
1513
1683
|
{
|
|
1514
|
-
className: `text-sm font-medium ${currentLanguage === lang ? "text-blumine-
|
|
1684
|
+
className: `text-sm font-medium ${currentLanguage === lang ? "text-blumine-800 font-semibold" : "text-blumine-500"}`,
|
|
1515
1685
|
children: LANGUAGE_CONFIGS[lang].name
|
|
1516
1686
|
}
|
|
1517
1687
|
),
|
|
1518
|
-
currentLanguage === lang && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-5 rounded-full bg-blumine-
|
|
1688
|
+
currentLanguage === lang && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-5 rounded-full bg-blumine-700 flex items-center justify-center text-white", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3 h-3", strokeWidth: 2 }) })
|
|
1519
1689
|
]
|
|
1520
1690
|
},
|
|
1521
1691
|
lang
|
|
@@ -1525,15 +1695,20 @@ function ScreenplayEditorView({
|
|
|
1525
1695
|
]
|
|
1526
1696
|
}
|
|
1527
1697
|
),
|
|
1528
|
-
LANGUAGE_CONFIGS[currentLanguage].isPhonetic && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1698
|
+
LANGUAGE_CONFIGS[currentLanguage].isPhonetic && /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: "Phonetic Keyboard Rules", align: "right", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1529
1699
|
"button",
|
|
1530
1700
|
{
|
|
1531
1701
|
onClick: () => setIsPhoneticGuideOpen(true),
|
|
1532
|
-
className: "w-
|
|
1533
|
-
|
|
1534
|
-
|
|
1702
|
+
className: "w-8 h-8 sm:w-9 sm:h-9 flex items-center justify-center rounded-lg text-blumine-400 hover:bg-blumine-100 hover:text-blumine-700 transition active:scale-95 ml-1",
|
|
1703
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1704
|
+
lucideReact.Keyboard,
|
|
1705
|
+
{
|
|
1706
|
+
className: "w-4 h-4 sm:w-[18px] sm:h-[18px]",
|
|
1707
|
+
strokeWidth: 1.5
|
|
1708
|
+
}
|
|
1709
|
+
)
|
|
1535
1710
|
}
|
|
1536
|
-
)
|
|
1711
|
+
) })
|
|
1537
1712
|
] })
|
|
1538
1713
|
] }),
|
|
1539
1714
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1544,7 +1719,7 @@ function ScreenplayEditorView({
|
|
|
1544
1719
|
onClose: () => setIsPhoneticGuideOpen(false)
|
|
1545
1720
|
}
|
|
1546
1721
|
),
|
|
1547
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-12 pb-24 w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1722
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-12 pb-24 w-full items-center justify-center px-4 sm:px-6 md:px-8", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1548
1723
|
"div",
|
|
1549
1724
|
{
|
|
1550
1725
|
className: "relative bg-[#fdfdfc] shadow-2xl shadow-zinc-300/60 border border-zinc-100 rounded-sm md:rounded-md pl-[1.5in] py-[1in] pr-[1in] flex flex-col w-[210mm] min-h-[297mm] shrink-0",
|
|
@@ -1557,346 +1732,370 @@ function ScreenplayEditorView({
|
|
|
1557
1732
|
},
|
|
1558
1733
|
"data-screenplay-editor": "true",
|
|
1559
1734
|
dir: LANGUAGE_CONFIGS[currentLanguage].dir,
|
|
1560
|
-
children: blocks.map((block) =>
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
"select",
|
|
1593
|
-
{
|
|
1594
|
-
className: "rounded-md text-zinc-800 font-bold px-1.5 py-1 appearance-none bg-transparent hover:bg-zinc-200/50 outline-none cursor-pointer w-fit transition-colors",
|
|
1595
|
-
"aria-label": "Scene Type",
|
|
1596
|
-
disabled: isLocked,
|
|
1597
|
-
value: (_a = block.sceneType) != null ? _a : "INT.",
|
|
1598
|
-
onChange: (e) => handleSceneTypeChange(block.id, e.target.value),
|
|
1599
|
-
style: {
|
|
1600
|
-
appearance: "none"
|
|
1601
|
-
},
|
|
1602
|
-
children: [
|
|
1603
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "INT." }),
|
|
1604
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "EXT." }),
|
|
1605
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "INT/EXT." })
|
|
1606
|
-
]
|
|
1607
|
-
}
|
|
1608
|
-
),
|
|
1609
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1610
|
-
"div",
|
|
1611
|
-
{
|
|
1612
|
-
ref: (el) => {
|
|
1613
|
-
if (!el) return;
|
|
1614
|
-
refs.current[block.id] = el;
|
|
1615
|
-
},
|
|
1616
|
-
contentEditable: !isLocked,
|
|
1617
|
-
suppressContentEditableWarning: true,
|
|
1618
|
-
"aria-label": `Scene Heading: ${block.text}`,
|
|
1619
|
-
"aria-haspopup": "listbox",
|
|
1620
|
-
"aria-expanded": focusedBlockId === block.id && showSuggestions && locations.length > 0,
|
|
1621
|
-
spellCheck: false,
|
|
1622
|
-
className: "min-w-[3rem] py-1 outline-none text-base font-bold uppercase tracking-widest break-all bg-transparent",
|
|
1623
|
-
style: {
|
|
1624
|
-
minWidth: "3rem"
|
|
1625
|
-
},
|
|
1626
|
-
onInput: (e) => handleBlockTextChange(
|
|
1627
|
-
block.id,
|
|
1628
|
-
e.target.innerText
|
|
1629
|
-
),
|
|
1630
|
-
onKeyDown: (e) => handleKeyDown(e, block.id, block.text),
|
|
1631
|
-
onFocus: () => handleFocus(block.id),
|
|
1632
|
-
onBlur: () => handleBlur(block.id)
|
|
1633
|
-
}
|
|
1634
|
-
),
|
|
1635
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-400/80 font-bold", children: "-" }),
|
|
1636
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1637
|
-
"select",
|
|
1638
|
-
{
|
|
1639
|
-
className: "rounded-md text-zinc-800 font-bold px-1.5 py-1 appearance-none bg-transparent hover:bg-zinc-200/50 outline-none cursor-pointer transition-colors",
|
|
1640
|
-
"aria-label": "Time of Day",
|
|
1641
|
-
disabled: isLocked,
|
|
1642
|
-
value: (_b = block.timeOfDay) != null ? _b : "DAY",
|
|
1643
|
-
style: {
|
|
1644
|
-
appearance: "none"
|
|
1645
|
-
},
|
|
1646
|
-
onChange: (e) => handleTimeOfDayChange(block.id, e.target.value),
|
|
1647
|
-
children: timeOfDayOptions.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { children: t }, t))
|
|
1648
|
-
}
|
|
1649
|
-
)
|
|
1650
|
-
] }),
|
|
1651
|
-
focusedBlockId === block.id && showSuggestions && locations.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1652
|
-
"div",
|
|
1653
|
-
{
|
|
1654
|
-
role: "listbox",
|
|
1655
|
-
id: `suggestions-${block.id}`,
|
|
1656
|
-
className: "absolute top-[calc(100%+6px)] left-0 min-w-[240px] z-50 bg-white border border-slate-200/80 shadow-xl shadow-slate-200/40 rounded-xl py-1 overflow-hidden animate-in fade-in zoom-in-95 duration-150",
|
|
1657
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-60 overflow-y-auto custom-scrollbar", children: locations.filter(
|
|
1658
|
-
(loc) => loc.startsWith(block.text.toUpperCase()) && loc !== block.text.toUpperCase()
|
|
1659
|
-
).map((loc) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1660
|
-
"div",
|
|
1661
|
-
{
|
|
1662
|
-
role: "option",
|
|
1663
|
-
className: "group flex items-center justify-between px-4 py-2.5 cursor-pointer transition-all duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
1664
|
-
onMouseDown: (e) => {
|
|
1665
|
-
e.preventDefault();
|
|
1666
|
-
const element = refs.current[block.id];
|
|
1667
|
-
if (element) {
|
|
1668
|
-
element.innerText = loc;
|
|
1669
|
-
handleBlockTextChange(block.id, loc);
|
|
1670
|
-
element.focus();
|
|
1671
|
-
const range = document.createRange();
|
|
1672
|
-
const sel = window.getSelection();
|
|
1673
|
-
range.selectNodeContents(element);
|
|
1674
|
-
range.collapse(false);
|
|
1675
|
-
sel == null ? void 0 : sel.removeAllRanges();
|
|
1676
|
-
sel == null ? void 0 : sel.addRange(range);
|
|
1677
|
-
}
|
|
1678
|
-
handleBlur(block.id);
|
|
1679
|
-
},
|
|
1680
|
-
children: [
|
|
1681
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] font-semibold tracking-wide text-slate-600 uppercase line-clamp-1", children: loc }),
|
|
1682
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "w-3.5 h-3.5 text-slate-300 opacity-0 -translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200" })
|
|
1683
|
-
]
|
|
1684
|
-
},
|
|
1685
|
-
loc
|
|
1686
|
-
)) })
|
|
1687
|
-
}
|
|
1688
|
-
),
|
|
1689
|
-
focusedBlockId === block.id && phoneticSuggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1690
|
-
PhoneticSuggestions,
|
|
1691
|
-
{
|
|
1692
|
-
suggestions: phoneticSuggestions,
|
|
1693
|
-
onSelect: handleSelectPhoneticSuggestion
|
|
1694
|
-
}
|
|
1695
|
-
)
|
|
1696
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1697
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1698
|
-
"div",
|
|
1699
|
-
{
|
|
1700
|
-
ref: (el) => {
|
|
1701
|
-
if (!el) return;
|
|
1702
|
-
refs.current[block.id] = el;
|
|
1703
|
-
},
|
|
1704
|
-
contentEditable: !isLocked,
|
|
1705
|
-
suppressContentEditableWarning: true,
|
|
1706
|
-
"aria-label": `${blockStyles[block.type].label} text`,
|
|
1707
|
-
"aria-multiline": block.type === "ACTION" || block.type === "DIALOGUE",
|
|
1708
|
-
spellCheck: false,
|
|
1709
|
-
className: `block outline-none w-full min-h-[2.5rem] px-4 py-2 break-words ${blockStyles[block.type].className}`,
|
|
1710
|
-
onInput: (e) => handleBlockTextChange(
|
|
1711
|
-
block.id,
|
|
1712
|
-
e.target.innerText
|
|
1713
|
-
),
|
|
1714
|
-
onKeyDown: (e) => handleKeyDown(e, block.id, block.text),
|
|
1715
|
-
onFocus: () => handleFocus(block.id),
|
|
1716
|
-
onBlur: () => handleBlur(block.id),
|
|
1717
|
-
style: blockStyles[block.type].inputStyle
|
|
1718
|
-
}
|
|
1719
|
-
),
|
|
1720
|
-
focusedBlockId === block.id && block.type === "CHARACTER" && showSuggestions && characters.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1721
|
-
"div",
|
|
1722
|
-
{
|
|
1723
|
-
role: "listbox",
|
|
1724
|
-
id: `suggestions-${block.id}`,
|
|
1725
|
-
className: "absolute top-[calc(100%+8px)] left-1/2 -translate-x-1/2 w-72 z-50 bg-white border border-slate-200 shadow-2xl shadow-slate-200/60 rounded-xl py-2 overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
1726
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characters.filter(
|
|
1727
|
-
(char) => char.startsWith(block.text.toUpperCase()) && char !== block.text.toUpperCase()
|
|
1728
|
-
).map((char) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1729
|
-
"div",
|
|
1730
|
-
{
|
|
1731
|
-
role: "option",
|
|
1732
|
-
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
1733
|
-
onMouseDown: (e) => {
|
|
1734
|
-
e.preventDefault();
|
|
1735
|
-
const element = refs.current[block.id];
|
|
1736
|
-
if (element) {
|
|
1737
|
-
element.innerText = char;
|
|
1738
|
-
handleBlockTextChange(block.id, char);
|
|
1739
|
-
element.focus();
|
|
1740
|
-
const range = document.createRange();
|
|
1741
|
-
const sel = window.getSelection();
|
|
1742
|
-
range.selectNodeContents(element);
|
|
1743
|
-
range.collapse(false);
|
|
1744
|
-
sel == null ? void 0 : sel.removeAllRanges();
|
|
1745
|
-
sel == null ? void 0 : sel.addRange(range);
|
|
1746
|
-
}
|
|
1747
|
-
handleBlur(block.id);
|
|
1748
|
-
},
|
|
1749
|
-
children: [
|
|
1750
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-3.5 h-3.5 text-slate-300 group-hover:text-blumine-500 transition-colors mr-3" }),
|
|
1751
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: char }),
|
|
1752
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3 h-3 text-slate-200 opacity-0 group-hover:opacity-100 transition-all -translate-x-1 group-hover:translate-x-0" })
|
|
1753
|
-
]
|
|
1754
|
-
},
|
|
1755
|
-
char
|
|
1756
|
-
)) })
|
|
1757
|
-
}
|
|
1758
|
-
),
|
|
1759
|
-
focusedBlockId === block.id && block.type === "CHARACTER" && showExtensionSuggestions && characterExtensions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1760
|
-
"div",
|
|
1761
|
-
{
|
|
1762
|
-
role: "listbox",
|
|
1763
|
-
id: `extension-suggestions-${block.id}`,
|
|
1764
|
-
className: "absolute top-[calc(100%+8px)] left-1/2 -translate-x-1/2 w-72 z-50 bg-white border border-slate-200 shadow-2xl shadow-slate-200/60 rounded-xl py-2 overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
1765
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characterExtensions.filter((ext) => {
|
|
1766
|
-
const openParenIndex = block.text.lastIndexOf("(");
|
|
1767
|
-
const query = openParenIndex > -1 ? block.text.substring(openParenIndex + 1).toUpperCase() : "";
|
|
1768
|
-
return ext.toUpperCase().includes(query);
|
|
1769
|
-
}).map((ext) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1770
|
-
"div",
|
|
1771
|
-
{
|
|
1772
|
-
role: "option",
|
|
1773
|
-
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
1774
|
-
onMouseDown: (e) => {
|
|
1775
|
-
e.preventDefault();
|
|
1776
|
-
handleSelectCharacterExtension(ext);
|
|
1777
|
-
},
|
|
1778
|
-
children: [
|
|
1779
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: ext }),
|
|
1780
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3 h-3 text-slate-200 opacity-0 group-hover:opacity-100 transition-all -translate-x-1 group-hover:translate-x-0" })
|
|
1781
|
-
]
|
|
1782
|
-
},
|
|
1783
|
-
ext
|
|
1784
|
-
)) })
|
|
1785
|
-
}
|
|
1786
|
-
),
|
|
1787
|
-
focusedBlockId === block.id && !enhancingBlockId && (block.type === "ACTION" || block.type === "DIALOGUE") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1788
|
-
"button",
|
|
1789
|
-
{
|
|
1790
|
-
onClick: () => handleEnhance(block),
|
|
1791
|
-
className: "absolute -right-12 top-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 rounded-xl border border-blumine-200/50 bg-white text-blumine-500 hover:border-blumine-400/60 hover:bg-blumine-50/90 hover:ring-2 hover:ring-blumine-100 hover:scale-105 active:scale-95 transition-all duration-200 group/enhance animate-in fade-in slide-in-from-left-2",
|
|
1792
|
-
title: "Enhance with AI",
|
|
1793
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-4 h-4 group-hover/enhance:animate-pulse transition-colors" })
|
|
1794
|
-
}
|
|
1795
|
-
),
|
|
1796
|
-
enhancingBlockId === block.id && (enhancementSuggestion || isEnhancing) && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1797
|
-
"div",
|
|
1798
|
-
{
|
|
1799
|
-
className: "absolute left-0 right-0 -top-4 -translate-y-full z-[50] animate-in fade-in zoom-in-95 slide-in-from-bottom-4 duration-300",
|
|
1800
|
-
style: { width: "calc(100% + 2rem)", left: "-1rem" },
|
|
1801
|
-
children: [
|
|
1802
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto w-full rounded-[2rem] bg-white border border-white/60 shadow-[0_14px_34px_rgba(16,37,54,0.1),0_2px_8px_rgba(16,37,54,0.05)] backdrop-blur-xl p-2 select-none transition-all", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
1803
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pl-1", children: [
|
|
1804
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1805
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9 h-9 flex items-center justify-center rounded-full bg-blumine-50 text-blumine-500 border border-blumine-100 shadow-inner", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-4 h-4" }) }),
|
|
1806
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1807
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-bold text-zinc-900 uppercase tracking-wider leading-none", children: "AI Suggestion" }),
|
|
1808
|
-
!isEnhancing && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-zinc-500 mt-1 font-medium", children: "Refining your screenplay dialogue" })
|
|
1809
|
-
] })
|
|
1810
|
-
] }),
|
|
1811
|
-
!isEnhancing && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
1812
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1813
|
-
"button",
|
|
1814
|
-
{
|
|
1815
|
-
onClick: handleRejectEnhance,
|
|
1816
|
-
className: "px-3 py-2 rounded-full text-xs font-bold text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 transition-all active:scale-95",
|
|
1817
|
-
children: "Discard"
|
|
1818
|
-
}
|
|
1819
|
-
),
|
|
1820
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1821
|
-
"button",
|
|
1822
|
-
{
|
|
1823
|
-
onClick: handleApproveEnhance,
|
|
1824
|
-
className: "px-4 py-2 rounded-full text-xs font-bold bg-blumine-500 text-white hover:bg-blumine-600 shadow-lg shadow-blumine-500/20 transition-all active:scale-95 flex items-center gap-1.5",
|
|
1825
|
-
children: [
|
|
1826
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5" }),
|
|
1827
|
-
"Apply Changes"
|
|
1828
|
-
]
|
|
1829
|
-
}
|
|
1830
|
-
)
|
|
1831
|
-
] })
|
|
1832
|
-
] }),
|
|
1833
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative overflow-hidden rounded-[1.25rem] bg-zinc-50/50 border border-zinc-100 p-4 min-h-[4rem]", children: isEnhancing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2.5 py-1", children: [
|
|
1834
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-3/4 bg-zinc-200/60 animate-pulse rounded-full" }),
|
|
1835
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-1/2 bg-zinc-200/60 animate-pulse rounded-full" })
|
|
1836
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
1837
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-zinc-400 line-through decoration-zinc-300 italic", children: block.text }),
|
|
1838
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-zinc-800 font-medium leading-relaxed", children: enhancementSuggestion })
|
|
1839
|
-
] }) })
|
|
1840
|
-
] }) }),
|
|
1841
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -bottom-1.5 left-1/2 -translate-x-1/2 w-3.5 h-3.5 bg-white border-r border-b border-zinc-200/50 rotate-45" })
|
|
1842
|
-
]
|
|
1843
|
-
}
|
|
1844
|
-
),
|
|
1845
|
-
focusedBlockId === block.id && phoneticSuggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1846
|
-
PhoneticSuggestions,
|
|
1847
|
-
{
|
|
1848
|
-
suggestions: phoneticSuggestions,
|
|
1849
|
-
onSelect: handleSelectPhoneticSuggestion
|
|
1850
|
-
}
|
|
1851
|
-
)
|
|
1852
|
-
] })
|
|
1853
|
-
},
|
|
1854
|
-
block.id + "-" + block.type
|
|
1855
|
-
);
|
|
1856
|
-
})
|
|
1735
|
+
children: blocks.map((block) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1736
|
+
ScreenplayBlockItem,
|
|
1737
|
+
{
|
|
1738
|
+
block,
|
|
1739
|
+
isFocused: focusedBlockId === block.id,
|
|
1740
|
+
isLocked,
|
|
1741
|
+
showSuggestions,
|
|
1742
|
+
showExtensionSuggestions,
|
|
1743
|
+
locations,
|
|
1744
|
+
characters,
|
|
1745
|
+
phoneticSuggestions,
|
|
1746
|
+
characterExtensions,
|
|
1747
|
+
enhancingBlockId,
|
|
1748
|
+
enhancementSuggestion,
|
|
1749
|
+
isEnhancing,
|
|
1750
|
+
currentLanguage,
|
|
1751
|
+
refs,
|
|
1752
|
+
handleSceneNumberChange,
|
|
1753
|
+
handleSceneTypeChange,
|
|
1754
|
+
handleTimeOfDayChange,
|
|
1755
|
+
handleBlockTextChange,
|
|
1756
|
+
handleKeyDown,
|
|
1757
|
+
handleFocus,
|
|
1758
|
+
handleBlur,
|
|
1759
|
+
handleSelectPhoneticSuggestion,
|
|
1760
|
+
handleSelectCharacterExtension,
|
|
1761
|
+
handleEnhance,
|
|
1762
|
+
handleApproveEnhance,
|
|
1763
|
+
handleRejectEnhance
|
|
1764
|
+
},
|
|
1765
|
+
block.id + "-" + block.type
|
|
1766
|
+
))
|
|
1857
1767
|
}
|
|
1858
|
-
) })
|
|
1859
|
-
|
|
1768
|
+
) })
|
|
1769
|
+
] });
|
|
1770
|
+
}
|
|
1771
|
+
var ScreenplayBlockItem = React4__default.default.memo(
|
|
1772
|
+
function ScreenplayBlockItem2({
|
|
1773
|
+
block,
|
|
1774
|
+
isFocused,
|
|
1775
|
+
isLocked,
|
|
1776
|
+
showSuggestions,
|
|
1777
|
+
showExtensionSuggestions,
|
|
1778
|
+
locations,
|
|
1779
|
+
characters,
|
|
1780
|
+
phoneticSuggestions,
|
|
1781
|
+
characterExtensions,
|
|
1782
|
+
enhancingBlockId,
|
|
1783
|
+
enhancementSuggestion,
|
|
1784
|
+
isEnhancing,
|
|
1785
|
+
currentLanguage,
|
|
1786
|
+
refs,
|
|
1787
|
+
handleSceneNumberChange,
|
|
1788
|
+
handleSceneTypeChange,
|
|
1789
|
+
handleTimeOfDayChange,
|
|
1790
|
+
handleBlockTextChange,
|
|
1791
|
+
handleKeyDown,
|
|
1792
|
+
handleFocus,
|
|
1793
|
+
handleBlur,
|
|
1794
|
+
handleSelectPhoneticSuggestion,
|
|
1795
|
+
handleSelectCharacterExtension,
|
|
1796
|
+
handleEnhance,
|
|
1797
|
+
handleApproveEnhance,
|
|
1798
|
+
handleRejectEnhance
|
|
1799
|
+
}) {
|
|
1800
|
+
var _a, _b;
|
|
1801
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1860
1802
|
"div",
|
|
1861
1803
|
{
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1868
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 flex items-center justify-center rounded-[2.5rem] bg-blumine-50 text-blumine-500 border border-blumine-100 shadow-inner", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Save, { className: "w-5 h-5" }) }),
|
|
1869
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mr-5", children: [
|
|
1870
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-bold text-zinc-900 leading-none", children: "Unsaved Changes" }),
|
|
1871
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-zinc-500 mt-1 font-medium", children: "You have changes that haven't been saved yet." })
|
|
1872
|
-
] })
|
|
1873
|
-
] }),
|
|
1874
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 w-full sm:w-auto", children: [
|
|
1804
|
+
"data-block-id": block.id,
|
|
1805
|
+
className: `relative rounded-sm transition-all duration-200 outline-none ${isFocused ? "bg-zinc-100/50" : "bg-transparent"}`,
|
|
1806
|
+
"data-block-type": block.type,
|
|
1807
|
+
children: block.type === "SCENE_HEADING" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1808
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-4 py-1 bg-transparent", children: [
|
|
1875
1809
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1876
|
-
"
|
|
1810
|
+
"input",
|
|
1877
1811
|
{
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1812
|
+
className: "absolute -left-16 top-2 w-12 text-right text-zinc-400 font-semibold select-none bg-transparent outline-none focus:ring-1 focus:ring-blue-400 rounded-sm",
|
|
1813
|
+
spellCheck: false,
|
|
1814
|
+
disabled: isLocked,
|
|
1815
|
+
value: block.sceneNumber || "",
|
|
1816
|
+
onChange: (e) => handleSceneNumberChange(
|
|
1817
|
+
block.id,
|
|
1818
|
+
e.target.value.toUpperCase()
|
|
1819
|
+
),
|
|
1820
|
+
onFocus: () => handleFocus(block.id),
|
|
1821
|
+
onBlur: () => handleBlur(block.id),
|
|
1822
|
+
onKeyDown: (e) => {
|
|
1823
|
+
if (e.key === "Enter" || e.key === "Backspace") {
|
|
1824
|
+
e.stopPropagation();
|
|
1825
|
+
}
|
|
1826
|
+
},
|
|
1827
|
+
"aria-label": "Scene Number"
|
|
1881
1828
|
}
|
|
1882
1829
|
),
|
|
1883
1830
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1884
|
-
"
|
|
1831
|
+
"select",
|
|
1885
1832
|
{
|
|
1886
|
-
|
|
1887
|
-
|
|
1833
|
+
className: "rounded-md text-zinc-800 font-bold px-1.5 py-1 appearance-none bg-transparent hover:bg-zinc-200/50 outline-none cursor-pointer w-fit transition-colors",
|
|
1834
|
+
"aria-label": "Scene Type",
|
|
1835
|
+
disabled: isLocked,
|
|
1836
|
+
value: (_a = block.sceneType) != null ? _a : "INT.",
|
|
1837
|
+
onChange: (e) => handleSceneTypeChange(block.id, e.target.value),
|
|
1838
|
+
style: {
|
|
1839
|
+
appearance: "none"
|
|
1840
|
+
},
|
|
1888
1841
|
children: [
|
|
1889
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1890
|
-
"
|
|
1842
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "INT." }),
|
|
1843
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "EXT." }),
|
|
1844
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "INT/EXT." })
|
|
1891
1845
|
]
|
|
1892
1846
|
}
|
|
1847
|
+
),
|
|
1848
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1849
|
+
"div",
|
|
1850
|
+
{
|
|
1851
|
+
ref: (el) => {
|
|
1852
|
+
if (!el) return;
|
|
1853
|
+
refs.current[block.id] = el;
|
|
1854
|
+
},
|
|
1855
|
+
contentEditable: !isLocked,
|
|
1856
|
+
suppressContentEditableWarning: true,
|
|
1857
|
+
"aria-label": `Scene Heading: ${block.text}`,
|
|
1858
|
+
"aria-haspopup": "listbox",
|
|
1859
|
+
"aria-expanded": isFocused && showSuggestions && locations.length > 0,
|
|
1860
|
+
spellCheck: false,
|
|
1861
|
+
className: "min-w-[3rem] py-1 outline-none text-base font-bold uppercase tracking-widest break-all bg-transparent",
|
|
1862
|
+
style: {
|
|
1863
|
+
minWidth: "3rem"
|
|
1864
|
+
},
|
|
1865
|
+
onInput: (e) => handleBlockTextChange(
|
|
1866
|
+
block.id,
|
|
1867
|
+
e.target.innerText
|
|
1868
|
+
),
|
|
1869
|
+
onKeyDown: (e) => handleKeyDown(e, block.id, block.text),
|
|
1870
|
+
onFocus: () => handleFocus(block.id),
|
|
1871
|
+
onBlur: () => handleBlur(block.id)
|
|
1872
|
+
}
|
|
1873
|
+
),
|
|
1874
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-400/80 font-bold", children: "-" }),
|
|
1875
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1876
|
+
"select",
|
|
1877
|
+
{
|
|
1878
|
+
className: "rounded-md text-zinc-800 font-bold px-1.5 py-1 appearance-none bg-transparent hover:bg-zinc-200/50 outline-none cursor-pointer transition-colors",
|
|
1879
|
+
"aria-label": "Time of Day",
|
|
1880
|
+
disabled: isLocked,
|
|
1881
|
+
value: (_b = block.timeOfDay) != null ? _b : "DAY",
|
|
1882
|
+
style: {
|
|
1883
|
+
appearance: "none"
|
|
1884
|
+
},
|
|
1885
|
+
onChange: (e) => handleTimeOfDayChange(block.id, e.target.value),
|
|
1886
|
+
children: timeOfDayOptions.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { children: t }, t))
|
|
1887
|
+
}
|
|
1893
1888
|
)
|
|
1894
|
-
] })
|
|
1889
|
+
] }),
|
|
1890
|
+
isFocused && showSuggestions && locations.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1891
|
+
"div",
|
|
1892
|
+
{
|
|
1893
|
+
role: "listbox",
|
|
1894
|
+
id: `suggestions-${block.id}`,
|
|
1895
|
+
className: "absolute top-[calc(100%+6px)] left-0 min-w-[240px] z-50 bg-white border border-slate-200/80 shadow-xl shadow-slate-200/40 rounded-xl py-1 overflow-hidden animate-in fade-in zoom-in-95 duration-150",
|
|
1896
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-60 overflow-y-auto custom-scrollbar", children: locations.filter(
|
|
1897
|
+
(loc) => loc.startsWith(block.text.toUpperCase()) && loc !== block.text.toUpperCase()
|
|
1898
|
+
).map((loc) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1899
|
+
"div",
|
|
1900
|
+
{
|
|
1901
|
+
role: "option",
|
|
1902
|
+
className: "group flex items-center justify-between px-4 py-2.5 cursor-pointer transition-all duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
1903
|
+
onMouseDown: (e) => {
|
|
1904
|
+
e.preventDefault();
|
|
1905
|
+
const element = refs.current[block.id];
|
|
1906
|
+
if (element) {
|
|
1907
|
+
element.innerText = loc;
|
|
1908
|
+
handleBlockTextChange(block.id, loc);
|
|
1909
|
+
element.focus();
|
|
1910
|
+
const range = document.createRange();
|
|
1911
|
+
const sel = window.getSelection();
|
|
1912
|
+
range.selectNodeContents(element);
|
|
1913
|
+
range.collapse(false);
|
|
1914
|
+
sel == null ? void 0 : sel.removeAllRanges();
|
|
1915
|
+
sel == null ? void 0 : sel.addRange(range);
|
|
1916
|
+
}
|
|
1917
|
+
handleBlur(block.id);
|
|
1918
|
+
},
|
|
1919
|
+
children: [
|
|
1920
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] font-semibold tracking-wide text-slate-600 uppercase line-clamp-1", children: loc }),
|
|
1921
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "w-3.5 h-3.5 text-slate-300 opacity-0 -translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200" })
|
|
1922
|
+
]
|
|
1923
|
+
},
|
|
1924
|
+
loc
|
|
1925
|
+
)) })
|
|
1926
|
+
}
|
|
1927
|
+
),
|
|
1928
|
+
isFocused && phoneticSuggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1929
|
+
PhoneticSuggestions,
|
|
1930
|
+
{
|
|
1931
|
+
suggestions: phoneticSuggestions,
|
|
1932
|
+
onSelect: handleSelectPhoneticSuggestion
|
|
1933
|
+
}
|
|
1934
|
+
)
|
|
1935
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1936
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1937
|
+
"div",
|
|
1938
|
+
{
|
|
1939
|
+
ref: (el) => {
|
|
1940
|
+
if (!el) return;
|
|
1941
|
+
refs.current[block.id] = el;
|
|
1942
|
+
},
|
|
1943
|
+
contentEditable: !isLocked,
|
|
1944
|
+
suppressContentEditableWarning: true,
|
|
1945
|
+
"aria-label": `${blockStyles[block.type].label} text`,
|
|
1946
|
+
"aria-multiline": block.type === "ACTION" || block.type === "DIALOGUE",
|
|
1947
|
+
spellCheck: false,
|
|
1948
|
+
className: `block outline-none w-full min-h-[2.5rem] px-4 py-2 break-words ${blockStyles[block.type].className}`,
|
|
1949
|
+
onInput: (e) => handleBlockTextChange(
|
|
1950
|
+
block.id,
|
|
1951
|
+
e.target.innerText
|
|
1952
|
+
),
|
|
1953
|
+
onKeyDown: (e) => handleKeyDown(e, block.id, block.text),
|
|
1954
|
+
onFocus: () => handleFocus(block.id),
|
|
1955
|
+
onBlur: () => handleBlur(block.id),
|
|
1956
|
+
style: blockStyles[block.type].inputStyle
|
|
1957
|
+
}
|
|
1958
|
+
),
|
|
1959
|
+
isFocused && block.type === "CHARACTER" && showSuggestions && characters.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1960
|
+
"div",
|
|
1961
|
+
{
|
|
1962
|
+
role: "listbox",
|
|
1963
|
+
id: `suggestions-${block.id}`,
|
|
1964
|
+
className: "absolute top-[calc(100%+8px)] left-1/2 -translate-x-1/2 w-72 z-50 bg-white border border-slate-200 shadow-2xl shadow-slate-200/60 rounded-xl py-2 overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
1965
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characters.filter(
|
|
1966
|
+
(char) => char.startsWith(block.text.toUpperCase()) && char !== block.text.toUpperCase()
|
|
1967
|
+
).map((char) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1968
|
+
"div",
|
|
1969
|
+
{
|
|
1970
|
+
role: "option",
|
|
1971
|
+
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
1972
|
+
onMouseDown: (e) => {
|
|
1973
|
+
e.preventDefault();
|
|
1974
|
+
const element = refs.current[block.id];
|
|
1975
|
+
if (element) {
|
|
1976
|
+
element.innerText = char;
|
|
1977
|
+
handleBlockTextChange(block.id, char);
|
|
1978
|
+
element.focus();
|
|
1979
|
+
const range = document.createRange();
|
|
1980
|
+
const sel = window.getSelection();
|
|
1981
|
+
range.selectNodeContents(element);
|
|
1982
|
+
range.collapse(false);
|
|
1983
|
+
sel == null ? void 0 : sel.removeAllRanges();
|
|
1984
|
+
sel == null ? void 0 : sel.addRange(range);
|
|
1985
|
+
}
|
|
1986
|
+
handleBlur(block.id);
|
|
1987
|
+
},
|
|
1988
|
+
children: [
|
|
1989
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-3.5 h-3.5 text-slate-300 group-hover:text-blumine-500 transition-colors mr-3" }),
|
|
1990
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: char }),
|
|
1991
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3 h-3 text-slate-200 opacity-0 group-hover:opacity-100 transition-all -translate-x-1 group-hover:translate-x-0" })
|
|
1992
|
+
]
|
|
1993
|
+
},
|
|
1994
|
+
char
|
|
1995
|
+
)) })
|
|
1996
|
+
}
|
|
1997
|
+
),
|
|
1998
|
+
isFocused && block.type === "CHARACTER" && showExtensionSuggestions && characterExtensions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1999
|
+
"div",
|
|
2000
|
+
{
|
|
2001
|
+
role: "listbox",
|
|
2002
|
+
id: `extension-suggestions-${block.id}`,
|
|
2003
|
+
className: "absolute top-[calc(100%+8px)] left-1/2 -translate-x-1/2 w-72 z-50 bg-white border border-slate-200 shadow-2xl shadow-slate-200/60 rounded-xl py-2 overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
2004
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characterExtensions.filter((ext) => {
|
|
2005
|
+
const openParenIndex = block.text.lastIndexOf("(");
|
|
2006
|
+
const query = openParenIndex > -1 ? block.text.substring(openParenIndex + 1).toUpperCase() : "";
|
|
2007
|
+
return ext.toUpperCase().includes(query);
|
|
2008
|
+
}).map((ext) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2009
|
+
"div",
|
|
2010
|
+
{
|
|
2011
|
+
role: "option",
|
|
2012
|
+
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
2013
|
+
onMouseDown: (e) => {
|
|
2014
|
+
e.preventDefault();
|
|
2015
|
+
handleSelectCharacterExtension(ext);
|
|
2016
|
+
},
|
|
2017
|
+
children: [
|
|
2018
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: ext }),
|
|
2019
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3 h-3 text-slate-200 opacity-0 group-hover:opacity-100 transition-all -translate-x-1 group-hover:translate-x-0" })
|
|
2020
|
+
]
|
|
2021
|
+
},
|
|
2022
|
+
ext
|
|
2023
|
+
)) })
|
|
2024
|
+
}
|
|
2025
|
+
),
|
|
2026
|
+
isFocused && !enhancingBlockId && (block.type === "ACTION" || block.type === "DIALOGUE") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2027
|
+
"button",
|
|
2028
|
+
{
|
|
2029
|
+
onClick: () => handleEnhance(block),
|
|
2030
|
+
className: "absolute -right-12 top-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 rounded-xl border border-blumine-200/50 bg-white text-blumine-500 hover:border-blumine-400/60 hover:bg-blumine-50/90 hover:ring-2 hover:ring-blumine-100 hover:scale-105 active:scale-95 transition-all duration-200 group/enhance animate-in fade-in slide-in-from-left-2",
|
|
2031
|
+
title: "Enhance with AI",
|
|
2032
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-4 h-4 group-hover/enhance:animate-pulse transition-colors" })
|
|
2033
|
+
}
|
|
2034
|
+
),
|
|
2035
|
+
enhancingBlockId === block.id && (enhancementSuggestion || isEnhancing) && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2036
|
+
"div",
|
|
2037
|
+
{
|
|
2038
|
+
className: "absolute left-0 right-0 -top-4 -translate-y-full z-[50] animate-in fade-in zoom-in-95 slide-in-from-bottom-4 duration-300",
|
|
2039
|
+
style: { width: "calc(100% + 2rem)", left: "-1rem" },
|
|
2040
|
+
children: [
|
|
2041
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto w-full rounded-[2rem] bg-white border border-white/60 shadow-[0_14px_34px_rgba(16,37,54,0.1),0_2px_8px_rgba(16,37,54,0.05)] backdrop-blur-xl p-2 select-none transition-all", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
2042
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pl-1", children: [
|
|
2043
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2044
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9 h-9 flex items-center justify-center rounded-full bg-blumine-50 text-blumine-500 border border-blumine-100 shadow-inner", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-4 h-4" }) }),
|
|
2045
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2046
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-bold text-zinc-900 uppercase tracking-wider leading-none", children: "AI Suggestion" }),
|
|
2047
|
+
!isEnhancing && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-zinc-500 mt-1 font-medium", children: "Refining your screenplay dialogue" })
|
|
2048
|
+
] })
|
|
2049
|
+
] }),
|
|
2050
|
+
!isEnhancing && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
2051
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2052
|
+
"button",
|
|
2053
|
+
{
|
|
2054
|
+
onClick: handleRejectEnhance,
|
|
2055
|
+
className: "px-3 py-2 rounded-full text-xs font-bold text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 transition-all active:scale-95",
|
|
2056
|
+
children: "Discard"
|
|
2057
|
+
}
|
|
2058
|
+
),
|
|
2059
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2060
|
+
"button",
|
|
2061
|
+
{
|
|
2062
|
+
onClick: handleApproveEnhance,
|
|
2063
|
+
className: "px-4 py-2 rounded-full text-xs font-bold bg-blumine-500 text-white hover:bg-blumine-600 shadow-lg shadow-blumine-500/20 transition-all active:scale-95 flex items-center gap-1.5",
|
|
2064
|
+
children: [
|
|
2065
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5" }),
|
|
2066
|
+
"Apply Changes"
|
|
2067
|
+
]
|
|
2068
|
+
}
|
|
2069
|
+
)
|
|
2070
|
+
] })
|
|
2071
|
+
] }),
|
|
2072
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative overflow-hidden rounded-[1.25rem] bg-zinc-50/50 border border-zinc-100 p-4 min-h-[4rem]", children: isEnhancing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2.5 py-1", children: [
|
|
2073
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-3/4 bg-zinc-200/60 animate-pulse rounded-full" }),
|
|
2074
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-1/2 bg-zinc-200/60 animate-pulse rounded-full" })
|
|
2075
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
2076
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-zinc-400 line-through decoration-zinc-300 italic", children: block.text }),
|
|
2077
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-zinc-800 font-medium leading-relaxed", children: enhancementSuggestion })
|
|
2078
|
+
] }) })
|
|
2079
|
+
] }) }),
|
|
2080
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -bottom-1.5 left-1/2 -translate-x-1/2 w-3.5 h-3.5 bg-white border-r border-b border-zinc-200/50 rotate-45" })
|
|
2081
|
+
]
|
|
2082
|
+
}
|
|
2083
|
+
),
|
|
2084
|
+
isFocused && phoneticSuggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2085
|
+
PhoneticSuggestions,
|
|
2086
|
+
{
|
|
2087
|
+
suggestions: phoneticSuggestions,
|
|
2088
|
+
onSelect: handleSelectPhoneticSuggestion
|
|
2089
|
+
}
|
|
2090
|
+
)
|
|
1895
2091
|
] })
|
|
1896
2092
|
}
|
|
1897
|
-
)
|
|
1898
|
-
|
|
1899
|
-
|
|
2093
|
+
);
|
|
2094
|
+
},
|
|
2095
|
+
(prevProps, nextProps) => {
|
|
2096
|
+
return prevProps.isLocked === nextProps.isLocked && prevProps.isFocused === nextProps.isFocused && prevProps.currentLanguage === nextProps.currentLanguage && prevProps.enhancingBlockId === nextProps.enhancingBlockId && prevProps.isEnhancing === nextProps.isEnhancing && prevProps.enhancementSuggestion === nextProps.enhancementSuggestion && prevProps.block.text === nextProps.block.text && prevProps.block.type === nextProps.block.type && prevProps.block.sceneNumber === nextProps.block.sceneNumber && prevProps.block.sceneType === nextProps.block.sceneType && prevProps.block.timeOfDay === nextProps.block.timeOfDay && (!nextProps.isFocused || prevProps.showSuggestions === nextProps.showSuggestions && prevProps.showExtensionSuggestions === nextProps.showExtensionSuggestions && prevProps.phoneticSuggestions === nextProps.phoneticSuggestions && prevProps.locations === nextProps.locations && prevProps.characters === nextProps.characters);
|
|
2097
|
+
}
|
|
2098
|
+
);
|
|
1900
2099
|
|
|
1901
2100
|
// app/service/screenplay-editor.service.ts
|
|
1902
2101
|
function getNextBlockType(currentType) {
|
|
@@ -2224,7 +2423,25 @@ function createRangeFromGlobalOffset(element, startOffset, endOffset) {
|
|
|
2224
2423
|
return null;
|
|
2225
2424
|
}
|
|
2226
2425
|
function useScreenplayEditor(options) {
|
|
2227
|
-
|
|
2426
|
+
var _a;
|
|
2427
|
+
const [blocks, setBlocks] = React4.useState(
|
|
2428
|
+
(options == null ? void 0 : options.blocks) || initialBlocks
|
|
2429
|
+
);
|
|
2430
|
+
const hasInitializedRef = React4.useRef(
|
|
2431
|
+
!!((options == null ? void 0 : options.blocks) && options.blocks.length > 0)
|
|
2432
|
+
);
|
|
2433
|
+
React4.useEffect(() => {
|
|
2434
|
+
if ((options == null ? void 0 : options.blocks) && options.blocks.length > 0 && !hasInitializedRef.current) {
|
|
2435
|
+
setIsLoading(true);
|
|
2436
|
+
isWaitingForDOMRef.current = true;
|
|
2437
|
+
skipAutosaveRef.current = true;
|
|
2438
|
+
const timer = setTimeout(() => {
|
|
2439
|
+
setBlocks(options.blocks);
|
|
2440
|
+
hasInitializedRef.current = true;
|
|
2441
|
+
}, 50);
|
|
2442
|
+
return () => clearTimeout(timer);
|
|
2443
|
+
}
|
|
2444
|
+
}, [options == null ? void 0 : options.blocks]);
|
|
2228
2445
|
const refs = React4.useRef({});
|
|
2229
2446
|
const [focusedBlockId, setFocusedBlockId] = React4.useState(
|
|
2230
2447
|
initialBlocks[0].id
|
|
@@ -2232,7 +2449,9 @@ function useScreenplayEditor(options) {
|
|
|
2232
2449
|
const [newBlockId, setNewBlockId] = React4.useState(null);
|
|
2233
2450
|
const [showSuggestions, setShowSuggestions] = React4.useState(false);
|
|
2234
2451
|
const [showExtensionSuggestions, setShowExtensionSuggestions] = React4.useState(false);
|
|
2235
|
-
const [isLoading, setIsLoading] = React4.useState(
|
|
2452
|
+
const [isLoading, setIsLoading] = React4.useState(
|
|
2453
|
+
!!((options == null ? void 0 : options.initialUrl) || (options == null ? void 0 : options.blocks) && options.blocks.length > 0)
|
|
2454
|
+
);
|
|
2236
2455
|
const [enhancingBlockId, setEnhancingBlockId] = React4.useState(null);
|
|
2237
2456
|
const [enhancementSuggestion, setEnhancementSuggestion] = React4.useState(null);
|
|
2238
2457
|
const [isEnhancing, setIsEnhancing] = React4.useState(false);
|
|
@@ -2241,13 +2460,21 @@ function useScreenplayEditor(options) {
|
|
|
2241
2460
|
const popoverTimeoutRef = React4.useRef(null);
|
|
2242
2461
|
const blurTimeout = React4.useRef(null);
|
|
2243
2462
|
const isInitialLoadRef = React4.useRef(true);
|
|
2463
|
+
const skipAutosaveRef = React4.useRef(false);
|
|
2464
|
+
const isWaitingForDOMRef = React4.useRef(
|
|
2465
|
+
!!((options == null ? void 0 : options.initialUrl) || (options == null ? void 0 : options.blocks) && options.blocks.length > 0)
|
|
2466
|
+
);
|
|
2244
2467
|
const loadedUrlRef = React4.useRef(null);
|
|
2245
2468
|
const lastSavedContent = React4.useRef(null);
|
|
2246
2469
|
const onSaveRef = React4.useRef(options == null ? void 0 : options.onSave);
|
|
2470
|
+
const onSaveBlobRef = React4.useRef(options == null ? void 0 : options.onSaveBlob);
|
|
2247
2471
|
const onSyncWithCloudRef = React4.useRef(options == null ? void 0 : options.onSyncWithCloud);
|
|
2472
|
+
const [autosaveStatus, setAutosaveStatus] = React4.useState("idle");
|
|
2473
|
+
const isLocked = (_a = options == null ? void 0 : options.isLocked) != null ? _a : false;
|
|
2248
2474
|
const [currentLanguage, setCurrentLanguage] = React4.useState("English");
|
|
2249
2475
|
const [lastPhoneticAction, setLastPhoneticAction] = React4.useState(null);
|
|
2250
2476
|
const [phoneticSuggestions, setPhoneticSuggestions] = React4.useState([]);
|
|
2477
|
+
const lastSyncedTextRef = React4.useRef({});
|
|
2251
2478
|
React4.useEffect(() => {
|
|
2252
2479
|
setLastPhoneticAction(null);
|
|
2253
2480
|
setPhoneticSuggestions([]);
|
|
@@ -2260,8 +2487,79 @@ function useScreenplayEditor(options) {
|
|
|
2260
2487
|
}, [currentLanguage]);
|
|
2261
2488
|
React4.useEffect(() => {
|
|
2262
2489
|
onSaveRef.current = options == null ? void 0 : options.onSave;
|
|
2490
|
+
onSaveBlobRef.current = options == null ? void 0 : options.onSaveBlob;
|
|
2263
2491
|
onSyncWithCloudRef.current = options == null ? void 0 : options.onSyncWithCloud;
|
|
2264
|
-
}, [options == null ? void 0 : options.onSave, options == null ? void 0 : options.onSyncWithCloud]);
|
|
2492
|
+
}, [options == null ? void 0 : options.onSave, options == null ? void 0 : options.onSaveBlob, options == null ? void 0 : options.onSyncWithCloud]);
|
|
2493
|
+
const isFirstAutosaveRender = React4.useRef(true);
|
|
2494
|
+
const prevBlocksRef = React4.useRef(blocks);
|
|
2495
|
+
React4.useEffect(() => {
|
|
2496
|
+
if (isLocked) {
|
|
2497
|
+
setAutosaveStatus("idle");
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2500
|
+
if (isFirstAutosaveRender.current) {
|
|
2501
|
+
isFirstAutosaveRender.current = false;
|
|
2502
|
+
prevBlocksRef.current = blocks;
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
if (skipAutosaveRef.current) {
|
|
2506
|
+
skipAutosaveRef.current = false;
|
|
2507
|
+
prevBlocksRef.current = blocks;
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
const hasChanged = blocks.length !== prevBlocksRef.current.length || blocks.some((b, i) => b !== prevBlocksRef.current[i]);
|
|
2511
|
+
if (!hasChanged) {
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
setAutosaveStatus("typing");
|
|
2515
|
+
prevBlocksRef.current = blocks;
|
|
2516
|
+
const timer = setTimeout(async () => {
|
|
2517
|
+
if (onSaveRef.current) {
|
|
2518
|
+
setAutosaveStatus("saving");
|
|
2519
|
+
try {
|
|
2520
|
+
await onSaveRef.current(blocks);
|
|
2521
|
+
setAutosaveStatus("saved");
|
|
2522
|
+
setTimeout(() => {
|
|
2523
|
+
setAutosaveStatus((prev) => prev === "saved" ? "idle" : prev);
|
|
2524
|
+
}, 2e3);
|
|
2525
|
+
} catch (error) {
|
|
2526
|
+
console.error("Autosave failed:", error);
|
|
2527
|
+
setAutosaveStatus("error");
|
|
2528
|
+
}
|
|
2529
|
+
} else {
|
|
2530
|
+
setAutosaveStatus("idle");
|
|
2531
|
+
}
|
|
2532
|
+
}, 3e3);
|
|
2533
|
+
return () => clearTimeout(timer);
|
|
2534
|
+
}, [blocks, isLocked]);
|
|
2535
|
+
const latestBlocksRef = React4.useRef(blocks);
|
|
2536
|
+
React4.useEffect(() => {
|
|
2537
|
+
latestBlocksRef.current = blocks;
|
|
2538
|
+
}, [blocks]);
|
|
2539
|
+
React4.useEffect(() => {
|
|
2540
|
+
const handleBeforeUnload = (e) => {
|
|
2541
|
+
if (autosaveStatus === "typing" || autosaveStatus === "saving") {
|
|
2542
|
+
e.preventDefault();
|
|
2543
|
+
e.returnValue = "Changes you made may not be saved.";
|
|
2544
|
+
return e.returnValue;
|
|
2545
|
+
}
|
|
2546
|
+
};
|
|
2547
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
2548
|
+
return () => {
|
|
2549
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
2550
|
+
};
|
|
2551
|
+
}, [autosaveStatus]);
|
|
2552
|
+
const autosaveStatusRef = React4.useRef(autosaveStatus);
|
|
2553
|
+
React4.useEffect(() => {
|
|
2554
|
+
autosaveStatusRef.current = autosaveStatus;
|
|
2555
|
+
}, [autosaveStatus]);
|
|
2556
|
+
React4.useEffect(() => {
|
|
2557
|
+
return () => {
|
|
2558
|
+
if (autosaveStatusRef.current === "typing" && onSaveRef.current) {
|
|
2559
|
+
onSaveRef.current(latestBlocksRef.current);
|
|
2560
|
+
}
|
|
2561
|
+
};
|
|
2562
|
+
}, []);
|
|
2265
2563
|
const characterExtensions = React4.useMemo(
|
|
2266
2564
|
() => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
|
|
2267
2565
|
[]
|
|
@@ -2276,37 +2574,43 @@ function useScreenplayEditor(options) {
|
|
|
2276
2574
|
},
|
|
2277
2575
|
[]
|
|
2278
2576
|
);
|
|
2279
|
-
const locations = React4.useMemo(() => {
|
|
2280
|
-
const
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
const characters = React4.useMemo(() => {
|
|
2284
|
-
const chars = blocks.filter((b) => b.type === "CHARACTER" && b.text.trim() !== "").map((b) => {
|
|
2285
|
-
const text = b.text.trim().toUpperCase();
|
|
2286
|
-
const parenIndex = text.indexOf("(");
|
|
2287
|
-
if (parenIndex > -1) {
|
|
2288
|
-
return text.substring(0, parenIndex).trim();
|
|
2289
|
-
}
|
|
2290
|
-
return text;
|
|
2291
|
-
}).filter(Boolean);
|
|
2292
|
-
return [...new Set(chars)];
|
|
2293
|
-
}, [blocks]);
|
|
2294
|
-
const sceneNumbers = React4.useMemo(() => {
|
|
2295
|
-
const map = {};
|
|
2577
|
+
const { locations, characters, sceneNumbers } = React4.useMemo(() => {
|
|
2578
|
+
const locsSet = /* @__PURE__ */ new Set();
|
|
2579
|
+
const charsSet = /* @__PURE__ */ new Set();
|
|
2580
|
+
const sceneNumMap = {};
|
|
2296
2581
|
let fallbackCount = 0;
|
|
2297
2582
|
blocks.forEach((block) => {
|
|
2583
|
+
const textTrim = block.text.trim();
|
|
2298
2584
|
if (block.type === "SCENE_HEADING") {
|
|
2585
|
+
if (textTrim !== "") {
|
|
2586
|
+
locsSet.add(textTrim.toUpperCase());
|
|
2587
|
+
}
|
|
2299
2588
|
if (block.sceneNumber) {
|
|
2300
|
-
|
|
2589
|
+
sceneNumMap[block.id] = block.sceneNumber;
|
|
2301
2590
|
const base = parseInt(block.sceneNumber);
|
|
2302
2591
|
if (!isNaN(base)) fallbackCount = Math.max(fallbackCount, base);
|
|
2303
2592
|
} else {
|
|
2304
2593
|
fallbackCount++;
|
|
2305
|
-
|
|
2594
|
+
sceneNumMap[block.id] = String(fallbackCount);
|
|
2595
|
+
}
|
|
2596
|
+
} else if (block.type === "CHARACTER") {
|
|
2597
|
+
if (textTrim !== "") {
|
|
2598
|
+
const textUpper = textTrim.toUpperCase();
|
|
2599
|
+
const parenIndex = textUpper.indexOf("(");
|
|
2600
|
+
if (parenIndex > -1) {
|
|
2601
|
+
const charName = textUpper.substring(0, parenIndex).trim();
|
|
2602
|
+
if (charName) charsSet.add(charName);
|
|
2603
|
+
} else {
|
|
2604
|
+
charsSet.add(textUpper);
|
|
2605
|
+
}
|
|
2306
2606
|
}
|
|
2307
2607
|
}
|
|
2308
2608
|
});
|
|
2309
|
-
return
|
|
2609
|
+
return {
|
|
2610
|
+
locations: Array.from(locsSet),
|
|
2611
|
+
characters: Array.from(charsSet),
|
|
2612
|
+
sceneNumbers: sceneNumMap
|
|
2613
|
+
};
|
|
2310
2614
|
}, [blocks]);
|
|
2311
2615
|
React4.useEffect(() => {
|
|
2312
2616
|
if (newBlockId && refs.current[newBlockId]) {
|
|
@@ -2314,7 +2618,8 @@ function useScreenplayEditor(options) {
|
|
|
2314
2618
|
const el = refs.current[newBlockId];
|
|
2315
2619
|
if (el && block) {
|
|
2316
2620
|
el.focus();
|
|
2317
|
-
el.
|
|
2621
|
+
el.textContent = block.text;
|
|
2622
|
+
lastSyncedTextRef.current[newBlockId] = block.text;
|
|
2318
2623
|
if (block.type === "PARENTHETICAL") {
|
|
2319
2624
|
setTimeout(() => setCaretPosition(el, 1), 0);
|
|
2320
2625
|
} else {
|
|
@@ -2329,11 +2634,51 @@ function useScreenplayEditor(options) {
|
|
|
2329
2634
|
blocks.forEach((block) => {
|
|
2330
2635
|
const element = refs.current[block.id];
|
|
2331
2636
|
if (element) {
|
|
2332
|
-
if (
|
|
2333
|
-
element.
|
|
2637
|
+
if (lastSyncedTextRef.current[block.id] !== block.text && document.activeElement !== element) {
|
|
2638
|
+
element.textContent = block.text;
|
|
2639
|
+
lastSyncedTextRef.current[block.id] = block.text;
|
|
2334
2640
|
}
|
|
2335
2641
|
}
|
|
2336
2642
|
});
|
|
2643
|
+
if (isWaitingForDOMRef.current) {
|
|
2644
|
+
const dismissLoader = () => {
|
|
2645
|
+
if (typeof window !== "undefined") {
|
|
2646
|
+
void document.body.scrollHeight;
|
|
2647
|
+
}
|
|
2648
|
+
setIsLoading(false);
|
|
2649
|
+
isWaitingForDOMRef.current = false;
|
|
2650
|
+
};
|
|
2651
|
+
const waitForAllReady = async () => {
|
|
2652
|
+
if (typeof window !== "undefined") {
|
|
2653
|
+
try {
|
|
2654
|
+
if (document.fonts && document.fonts.ready) {
|
|
2655
|
+
await document.fonts.ready;
|
|
2656
|
+
}
|
|
2657
|
+
} catch (e) {
|
|
2658
|
+
console.error("Font loading detection failed:", e);
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
if (typeof window !== "undefined" && "requestIdleCallback" in window) {
|
|
2662
|
+
requestAnimationFrame(() => {
|
|
2663
|
+
requestAnimationFrame(() => {
|
|
2664
|
+
window.requestIdleCallback(
|
|
2665
|
+
() => {
|
|
2666
|
+
setTimeout(dismissLoader, 150);
|
|
2667
|
+
},
|
|
2668
|
+
{ timeout: 2e3 }
|
|
2669
|
+
);
|
|
2670
|
+
});
|
|
2671
|
+
});
|
|
2672
|
+
} else {
|
|
2673
|
+
requestAnimationFrame(() => {
|
|
2674
|
+
requestAnimationFrame(() => {
|
|
2675
|
+
setTimeout(dismissLoader, 400);
|
|
2676
|
+
});
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
2679
|
+
};
|
|
2680
|
+
waitForAllReady();
|
|
2681
|
+
}
|
|
2337
2682
|
}, [blocks]);
|
|
2338
2683
|
React4.useEffect(() => {
|
|
2339
2684
|
const handleClickOutside = (e) => {
|
|
@@ -2389,9 +2734,12 @@ function useScreenplayEditor(options) {
|
|
|
2389
2734
|
const offset = getCaretCharacterOffsetWithin(el);
|
|
2390
2735
|
const charsBeforeCaret = text.substring(0, offset).replace(/[()]/g, "").length;
|
|
2391
2736
|
const newOffset = 1 + charsBeforeCaret;
|
|
2392
|
-
el.
|
|
2737
|
+
el.textContent = processedText;
|
|
2738
|
+
lastSyncedTextRef.current[id] = processedText;
|
|
2393
2739
|
setCaretPosition(el, newOffset);
|
|
2394
2740
|
}
|
|
2741
|
+
} else {
|
|
2742
|
+
lastSyncedTextRef.current[id] = text;
|
|
2395
2743
|
}
|
|
2396
2744
|
},
|
|
2397
2745
|
[blocks, showExtensionSuggestions]
|
|
@@ -2402,8 +2750,8 @@ function useScreenplayEditor(options) {
|
|
|
2402
2750
|
(bs) => updateBlock(bs, id, "sceneType", sceneType)
|
|
2403
2751
|
);
|
|
2404
2752
|
setTimeout(() => {
|
|
2405
|
-
var
|
|
2406
|
-
(
|
|
2753
|
+
var _a2;
|
|
2754
|
+
(_a2 = refs.current[id]) == null ? void 0 : _a2.focus();
|
|
2407
2755
|
setFocusedBlockId(id);
|
|
2408
2756
|
}, 10);
|
|
2409
2757
|
},
|
|
@@ -2412,8 +2760,8 @@ function useScreenplayEditor(options) {
|
|
|
2412
2760
|
const handleTimeOfDayChange = React4.useCallback((id, time) => {
|
|
2413
2761
|
setBlocks((bs) => updateBlock(bs, id, "timeOfDay", time));
|
|
2414
2762
|
setTimeout(() => {
|
|
2415
|
-
var
|
|
2416
|
-
(
|
|
2763
|
+
var _a2;
|
|
2764
|
+
(_a2 = refs.current[id]) == null ? void 0 : _a2.focus();
|
|
2417
2765
|
setFocusedBlockId(id);
|
|
2418
2766
|
}, 10);
|
|
2419
2767
|
}, []);
|
|
@@ -2514,7 +2862,7 @@ function useScreenplayEditor(options) {
|
|
|
2514
2862
|
};
|
|
2515
2863
|
const handleKeyDown = React4.useCallback(
|
|
2516
2864
|
(e, id, text) => {
|
|
2517
|
-
var
|
|
2865
|
+
var _a2;
|
|
2518
2866
|
if (currentLanguage !== "English") {
|
|
2519
2867
|
const config = LANGUAGE_CONFIGS[currentLanguage];
|
|
2520
2868
|
const char = getMappedCharacter(
|
|
@@ -2681,7 +3029,7 @@ function useScreenplayEditor(options) {
|
|
|
2681
3029
|
const range = selection.getRangeAt(0);
|
|
2682
3030
|
const contentEditable = e.currentTarget;
|
|
2683
3031
|
const container = range.endContainer;
|
|
2684
|
-
const isAtEndOffset = range.endOffset === (container.nodeType === Node.TEXT_NODE ? (
|
|
3032
|
+
const isAtEndOffset = range.endOffset === (container.nodeType === Node.TEXT_NODE ? (_a2 = container.textContent) == null ? void 0 : _a2.length : container.childNodes.length);
|
|
2685
3033
|
if (isAtEndOffset) {
|
|
2686
3034
|
let atEnd = false;
|
|
2687
3035
|
let node = container;
|
|
@@ -2737,6 +3085,9 @@ function useScreenplayEditor(options) {
|
|
|
2737
3085
|
parsedBlocks = parseScreenplayText(content);
|
|
2738
3086
|
}
|
|
2739
3087
|
if (parsedBlocks.length > 0) {
|
|
3088
|
+
setIsLoading(true);
|
|
3089
|
+
isWaitingForDOMRef.current = true;
|
|
3090
|
+
skipAutosaveRef.current = true;
|
|
2740
3091
|
let fallbackCount = 1;
|
|
2741
3092
|
const finalizedBlocks = parsedBlocks.map((block) => {
|
|
2742
3093
|
if (block.type === "SCENE_HEADING") {
|
|
@@ -2751,25 +3102,30 @@ function useScreenplayEditor(options) {
|
|
|
2751
3102
|
}
|
|
2752
3103
|
return block;
|
|
2753
3104
|
});
|
|
2754
|
-
setBlocks(finalizedBlocks);
|
|
2755
|
-
if (onSaveRef.current) {
|
|
2756
|
-
const sbxData = serializeToSbx(finalizedBlocks);
|
|
2757
|
-
if (sbxData !== lastSavedContent.current) {
|
|
2758
|
-
lastSavedContent.current = sbxData;
|
|
2759
|
-
if (!isInitialLoad) {
|
|
2760
|
-
const blob = new Blob([sbxData], { type: "text/plain" });
|
|
2761
|
-
onSaveRef.current(blob);
|
|
2762
|
-
}
|
|
2763
|
-
}
|
|
2764
|
-
}
|
|
2765
3105
|
setTimeout(() => {
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
3106
|
+
setBlocks(finalizedBlocks);
|
|
3107
|
+
if (onSaveBlobRef.current) {
|
|
3108
|
+
const sbxData = serializeToSbx(finalizedBlocks);
|
|
3109
|
+
if (sbxData !== lastSavedContent.current) {
|
|
3110
|
+
lastSavedContent.current = sbxData;
|
|
3111
|
+
if (!isInitialLoad) {
|
|
3112
|
+
const blob = new Blob([sbxData], { type: "text/plain" });
|
|
3113
|
+
onSaveBlobRef.current(blob);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
2771
3116
|
}
|
|
2772
|
-
|
|
3117
|
+
setTimeout(() => {
|
|
3118
|
+
var _a2;
|
|
3119
|
+
const firstId = parsedBlocks[0].id;
|
|
3120
|
+
if (firstId && refs.current[firstId]) {
|
|
3121
|
+
setFocusedBlockId(firstId);
|
|
3122
|
+
(_a2 = refs.current[firstId]) == null ? void 0 : _a2.focus();
|
|
3123
|
+
}
|
|
3124
|
+
}, 100);
|
|
3125
|
+
}, 50);
|
|
3126
|
+
} else {
|
|
3127
|
+
setIsLoading(false);
|
|
3128
|
+
isWaitingForDOMRef.current = false;
|
|
2773
3129
|
}
|
|
2774
3130
|
},
|
|
2775
3131
|
[refs]
|
|
@@ -2840,7 +3196,7 @@ function useScreenplayEditor(options) {
|
|
|
2840
3196
|
}, []);
|
|
2841
3197
|
const loadFromUrl = React4.useCallback(
|
|
2842
3198
|
async (url, fetchOptions = {}, isInitialLoad) => {
|
|
2843
|
-
var
|
|
3199
|
+
var _a2;
|
|
2844
3200
|
setIsLoading(true);
|
|
2845
3201
|
try {
|
|
2846
3202
|
const response = await fetch(url, fetchOptions);
|
|
@@ -2852,7 +3208,15 @@ function useScreenplayEditor(options) {
|
|
|
2852
3208
|
let text = await response.text();
|
|
2853
3209
|
let preParsedBlocks = void 0;
|
|
2854
3210
|
let scriptContent = text;
|
|
2855
|
-
|
|
3211
|
+
try {
|
|
3212
|
+
const parsed = JSON.parse(text);
|
|
3213
|
+
if (Array.isArray(parsed)) {
|
|
3214
|
+
preParsedBlocks = parsed;
|
|
3215
|
+
scriptContent = "";
|
|
3216
|
+
}
|
|
3217
|
+
} catch (e) {
|
|
3218
|
+
}
|
|
3219
|
+
if (!preParsedBlocks && (text.includes('class="divtype') || url.toLowerCase().includes(".sbx"))) {
|
|
2856
3220
|
if (text.includes("<div")) {
|
|
2857
3221
|
const textarea = document.createElement("textarea");
|
|
2858
3222
|
textarea.innerHTML = text;
|
|
@@ -2871,8 +3235,8 @@ function useScreenplayEditor(options) {
|
|
|
2871
3235
|
divtype6: "TRANSITION"
|
|
2872
3236
|
};
|
|
2873
3237
|
divs.forEach((div) => {
|
|
2874
|
-
var
|
|
2875
|
-
let divText = ((
|
|
3238
|
+
var _a3;
|
|
3239
|
+
let divText = ((_a3 = div.textContent) == null ? void 0 : _a3.trim()) || "";
|
|
2876
3240
|
if (!divText) return;
|
|
2877
3241
|
let type = "ACTION";
|
|
2878
3242
|
for (const className of Array.from(div.classList)) {
|
|
@@ -2911,15 +3275,18 @@ function useScreenplayEditor(options) {
|
|
|
2911
3275
|
scriptContent = "";
|
|
2912
3276
|
}
|
|
2913
3277
|
}
|
|
2914
|
-
const filename = ((
|
|
3278
|
+
const filename = ((_a2 = url.split("/").pop()) == null ? void 0 : _a2.replace(/\.sbx$/i, "")) || "Imported from URL";
|
|
3279
|
+
isWaitingForDOMRef.current = true;
|
|
2915
3280
|
handleScriptImport(
|
|
2916
3281
|
filename,
|
|
2917
3282
|
scriptContent,
|
|
2918
3283
|
preParsedBlocks,
|
|
2919
3284
|
isInitialLoad
|
|
2920
3285
|
);
|
|
2921
|
-
}
|
|
3286
|
+
} catch (error) {
|
|
3287
|
+
console.error("Failed to load screenplay from URL:", error);
|
|
2922
3288
|
setIsLoading(false);
|
|
3289
|
+
isWaitingForDOMRef.current = false;
|
|
2923
3290
|
}
|
|
2924
3291
|
},
|
|
2925
3292
|
[handleScriptImport]
|
|
@@ -2929,28 +3296,45 @@ function useScreenplayEditor(options) {
|
|
|
2929
3296
|
isInitialLoadRef.current = true;
|
|
2930
3297
|
return;
|
|
2931
3298
|
}
|
|
2932
|
-
const
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
3299
|
+
const handler = setTimeout(() => {
|
|
3300
|
+
const runSerialization = () => {
|
|
3301
|
+
const currentSbx = serializeToSbx(blocks);
|
|
3302
|
+
if (isInitialLoadRef.current) {
|
|
3303
|
+
if (lastSavedContent.current === null) {
|
|
3304
|
+
lastSavedContent.current = currentSbx;
|
|
3305
|
+
}
|
|
3306
|
+
isInitialLoadRef.current = false;
|
|
3307
|
+
return;
|
|
3308
|
+
}
|
|
3309
|
+
const hasActualChanges = lastSavedContent.current !== null && currentSbx !== lastSavedContent.current;
|
|
3310
|
+
if (hasActualChanges) {
|
|
3311
|
+
setHasUnsavedChanges(true);
|
|
3312
|
+
if (popoverTimeoutRef.current)
|
|
3313
|
+
clearTimeout(popoverTimeoutRef.current);
|
|
3314
|
+
setShowUnsavedPopover(false);
|
|
3315
|
+
popoverTimeoutRef.current = setTimeout(() => {
|
|
3316
|
+
setShowUnsavedPopover(true);
|
|
3317
|
+
}, 1e3);
|
|
3318
|
+
} else {
|
|
3319
|
+
setHasUnsavedChanges(false);
|
|
3320
|
+
setShowUnsavedPopover(false);
|
|
3321
|
+
if (popoverTimeoutRef.current)
|
|
3322
|
+
clearTimeout(popoverTimeoutRef.current);
|
|
3323
|
+
}
|
|
3324
|
+
};
|
|
3325
|
+
if (typeof window !== "undefined" && "requestIdleCallback" in window) {
|
|
3326
|
+
window.requestIdleCallback(
|
|
3327
|
+
() => {
|
|
3328
|
+
runSerialization();
|
|
3329
|
+
},
|
|
3330
|
+
{ timeout: 1e3 }
|
|
3331
|
+
);
|
|
3332
|
+
} else {
|
|
3333
|
+
runSerialization();
|
|
2936
3334
|
}
|
|
2937
|
-
|
|
2938
|
-
return;
|
|
2939
|
-
}
|
|
2940
|
-
const hasActualChanges = lastSavedContent.current !== null && currentSbx !== lastSavedContent.current;
|
|
2941
|
-
if (hasActualChanges) {
|
|
2942
|
-
setHasUnsavedChanges(true);
|
|
2943
|
-
if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
|
|
2944
|
-
setShowUnsavedPopover(false);
|
|
2945
|
-
popoverTimeoutRef.current = setTimeout(() => {
|
|
2946
|
-
setShowUnsavedPopover(true);
|
|
2947
|
-
}, 1e3);
|
|
2948
|
-
} else {
|
|
2949
|
-
setHasUnsavedChanges(false);
|
|
2950
|
-
setShowUnsavedPopover(false);
|
|
2951
|
-
if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
|
|
2952
|
-
}
|
|
3335
|
+
}, 250);
|
|
2953
3336
|
return () => {
|
|
3337
|
+
clearTimeout(handler);
|
|
2954
3338
|
if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
|
|
2955
3339
|
};
|
|
2956
3340
|
}, [blocks, isLoading]);
|
|
@@ -3040,7 +3424,9 @@ function useScreenplayEditor(options) {
|
|
|
3040
3424
|
currentLanguage,
|
|
3041
3425
|
setCurrentLanguage,
|
|
3042
3426
|
phoneticSuggestions,
|
|
3043
|
-
handleSelectPhoneticSuggestion
|
|
3427
|
+
handleSelectPhoneticSuggestion,
|
|
3428
|
+
isLocked,
|
|
3429
|
+
autosaveStatus
|
|
3044
3430
|
};
|
|
3045
3431
|
}
|
|
3046
3432
|
|