@yoamigo.com/core 0.1.11 → 0.1.13
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/{MarkdownText-mylt-QX-.d.ts → MarkdownText-BUTYfqXS.d.ts} +7 -1
- package/dist/index.d.ts +392 -3
- package/dist/index.js +733 -153
- package/dist/plugin.js +2 -2
- package/dist/prod.d.ts +2 -2
- package/dist/prod.js +3 -2
- package/dist/router.d.ts +1 -1
- package/dist/router.js +3 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -938,7 +938,7 @@ function ContentStoreProvider2({ children }) {
|
|
|
938
938
|
}
|
|
939
939
|
|
|
940
940
|
// src/components/YaText.tsx
|
|
941
|
-
import { useEffect as
|
|
941
|
+
import { useEffect as useEffect4, useRef as useRef5, useState as useState4, useCallback as useCallback5 } from "react";
|
|
942
942
|
import { createPortal as createPortal2 } from "react-dom";
|
|
943
943
|
import { useEditor, EditorContent } from "@tiptap/react";
|
|
944
944
|
import { BubbleMenu } from "@tiptap/react/menus";
|
|
@@ -1165,6 +1165,523 @@ function SafeHtml({ content, className, mode = "read-only" }) {
|
|
|
1165
1165
|
] });
|
|
1166
1166
|
}
|
|
1167
1167
|
|
|
1168
|
+
// src/hooks/useAnimatedText.ts
|
|
1169
|
+
import { useMemo as useMemo3 } from "react";
|
|
1170
|
+
|
|
1171
|
+
// src/hooks/useAIEditAnimation.ts
|
|
1172
|
+
import { useState as useState3, useEffect as useEffect3, useRef as useRef4, useCallback as useCallback4, useMemo as useMemo2 } from "react";
|
|
1173
|
+
|
|
1174
|
+
// src/contexts/AIEditContext.tsx
|
|
1175
|
+
import { createContext as createContext3, useContext as useContext3, useCallback as useCallback3, useRef as useRef3, useMemo } from "react";
|
|
1176
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1177
|
+
var AIEditContext = createContext3(null);
|
|
1178
|
+
function useAIEditContext() {
|
|
1179
|
+
const context = useContext3(AIEditContext);
|
|
1180
|
+
if (!context) {
|
|
1181
|
+
throw new Error("useAIEditContext must be used within an AIEditProvider");
|
|
1182
|
+
}
|
|
1183
|
+
return context;
|
|
1184
|
+
}
|
|
1185
|
+
function useAIEditContextOptional() {
|
|
1186
|
+
return useContext3(AIEditContext);
|
|
1187
|
+
}
|
|
1188
|
+
function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
1189
|
+
const animationsRef = useRef3(/* @__PURE__ */ new Map());
|
|
1190
|
+
const listenersRef = useRef3(/* @__PURE__ */ new Map());
|
|
1191
|
+
const queueRef = useRef3([]);
|
|
1192
|
+
const processingRef = useRef3(false);
|
|
1193
|
+
const notifyListeners = useCallback3((fieldId) => {
|
|
1194
|
+
const listeners = listenersRef.current.get(fieldId);
|
|
1195
|
+
if (listeners) {
|
|
1196
|
+
listeners.forEach((listener) => listener());
|
|
1197
|
+
}
|
|
1198
|
+
}, []);
|
|
1199
|
+
const processQueue = useCallback3(() => {
|
|
1200
|
+
if (processingRef.current || queueRef.current.length === 0) return;
|
|
1201
|
+
processingRef.current = true;
|
|
1202
|
+
const fieldId = queueRef.current.shift();
|
|
1203
|
+
const state = animationsRef.current.get(fieldId);
|
|
1204
|
+
if (state && state.status === "pending") {
|
|
1205
|
+
state.status = "animating";
|
|
1206
|
+
state.startTime = performance.now();
|
|
1207
|
+
notifyListeners(fieldId);
|
|
1208
|
+
}
|
|
1209
|
+
setTimeout(() => {
|
|
1210
|
+
processingRef.current = false;
|
|
1211
|
+
processQueue();
|
|
1212
|
+
}, staggerDelay);
|
|
1213
|
+
}, [staggerDelay, notifyListeners]);
|
|
1214
|
+
const queueAnimation = useCallback3(
|
|
1215
|
+
(fieldId, config) => {
|
|
1216
|
+
const existing = animationsRef.current.get(fieldId);
|
|
1217
|
+
if (existing && existing.status === "animating") {
|
|
1218
|
+
animationsRef.current.delete(fieldId);
|
|
1219
|
+
}
|
|
1220
|
+
const state = {
|
|
1221
|
+
fieldId,
|
|
1222
|
+
status: "pending",
|
|
1223
|
+
startTime: 0,
|
|
1224
|
+
duration: config.duration,
|
|
1225
|
+
onComplete: config.onComplete
|
|
1226
|
+
};
|
|
1227
|
+
animationsRef.current.set(fieldId, state);
|
|
1228
|
+
config.onStart?.();
|
|
1229
|
+
if (!queueRef.current.includes(fieldId)) {
|
|
1230
|
+
queueRef.current.push(fieldId);
|
|
1231
|
+
}
|
|
1232
|
+
notifyListeners(fieldId);
|
|
1233
|
+
processQueue();
|
|
1234
|
+
},
|
|
1235
|
+
[notifyListeners, processQueue]
|
|
1236
|
+
);
|
|
1237
|
+
const cancelAnimation = useCallback3(
|
|
1238
|
+
(fieldId) => {
|
|
1239
|
+
const state = animationsRef.current.get(fieldId);
|
|
1240
|
+
if (state) {
|
|
1241
|
+
animationsRef.current.delete(fieldId);
|
|
1242
|
+
const queueIndex = queueRef.current.indexOf(fieldId);
|
|
1243
|
+
if (queueIndex >= 0) {
|
|
1244
|
+
queueRef.current.splice(queueIndex, 1);
|
|
1245
|
+
}
|
|
1246
|
+
notifyListeners(fieldId);
|
|
1247
|
+
}
|
|
1248
|
+
},
|
|
1249
|
+
[notifyListeners]
|
|
1250
|
+
);
|
|
1251
|
+
const isAnimating = useCallback3((fieldId) => {
|
|
1252
|
+
const state = animationsRef.current.get(fieldId);
|
|
1253
|
+
return state?.status === "animating" || state?.status === "pending";
|
|
1254
|
+
}, []);
|
|
1255
|
+
const getAnimationState = useCallback3((fieldId) => {
|
|
1256
|
+
return animationsRef.current.get(fieldId);
|
|
1257
|
+
}, []);
|
|
1258
|
+
const subscribe = useCallback3((fieldId, listener) => {
|
|
1259
|
+
let listeners = listenersRef.current.get(fieldId);
|
|
1260
|
+
if (!listeners) {
|
|
1261
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1262
|
+
listenersRef.current.set(fieldId, listeners);
|
|
1263
|
+
}
|
|
1264
|
+
listeners.add(listener);
|
|
1265
|
+
return () => {
|
|
1266
|
+
listeners?.delete(listener);
|
|
1267
|
+
if (listeners?.size === 0) {
|
|
1268
|
+
listenersRef.current.delete(fieldId);
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
}, []);
|
|
1272
|
+
const completeAnimation = useCallback3(
|
|
1273
|
+
(fieldId) => {
|
|
1274
|
+
const state = animationsRef.current.get(fieldId);
|
|
1275
|
+
if (state) {
|
|
1276
|
+
state.status = "complete";
|
|
1277
|
+
state.onComplete?.();
|
|
1278
|
+
setTimeout(() => {
|
|
1279
|
+
animationsRef.current.delete(fieldId);
|
|
1280
|
+
notifyListeners(fieldId);
|
|
1281
|
+
}, 500);
|
|
1282
|
+
notifyListeners(fieldId);
|
|
1283
|
+
}
|
|
1284
|
+
},
|
|
1285
|
+
[notifyListeners]
|
|
1286
|
+
);
|
|
1287
|
+
const value = useMemo(
|
|
1288
|
+
() => ({
|
|
1289
|
+
queueAnimation,
|
|
1290
|
+
cancelAnimation,
|
|
1291
|
+
isAnimating,
|
|
1292
|
+
getAnimationState,
|
|
1293
|
+
subscribe,
|
|
1294
|
+
completeAnimation
|
|
1295
|
+
}),
|
|
1296
|
+
[queueAnimation, cancelAnimation, isAnimating, getAnimationState, subscribe, completeAnimation]
|
|
1297
|
+
);
|
|
1298
|
+
return /* @__PURE__ */ jsx4(AIEditContext.Provider, { value, children });
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// src/hooks/useAIEditAnimation.ts
|
|
1302
|
+
function useAIEditAnimation(fieldId, value, options) {
|
|
1303
|
+
const { enabled = true, strategy, maxDuration = 2e3, onStart, onComplete } = options;
|
|
1304
|
+
const context = useAIEditContextOptional();
|
|
1305
|
+
const previousValueRef = useRef4(value);
|
|
1306
|
+
const isFirstRender = useRef4(true);
|
|
1307
|
+
const [phase, setPhase] = useState3("idle");
|
|
1308
|
+
const [progress, setProgress] = useState3(0);
|
|
1309
|
+
const [displayValue, setDisplayValue] = useState3(value);
|
|
1310
|
+
const metadataRef = useRef4(null);
|
|
1311
|
+
const animationFrameRef = useRef4(null);
|
|
1312
|
+
const startTimeRef = useRef4(0);
|
|
1313
|
+
const cancel = useCallback4(() => {
|
|
1314
|
+
if (animationFrameRef.current !== null) {
|
|
1315
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
1316
|
+
animationFrameRef.current = null;
|
|
1317
|
+
}
|
|
1318
|
+
setPhase("idle");
|
|
1319
|
+
setProgress(0);
|
|
1320
|
+
setDisplayValue(value);
|
|
1321
|
+
metadataRef.current = null;
|
|
1322
|
+
context?.cancelAnimation(fieldId);
|
|
1323
|
+
}, [value, context, fieldId]);
|
|
1324
|
+
const runAnimation = useCallback4(() => {
|
|
1325
|
+
if (!metadataRef.current) return;
|
|
1326
|
+
const metadata = metadataRef.current;
|
|
1327
|
+
const elapsed = performance.now() - startTimeRef.current;
|
|
1328
|
+
const duration = Math.min(metadata.duration, maxDuration);
|
|
1329
|
+
if (elapsed >= duration) {
|
|
1330
|
+
setProgress(1);
|
|
1331
|
+
setDisplayValue(metadata.newValue);
|
|
1332
|
+
setPhase("complete");
|
|
1333
|
+
metadataRef.current = null;
|
|
1334
|
+
animationFrameRef.current = null;
|
|
1335
|
+
context?.completeAnimation(fieldId);
|
|
1336
|
+
onComplete?.();
|
|
1337
|
+
setTimeout(() => {
|
|
1338
|
+
setPhase("idle");
|
|
1339
|
+
}, 400);
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
const currentProgress = elapsed / duration;
|
|
1343
|
+
setProgress(currentProgress);
|
|
1344
|
+
const interpolatedValue = strategy.interpolate(metadata, currentProgress);
|
|
1345
|
+
setDisplayValue(interpolatedValue);
|
|
1346
|
+
animationFrameRef.current = requestAnimationFrame(runAnimation);
|
|
1347
|
+
}, [strategy, maxDuration, context, fieldId, onComplete]);
|
|
1348
|
+
useEffect3(() => {
|
|
1349
|
+
if (isFirstRender.current) {
|
|
1350
|
+
isFirstRender.current = false;
|
|
1351
|
+
previousValueRef.current = value;
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
if (!enabled) {
|
|
1355
|
+
setDisplayValue(value);
|
|
1356
|
+
previousValueRef.current = value;
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
const oldValue = previousValueRef.current;
|
|
1360
|
+
const newValue = value;
|
|
1361
|
+
if (!strategy.canAnimate(oldValue, newValue)) {
|
|
1362
|
+
setDisplayValue(newValue);
|
|
1363
|
+
previousValueRef.current = newValue;
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
if (animationFrameRef.current !== null) {
|
|
1367
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
1368
|
+
}
|
|
1369
|
+
const metadata = strategy.prepare(oldValue, newValue);
|
|
1370
|
+
metadataRef.current = metadata;
|
|
1371
|
+
startTimeRef.current = performance.now();
|
|
1372
|
+
context?.queueAnimation(fieldId, {
|
|
1373
|
+
duration: Math.min(metadata.duration, maxDuration),
|
|
1374
|
+
onComplete: () => {
|
|
1375
|
+
}
|
|
1376
|
+
});
|
|
1377
|
+
onStart?.();
|
|
1378
|
+
setPhase("animating");
|
|
1379
|
+
setProgress(0);
|
|
1380
|
+
animationFrameRef.current = requestAnimationFrame(runAnimation);
|
|
1381
|
+
previousValueRef.current = newValue;
|
|
1382
|
+
return () => {
|
|
1383
|
+
if (animationFrameRef.current !== null) {
|
|
1384
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
}, [value, enabled, strategy, context, fieldId, maxDuration, onStart, runAnimation]);
|
|
1388
|
+
useEffect3(() => {
|
|
1389
|
+
return () => {
|
|
1390
|
+
if (animationFrameRef.current !== null) {
|
|
1391
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
}, []);
|
|
1395
|
+
const wrapperProps = useMemo2(
|
|
1396
|
+
() => ({
|
|
1397
|
+
className: phase === "animating" ? "ya-ai-editing" : phase === "complete" ? "ya-ai-complete" : "",
|
|
1398
|
+
"data-ai-editing": phase === "animating"
|
|
1399
|
+
}),
|
|
1400
|
+
[phase]
|
|
1401
|
+
);
|
|
1402
|
+
return {
|
|
1403
|
+
displayValue,
|
|
1404
|
+
isAnimating: phase === "animating",
|
|
1405
|
+
phase,
|
|
1406
|
+
progress,
|
|
1407
|
+
cancel,
|
|
1408
|
+
wrapperProps
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// src/lib/text-diff.ts
|
|
1413
|
+
function containsHtml(text) {
|
|
1414
|
+
return /<[^>]+>/.test(text);
|
|
1415
|
+
}
|
|
1416
|
+
function stripHtml(html) {
|
|
1417
|
+
const withNewlines = html.replace(/<br\s*\/?>/gi, "\n");
|
|
1418
|
+
return withNewlines.replace(/<[^>]+>/g, "");
|
|
1419
|
+
}
|
|
1420
|
+
function commonPrefixLength(a, b) {
|
|
1421
|
+
const minLen = Math.min(a.length, b.length);
|
|
1422
|
+
let i = 0;
|
|
1423
|
+
while (i < minLen && a[i] === b[i]) {
|
|
1424
|
+
i++;
|
|
1425
|
+
}
|
|
1426
|
+
return i;
|
|
1427
|
+
}
|
|
1428
|
+
function commonSuffixLength(a, b, prefixLen) {
|
|
1429
|
+
const maxSuffixA = a.length - prefixLen;
|
|
1430
|
+
const maxSuffixB = b.length - prefixLen;
|
|
1431
|
+
const maxSuffix = Math.min(maxSuffixA, maxSuffixB);
|
|
1432
|
+
let i = 0;
|
|
1433
|
+
while (i < maxSuffix && a[a.length - 1 - i] === b[b.length - 1 - i]) {
|
|
1434
|
+
i++;
|
|
1435
|
+
}
|
|
1436
|
+
return i;
|
|
1437
|
+
}
|
|
1438
|
+
function computeTextDiff(oldText, newText) {
|
|
1439
|
+
const normalizedOld = oldText ?? "";
|
|
1440
|
+
const normalizedNew = newText ?? "";
|
|
1441
|
+
const hasHtml = containsHtml(normalizedOld) || containsHtml(normalizedNew);
|
|
1442
|
+
if (hasHtml) {
|
|
1443
|
+
return {
|
|
1444
|
+
type: "complex",
|
|
1445
|
+
deletions: normalizedOld,
|
|
1446
|
+
additions: normalizedNew,
|
|
1447
|
+
commonPrefix: "",
|
|
1448
|
+
commonSuffix: "",
|
|
1449
|
+
oldText: normalizedOld,
|
|
1450
|
+
newText: normalizedNew
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
const prefixLen = commonPrefixLength(normalizedOld, normalizedNew);
|
|
1454
|
+
const suffixLen = commonSuffixLength(normalizedOld, normalizedNew, prefixLen);
|
|
1455
|
+
const commonPrefix = normalizedOld.slice(0, prefixLen);
|
|
1456
|
+
const commonSuffix = suffixLen > 0 ? normalizedOld.slice(-suffixLen) : "";
|
|
1457
|
+
const deletions = normalizedOld.slice(prefixLen, normalizedOld.length - suffixLen);
|
|
1458
|
+
const additions = normalizedNew.slice(prefixLen, normalizedNew.length - suffixLen);
|
|
1459
|
+
return {
|
|
1460
|
+
type: "simple",
|
|
1461
|
+
deletions,
|
|
1462
|
+
additions,
|
|
1463
|
+
commonPrefix,
|
|
1464
|
+
commonSuffix,
|
|
1465
|
+
oldText: normalizedOld,
|
|
1466
|
+
newText: normalizedNew
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
function buildIntermediateText(diff, deleteProgress, typeProgress) {
|
|
1470
|
+
if (diff.type === "complex") {
|
|
1471
|
+
return typeProgress >= 1 ? diff.newText : diff.oldText;
|
|
1472
|
+
}
|
|
1473
|
+
const deletionsRemaining = Math.floor(diff.deletions.length * (1 - deleteProgress));
|
|
1474
|
+
const additionsShown = Math.floor(diff.additions.length * typeProgress);
|
|
1475
|
+
const remainingDeletions = diff.deletions.slice(0, deletionsRemaining);
|
|
1476
|
+
const typedAdditions = diff.additions.slice(0, additionsShown);
|
|
1477
|
+
return diff.commonPrefix + remainingDeletions + typedAdditions + diff.commonSuffix;
|
|
1478
|
+
}
|
|
1479
|
+
function calculateAnimationTiming(diff, options = {}) {
|
|
1480
|
+
const { charDelay = 50, maxDuration = 2e3 } = options;
|
|
1481
|
+
if (diff.type === "complex") {
|
|
1482
|
+
return { deleteDuration: 150, typeDuration: 150 };
|
|
1483
|
+
}
|
|
1484
|
+
const deleteChars = diff.deletions.length;
|
|
1485
|
+
const typeChars = diff.additions.length;
|
|
1486
|
+
const totalChars = deleteChars + typeChars;
|
|
1487
|
+
if (totalChars === 0) {
|
|
1488
|
+
return { deleteDuration: 0, typeDuration: 0 };
|
|
1489
|
+
}
|
|
1490
|
+
const baseDuration = totalChars * charDelay;
|
|
1491
|
+
const scale = baseDuration > maxDuration ? maxDuration / baseDuration : 1;
|
|
1492
|
+
const deleteDuration = Math.round(deleteChars * charDelay * scale);
|
|
1493
|
+
const typeDuration = Math.round(typeChars * charDelay * scale);
|
|
1494
|
+
return { deleteDuration, typeDuration };
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
// src/lib/animation-strategies.ts
|
|
1498
|
+
var textTypingStrategy = {
|
|
1499
|
+
name: "text-typing",
|
|
1500
|
+
canAnimate(oldValue, newValue) {
|
|
1501
|
+
if (oldValue === newValue) return false;
|
|
1502
|
+
if (!oldValue && !newValue) return false;
|
|
1503
|
+
return true;
|
|
1504
|
+
},
|
|
1505
|
+
prepare(oldValue, newValue) {
|
|
1506
|
+
const diff = computeTextDiff(oldValue, newValue);
|
|
1507
|
+
const { deleteDuration, typeDuration } = calculateAnimationTiming(diff, {
|
|
1508
|
+
charDelay: 50,
|
|
1509
|
+
// Fast & snappy
|
|
1510
|
+
maxDuration: 2e3
|
|
1511
|
+
});
|
|
1512
|
+
return {
|
|
1513
|
+
oldValue,
|
|
1514
|
+
newValue,
|
|
1515
|
+
duration: deleteDuration + typeDuration,
|
|
1516
|
+
data: {
|
|
1517
|
+
diff,
|
|
1518
|
+
deleteDuration,
|
|
1519
|
+
typeDuration
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
},
|
|
1523
|
+
interpolate(metadata, progress) {
|
|
1524
|
+
const data = metadata.data;
|
|
1525
|
+
const { diff, deleteDuration, typeDuration } = data;
|
|
1526
|
+
const totalDuration = deleteDuration + typeDuration;
|
|
1527
|
+
if (totalDuration === 0) {
|
|
1528
|
+
return metadata.newValue;
|
|
1529
|
+
}
|
|
1530
|
+
const deleteEnd = deleteDuration / totalDuration;
|
|
1531
|
+
let deleteProgress;
|
|
1532
|
+
let typeProgress;
|
|
1533
|
+
if (progress <= deleteEnd) {
|
|
1534
|
+
deleteProgress = deleteEnd > 0 ? progress / deleteEnd : 1;
|
|
1535
|
+
typeProgress = 0;
|
|
1536
|
+
} else {
|
|
1537
|
+
deleteProgress = 1;
|
|
1538
|
+
const typeStart = deleteEnd;
|
|
1539
|
+
const typeRange = 1 - deleteEnd;
|
|
1540
|
+
typeProgress = typeRange > 0 ? (progress - typeStart) / typeRange : 1;
|
|
1541
|
+
}
|
|
1542
|
+
return buildIntermediateText(diff, deleteProgress, typeProgress);
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
var imageCrossfadeStrategy = {
|
|
1546
|
+
name: "image-crossfade",
|
|
1547
|
+
canAnimate(oldValue, newValue) {
|
|
1548
|
+
if (oldValue?.src === newValue?.src) return false;
|
|
1549
|
+
return true;
|
|
1550
|
+
},
|
|
1551
|
+
prepare(oldValue, newValue) {
|
|
1552
|
+
return {
|
|
1553
|
+
oldValue,
|
|
1554
|
+
newValue,
|
|
1555
|
+
duration: 300
|
|
1556
|
+
// Quick crossfade
|
|
1557
|
+
};
|
|
1558
|
+
},
|
|
1559
|
+
interpolate(metadata, progress) {
|
|
1560
|
+
return progress >= 0.5 ? metadata.newValue : metadata.oldValue;
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
var linkTransitionStrategy = {
|
|
1564
|
+
name: "link-transition",
|
|
1565
|
+
canAnimate(oldValue, newValue) {
|
|
1566
|
+
if (oldValue?.text === newValue?.text && oldValue?.href === newValue?.href) {
|
|
1567
|
+
return false;
|
|
1568
|
+
}
|
|
1569
|
+
return true;
|
|
1570
|
+
},
|
|
1571
|
+
prepare(oldValue, newValue) {
|
|
1572
|
+
const textDiff = computeTextDiff(oldValue?.text ?? "", newValue?.text ?? "");
|
|
1573
|
+
const { deleteDuration, typeDuration } = calculateAnimationTiming(textDiff, {
|
|
1574
|
+
charDelay: 50,
|
|
1575
|
+
maxDuration: 1500
|
|
1576
|
+
});
|
|
1577
|
+
return {
|
|
1578
|
+
oldValue,
|
|
1579
|
+
newValue,
|
|
1580
|
+
duration: deleteDuration + typeDuration + 100,
|
|
1581
|
+
// Extra for href highlight
|
|
1582
|
+
data: { textDiff }
|
|
1583
|
+
};
|
|
1584
|
+
},
|
|
1585
|
+
interpolate(metadata, progress) {
|
|
1586
|
+
return progress >= 0.5 ? metadata.newValue : metadata.oldValue;
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
function getTextCursorPosition(metadata, progress) {
|
|
1590
|
+
const { diff, deleteDuration, typeDuration } = metadata.data;
|
|
1591
|
+
const totalDuration = deleteDuration + typeDuration;
|
|
1592
|
+
if (totalDuration === 0) return null;
|
|
1593
|
+
const deleteEnd = deleteDuration / totalDuration;
|
|
1594
|
+
let textLength;
|
|
1595
|
+
if (progress <= deleteEnd) {
|
|
1596
|
+
const deleteProgress = deleteEnd > 0 ? progress / deleteEnd : 1;
|
|
1597
|
+
const deletionsRemaining = Math.floor(diff.deletions.length * (1 - deleteProgress));
|
|
1598
|
+
textLength = diff.commonPrefix.length + deletionsRemaining;
|
|
1599
|
+
} else {
|
|
1600
|
+
const typeProgress = (progress - deleteEnd) / (1 - deleteEnd);
|
|
1601
|
+
const additionsShown = Math.floor(diff.additions.length * typeProgress);
|
|
1602
|
+
textLength = diff.commonPrefix.length + additionsShown;
|
|
1603
|
+
}
|
|
1604
|
+
return textLength;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// src/hooks/useAnimatedText.ts
|
|
1608
|
+
function useAnimatedText(fieldId, content, options = {}) {
|
|
1609
|
+
const {
|
|
1610
|
+
enabled = true,
|
|
1611
|
+
charDelay = 50,
|
|
1612
|
+
maxDuration = 2e3,
|
|
1613
|
+
onStart,
|
|
1614
|
+
onComplete
|
|
1615
|
+
} = options;
|
|
1616
|
+
const customStrategy = useMemo3(() => {
|
|
1617
|
+
if (charDelay === 50) {
|
|
1618
|
+
return textTypingStrategy;
|
|
1619
|
+
}
|
|
1620
|
+
return {
|
|
1621
|
+
...textTypingStrategy,
|
|
1622
|
+
prepare(oldValue, newValue) {
|
|
1623
|
+
const diff = computeTextDiff(oldValue, newValue);
|
|
1624
|
+
const { deleteDuration, typeDuration } = calculateAnimationTiming(diff, {
|
|
1625
|
+
charDelay,
|
|
1626
|
+
maxDuration
|
|
1627
|
+
});
|
|
1628
|
+
return {
|
|
1629
|
+
oldValue,
|
|
1630
|
+
newValue,
|
|
1631
|
+
duration: deleteDuration + typeDuration,
|
|
1632
|
+
data: {
|
|
1633
|
+
diff,
|
|
1634
|
+
deleteDuration,
|
|
1635
|
+
typeDuration
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
};
|
|
1640
|
+
}, [charDelay, maxDuration]);
|
|
1641
|
+
const {
|
|
1642
|
+
displayValue,
|
|
1643
|
+
isAnimating,
|
|
1644
|
+
phase,
|
|
1645
|
+
progress,
|
|
1646
|
+
cancel,
|
|
1647
|
+
wrapperProps
|
|
1648
|
+
} = useAIEditAnimation(fieldId, content, {
|
|
1649
|
+
enabled,
|
|
1650
|
+
strategy: customStrategy,
|
|
1651
|
+
maxDuration,
|
|
1652
|
+
onStart,
|
|
1653
|
+
onComplete
|
|
1654
|
+
});
|
|
1655
|
+
const cursorPosition = useMemo3(() => {
|
|
1656
|
+
if (!isAnimating) return null;
|
|
1657
|
+
const diff = computeTextDiff(content, displayValue);
|
|
1658
|
+
const { deleteDuration, typeDuration } = calculateAnimationTiming(diff, {
|
|
1659
|
+
charDelay,
|
|
1660
|
+
maxDuration
|
|
1661
|
+
});
|
|
1662
|
+
const metadata = {
|
|
1663
|
+
oldValue: content,
|
|
1664
|
+
newValue: displayValue,
|
|
1665
|
+
duration: deleteDuration + typeDuration,
|
|
1666
|
+
data: {
|
|
1667
|
+
diff,
|
|
1668
|
+
deleteDuration,
|
|
1669
|
+
typeDuration
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
return getTextCursorPosition(metadata, progress);
|
|
1673
|
+
}, [isAnimating, content, displayValue, charDelay, maxDuration, progress]);
|
|
1674
|
+
return {
|
|
1675
|
+
displayContent: displayValue,
|
|
1676
|
+
isAnimating,
|
|
1677
|
+
phase,
|
|
1678
|
+
progress,
|
|
1679
|
+
cursorPosition,
|
|
1680
|
+
wrapperClassName: wrapperProps.className,
|
|
1681
|
+
cancel
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1168
1685
|
// #style-inject:#style-inject
|
|
1169
1686
|
function styleInject(css, { insertAt } = {}) {
|
|
1170
1687
|
if (!css || typeof document === "undefined") return;
|
|
@@ -1415,8 +1932,11 @@ body.builder-selector-active .ya-text-editable:hover {
|
|
|
1415
1932
|
}
|
|
1416
1933
|
`);
|
|
1417
1934
|
|
|
1935
|
+
// src/styles/ai-animations.css
|
|
1936
|
+
styleInject('.ya-ai-editing {\n position: relative;\n}\n.ya-ai-editing::before {\n content: "";\n position: absolute;\n inset: -4px;\n border: 2px solid;\n border-radius: 6px;\n animation: ya-ai-focus-pulse 1.5s ease-in-out infinite;\n pointer-events: none;\n z-index: 10;\n}\n@keyframes ya-ai-focus-pulse {\n 0%, 100% {\n border-color: rgba(239, 68, 68, 0.6);\n box-shadow: 0 0 15px rgba(239, 68, 68, 0.2);\n }\n 50% {\n border-color: rgba(249, 115, 22, 0.8);\n box-shadow: 0 0 25px rgba(249, 115, 22, 0.3);\n }\n}\n.ya-typing-cursor {\n display: inline-block;\n width: 2px;\n height: 1.1em;\n background:\n linear-gradient(\n 180deg,\n #EF4444,\n #F97316);\n animation: ya-cursor-blink 0.5s step-end infinite;\n margin-left: 1px;\n vertical-align: text-bottom;\n border-radius: 1px;\n}\n@keyframes ya-cursor-blink {\n 50% {\n opacity: 0;\n }\n}\n.ya-ai-complete {\n animation: ya-complete-glow 0.4s ease-out forwards;\n}\n@keyframes ya-complete-glow {\n 0% {\n box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.5);\n }\n 50% {\n box-shadow: 0 0 20px 5px rgba(16, 185, 129, 0.3);\n }\n 100% {\n box-shadow: 0 0 0 0 transparent;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .ya-ai-editing::before {\n animation: none;\n border-color: rgba(239, 68, 68, 0.6);\n box-shadow: 0 0 15px rgba(239, 68, 68, 0.2);\n }\n .ya-typing-cursor {\n animation: none;\n opacity: 1;\n }\n .ya-ai-complete {\n animation: ya-complete-glow-reduced 0.2s ease-out forwards;\n }\n @keyframes ya-complete-glow-reduced {\n 0% {\n background-color: rgba(16, 185, 129, 0.1);\n }\n 100% {\n background-color: transparent;\n }\n }\n}\n.ya-ai-hidden {\n opacity: 0;\n visibility: hidden;\n}\n.ya-ai-fade-in {\n animation: ya-fade-in 0.3s ease-out forwards;\n}\n@keyframes ya-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n.ya-ai-pulse {\n animation: ya-scale-pulse 0.3s ease-out forwards;\n}\n@keyframes ya-scale-pulse {\n 0% {\n transform: scale(1);\n }\n 50% {\n transform: scale(1.02);\n }\n 100% {\n transform: scale(1);\n }\n}\n');
|
|
1937
|
+
|
|
1418
1938
|
// src/components/YaText.tsx
|
|
1419
|
-
import { Fragment as Fragment2, jsx as
|
|
1939
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1420
1940
|
var FontSize = Extension.create({
|
|
1421
1941
|
name: "fontSize",
|
|
1422
1942
|
addOptions() {
|
|
@@ -1511,10 +2031,17 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1511
2031
|
const { getValue, setValue, mode, saveToWorker, activeFieldId, setActiveField } = useContentStore();
|
|
1512
2032
|
const storeContent = getValue(fieldId);
|
|
1513
2033
|
const content = storeContent || (typeof children === "string" ? children : "");
|
|
1514
|
-
const [isEditing, setIsEditing] =
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
|
|
2034
|
+
const [isEditing, setIsEditing] = useState4(false);
|
|
2035
|
+
const {
|
|
2036
|
+
displayContent,
|
|
2037
|
+
isAnimating,
|
|
2038
|
+
wrapperClassName
|
|
2039
|
+
} = useAnimatedText(fieldId, content, {
|
|
2040
|
+
enabled: mode === "inline-edit" && !isEditing
|
|
2041
|
+
});
|
|
2042
|
+
const [originalContent, setOriginalContent] = useState4(content);
|
|
2043
|
+
const containerRef = useRef5(null);
|
|
2044
|
+
const originalContentRef = useRef5(content);
|
|
1518
2045
|
const editor = useEditor({
|
|
1519
2046
|
extensions: [
|
|
1520
2047
|
StarterKit.configure({
|
|
@@ -1548,19 +2075,19 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1548
2075
|
},
|
|
1549
2076
|
parseOptions: { preserveWhitespace: "full" }
|
|
1550
2077
|
});
|
|
1551
|
-
|
|
2078
|
+
useEffect4(() => {
|
|
1552
2079
|
if (editor && !isEditing) {
|
|
1553
2080
|
if (editor.getHTML() !== content) {
|
|
1554
2081
|
editor.commands.setContent(content, { parseOptions: { preserveWhitespace: "full" } });
|
|
1555
2082
|
}
|
|
1556
2083
|
}
|
|
1557
2084
|
}, [content, editor, isEditing]);
|
|
1558
|
-
|
|
2085
|
+
useEffect4(() => {
|
|
1559
2086
|
if (isEditing && activeFieldId !== null && activeFieldId !== fieldId) {
|
|
1560
2087
|
setIsEditing(false);
|
|
1561
2088
|
}
|
|
1562
2089
|
}, [activeFieldId, fieldId, isEditing]);
|
|
1563
|
-
const handleSave =
|
|
2090
|
+
const handleSave = useCallback5(() => {
|
|
1564
2091
|
if (!editor) return;
|
|
1565
2092
|
let html = editor.getHTML();
|
|
1566
2093
|
html = html.replace(/<\/p><p>/g, "<br><br>").replace(/^<p>/, "").replace(/<\/p>$/, "");
|
|
@@ -1568,13 +2095,13 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1568
2095
|
saveToWorker?.(fieldId, html);
|
|
1569
2096
|
setIsEditing(false);
|
|
1570
2097
|
}, [editor, fieldId, setValue, saveToWorker]);
|
|
1571
|
-
const handleCancel =
|
|
2098
|
+
const handleCancel = useCallback5(() => {
|
|
1572
2099
|
if (editor) {
|
|
1573
2100
|
editor.commands.setContent(originalContent, { parseOptions: { preserveWhitespace: "full" } });
|
|
1574
2101
|
}
|
|
1575
2102
|
setIsEditing(false);
|
|
1576
2103
|
}, [editor, originalContent]);
|
|
1577
|
-
const handleClose =
|
|
2104
|
+
const handleClose = useCallback5(() => {
|
|
1578
2105
|
if (!editor) {
|
|
1579
2106
|
setIsEditing(false);
|
|
1580
2107
|
return;
|
|
@@ -1587,7 +2114,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1587
2114
|
}
|
|
1588
2115
|
setIsEditing(false);
|
|
1589
2116
|
}, [editor, fieldId, setValue, saveToWorker]);
|
|
1590
|
-
const handleClick =
|
|
2117
|
+
const handleClick = useCallback5((e) => {
|
|
1591
2118
|
if (isEditing) {
|
|
1592
2119
|
e.preventDefault();
|
|
1593
2120
|
e.stopPropagation();
|
|
@@ -1613,7 +2140,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1613
2140
|
}, 20);
|
|
1614
2141
|
}
|
|
1615
2142
|
}, [mode, isEditing, content, editor, fieldId, setActiveField, handleClose]);
|
|
1616
|
-
const handleKeyDown =
|
|
2143
|
+
const handleKeyDown = useCallback5(
|
|
1617
2144
|
(event) => {
|
|
1618
2145
|
if (!isEditing) return;
|
|
1619
2146
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
@@ -1634,7 +2161,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1634
2161
|
},
|
|
1635
2162
|
[isEditing, handleSave, handleCancel]
|
|
1636
2163
|
);
|
|
1637
|
-
const handleLink =
|
|
2164
|
+
const handleLink = useCallback5(() => {
|
|
1638
2165
|
if (!editor) return;
|
|
1639
2166
|
const previousUrl = editor.getAttributes("link").href;
|
|
1640
2167
|
const url = window.prompt("Enter URL:", previousUrl || "https://");
|
|
@@ -1645,7 +2172,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1645
2172
|
editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
|
|
1646
2173
|
}
|
|
1647
2174
|
}, [editor]);
|
|
1648
|
-
const handleFontSizeChange =
|
|
2175
|
+
const handleFontSizeChange = useCallback5(
|
|
1649
2176
|
(e) => {
|
|
1650
2177
|
if (!editor) return;
|
|
1651
2178
|
const size = e.target.value;
|
|
@@ -1657,7 +2184,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1657
2184
|
},
|
|
1658
2185
|
[editor]
|
|
1659
2186
|
);
|
|
1660
|
-
const handleFontWeightChange =
|
|
2187
|
+
const handleFontWeightChange = useCallback5(
|
|
1661
2188
|
(e) => {
|
|
1662
2189
|
if (!editor) return;
|
|
1663
2190
|
const weight = e.target.value;
|
|
@@ -1680,24 +2207,30 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1680
2207
|
return attrs.fontWeight || "";
|
|
1681
2208
|
};
|
|
1682
2209
|
if (mode === "read-only") {
|
|
1683
|
-
return /* @__PURE__ */
|
|
2210
|
+
return /* @__PURE__ */ jsx5(
|
|
1684
2211
|
Component,
|
|
1685
2212
|
{
|
|
1686
2213
|
ref: containerRef,
|
|
1687
2214
|
className,
|
|
1688
2215
|
"data-ya-restricted": "true",
|
|
1689
2216
|
"data-field-id": fieldId,
|
|
1690
|
-
children: /* @__PURE__ */
|
|
2217
|
+
children: /* @__PURE__ */ jsx5(SafeHtml, { content, mode })
|
|
1691
2218
|
}
|
|
1692
2219
|
);
|
|
1693
2220
|
}
|
|
1694
|
-
|
|
2221
|
+
const combinedClassName = [
|
|
2222
|
+
className || "",
|
|
2223
|
+
isEditing ? "ya-text-editing" : "ya-text-editable",
|
|
2224
|
+
wrapperClassName
|
|
2225
|
+
].filter(Boolean).join(" ");
|
|
2226
|
+
return /* @__PURE__ */ jsx5(
|
|
1695
2227
|
Component,
|
|
1696
2228
|
{
|
|
1697
2229
|
ref: containerRef,
|
|
1698
|
-
className:
|
|
2230
|
+
className: combinedClassName,
|
|
1699
2231
|
"data-ya-restricted": "true",
|
|
1700
2232
|
"data-field-id": fieldId,
|
|
2233
|
+
"data-ai-editing": isAnimating || void 0,
|
|
1701
2234
|
onClick: handleClick,
|
|
1702
2235
|
onKeyDown: handleKeyDown,
|
|
1703
2236
|
children: editor ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
@@ -1710,37 +2243,37 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1710
2243
|
options: { offset: 6, placement: "top" },
|
|
1711
2244
|
className: "ya-bubble-menu",
|
|
1712
2245
|
children: [
|
|
1713
|
-
/* @__PURE__ */
|
|
2246
|
+
/* @__PURE__ */ jsx5(
|
|
1714
2247
|
"button",
|
|
1715
2248
|
{
|
|
1716
2249
|
type: "button",
|
|
1717
2250
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
1718
2251
|
className: `ya-bubble-btn ${editor.isActive("bold") ? "is-active" : ""}`,
|
|
1719
2252
|
title: "Bold",
|
|
1720
|
-
children: /* @__PURE__ */
|
|
2253
|
+
children: /* @__PURE__ */ jsx5("strong", { children: "B" })
|
|
1721
2254
|
}
|
|
1722
2255
|
),
|
|
1723
|
-
/* @__PURE__ */
|
|
2256
|
+
/* @__PURE__ */ jsx5(
|
|
1724
2257
|
"button",
|
|
1725
2258
|
{
|
|
1726
2259
|
type: "button",
|
|
1727
2260
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
1728
2261
|
className: `ya-bubble-btn ${editor.isActive("italic") ? "is-active" : ""}`,
|
|
1729
2262
|
title: "Italic",
|
|
1730
|
-
children: /* @__PURE__ */
|
|
2263
|
+
children: /* @__PURE__ */ jsx5("em", { children: "I" })
|
|
1731
2264
|
}
|
|
1732
2265
|
),
|
|
1733
|
-
/* @__PURE__ */
|
|
2266
|
+
/* @__PURE__ */ jsx5(
|
|
1734
2267
|
"button",
|
|
1735
2268
|
{
|
|
1736
2269
|
type: "button",
|
|
1737
2270
|
onClick: handleLink,
|
|
1738
2271
|
className: `ya-bubble-btn ${editor.isActive("link") ? "is-active" : ""}`,
|
|
1739
2272
|
title: "Link",
|
|
1740
|
-
children: /* @__PURE__ */
|
|
2273
|
+
children: /* @__PURE__ */ jsx5("span", { children: "\u{1F517}" })
|
|
1741
2274
|
}
|
|
1742
2275
|
),
|
|
1743
|
-
/* @__PURE__ */
|
|
2276
|
+
/* @__PURE__ */ jsx5("span", { className: "ya-bubble-divider" }),
|
|
1744
2277
|
/* @__PURE__ */ jsxs2(
|
|
1745
2278
|
"select",
|
|
1746
2279
|
{
|
|
@@ -1749,8 +2282,8 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1749
2282
|
className: "ya-bubble-select",
|
|
1750
2283
|
title: "Font Size",
|
|
1751
2284
|
children: [
|
|
1752
|
-
/* @__PURE__ */
|
|
1753
|
-
Object.entries(SIZE_PRESETS).map(([name, size]) => /* @__PURE__ */
|
|
2285
|
+
/* @__PURE__ */ jsx5("option", { value: "", children: "Size" }),
|
|
2286
|
+
Object.entries(SIZE_PRESETS).map(([name, size]) => /* @__PURE__ */ jsx5("option", { value: size, children: name }, name))
|
|
1754
2287
|
]
|
|
1755
2288
|
}
|
|
1756
2289
|
),
|
|
@@ -1762,8 +2295,8 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1762
2295
|
className: "ya-bubble-select",
|
|
1763
2296
|
title: "Font Weight",
|
|
1764
2297
|
children: [
|
|
1765
|
-
/* @__PURE__ */
|
|
1766
|
-
Object.entries(WEIGHT_PRESETS).map(([name, weight]) => /* @__PURE__ */
|
|
2298
|
+
/* @__PURE__ */ jsx5("option", { value: "", children: "Weight" }),
|
|
2299
|
+
Object.entries(WEIGHT_PRESETS).map(([name, weight]) => /* @__PURE__ */ jsx5("option", { value: weight, children: name }, name))
|
|
1767
2300
|
]
|
|
1768
2301
|
}
|
|
1769
2302
|
)
|
|
@@ -1773,9 +2306,9 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1773
2306
|
document.body
|
|
1774
2307
|
),
|
|
1775
2308
|
isEditing ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1776
|
-
/* @__PURE__ */
|
|
2309
|
+
/* @__PURE__ */ jsx5(EditorContent, { editor }),
|
|
1777
2310
|
/* @__PURE__ */ jsxs2("div", { className: "ya-text-actions", children: [
|
|
1778
|
-
/* @__PURE__ */
|
|
2311
|
+
/* @__PURE__ */ jsx5(
|
|
1779
2312
|
"button",
|
|
1780
2313
|
{
|
|
1781
2314
|
type: "button",
|
|
@@ -1784,7 +2317,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1784
2317
|
children: "Cancel"
|
|
1785
2318
|
}
|
|
1786
2319
|
),
|
|
1787
|
-
/* @__PURE__ */
|
|
2320
|
+
/* @__PURE__ */ jsx5(
|
|
1788
2321
|
"button",
|
|
1789
2322
|
{
|
|
1790
2323
|
type: "button",
|
|
@@ -1794,14 +2327,20 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
1794
2327
|
}
|
|
1795
2328
|
)
|
|
1796
2329
|
] })
|
|
1797
|
-
] }) : /* @__PURE__ */
|
|
1798
|
-
|
|
2330
|
+
] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
2331
|
+
/* @__PURE__ */ jsx5(SafeHtml, { content: displayContent, mode }),
|
|
2332
|
+
isAnimating && /* @__PURE__ */ jsx5("span", { className: "ya-typing-cursor" })
|
|
2333
|
+
] })
|
|
2334
|
+
] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
2335
|
+
/* @__PURE__ */ jsx5(SafeHtml, { content: displayContent, mode }),
|
|
2336
|
+
isAnimating && /* @__PURE__ */ jsx5("span", { className: "ya-typing-cursor" })
|
|
2337
|
+
] })
|
|
1799
2338
|
}
|
|
1800
2339
|
);
|
|
1801
2340
|
}
|
|
1802
2341
|
|
|
1803
2342
|
// src/components/YaImage.tsx
|
|
1804
|
-
import { useCallback as
|
|
2343
|
+
import { useCallback as useCallback7, useEffect as useEffect6, useRef as useRef7, useState as useState6 } from "react";
|
|
1805
2344
|
|
|
1806
2345
|
// src/lib/asset-resolver.ts
|
|
1807
2346
|
var assetResolver = (path) => path;
|
|
@@ -1817,25 +2356,25 @@ function resolveAssetUrl(path) {
|
|
|
1817
2356
|
}
|
|
1818
2357
|
|
|
1819
2358
|
// src/components/YaTooltip.tsx
|
|
1820
|
-
import { useEffect as
|
|
2359
|
+
import { useEffect as useEffect5, useRef as useRef6, useState as useState5, useCallback as useCallback6 } from "react";
|
|
1821
2360
|
import { createPortal as createPortal3 } from "react-dom";
|
|
1822
2361
|
|
|
1823
2362
|
// src/components/ya-tooltip.css
|
|
1824
2363
|
styleInject('.ya-tooltip {\n position: fixed;\n z-index: 9999;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 12px;\n background: #1a1a1a;\n color: white;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n white-space: nowrap;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n animation: ya-tooltip-fade-in 0.15s ease;\n pointer-events: none;\n}\n@keyframes ya-tooltip-fade-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n.ya-tooltip svg {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n}\n.ya-tooltip-bottom {\n transform: translateX(-50%);\n}\n.ya-tooltip-bottom::before {\n content: "";\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 6px solid transparent;\n border-bottom-color: #1a1a1a;\n}\n.ya-tooltip-top {\n transform: translateX(-50%);\n}\n.ya-tooltip-top::before {\n content: "";\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 6px solid transparent;\n border-top-color: #1a1a1a;\n}\n.ya-tooltip-right {\n transform: translateY(-50%);\n}\n.ya-tooltip-right::before {\n content: "";\n position: absolute;\n right: 100%;\n top: 50%;\n transform: translateY(-50%);\n border: 6px solid transparent;\n border-right-color: #1a1a1a;\n}\n.ya-tooltip-left {\n transform: translateY(-50%);\n}\n.ya-tooltip-left::before {\n content: "";\n position: absolute;\n left: 100%;\n top: 50%;\n transform: translateY(-50%);\n border: 6px solid transparent;\n border-left-color: #1a1a1a;\n}\n');
|
|
1825
2364
|
|
|
1826
2365
|
// src/components/YaTooltip.tsx
|
|
1827
|
-
import { jsx as
|
|
2366
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1828
2367
|
function YaTooltip({
|
|
1829
2368
|
anchorRef,
|
|
1830
2369
|
children,
|
|
1831
2370
|
show,
|
|
1832
2371
|
preferredPosition = "bottom"
|
|
1833
2372
|
}) {
|
|
1834
|
-
const [position, setPosition] =
|
|
1835
|
-
const [coords, setCoords] =
|
|
1836
|
-
const [isPositioned, setIsPositioned] =
|
|
1837
|
-
const tooltipRef =
|
|
1838
|
-
const calculatePosition =
|
|
2373
|
+
const [position, setPosition] = useState5(preferredPosition);
|
|
2374
|
+
const [coords, setCoords] = useState5({ top: 0, left: 0 });
|
|
2375
|
+
const [isPositioned, setIsPositioned] = useState5(false);
|
|
2376
|
+
const tooltipRef = useRef6(null);
|
|
2377
|
+
const calculatePosition = useCallback6(() => {
|
|
1839
2378
|
if (!anchorRef.current) return;
|
|
1840
2379
|
const anchor = anchorRef.current.getBoundingClientRect();
|
|
1841
2380
|
const tooltip = tooltipRef.current?.getBoundingClientRect();
|
|
@@ -1910,7 +2449,7 @@ function YaTooltip({
|
|
|
1910
2449
|
setCoords({ top, left });
|
|
1911
2450
|
setIsPositioned(true);
|
|
1912
2451
|
}, [anchorRef, preferredPosition]);
|
|
1913
|
-
|
|
2452
|
+
useEffect5(() => {
|
|
1914
2453
|
if (!show) {
|
|
1915
2454
|
setIsPositioned(false);
|
|
1916
2455
|
return;
|
|
@@ -1923,14 +2462,14 @@ function YaTooltip({
|
|
|
1923
2462
|
window.removeEventListener("resize", calculatePosition);
|
|
1924
2463
|
};
|
|
1925
2464
|
}, [show, calculatePosition]);
|
|
1926
|
-
|
|
2465
|
+
useEffect5(() => {
|
|
1927
2466
|
if (show && tooltipRef.current) {
|
|
1928
2467
|
calculatePosition();
|
|
1929
2468
|
}
|
|
1930
2469
|
}, [show, children, calculatePosition]);
|
|
1931
2470
|
if (!show) return null;
|
|
1932
2471
|
return createPortal3(
|
|
1933
|
-
/* @__PURE__ */
|
|
2472
|
+
/* @__PURE__ */ jsx6(
|
|
1934
2473
|
"div",
|
|
1935
2474
|
{
|
|
1936
2475
|
ref: tooltipRef,
|
|
@@ -1953,7 +2492,7 @@ function YaTooltip({
|
|
|
1953
2492
|
styleInject('.ya-image-container {\n position: relative;\n display: inline-block;\n min-width: 45px;\n min-height: 45px;\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-image-container img {\n display: block;\n}\n.ya-image-editable {\n cursor: pointer;\n}\n.ya-image-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-selected {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background: rgba(0, 0, 0, 0.5);\n opacity: 0;\n transition: opacity 0.2s ease;\n pointer-events: none;\n border-radius: inherit;\n}\n.ya-image-editable:hover .ya-image-overlay {\n opacity: 1;\n}\n.ya-image-selected .ya-image-overlay {\n opacity: 0;\n}\n.ya-image-edit-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n background: white;\n border-radius: 50%;\n color: #1a1a1a;\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);\n}\n.ya-image-edit-icon svg {\n width: 24px;\n height: 24px;\n}\n.ya-image-edit-label {\n color: white;\n font-size: 14px;\n font-weight: 500;\n text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);\n}\n@keyframes ya-image-success {\n 0% {\n outline-color: var(--color-primary, #D4A574);\n }\n 50% {\n outline-color: #22c55e;\n outline-width: 4px;\n }\n 100% {\n outline-color: var(--color-primary, #D4A574);\n outline-width: 2px;\n }\n}\n.ya-image-success {\n animation: ya-image-success 0.4s ease;\n}\n.ya-image-loading::after {\n content: "";\n position: absolute;\n inset: 0;\n background:\n linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0) 0%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0) 100%);\n background-size: 200% 100%;\n animation: ya-image-shimmer 1.5s infinite;\n}\n@keyframes ya-image-shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n.ya-image-container:focus {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-container:focus:not(:focus-visible) {\n outline: none;\n}\n.ya-image-container:focus-visible {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-small .ya-image-overlay {\n display: none;\n}\n');
|
|
1954
2493
|
|
|
1955
2494
|
// src/components/YaImage.tsx
|
|
1956
|
-
import { jsx as
|
|
2495
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1957
2496
|
function parseImageValue(value) {
|
|
1958
2497
|
if (!value) {
|
|
1959
2498
|
return { src: "" };
|
|
@@ -1977,6 +2516,16 @@ function getObjectPosition(imageData) {
|
|
|
1977
2516
|
return imageData.objectPosition || "50% 50%";
|
|
1978
2517
|
}
|
|
1979
2518
|
var SMALL_IMAGE_THRESHOLD = 100;
|
|
2519
|
+
var PLACEHOLDER_SVG = `data:image/svg+xml,${encodeURIComponent(`
|
|
2520
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="300" viewBox="0 0 400 300">
|
|
2521
|
+
<rect fill="#e5e7eb" width="400" height="300"/>
|
|
2522
|
+
<g fill="#9ca3af" transform="translate(175, 125)">
|
|
2523
|
+
<rect x="3" y="3" width="44" height="44" rx="4" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
2524
|
+
<circle cx="15" cy="15" r="4" fill="currentColor"/>
|
|
2525
|
+
<path d="M47 35 L32 20 L17 35 L12 30 L3 39 L3 43 L47 43 Z" fill="currentColor"/>
|
|
2526
|
+
</g>
|
|
2527
|
+
</svg>
|
|
2528
|
+
`)}`;
|
|
1980
2529
|
function YaImage({
|
|
1981
2530
|
fieldId,
|
|
1982
2531
|
className,
|
|
@@ -1988,18 +2537,18 @@ function YaImage({
|
|
|
1988
2537
|
fallbackAlt
|
|
1989
2538
|
}) {
|
|
1990
2539
|
const { getValue, mode } = useContentStore();
|
|
1991
|
-
const containerRef =
|
|
1992
|
-
const imgRef =
|
|
1993
|
-
const [isSelected, setIsSelected] =
|
|
1994
|
-
const [isHovered, setIsHovered] =
|
|
1995
|
-
const [isSmallImage, setIsSmallImage] =
|
|
2540
|
+
const containerRef = useRef7(null);
|
|
2541
|
+
const imgRef = useRef7(null);
|
|
2542
|
+
const [isSelected, setIsSelected] = useState6(false);
|
|
2543
|
+
const [isHovered, setIsHovered] = useState6(false);
|
|
2544
|
+
const [isSmallImage, setIsSmallImage] = useState6(false);
|
|
1996
2545
|
const rawValue = getValue(fieldId);
|
|
1997
2546
|
const imageData = parseImageValue(rawValue);
|
|
1998
|
-
const src = imageData.src || fallbackSrc ||
|
|
2547
|
+
const src = imageData.src || fallbackSrc || PLACEHOLDER_SVG;
|
|
1999
2548
|
const altText = imageData.alt || alt || fallbackAlt || "";
|
|
2000
2549
|
const objectFit = imageData.objectFit || propObjectFit || "cover";
|
|
2001
2550
|
const objectPosition = getObjectPosition(imageData) || propObjectPosition || "50% 50%";
|
|
2002
|
-
const handleClick =
|
|
2551
|
+
const handleClick = useCallback7(() => {
|
|
2003
2552
|
if (mode !== "inline-edit") return;
|
|
2004
2553
|
if (document.body.classList.contains("builder-selector-active")) return;
|
|
2005
2554
|
setIsSelected(true);
|
|
@@ -2027,7 +2576,7 @@ function YaImage({
|
|
|
2027
2576
|
"*"
|
|
2028
2577
|
);
|
|
2029
2578
|
}, [mode, fieldId, imageData, src, altText, objectFit, objectPosition]);
|
|
2030
|
-
|
|
2579
|
+
useEffect6(() => {
|
|
2031
2580
|
if (mode !== "inline-edit") return;
|
|
2032
2581
|
const handleMessage2 = (event) => {
|
|
2033
2582
|
if (event.data?.type === "YA_IMAGE_EDIT_COMPLETE" && event.data.fieldId === fieldId) {
|
|
@@ -2042,7 +2591,7 @@ function YaImage({
|
|
|
2042
2591
|
window.addEventListener("message", handleMessage2);
|
|
2043
2592
|
return () => window.removeEventListener("message", handleMessage2);
|
|
2044
2593
|
}, [mode, fieldId]);
|
|
2045
|
-
|
|
2594
|
+
useEffect6(() => {
|
|
2046
2595
|
if (mode !== "inline-edit") return;
|
|
2047
2596
|
const checkSize = () => {
|
|
2048
2597
|
if (imgRef.current) {
|
|
@@ -2064,7 +2613,7 @@ function YaImage({
|
|
|
2064
2613
|
window.removeEventListener("resize", checkSize);
|
|
2065
2614
|
};
|
|
2066
2615
|
}, [mode]);
|
|
2067
|
-
|
|
2616
|
+
useEffect6(() => {
|
|
2068
2617
|
if (!isSelected || mode !== "inline-edit") return;
|
|
2069
2618
|
let lastRectKey = "";
|
|
2070
2619
|
let lastTime = 0;
|
|
@@ -2099,7 +2648,7 @@ function YaImage({
|
|
|
2099
2648
|
return () => cancelAnimationFrame(rafId);
|
|
2100
2649
|
}, [isSelected, fieldId, mode]);
|
|
2101
2650
|
if (mode === "read-only") {
|
|
2102
|
-
return /* @__PURE__ */
|
|
2651
|
+
return /* @__PURE__ */ jsx7(
|
|
2103
2652
|
"img",
|
|
2104
2653
|
{
|
|
2105
2654
|
src: resolveAssetUrl(src),
|
|
@@ -2127,9 +2676,9 @@ function YaImage({
|
|
|
2127
2676
|
strokeLinecap: "round",
|
|
2128
2677
|
strokeLinejoin: "round",
|
|
2129
2678
|
children: [
|
|
2130
|
-
/* @__PURE__ */
|
|
2131
|
-
/* @__PURE__ */
|
|
2132
|
-
/* @__PURE__ */
|
|
2679
|
+
/* @__PURE__ */ jsx7("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
2680
|
+
/* @__PURE__ */ jsx7("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
2681
|
+
/* @__PURE__ */ jsx7("polyline", { points: "21 15 16 10 5 21" })
|
|
2133
2682
|
]
|
|
2134
2683
|
}
|
|
2135
2684
|
);
|
|
@@ -2154,7 +2703,7 @@ function YaImage({
|
|
|
2154
2703
|
}
|
|
2155
2704
|
},
|
|
2156
2705
|
children: [
|
|
2157
|
-
/* @__PURE__ */
|
|
2706
|
+
/* @__PURE__ */ jsx7(
|
|
2158
2707
|
"img",
|
|
2159
2708
|
{
|
|
2160
2709
|
ref: imgRef,
|
|
@@ -2170,12 +2719,12 @@ function YaImage({
|
|
|
2170
2719
|
),
|
|
2171
2720
|
isSmallImage ? /* @__PURE__ */ jsxs3(YaTooltip, { anchorRef: containerRef, show: isHovered && !isSelected, children: [
|
|
2172
2721
|
editIcon,
|
|
2173
|
-
/* @__PURE__ */
|
|
2722
|
+
/* @__PURE__ */ jsx7("span", { children: "Click to edit" })
|
|
2174
2723
|
] }) : (
|
|
2175
2724
|
/* For large images: show overlay inside the image */
|
|
2176
2725
|
/* @__PURE__ */ jsxs3("div", { className: "ya-image-overlay", children: [
|
|
2177
|
-
/* @__PURE__ */
|
|
2178
|
-
/* @__PURE__ */
|
|
2726
|
+
/* @__PURE__ */ jsx7("div", { className: "ya-image-edit-icon", children: editIcon }),
|
|
2727
|
+
/* @__PURE__ */ jsx7("span", { className: "ya-image-edit-label", children: "Click to edit" })
|
|
2179
2728
|
] })
|
|
2180
2729
|
)
|
|
2181
2730
|
]
|
|
@@ -2184,7 +2733,7 @@ function YaImage({
|
|
|
2184
2733
|
}
|
|
2185
2734
|
|
|
2186
2735
|
// src/components/YaLink.tsx
|
|
2187
|
-
import { useEffect as
|
|
2736
|
+
import { useEffect as useEffect7, useRef as useRef8, useState as useState7, useCallback as useCallback8 } from "react";
|
|
2188
2737
|
import { createPortal as createPortal4 } from "react-dom";
|
|
2189
2738
|
import { useEditor as useEditor2, EditorContent as EditorContent2 } from "@tiptap/react";
|
|
2190
2739
|
import { BubbleMenu as BubbleMenu2 } from "@tiptap/react/menus";
|
|
@@ -2197,7 +2746,7 @@ import { Link as WouterLink, useLocation } from "wouter";
|
|
|
2197
2746
|
styleInject('.ya-link-wrapper {\n position: relative;\n display: inline;\n}\n.ya-link-editable {\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-link-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: 4px;\n border-radius: 4px;\n}\nbody.builder-selector-active .ya-link-editable:hover {\n outline: none;\n cursor: inherit;\n}\n.ya-link-editing {\n outline: 2px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n border-radius: 4px;\n position: relative;\n}\n.ya-link-actions {\n display: flex;\n gap: 8px;\n position: absolute;\n bottom: -60px;\n right: 0;\n z-index: 10;\n background: rgba(26, 26, 26, 0.95);\n padding: 8px 10px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n}\n.ya-link-btn {\n padding: 6px 14px;\n font-size: 12px;\n font-weight: 500;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n border: none;\n}\n.ya-link-btn-cancel {\n background: #333333;\n color: #ffffff;\n border: 1px solid #555555;\n}\n.ya-link-btn-cancel:hover {\n background: #444444;\n color: #ffffff;\n border-color: #666666;\n}\n.ya-link-btn-save {\n background: #D4A574;\n color: #1a1a1a;\n}\n.ya-link-btn-save:hover {\n background: #c4956a;\n}\n.ya-href-popover {\n position: absolute;\n top: 100%;\n left: 50%;\n margin-top: 8px;\n z-index: 10;\n min-width: 280px;\n max-width: 320px;\n background: #1a1a1a;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n transform: translateX(-50%);\n animation: ya-href-popover-fade-in 0.15s ease;\n overflow: hidden;\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n}\n@keyframes ya-href-popover-fade-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n.ya-href-popover::before {\n content: "";\n position: absolute;\n top: -6px;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-bottom: 8px solid #1a1a1a;\n}\n.ya-href-popover-header {\n padding: 12px 16px;\n font-size: 13px;\n font-weight: 600;\n color: #ffffff;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n.ya-href-popover-section {\n padding: 12px 16px;\n}\n.ya-href-popover-label {\n display: block;\n font-size: 11px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.6);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 8px;\n}\n.ya-href-collapsible-header {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 0;\n background: transparent;\n border: none;\n cursor: pointer;\n transition: color 0.15s ease;\n}\n.ya-href-collapsible-header:hover {\n color: rgba(255, 255, 255, 0.8);\n}\n.ya-href-chevron {\n font-size: 8px;\n color: rgba(255, 255, 255, 0.4);\n}\n.ya-href-popover-pages {\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: 200px;\n overflow-y: auto;\n}\n.ya-href-page-btn {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n width: 100%;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid transparent;\n border-radius: 8px;\n color: #e0e0e0;\n font-size: 13px;\n font-weight: 500;\n text-align: left;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.ya-href-page-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n border-color: rgba(255, 255, 255, 0.2);\n}\n.ya-href-page-btn.is-selected {\n background: #D4A574;\n color: #1a1a1a;\n}\n.ya-href-page-btn.is-selected .ya-href-page-path {\n color: rgba(26, 26, 26, 0.6);\n}\n.ya-href-page-path {\n font-size: 11px;\n color: rgba(255, 255, 255, 0.4);\n font-family: monospace;\n word-break: break-all;\n}\n.ya-href-external-toggle {\n display: block;\n width: 100%;\n padding: 10px 16px;\n background: transparent;\n border: none;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n color: #D4A574;\n font-size: 12px;\n font-weight: 500;\n text-align: center;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n.ya-href-external-toggle:hover {\n background: rgba(255, 255, 255, 0.05);\n}\n.ya-href-url-input {\n width: 100%;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 8px;\n color: #ffffff;\n font-size: 13px;\n outline: none;\n transition: border-color 0.15s ease;\n}\n.ya-href-url-input::placeholder {\n color: rgba(255, 255, 255, 0.4);\n}\n.ya-href-url-input:focus {\n border-color: var(--color-primary, #D4A574);\n}\n.ya-href-popover-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 16px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n}\n.ya-link-edit-popover {\n position: absolute;\n top: 100%;\n left: 50%;\n margin-top: 8px;\n z-index: 10;\n background: #2a2a2a;\n border-radius: 6px;\n padding: 4px;\n display: flex;\n gap: 4px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n transform: translateX(-50%);\n animation: ya-edit-popover-fade-in 0.1s ease;\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n white-space: nowrap;\n}\n@keyframes ya-edit-popover-fade-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n.ya-link-edit-popover::before {\n content: "";\n position: absolute;\n top: -5px;\n left: 50%;\n transform: translateX(-50%);\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-bottom: 6px solid #2a2a2a;\n}\n.ya-link-edit-popover button {\n background: #3a3a3a;\n border: none;\n color: #fff;\n padding: 6px 12px;\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n transition: background 0.15s ease;\n}\n.ya-link-edit-popover button:hover {\n background: #4a4a4a;\n}\n');
|
|
2198
2747
|
|
|
2199
2748
|
// src/components/YaLink.tsx
|
|
2200
|
-
import { Fragment as Fragment3, jsx as
|
|
2749
|
+
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2201
2750
|
function isInternalPath(path) {
|
|
2202
2751
|
if (!path) return false;
|
|
2203
2752
|
if (path.startsWith("#")) return false;
|
|
@@ -2206,6 +2755,12 @@ function isInternalPath(path) {
|
|
|
2206
2755
|
if (path.startsWith("mailto:") || path.startsWith("tel:")) return false;
|
|
2207
2756
|
return path.startsWith("/");
|
|
2208
2757
|
}
|
|
2758
|
+
function isExternalHref(url) {
|
|
2759
|
+
if (!url) return false;
|
|
2760
|
+
if (url.startsWith("//")) return true;
|
|
2761
|
+
if (url.startsWith("http://") || url.startsWith("https://")) return true;
|
|
2762
|
+
return false;
|
|
2763
|
+
}
|
|
2209
2764
|
var FontSize2 = Extension2.create({
|
|
2210
2765
|
name: "fontSize",
|
|
2211
2766
|
addOptions() {
|
|
@@ -2288,12 +2843,12 @@ function discoverSectionsFromDOM() {
|
|
|
2288
2843
|
});
|
|
2289
2844
|
return sections;
|
|
2290
2845
|
}
|
|
2291
|
-
function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "a", children, availablePages, onClick }) {
|
|
2846
|
+
function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Component = "a", children, availablePages, onClick, target, rel }) {
|
|
2292
2847
|
const { getValue, setValue, mode, saveToWorker, getPages } = useContentStore();
|
|
2293
2848
|
const [, navigate] = useLocation();
|
|
2294
2849
|
const pages = availablePages ?? getPages();
|
|
2295
|
-
const [sections, setSections] =
|
|
2296
|
-
const [sectionsExpanded, setSectionsExpanded] =
|
|
2850
|
+
const [sections, setSections] = useState7([]);
|
|
2851
|
+
const [sectionsExpanded, setSectionsExpanded] = useState7(false);
|
|
2297
2852
|
const textFieldId = `${fieldId}.text`;
|
|
2298
2853
|
const hrefFieldId = `${fieldId}.href`;
|
|
2299
2854
|
const storeText = getValue(textFieldId);
|
|
@@ -2301,16 +2856,19 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2301
2856
|
const isIconMode = children != null && typeof children !== "string";
|
|
2302
2857
|
const text = storeText || (typeof children === "string" ? children : "");
|
|
2303
2858
|
const href = storeHref || defaultHref;
|
|
2304
|
-
const
|
|
2305
|
-
const
|
|
2306
|
-
const
|
|
2307
|
-
const [
|
|
2308
|
-
const [
|
|
2309
|
-
const [
|
|
2310
|
-
const [
|
|
2311
|
-
const
|
|
2312
|
-
const
|
|
2313
|
-
const
|
|
2859
|
+
const isExternal = isExternalHref(href);
|
|
2860
|
+
const effectiveTarget = target ?? (isExternal ? "_blank" : void 0);
|
|
2861
|
+
const effectiveRel = rel ?? (isExternal ? "noopener noreferrer" : void 0);
|
|
2862
|
+
const [editingMode, setEditingMode] = useState7(null);
|
|
2863
|
+
const [showEditPopover, setShowEditPopover] = useState7(false);
|
|
2864
|
+
const [originalText, setOriginalText] = useState7(text);
|
|
2865
|
+
const [originalHref, setOriginalHref] = useState7(href);
|
|
2866
|
+
const [currentHref, setCurrentHref] = useState7(href);
|
|
2867
|
+
const [isExternalUrl, setIsExternalUrl] = useState7(false);
|
|
2868
|
+
const [externalUrl, setExternalUrl] = useState7("");
|
|
2869
|
+
const containerRef = useRef8(null);
|
|
2870
|
+
const hrefPopoverRef = useRef8(null);
|
|
2871
|
+
const hidePopoverTimeoutRef = useRef8(null);
|
|
2314
2872
|
const editor = useEditor2({
|
|
2315
2873
|
extensions: [
|
|
2316
2874
|
StarterKit2.configure({
|
|
@@ -2333,31 +2891,31 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2333
2891
|
}
|
|
2334
2892
|
}
|
|
2335
2893
|
});
|
|
2336
|
-
|
|
2894
|
+
useEffect7(() => {
|
|
2337
2895
|
if (editor && editingMode !== "text") {
|
|
2338
2896
|
if (editor.getHTML() !== text) {
|
|
2339
2897
|
editor.commands.setContent(text);
|
|
2340
2898
|
}
|
|
2341
2899
|
}
|
|
2342
2900
|
}, [text, editor, editingMode]);
|
|
2343
|
-
|
|
2901
|
+
useEffect7(() => {
|
|
2344
2902
|
if (editingMode !== "link") {
|
|
2345
2903
|
setCurrentHref(href);
|
|
2346
2904
|
}
|
|
2347
2905
|
}, [href, editingMode]);
|
|
2348
|
-
|
|
2906
|
+
useEffect7(() => {
|
|
2349
2907
|
return () => {
|
|
2350
2908
|
if (hidePopoverTimeoutRef.current) {
|
|
2351
2909
|
clearTimeout(hidePopoverTimeoutRef.current);
|
|
2352
2910
|
}
|
|
2353
2911
|
};
|
|
2354
2912
|
}, []);
|
|
2355
|
-
|
|
2913
|
+
useEffect7(() => {
|
|
2356
2914
|
if (editingMode !== "link") return;
|
|
2357
2915
|
const handleClickOutside = (event) => {
|
|
2358
|
-
const
|
|
2359
|
-
if (hrefPopoverRef.current?.contains(
|
|
2360
|
-
if (containerRef.current?.contains(
|
|
2916
|
+
const target2 = event.target;
|
|
2917
|
+
if (hrefPopoverRef.current?.contains(target2)) return;
|
|
2918
|
+
if (containerRef.current?.contains(target2)) return;
|
|
2361
2919
|
setCurrentHref(originalHref);
|
|
2362
2920
|
setEditingMode(null);
|
|
2363
2921
|
setIsExternalUrl(false);
|
|
@@ -2366,7 +2924,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2366
2924
|
document.addEventListener("mousedown", handleClickOutside);
|
|
2367
2925
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
2368
2926
|
}, [editingMode, originalHref]);
|
|
2369
|
-
const handleSaveText =
|
|
2927
|
+
const handleSaveText = useCallback8(() => {
|
|
2370
2928
|
if (!editor) return;
|
|
2371
2929
|
let html = editor.getHTML();
|
|
2372
2930
|
html = html.replace(/<\/p><p>/g, "<br><br>").replace(/^<p>/, "").replace(/<\/p>$/, "");
|
|
@@ -2374,26 +2932,26 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2374
2932
|
saveToWorker?.(textFieldId, html);
|
|
2375
2933
|
setEditingMode(null);
|
|
2376
2934
|
}, [editor, textFieldId, setValue, saveToWorker]);
|
|
2377
|
-
const handleSaveLink =
|
|
2935
|
+
const handleSaveLink = useCallback8(() => {
|
|
2378
2936
|
setValue(hrefFieldId, currentHref);
|
|
2379
2937
|
saveToWorker?.(hrefFieldId, currentHref);
|
|
2380
2938
|
setEditingMode(null);
|
|
2381
2939
|
setIsExternalUrl(false);
|
|
2382
2940
|
setExternalUrl("");
|
|
2383
2941
|
}, [hrefFieldId, currentHref, setValue, saveToWorker]);
|
|
2384
|
-
const handleCancelText =
|
|
2942
|
+
const handleCancelText = useCallback8(() => {
|
|
2385
2943
|
if (editor) {
|
|
2386
2944
|
editor.commands.setContent(originalText);
|
|
2387
2945
|
}
|
|
2388
2946
|
setEditingMode(null);
|
|
2389
2947
|
}, [editor, originalText]);
|
|
2390
|
-
const handleCancelLink =
|
|
2948
|
+
const handleCancelLink = useCallback8(() => {
|
|
2391
2949
|
setCurrentHref(originalHref);
|
|
2392
2950
|
setEditingMode(null);
|
|
2393
2951
|
setIsExternalUrl(false);
|
|
2394
2952
|
setExternalUrl("");
|
|
2395
2953
|
}, [originalHref]);
|
|
2396
|
-
const handleClick =
|
|
2954
|
+
const handleClick = useCallback8(
|
|
2397
2955
|
(e) => {
|
|
2398
2956
|
const selectModeEnabled = window.__builderSelectModeEnabled;
|
|
2399
2957
|
if (selectModeEnabled) {
|
|
@@ -2425,7 +2983,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2425
2983
|
},
|
|
2426
2984
|
[href, navigate, onClick]
|
|
2427
2985
|
);
|
|
2428
|
-
const handleMouseEnter =
|
|
2986
|
+
const handleMouseEnter = useCallback8(() => {
|
|
2429
2987
|
if (hidePopoverTimeoutRef.current) {
|
|
2430
2988
|
clearTimeout(hidePopoverTimeoutRef.current);
|
|
2431
2989
|
hidePopoverTimeoutRef.current = null;
|
|
@@ -2434,19 +2992,19 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2434
2992
|
setShowEditPopover(true);
|
|
2435
2993
|
}
|
|
2436
2994
|
}, [mode, editingMode]);
|
|
2437
|
-
const handleMouseLeave =
|
|
2995
|
+
const handleMouseLeave = useCallback8(() => {
|
|
2438
2996
|
hidePopoverTimeoutRef.current = window.setTimeout(() => {
|
|
2439
2997
|
if (!editingMode) {
|
|
2440
2998
|
setShowEditPopover(false);
|
|
2441
2999
|
}
|
|
2442
3000
|
}, 150);
|
|
2443
3001
|
}, [editingMode]);
|
|
2444
|
-
const handleFocus =
|
|
3002
|
+
const handleFocus = useCallback8(() => {
|
|
2445
3003
|
if (mode === "inline-edit" && !editingMode) {
|
|
2446
3004
|
setShowEditPopover(true);
|
|
2447
3005
|
}
|
|
2448
3006
|
}, [mode, editingMode]);
|
|
2449
|
-
const startEditText =
|
|
3007
|
+
const startEditText = useCallback8(() => {
|
|
2450
3008
|
setShowEditPopover(false);
|
|
2451
3009
|
setEditingMode("text");
|
|
2452
3010
|
setOriginalText(text);
|
|
@@ -2454,14 +3012,14 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2454
3012
|
editor?.chain().focus().selectAll().run();
|
|
2455
3013
|
}, 20);
|
|
2456
3014
|
}, [text, editor]);
|
|
2457
|
-
const startEditLink =
|
|
3015
|
+
const startEditLink = useCallback8(() => {
|
|
2458
3016
|
setShowEditPopover(false);
|
|
2459
3017
|
setEditingMode("link");
|
|
2460
3018
|
setOriginalHref(href);
|
|
2461
3019
|
setCurrentHref(href);
|
|
2462
3020
|
setSections(discoverSectionsFromDOM());
|
|
2463
3021
|
}, [href]);
|
|
2464
|
-
const handleKeyDown =
|
|
3022
|
+
const handleKeyDown = useCallback8(
|
|
2465
3023
|
(event) => {
|
|
2466
3024
|
if (editingMode !== "text") return;
|
|
2467
3025
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
@@ -2482,7 +3040,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2482
3040
|
},
|
|
2483
3041
|
[editingMode, handleSaveText, handleCancelText]
|
|
2484
3042
|
);
|
|
2485
|
-
const handleFontSizeChange =
|
|
3043
|
+
const handleFontSizeChange = useCallback8(
|
|
2486
3044
|
(e) => {
|
|
2487
3045
|
if (!editor) return;
|
|
2488
3046
|
const size = e.target.value;
|
|
@@ -2494,7 +3052,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2494
3052
|
},
|
|
2495
3053
|
[editor]
|
|
2496
3054
|
);
|
|
2497
|
-
const handleFontWeightChange =
|
|
3055
|
+
const handleFontWeightChange = useCallback8(
|
|
2498
3056
|
(e) => {
|
|
2499
3057
|
if (!editor) return;
|
|
2500
3058
|
const weight = e.target.value;
|
|
@@ -2506,11 +3064,11 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2506
3064
|
},
|
|
2507
3065
|
[editor]
|
|
2508
3066
|
);
|
|
2509
|
-
const handlePageSelect =
|
|
3067
|
+
const handlePageSelect = useCallback8((path) => {
|
|
2510
3068
|
setCurrentHref(path);
|
|
2511
3069
|
setIsExternalUrl(false);
|
|
2512
3070
|
}, []);
|
|
2513
|
-
const handleExternalUrlApply =
|
|
3071
|
+
const handleExternalUrlApply = useCallback8(() => {
|
|
2514
3072
|
if (externalUrl) {
|
|
2515
3073
|
setCurrentHref(externalUrl);
|
|
2516
3074
|
}
|
|
@@ -2526,25 +3084,29 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2526
3084
|
return attrs.fontWeight || "";
|
|
2527
3085
|
};
|
|
2528
3086
|
if (mode === "read-only") {
|
|
2529
|
-
const content = isIconMode ? children : /* @__PURE__ */
|
|
3087
|
+
const content = isIconMode ? children : /* @__PURE__ */ jsx8(SafeHtml, { content: text, mode });
|
|
2530
3088
|
if (isInternalPath(href)) {
|
|
2531
|
-
return /* @__PURE__ */
|
|
3089
|
+
return /* @__PURE__ */ jsx8(
|
|
2532
3090
|
WouterLink,
|
|
2533
3091
|
{
|
|
2534
3092
|
href,
|
|
2535
3093
|
className,
|
|
3094
|
+
style,
|
|
2536
3095
|
"data-ya-restricted": "true",
|
|
2537
3096
|
"data-field-id": fieldId,
|
|
2538
3097
|
children: content
|
|
2539
3098
|
}
|
|
2540
3099
|
);
|
|
2541
3100
|
}
|
|
2542
|
-
return /* @__PURE__ */
|
|
3101
|
+
return /* @__PURE__ */ jsx8(
|
|
2543
3102
|
Component,
|
|
2544
3103
|
{
|
|
2545
3104
|
ref: containerRef,
|
|
2546
3105
|
href: Component === "a" ? href : void 0,
|
|
3106
|
+
target: Component === "a" ? effectiveTarget : void 0,
|
|
3107
|
+
rel: Component === "a" ? effectiveRel : void 0,
|
|
2547
3108
|
className,
|
|
3109
|
+
style,
|
|
2548
3110
|
"data-ya-restricted": "true",
|
|
2549
3111
|
"data-field-id": fieldId,
|
|
2550
3112
|
children: content
|
|
@@ -2552,12 +3114,15 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2552
3114
|
);
|
|
2553
3115
|
}
|
|
2554
3116
|
return /* @__PURE__ */ jsxs4("span", { className: "ya-link-wrapper", children: [
|
|
2555
|
-
/* @__PURE__ */
|
|
3117
|
+
/* @__PURE__ */ jsx8(
|
|
2556
3118
|
Component,
|
|
2557
3119
|
{
|
|
2558
3120
|
ref: containerRef,
|
|
2559
3121
|
href: Component === "a" ? href : void 0,
|
|
3122
|
+
target: Component === "a" ? effectiveTarget : void 0,
|
|
3123
|
+
rel: Component === "a" ? effectiveRel : void 0,
|
|
2560
3124
|
className: `${className || ""} ${editingMode ? "ya-link-editing" : "ya-link-editable"}`,
|
|
3125
|
+
style,
|
|
2561
3126
|
"data-ya-restricted": "true",
|
|
2562
3127
|
"data-field-id": fieldId,
|
|
2563
3128
|
onClick: handleClick,
|
|
@@ -2578,27 +3143,27 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2578
3143
|
options: { offset: 6, placement: "top" },
|
|
2579
3144
|
className: "ya-bubble-menu",
|
|
2580
3145
|
children: [
|
|
2581
|
-
/* @__PURE__ */
|
|
3146
|
+
/* @__PURE__ */ jsx8(
|
|
2582
3147
|
"button",
|
|
2583
3148
|
{
|
|
2584
3149
|
type: "button",
|
|
2585
3150
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
2586
3151
|
className: `ya-bubble-btn ${editor.isActive("bold") ? "is-active" : ""}`,
|
|
2587
3152
|
title: "Bold",
|
|
2588
|
-
children: /* @__PURE__ */
|
|
3153
|
+
children: /* @__PURE__ */ jsx8("strong", { children: "B" })
|
|
2589
3154
|
}
|
|
2590
3155
|
),
|
|
2591
|
-
/* @__PURE__ */
|
|
3156
|
+
/* @__PURE__ */ jsx8(
|
|
2592
3157
|
"button",
|
|
2593
3158
|
{
|
|
2594
3159
|
type: "button",
|
|
2595
3160
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
2596
3161
|
className: `ya-bubble-btn ${editor.isActive("italic") ? "is-active" : ""}`,
|
|
2597
3162
|
title: "Italic",
|
|
2598
|
-
children: /* @__PURE__ */
|
|
3163
|
+
children: /* @__PURE__ */ jsx8("em", { children: "I" })
|
|
2599
3164
|
}
|
|
2600
3165
|
),
|
|
2601
|
-
/* @__PURE__ */
|
|
3166
|
+
/* @__PURE__ */ jsx8("span", { className: "ya-bubble-divider" }),
|
|
2602
3167
|
/* @__PURE__ */ jsxs4(
|
|
2603
3168
|
"select",
|
|
2604
3169
|
{
|
|
@@ -2607,8 +3172,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2607
3172
|
className: "ya-bubble-select",
|
|
2608
3173
|
title: "Font Size",
|
|
2609
3174
|
children: [
|
|
2610
|
-
/* @__PURE__ */
|
|
2611
|
-
Object.entries(SIZE_PRESETS2).map(([name, size]) => /* @__PURE__ */
|
|
3175
|
+
/* @__PURE__ */ jsx8("option", { value: "", children: "Size" }),
|
|
3176
|
+
Object.entries(SIZE_PRESETS2).map(([name, size]) => /* @__PURE__ */ jsx8("option", { value: size, children: name }, name))
|
|
2612
3177
|
]
|
|
2613
3178
|
}
|
|
2614
3179
|
),
|
|
@@ -2620,8 +3185,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2620
3185
|
className: "ya-bubble-select",
|
|
2621
3186
|
title: "Font Weight",
|
|
2622
3187
|
children: [
|
|
2623
|
-
/* @__PURE__ */
|
|
2624
|
-
Object.entries(WEIGHT_PRESETS2).map(([name, weight]) => /* @__PURE__ */
|
|
3188
|
+
/* @__PURE__ */ jsx8("option", { value: "", children: "Weight" }),
|
|
3189
|
+
Object.entries(WEIGHT_PRESETS2).map(([name, weight]) => /* @__PURE__ */ jsx8("option", { value: weight, children: name }, name))
|
|
2625
3190
|
]
|
|
2626
3191
|
}
|
|
2627
3192
|
)
|
|
@@ -2631,21 +3196,21 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2631
3196
|
document.body
|
|
2632
3197
|
),
|
|
2633
3198
|
editingMode === "text" ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2634
|
-
/* @__PURE__ */
|
|
3199
|
+
/* @__PURE__ */ jsx8(EditorContent2, { editor }),
|
|
2635
3200
|
/* @__PURE__ */ jsxs4("div", { className: "ya-link-actions", children: [
|
|
2636
|
-
/* @__PURE__ */
|
|
2637
|
-
/* @__PURE__ */
|
|
3201
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: handleCancelText, className: "ya-link-btn ya-link-btn-cancel", children: "Cancel" }),
|
|
3202
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: handleSaveText, className: "ya-link-btn ya-link-btn-save", children: "Save" })
|
|
2638
3203
|
] })
|
|
2639
|
-
] }) : /* @__PURE__ */
|
|
2640
|
-
] }) : /* @__PURE__ */
|
|
3204
|
+
] }) : /* @__PURE__ */ jsx8(SafeHtml, { content: text, mode })
|
|
3205
|
+
] }) : /* @__PURE__ */ jsx8(SafeHtml, { content: text, mode })
|
|
2641
3206
|
}
|
|
2642
3207
|
),
|
|
2643
3208
|
showEditPopover && !editingMode && mode === "inline-edit" && /* @__PURE__ */ jsxs4("div", { className: "ya-link-edit-popover", onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [
|
|
2644
|
-
!isIconMode && /* @__PURE__ */
|
|
2645
|
-
/* @__PURE__ */
|
|
3209
|
+
!isIconMode && /* @__PURE__ */ jsx8("button", { type: "button", onClick: startEditText, children: "Edit text" }),
|
|
3210
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: startEditLink, children: "Edit link" })
|
|
2646
3211
|
] }),
|
|
2647
3212
|
editingMode === "link" && /* @__PURE__ */ jsxs4("div", { ref: hrefPopoverRef, className: "ya-href-popover", children: [
|
|
2648
|
-
/* @__PURE__ */
|
|
3213
|
+
/* @__PURE__ */ jsx8("div", { className: "ya-href-popover-header", children: "Link destination" }),
|
|
2649
3214
|
!isExternalUrl ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2650
3215
|
sections.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "ya-href-popover-section", children: [
|
|
2651
3216
|
/* @__PURE__ */ jsxs4(
|
|
@@ -2655,14 +3220,14 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2655
3220
|
className: "ya-href-popover-label ya-href-collapsible-header",
|
|
2656
3221
|
onClick: () => setSectionsExpanded(!sectionsExpanded),
|
|
2657
3222
|
children: [
|
|
2658
|
-
/* @__PURE__ */
|
|
3223
|
+
/* @__PURE__ */ jsx8("span", { className: "ya-href-chevron", children: sectionsExpanded ? "\u25BC" : "\u25B6" }),
|
|
2659
3224
|
"Scroll to section (",
|
|
2660
3225
|
sections.length,
|
|
2661
3226
|
")"
|
|
2662
3227
|
]
|
|
2663
3228
|
}
|
|
2664
3229
|
),
|
|
2665
|
-
sectionsExpanded && /* @__PURE__ */
|
|
3230
|
+
sectionsExpanded && /* @__PURE__ */ jsx8("div", { className: "ya-href-popover-pages", children: sections.map((section) => /* @__PURE__ */ jsxs4(
|
|
2666
3231
|
"button",
|
|
2667
3232
|
{
|
|
2668
3233
|
type: "button",
|
|
@@ -2670,15 +3235,15 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2670
3235
|
onClick: () => handlePageSelect(section.path),
|
|
2671
3236
|
children: [
|
|
2672
3237
|
section.label,
|
|
2673
|
-
/* @__PURE__ */
|
|
3238
|
+
/* @__PURE__ */ jsx8("span", { className: "ya-href-page-path", children: section.path })
|
|
2674
3239
|
]
|
|
2675
3240
|
},
|
|
2676
3241
|
section.path
|
|
2677
3242
|
)) })
|
|
2678
3243
|
] }),
|
|
2679
3244
|
pages.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "ya-href-popover-section", children: [
|
|
2680
|
-
/* @__PURE__ */
|
|
2681
|
-
/* @__PURE__ */
|
|
3245
|
+
/* @__PURE__ */ jsx8("label", { className: "ya-href-popover-label", children: "Navigate to page" }),
|
|
3246
|
+
/* @__PURE__ */ jsx8("div", { className: "ya-href-popover-pages", children: pages.map((page) => /* @__PURE__ */ jsxs4(
|
|
2682
3247
|
"button",
|
|
2683
3248
|
{
|
|
2684
3249
|
type: "button",
|
|
@@ -2686,13 +3251,13 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2686
3251
|
onClick: () => handlePageSelect(page.path),
|
|
2687
3252
|
children: [
|
|
2688
3253
|
page.label,
|
|
2689
|
-
/* @__PURE__ */
|
|
3254
|
+
/* @__PURE__ */ jsx8("span", { className: "ya-href-page-path", children: page.path })
|
|
2690
3255
|
]
|
|
2691
3256
|
},
|
|
2692
3257
|
page.path
|
|
2693
3258
|
)) })
|
|
2694
3259
|
] }),
|
|
2695
|
-
/* @__PURE__ */
|
|
3260
|
+
/* @__PURE__ */ jsx8(
|
|
2696
3261
|
"button",
|
|
2697
3262
|
{
|
|
2698
3263
|
type: "button",
|
|
@@ -2706,8 +3271,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2706
3271
|
)
|
|
2707
3272
|
] }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2708
3273
|
/* @__PURE__ */ jsxs4("div", { className: "ya-href-popover-section", children: [
|
|
2709
|
-
/* @__PURE__ */
|
|
2710
|
-
/* @__PURE__ */
|
|
3274
|
+
/* @__PURE__ */ jsx8("label", { className: "ya-href-popover-label", children: "External URL" }),
|
|
3275
|
+
/* @__PURE__ */ jsx8(
|
|
2711
3276
|
"input",
|
|
2712
3277
|
{
|
|
2713
3278
|
type: "url",
|
|
@@ -2719,21 +3284,21 @@ function YaLink({ fieldId, href: defaultHref = "#", className, as: Component = "
|
|
|
2719
3284
|
}
|
|
2720
3285
|
)
|
|
2721
3286
|
] }),
|
|
2722
|
-
/* @__PURE__ */
|
|
3287
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: "ya-href-external-toggle", onClick: () => setIsExternalUrl(false), children: "\u2190 Back to pages" })
|
|
2723
3288
|
] }),
|
|
2724
3289
|
/* @__PURE__ */ jsxs4("div", { className: "ya-href-popover-actions", children: [
|
|
2725
|
-
/* @__PURE__ */
|
|
2726
|
-
isExternalUrl ? /* @__PURE__ */
|
|
3290
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: "ya-link-btn ya-link-btn-cancel", onClick: handleCancelLink, children: "Cancel" }),
|
|
3291
|
+
isExternalUrl ? /* @__PURE__ */ jsx8("button", { type: "button", className: "ya-link-btn ya-link-btn-save", onClick: handleExternalUrlApply, children: "Apply" }) : /* @__PURE__ */ jsx8("button", { type: "button", className: "ya-link-btn ya-link-btn-save", onClick: handleSaveLink, children: "Save" })
|
|
2727
3292
|
] })
|
|
2728
3293
|
] })
|
|
2729
3294
|
] });
|
|
2730
3295
|
}
|
|
2731
3296
|
|
|
2732
3297
|
// src/components/StaticText.tsx
|
|
2733
|
-
import { jsx as
|
|
3298
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
2734
3299
|
function MpText({ fieldId, className, as: Component = "span", children }) {
|
|
2735
3300
|
const content = getContent(fieldId) || (typeof children === "string" ? children : "");
|
|
2736
|
-
return /* @__PURE__ */
|
|
3301
|
+
return /* @__PURE__ */ jsx9(
|
|
2737
3302
|
Component,
|
|
2738
3303
|
{
|
|
2739
3304
|
className,
|
|
@@ -2744,7 +3309,7 @@ function MpText({ fieldId, className, as: Component = "span", children }) {
|
|
|
2744
3309
|
}
|
|
2745
3310
|
|
|
2746
3311
|
// src/components/StaticImage.tsx
|
|
2747
|
-
import { jsx as
|
|
3312
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
2748
3313
|
function parseImageValue2(value) {
|
|
2749
3314
|
if (!value) {
|
|
2750
3315
|
return { src: "" };
|
|
@@ -2780,7 +3345,7 @@ function MpImage({
|
|
|
2780
3345
|
const altText = imageData.alt || alt || fallbackAlt || "";
|
|
2781
3346
|
const objectFit = imageData.objectFit || propObjectFit || "cover";
|
|
2782
3347
|
const objectPosition = getObjectPosition2(imageData) || propObjectPosition || "50% 50%";
|
|
2783
|
-
return /* @__PURE__ */
|
|
3348
|
+
return /* @__PURE__ */ jsx10(
|
|
2784
3349
|
"img",
|
|
2785
3350
|
{
|
|
2786
3351
|
src: resolveAssetUrl(src),
|
|
@@ -2798,7 +3363,7 @@ function MpImage({
|
|
|
2798
3363
|
|
|
2799
3364
|
// src/components/MarkdownText.tsx
|
|
2800
3365
|
import { Fragment as Fragment4 } from "react";
|
|
2801
|
-
import { jsx as
|
|
3366
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
2802
3367
|
function tokenize(text) {
|
|
2803
3368
|
const tokens = [];
|
|
2804
3369
|
let remaining = text;
|
|
@@ -2860,13 +3425,13 @@ function tokensToElements(tokens) {
|
|
|
2860
3425
|
return tokens.map((token, index) => {
|
|
2861
3426
|
switch (token.type) {
|
|
2862
3427
|
case "text":
|
|
2863
|
-
return /* @__PURE__ */
|
|
3428
|
+
return /* @__PURE__ */ jsx11(Fragment4, { children: token.content }, index);
|
|
2864
3429
|
case "bold":
|
|
2865
|
-
return /* @__PURE__ */
|
|
3430
|
+
return /* @__PURE__ */ jsx11("strong", { children: token.content }, index);
|
|
2866
3431
|
case "italic":
|
|
2867
|
-
return /* @__PURE__ */
|
|
3432
|
+
return /* @__PURE__ */ jsx11("em", { children: token.content }, index);
|
|
2868
3433
|
case "link":
|
|
2869
|
-
return /* @__PURE__ */
|
|
3434
|
+
return /* @__PURE__ */ jsx11(
|
|
2870
3435
|
"a",
|
|
2871
3436
|
{
|
|
2872
3437
|
href: token.url,
|
|
@@ -2878,7 +3443,7 @@ function tokensToElements(tokens) {
|
|
|
2878
3443
|
index
|
|
2879
3444
|
);
|
|
2880
3445
|
case "newline":
|
|
2881
|
-
return /* @__PURE__ */
|
|
3446
|
+
return /* @__PURE__ */ jsx11("br", {}, index);
|
|
2882
3447
|
default:
|
|
2883
3448
|
return null;
|
|
2884
3449
|
}
|
|
@@ -2890,15 +3455,15 @@ function parseMarkdownToElements(content) {
|
|
|
2890
3455
|
}
|
|
2891
3456
|
function MarkdownText({ content, className }) {
|
|
2892
3457
|
const elements = parseMarkdownToElements(content);
|
|
2893
|
-
return /* @__PURE__ */
|
|
3458
|
+
return /* @__PURE__ */ jsx11("span", { className, children: elements });
|
|
2894
3459
|
}
|
|
2895
3460
|
|
|
2896
3461
|
// src/router/Link.tsx
|
|
2897
3462
|
import { Link as WouterLink2 } from "wouter";
|
|
2898
|
-
import { jsx as
|
|
3463
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
2899
3464
|
function Link2({ to, href, children, className, onClick, replace, ...props }) {
|
|
2900
3465
|
const target = href ?? to ?? "/";
|
|
2901
|
-
return /* @__PURE__ */
|
|
3466
|
+
return /* @__PURE__ */ jsx12(WouterLink2, { href: target, className, onClick, replace, ...props, children });
|
|
2902
3467
|
}
|
|
2903
3468
|
|
|
2904
3469
|
// src/router/useNavigate.ts
|
|
@@ -2917,7 +3482,7 @@ function useNavigate() {
|
|
|
2917
3482
|
|
|
2918
3483
|
// src/router/Router.tsx
|
|
2919
3484
|
import { Router as WouterRouter } from "wouter";
|
|
2920
|
-
import { jsx as
|
|
3485
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
2921
3486
|
function detectBasename() {
|
|
2922
3487
|
if (typeof window === "undefined") return "";
|
|
2923
3488
|
const sessionMatch = window.location.pathname.match(/^\/session\/[^/]+/);
|
|
@@ -2932,12 +3497,13 @@ function detectBasename() {
|
|
|
2932
3497
|
}
|
|
2933
3498
|
function Router({ children, base }) {
|
|
2934
3499
|
const basename = base ?? detectBasename();
|
|
2935
|
-
return /* @__PURE__ */
|
|
3500
|
+
return /* @__PURE__ */ jsx13(WouterRouter, { base: basename, children });
|
|
2936
3501
|
}
|
|
2937
3502
|
|
|
2938
3503
|
// src/router/index.ts
|
|
2939
|
-
import { Route, Switch } from "wouter";
|
|
3504
|
+
import { Route, Switch, useParams } from "wouter";
|
|
2940
3505
|
export {
|
|
3506
|
+
AIEditProvider,
|
|
2941
3507
|
ContentStoreProvider,
|
|
2942
3508
|
ContentStoreProvider2 as ContentStoreProviderProd,
|
|
2943
3509
|
Link2 as Link,
|
|
@@ -2951,16 +3517,30 @@ export {
|
|
|
2951
3517
|
YaImage,
|
|
2952
3518
|
YaLink,
|
|
2953
3519
|
YaText,
|
|
3520
|
+
buildIntermediateText,
|
|
3521
|
+
calculateAnimationTiming,
|
|
3522
|
+
computeTextDiff,
|
|
3523
|
+
containsHtml,
|
|
2954
3524
|
contentRegistry,
|
|
2955
3525
|
getAllContent,
|
|
2956
3526
|
getContent,
|
|
3527
|
+
getTextCursorPosition,
|
|
2957
3528
|
hasContent,
|
|
3529
|
+
imageCrossfadeStrategy,
|
|
2958
3530
|
initBuilderSelection,
|
|
3531
|
+
linkTransitionStrategy,
|
|
2959
3532
|
registerContent,
|
|
2960
3533
|
resolveAssetUrl,
|
|
2961
3534
|
serializeImageValue,
|
|
2962
3535
|
setAssetResolver,
|
|
3536
|
+
stripHtml,
|
|
3537
|
+
textTypingStrategy,
|
|
3538
|
+
useAIEditAnimation,
|
|
3539
|
+
useAIEditContext,
|
|
3540
|
+
useAIEditContextOptional,
|
|
3541
|
+
useAnimatedText,
|
|
2963
3542
|
useContentStore,
|
|
2964
3543
|
useContentStore2 as useContentStoreProd,
|
|
2965
|
-
useNavigate
|
|
3544
|
+
useNavigate,
|
|
3545
|
+
useParams
|
|
2966
3546
|
};
|