fable-editor 1.0.1 → 1.1.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fable-editor",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "FableEditor — a rich text editor with React and Angular wrappers",
5
5
  "author": "Sajin Satheesan <an.sajinsatheesan@gmail.com>",
6
6
  "repository": {
@@ -95,6 +95,7 @@
95
95
  "@playwright/test": "^1.61.1",
96
96
  "@testing-library/jest-dom": "^6.9.1",
97
97
  "@testing-library/react": "^16.3.2",
98
+ "@types/katex": "^0.16.8",
98
99
  "@types/react": "^18.3.0",
99
100
  "@types/react-dom": "^18.3.0",
100
101
  "@vitejs/plugin-react": "^4.3.0",
@@ -109,5 +110,8 @@
109
110
  "vite": "^5.3.0",
110
111
  "vite-plugin-dts": "^3.9.0",
111
112
  "vitest": "^4.1.9"
113
+ },
114
+ "dependencies": {
115
+ "katex": "^0.17.0"
112
116
  }
113
117
  }
package/style.css CHANGED
@@ -270,12 +270,12 @@
270
270
  word-break: break-word;
271
271
  }
272
272
 
273
- .earea img {
273
+ .earea img, .earea video {
274
274
  max-width: 100%;
275
275
  height: auto;
276
276
  }
277
277
 
278
- /* image upload placeholder */
278
+ /* image / video upload placeholder */
279
279
  .earea .img-ph {
280
280
  display: flex;
281
281
  align-items: center;
@@ -334,6 +334,148 @@
334
334
  fill: #c92a2a;
335
335
  }
336
336
 
337
+ /* template blocks (image + heading/paragraph layouts) */
338
+ .earea .tpl {
339
+ margin: 0 0 1em;
340
+ }
341
+
342
+ .earea .tpl::after {
343
+ content: "";
344
+ display: block;
345
+ clear: both;
346
+ }
347
+
348
+ .earea .tpl.tpl-selected {
349
+ outline: 2px dashed #a8c7f0;
350
+ outline-offset: 4px;
351
+ }
352
+
353
+ .earea .tpl-media {
354
+ text-align: center;
355
+ margin: 0 0 0.5em;
356
+ }
357
+
358
+ .earea .tpl-media img {
359
+ display: inline-block;
360
+ max-width: 100%;
361
+ }
362
+
363
+ .earea .tpl .img-ph {
364
+ display: inline-flex;
365
+ width: 300px;
366
+ max-width: 100%;
367
+ margin: 0;
368
+ box-sizing: border-box;
369
+ }
370
+
371
+ /* floated media shrink-wraps to the image, so a small image leaves
372
+ the text beside it and the paragraph overflows below it */
373
+ .earea .tpl-img-left .tpl-media {
374
+ float: left;
375
+ max-width: 50%;
376
+ margin: 0 1em 0.5em 0;
377
+ }
378
+
379
+ .earea .tpl-img-right .tpl-media {
380
+ float: right;
381
+ max-width: 50%;
382
+ margin: 0 0 0.5em 1em;
383
+ }
384
+
385
+ .earea .tpl-img-left .img-ph,
386
+ .earea .tpl-img-right .img-ph {
387
+ width: 260px;
388
+ }
389
+
390
+ .earea .tpl-text > :first-child {
391
+ margin-top: 0;
392
+ }
393
+
394
+ /* template picker (popup) — miniature previews of each layout */
395
+ .tplpick {
396
+ display: grid;
397
+ grid-template-columns: 1fr 1fr;
398
+ gap: 8px;
399
+ padding: 8px;
400
+ }
401
+
402
+ .tpltile {
403
+ display: flex;
404
+ flex-direction: column;
405
+ gap: 6px;
406
+ width: 132px;
407
+ padding: 6px;
408
+ border: 1px solid #cfd6df;
409
+ border-radius: 8px;
410
+ background: #fff;
411
+ cursor: pointer;
412
+ font: inherit;
413
+ }
414
+
415
+ .tpltile:hover {
416
+ border-color: #006ce7;
417
+ background: #f2f7ff;
418
+ }
419
+
420
+ .tpllbl {
421
+ font-size: 11px;
422
+ color: #556;
423
+ text-align: center;
424
+ }
425
+
426
+ .pv {
427
+ display: flex;
428
+ flex-direction: column;
429
+ gap: 4px;
430
+ height: 78px;
431
+ padding: 7px;
432
+ background: #f7f9fb;
433
+ border-radius: 5px;
434
+ box-sizing: border-box;
435
+ }
436
+
437
+ .pv-row {
438
+ display: flex;
439
+ gap: 5px;
440
+ flex: 1;
441
+ min-height: 0;
442
+ }
443
+
444
+ .pv-col {
445
+ display: flex;
446
+ flex-direction: column;
447
+ gap: 4px;
448
+ flex: 1;
449
+ }
450
+
451
+ .pv-img {
452
+ background: #c9d4e0;
453
+ border-radius: 3px;
454
+ width: 42%;
455
+ flex: none;
456
+ }
457
+
458
+ .pv-c .pv-img {
459
+ width: 68%;
460
+ height: 32px;
461
+ align-self: center;
462
+ }
463
+
464
+ .pv-h {
465
+ height: 7px;
466
+ width: 55%;
467
+ background: #8fa3b8;
468
+ border-radius: 2px;
469
+ flex: none;
470
+ }
471
+
472
+ .pv-l {
473
+ height: 5px;
474
+ background: #dbe3ec;
475
+ border-radius: 2px;
476
+ flex: none;
477
+ }
478
+
337
479
  .earea table.tbl-selected {
338
480
  outline: 2px solid #006ce7;
339
481
  outline-offset: 1px;
@@ -441,6 +583,50 @@
441
583
  outline-offset: 1px;
442
584
  }
443
585
 
586
+ /* video align toolbar */
587
+ .earea video.vid-selected,
588
+ .earea .video-embed.vid-selected {
589
+ outline: 2px solid #006ce7;
590
+ outline-offset: 1px;
591
+ }
592
+
593
+ /* embedded hosted players (YouTube & co.) */
594
+ .earea .video-embed {
595
+ display: inline-block;
596
+ max-width: 100%;
597
+ }
598
+
599
+ .earea .video-embed iframe {
600
+ /* clicks land on the wrapper so the embed can be selected/aligned */
601
+ pointer-events: none;
602
+ display: block;
603
+ border: 0;
604
+ max-width: 100%;
605
+ }
606
+
607
+ .earea .tpl-media video,
608
+ .earea .tpl-media .video-embed {
609
+ max-width: 100%;
610
+ }
611
+
612
+ /* math / LaTeX formulas */
613
+ .earea .math-fable {
614
+ cursor: pointer;
615
+ border-radius: 4px;
616
+ }
617
+
618
+ .earea .math-fable-block {
619
+ display: block;
620
+ text-align: center;
621
+ margin: 0.5em 0;
622
+ padding: 4px;
623
+ }
624
+
625
+ .earea .math-fable.math-selected {
626
+ outline: 2px solid #006ce7;
627
+ outline-offset: 2px;
628
+ }
629
+
444
630
  .img-handle {
445
631
  position: absolute;
446
632
  width: 10px;
@@ -1010,7 +1196,68 @@
1010
1196
  font-size: 14px;
1011
1197
  }
1012
1198
 
1013
- /* char map */
1199
+ /* char / emoji map: fixed category tabs left, glyph grid right */
1200
+ .chmap {
1201
+ display: flex;
1202
+ width: 560px;
1203
+ max-width: 76vw;
1204
+ height: 320px;
1205
+ max-height: 46vh;
1206
+ }
1207
+
1208
+ .chmap .chtabs {
1209
+ flex: 0 0 158px;
1210
+ display: flex;
1211
+ flex-direction: column;
1212
+ gap: 2px;
1213
+ overflow-y: auto;
1214
+ padding: 6px 8px 6px 0;
1215
+ border-inline-end: 1px solid #e3e8ee;
1216
+ }
1217
+
1218
+ .chmap .chtabs button {
1219
+ border: 0;
1220
+ background: none;
1221
+ text-align: start;
1222
+ padding: 8px 10px;
1223
+ border-radius: 6px;
1224
+ font: 13.5px inherit;
1225
+ color: #222f3e;
1226
+ cursor: pointer;
1227
+ white-space: nowrap;
1228
+ }
1229
+
1230
+ .chmap .chtabs button:hover {
1231
+ background: #f0f0f0;
1232
+ }
1233
+
1234
+ .chmap .chtabs button.on {
1235
+ background: #e8f1fb;
1236
+ color: #0060ce;
1237
+ font-weight: 600;
1238
+ }
1239
+
1240
+ .chmap .chpanel {
1241
+ flex: 1;
1242
+ overflow-y: auto;
1243
+ padding: 6px 0 6px 8px;
1244
+ }
1245
+
1246
+ .chmap .chgrid {
1247
+ grid-template-columns: repeat(auto-fill, minmax(32px, 1fr));
1248
+ padding: 0;
1249
+ }
1250
+
1251
+ .chmap .chgrid.emgrid {
1252
+ grid-template-columns: repeat(auto-fill, minmax(36px, 1fr));
1253
+ }
1254
+
1255
+ .chmap .chgrid.emgrid button {
1256
+ width: 36px;
1257
+ height: 36px;
1258
+ font-size: 20px;
1259
+ }
1260
+
1014
1261
  .chgrid {
1015
1262
  display: grid;
1016
1263
  grid-template-columns: repeat(8, 30px);
@@ -1151,6 +1398,84 @@
1151
1398
  resize: vertical;
1152
1399
  }
1153
1400
 
1401
+ /* tabbed dialogs (insert/edit video) */
1402
+ .dlg .dtabs {
1403
+ display: flex;
1404
+ gap: 2px;
1405
+ border-bottom: 1px solid #e3e8ee;
1406
+ margin-bottom: 14px;
1407
+ }
1408
+
1409
+ .dlg .dtabs button {
1410
+ border: 0;
1411
+ background: none;
1412
+ padding: 8px 14px;
1413
+ font: 14px inherit;
1414
+ color: #556;
1415
+ cursor: pointer;
1416
+ border-bottom: 2px solid transparent;
1417
+ margin-bottom: -1px;
1418
+ }
1419
+
1420
+ .dlg .dtabs button.on {
1421
+ color: #006ce7;
1422
+ border-bottom-color: #006ce7;
1423
+ font-weight: 600;
1424
+ }
1425
+
1426
+ .dlg .dtabbody {
1427
+ width: 460px;
1428
+ max-width: 76vw;
1429
+ min-height: 150px;
1430
+ }
1431
+
1432
+ .dlg .dtabpanel textarea {
1433
+ width: 100%;
1434
+ box-sizing: border-box;
1435
+ }
1436
+
1437
+ .dlg .dfield {
1438
+ display: flex;
1439
+ flex-direction: column;
1440
+ gap: 4px;
1441
+ margin-bottom: 12px;
1442
+ flex: 1;
1443
+ }
1444
+
1445
+ .dlg .dfield label {
1446
+ font-size: 13px;
1447
+ color: #445;
1448
+ }
1449
+
1450
+ .dlg .dfield input {
1451
+ border: 1px solid #cfd6df;
1452
+ border-radius: 6px;
1453
+ padding: 8px 10px;
1454
+ font: 14px inherit;
1455
+ min-width: 0;
1456
+ }
1457
+
1458
+ .dlg .drow {
1459
+ display: flex;
1460
+ gap: 10px;
1461
+ align-items: flex-end;
1462
+ }
1463
+
1464
+ .dlg button.txtbtn {
1465
+ border: 1px solid #cfd6df;
1466
+ background: #fff;
1467
+ color: #222f3e;
1468
+ border-radius: 6px;
1469
+ padding: 8px 12px;
1470
+ font: 13.5px inherit;
1471
+ cursor: pointer;
1472
+ white-space: nowrap;
1473
+ }
1474
+
1475
+ .dlg button.txtbtn:hover {
1476
+ background: #f0f4f9;
1477
+ }
1478
+
1154
1479
  .dlg table.kbd {
1155
1480
  border-collapse: collapse;
1156
1481
  width: 100%;