@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/index.js CHANGED
@@ -938,7 +938,7 @@ function ContentStoreProvider2({ children }) {
938
938
  }
939
939
 
940
940
  // src/components/YaText.tsx
941
- import { useEffect as useEffect3, useRef as useRef3, useState as useState3, useCallback as useCallback3 } from "react";
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 jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
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] = useState3(false);
1515
- const [originalContent, setOriginalContent] = useState3(content);
1516
- const containerRef = useRef3(null);
1517
- const originalContentRef = useRef3(content);
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
- useEffect3(() => {
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
- useEffect3(() => {
2085
+ useEffect4(() => {
1559
2086
  if (isEditing && activeFieldId !== null && activeFieldId !== fieldId) {
1560
2087
  setIsEditing(false);
1561
2088
  }
1562
2089
  }, [activeFieldId, fieldId, isEditing]);
1563
- const handleSave = useCallback3(() => {
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 = useCallback3(() => {
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 = useCallback3(() => {
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 = useCallback3((e) => {
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 = useCallback3(
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 = useCallback3(() => {
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 = useCallback3(
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 = useCallback3(
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__ */ jsx4(
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__ */ jsx4(SafeHtml, { content, mode })
2217
+ children: /* @__PURE__ */ jsx5(SafeHtml, { content, mode })
1691
2218
  }
1692
2219
  );
1693
2220
  }
1694
- return /* @__PURE__ */ jsx4(
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: `${className || ""} ${isEditing ? "ya-text-editing" : "ya-text-editable"}`,
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__ */ jsx4(
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__ */ jsx4("strong", { children: "B" })
2253
+ children: /* @__PURE__ */ jsx5("strong", { children: "B" })
1721
2254
  }
1722
2255
  ),
1723
- /* @__PURE__ */ jsx4(
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__ */ jsx4("em", { children: "I" })
2263
+ children: /* @__PURE__ */ jsx5("em", { children: "I" })
1731
2264
  }
1732
2265
  ),
1733
- /* @__PURE__ */ jsx4(
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__ */ jsx4("span", { children: "\u{1F517}" })
2273
+ children: /* @__PURE__ */ jsx5("span", { children: "\u{1F517}" })
1741
2274
  }
1742
2275
  ),
1743
- /* @__PURE__ */ jsx4("span", { className: "ya-bubble-divider" }),
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__ */ jsx4("option", { value: "", children: "Size" }),
1753
- Object.entries(SIZE_PRESETS).map(([name, size]) => /* @__PURE__ */ jsx4("option", { value: size, children: name }, name))
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__ */ jsx4("option", { value: "", children: "Weight" }),
1766
- Object.entries(WEIGHT_PRESETS).map(([name, weight]) => /* @__PURE__ */ jsx4("option", { value: weight, children: name }, name))
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__ */ jsx4(EditorContent, { editor }),
2309
+ /* @__PURE__ */ jsx5(EditorContent, { editor }),
1777
2310
  /* @__PURE__ */ jsxs2("div", { className: "ya-text-actions", children: [
1778
- /* @__PURE__ */ jsx4(
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__ */ jsx4(
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__ */ jsx4(SafeHtml, { content, mode })
1798
- ] }) : /* @__PURE__ */ jsx4(SafeHtml, { content, mode })
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 useCallback5, useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
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 useEffect4, useRef as useRef4, useState as useState4, useCallback as useCallback4 } from "react";
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 jsx5 } from "react/jsx-runtime";
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] = useState4(preferredPosition);
1835
- const [coords, setCoords] = useState4({ top: 0, left: 0 });
1836
- const [isPositioned, setIsPositioned] = useState4(false);
1837
- const tooltipRef = useRef4(null);
1838
- const calculatePosition = useCallback4(() => {
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
- useEffect4(() => {
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
- useEffect4(() => {
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__ */ jsx5(
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 jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
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 = useRef5(null);
1992
- const imgRef = useRef5(null);
1993
- const [isSelected, setIsSelected] = useState5(false);
1994
- const [isHovered, setIsHovered] = useState5(false);
1995
- const [isSmallImage, setIsSmallImage] = useState5(false);
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 = useCallback5(() => {
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
- useEffect5(() => {
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
- useEffect5(() => {
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
- useEffect5(() => {
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__ */ jsx6(
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__ */ jsx6("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
2131
- /* @__PURE__ */ jsx6("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
2132
- /* @__PURE__ */ jsx6("polyline", { points: "21 15 16 10 5 21" })
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__ */ jsx6(
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__ */ jsx6("span", { children: "Click to edit" })
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__ */ jsx6("div", { className: "ya-image-edit-icon", children: editIcon }),
2178
- /* @__PURE__ */ jsx6("span", { className: "ya-image-edit-label", children: "Click to edit" })
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 useEffect6, useRef as useRef6, useState as useState6, useCallback as useCallback6 } from "react";
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 jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
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] = useState6([]);
2296
- const [sectionsExpanded, setSectionsExpanded] = useState6(false);
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 [editingMode, setEditingMode] = useState6(null);
2305
- const [showEditPopover, setShowEditPopover] = useState6(false);
2306
- const [originalText, setOriginalText] = useState6(text);
2307
- const [originalHref, setOriginalHref] = useState6(href);
2308
- const [currentHref, setCurrentHref] = useState6(href);
2309
- const [isExternalUrl, setIsExternalUrl] = useState6(false);
2310
- const [externalUrl, setExternalUrl] = useState6("");
2311
- const containerRef = useRef6(null);
2312
- const hrefPopoverRef = useRef6(null);
2313
- const hidePopoverTimeoutRef = useRef6(null);
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
- useEffect6(() => {
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
- useEffect6(() => {
2901
+ useEffect7(() => {
2344
2902
  if (editingMode !== "link") {
2345
2903
  setCurrentHref(href);
2346
2904
  }
2347
2905
  }, [href, editingMode]);
2348
- useEffect6(() => {
2906
+ useEffect7(() => {
2349
2907
  return () => {
2350
2908
  if (hidePopoverTimeoutRef.current) {
2351
2909
  clearTimeout(hidePopoverTimeoutRef.current);
2352
2910
  }
2353
2911
  };
2354
2912
  }, []);
2355
- useEffect6(() => {
2913
+ useEffect7(() => {
2356
2914
  if (editingMode !== "link") return;
2357
2915
  const handleClickOutside = (event) => {
2358
- const target = event.target;
2359
- if (hrefPopoverRef.current?.contains(target)) return;
2360
- if (containerRef.current?.contains(target)) return;
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 = useCallback6(() => {
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 = useCallback6(() => {
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 = useCallback6(() => {
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 = useCallback6(() => {
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 = useCallback6(
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 = useCallback6(() => {
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 = useCallback6(() => {
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 = useCallback6(() => {
3002
+ const handleFocus = useCallback8(() => {
2445
3003
  if (mode === "inline-edit" && !editingMode) {
2446
3004
  setShowEditPopover(true);
2447
3005
  }
2448
3006
  }, [mode, editingMode]);
2449
- const startEditText = useCallback6(() => {
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 = useCallback6(() => {
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 = useCallback6(
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 = useCallback6(
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 = useCallback6(
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 = useCallback6((path) => {
3067
+ const handlePageSelect = useCallback8((path) => {
2510
3068
  setCurrentHref(path);
2511
3069
  setIsExternalUrl(false);
2512
3070
  }, []);
2513
- const handleExternalUrlApply = useCallback6(() => {
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__ */ jsx7(SafeHtml, { content: text, mode });
3087
+ const content = isIconMode ? children : /* @__PURE__ */ jsx8(SafeHtml, { content: text, mode });
2530
3088
  if (isInternalPath(href)) {
2531
- return /* @__PURE__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7("strong", { children: "B" })
3153
+ children: /* @__PURE__ */ jsx8("strong", { children: "B" })
2589
3154
  }
2590
3155
  ),
2591
- /* @__PURE__ */ jsx7(
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__ */ jsx7("em", { children: "I" })
3163
+ children: /* @__PURE__ */ jsx8("em", { children: "I" })
2599
3164
  }
2600
3165
  ),
2601
- /* @__PURE__ */ jsx7("span", { className: "ya-bubble-divider" }),
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__ */ jsx7("option", { value: "", children: "Size" }),
2611
- Object.entries(SIZE_PRESETS2).map(([name, size]) => /* @__PURE__ */ jsx7("option", { value: size, children: name }, name))
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__ */ jsx7("option", { value: "", children: "Weight" }),
2624
- Object.entries(WEIGHT_PRESETS2).map(([name, weight]) => /* @__PURE__ */ jsx7("option", { value: weight, children: name }, name))
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__ */ jsx7(EditorContent2, { editor }),
3199
+ /* @__PURE__ */ jsx8(EditorContent2, { editor }),
2635
3200
  /* @__PURE__ */ jsxs4("div", { className: "ya-link-actions", children: [
2636
- /* @__PURE__ */ jsx7("button", { type: "button", onClick: handleCancelText, className: "ya-link-btn ya-link-btn-cancel", children: "Cancel" }),
2637
- /* @__PURE__ */ jsx7("button", { type: "button", onClick: handleSaveText, className: "ya-link-btn ya-link-btn-save", children: "Save" })
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__ */ jsx7(SafeHtml, { content: text, mode })
2640
- ] }) : /* @__PURE__ */ jsx7(SafeHtml, { content: text, mode })
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__ */ jsx7("button", { type: "button", onClick: startEditText, children: "Edit text" }),
2645
- /* @__PURE__ */ jsx7("button", { type: "button", onClick: startEditLink, children: "Edit link" })
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__ */ jsx7("div", { className: "ya-href-popover-header", children: "Link destination" }),
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__ */ jsx7("span", { className: "ya-href-chevron", children: sectionsExpanded ? "\u25BC" : "\u25B6" }),
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__ */ jsx7("div", { className: "ya-href-popover-pages", children: sections.map((section) => /* @__PURE__ */ jsxs4(
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__ */ jsx7("span", { className: "ya-href-page-path", children: section.path })
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__ */ jsx7("label", { className: "ya-href-popover-label", children: "Navigate to page" }),
2681
- /* @__PURE__ */ jsx7("div", { className: "ya-href-popover-pages", children: pages.map((page) => /* @__PURE__ */ jsxs4(
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__ */ jsx7("span", { className: "ya-href-page-path", children: page.path })
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__ */ jsx7(
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__ */ jsx7("label", { className: "ya-href-popover-label", children: "External URL" }),
2710
- /* @__PURE__ */ jsx7(
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__ */ jsx7("button", { type: "button", className: "ya-href-external-toggle", onClick: () => setIsExternalUrl(false), children: "\u2190 Back to pages" })
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__ */ jsx7("button", { type: "button", className: "ya-link-btn ya-link-btn-cancel", onClick: handleCancelLink, children: "Cancel" }),
2726
- isExternalUrl ? /* @__PURE__ */ jsx7("button", { type: "button", className: "ya-link-btn ya-link-btn-save", onClick: handleExternalUrlApply, children: "Apply" }) : /* @__PURE__ */ jsx7("button", { type: "button", className: "ya-link-btn ya-link-btn-save", onClick: handleSaveLink, children: "Save" })
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 jsx8 } from "react/jsx-runtime";
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__ */ jsx8(
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 jsx9 } from "react/jsx-runtime";
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__ */ jsx9(
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 jsx10 } from "react/jsx-runtime";
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__ */ jsx10(Fragment4, { children: token.content }, index);
3428
+ return /* @__PURE__ */ jsx11(Fragment4, { children: token.content }, index);
2864
3429
  case "bold":
2865
- return /* @__PURE__ */ jsx10("strong", { children: token.content }, index);
3430
+ return /* @__PURE__ */ jsx11("strong", { children: token.content }, index);
2866
3431
  case "italic":
2867
- return /* @__PURE__ */ jsx10("em", { children: token.content }, index);
3432
+ return /* @__PURE__ */ jsx11("em", { children: token.content }, index);
2868
3433
  case "link":
2869
- return /* @__PURE__ */ jsx10(
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__ */ jsx10("br", {}, index);
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__ */ jsx10("span", { className, children: elements });
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 jsx11 } from "react/jsx-runtime";
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__ */ jsx11(WouterLink2, { href: target, className, onClick, replace, ...props, children });
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 jsx12 } from "react/jsx-runtime";
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__ */ jsx12(WouterRouter, { base: basename, children });
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
  };