@licium/editor-plugin-details 1.0.3 → 3.2.10

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.
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * TOAST UI Editor : Text Align Plugin
3
+ * @version 1.0.13 | Thu Jan 08 2026
4
+ * @author NHN Cloud FE Development Lab <dl_javascript@nhn.com>
5
+ * @license MIT
6
+ */.toastui-editor-toolbar-icons.details{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m19 21-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z'/%3E%3Cpath d='m9 10 3 3 3-3'/%3E%3C/svg%3E")!important;background-position:50%!important;background-repeat:no-repeat!important;background-size:24px 24px!important;text-indent:-9999px!important}details{background-color:#f7f7f7;border:1px solid #e1e1e1;border-radius:4px;display:block;margin:10px 0;padding:10px}summary{cursor:pointer;font-weight:700;margin-bottom:5px;outline:none}.toastui-editor-dark .toastui-editor-toolbar-icons.details{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23eee' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m19 21-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z'/%3E%3Cpath d='m9 10 3 3 3-3'/%3E%3C/svg%3E")!important}.toastui-editor-dark details{background-color:#282a36;border-color:#44475a;color:#f8f8f2}
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * TOAST UI Editor : Text Align Plugin
3
+ * @version 1.0.13 | Thu Jan 08 2026
4
+ * @author NHN Cloud FE Development Lab <dl_javascript@nhn.com>
5
+ * @license MIT
6
+ */
7
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.toastui=t():(e.toastui=e.toastui||{},e.toastui.Editor=e.toastui.Editor||{},e.toastui.Editor.plugin=e.toastui.Editor.plugin||{},e.toastui.Editor.plugin.details=t())}(self,(function(){return function(){"use strict";var e={d:function(t,n){for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})},o:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}},t={};e.d(t,{default:function(){return a}});var n=function(){return n=Object.assign||function(e){for(var t,n=1,a=arguments.length;n<a;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},n.apply(this,arguments)};Object.create;Object.create;"function"==typeof SuppressedError&&SuppressedError;function a(e,t){void 0===t&&(t={});var a=e.i18n,o=e.eventEmitter;!function(e){e.setLanguage(["en","en-US"],{details:"Collapsible Block",summary:"Summary header",content:"Hidden content"}),e.setLanguage(["ar","ar-AR"],{details:"كتلة قابلة للطي",summary:"عنوان الملخص",content:"المحتوى المخفي"}),e.setLanguage(["cs","cs-CZ"],{details:"Sbalitelný blok",summary:"Záhlaví shrnutí",content:"Skrytý obsah"}),e.setLanguage(["de","de-DE"],{details:"Aufklappbarer Block",summary:"Klick mich zum Aufklappen",content:"Hier steht der versteckte Inhalt"}),e.setLanguage(["es","es-ES"],{details:"Bloque plegable",summary:"Encabezado del resumen",content:"Contenido oculto"}),e.setLanguage(["fi","fi-FI"],{details:"Kokoontaitettava lohko",summary:"Yhteenveto",content:"Piilotettu sisältö"}),e.setLanguage(["fr","fr-FR"],{details:"Bloc pliable",summary:"Titre du résumé",content:"Contenu masqué"}),e.setLanguage(["gl","gl-ES"],{details:"Bloque pregable",summary:"Cabeceira do resumo",content:"Contido oculto"}),e.setLanguage(["hr","hr-HR"],{details:"Sklopivi blok",summary:"Naslov sažetka",content:"Skriveni sadržaj"}),e.setLanguage(["it","it-IT"],{details:"Blocco comprimibile",summary:"Intestazione riepilogo",content:"Contenuto nascosto"}),e.setLanguage(["ja","ja-JP"],{details:"折りたたみブロック",summary:"要約ヘッダー",content:"隠しコンテンツ"}),e.setLanguage(["ko","ko-KR"],{details:"접이식 블록",summary:"요약 헤더",content:"숨겨진 콘텐츠"}),e.setLanguage(["nb","nb-NO"],{details:"Sammenleggbar blokk",summary:"Sammendrag overskrift",content:"Skjult innhold"}),e.setLanguage(["nl","nl-NL"],{details:"Inklapbaar blok",summary:"Samenvatting kop",content:"Verborgen inhoud"}),e.setLanguage(["pl","pl-PL"],{details:"Blok zwijany",summary:"Nagłówek podsumowania",content:"Ukryta treść"}),e.setLanguage(["pt","pt-BR"],{details:"Bloco recolhível",summary:"Cabeçalho de resumo",content:"Conteúdo oculto"}),e.setLanguage(["ru","ru-RU"],{details:"Сворачиваемый блок",summary:"Заголовок резюме",content:"Скрытый контент"}),e.setLanguage(["sv","sv-SE"],{details:"Hopfällbart block",summary:"Sammanfattning rubrik",content:"Dolt innehåll"}),e.setLanguage(["tr","tr-TR"],{details:"Daraltılabilir blok",summary:"Özet başlığı",content:"Gizli içerik"}),e.setLanguage(["uk","uk-UA"],{details:"Згортаємий блок",summary:"Заголовок резюме",content:"Прихований вміст"}),e.setLanguage(["zh","zh-CN"],{details:"可折叠块",summary:"摘要标题",content:"隐藏内容"}),e.setLanguage(["zh-TW"],{details:"可折疊區塊",summary:"摘要標題",content:"隱藏內容"})}(a);var r=function(e){return{name:"details",tooltip:e.get("details"),className:"toastui-editor-toolbar-icons details",command:"details"}}(a);return o.listen("keydown",(function(t,n){console.log("[Details Plugin] keydown via eventEmitter!",{editorType:t,key:n.key}),"wysiwyg"===t&&("Enter"!==n.key&&"ArrowDown"!==n.key||function(e,t){var n=e.instance;if(n){var a=n.getCurrentModeEditor();if(a&&a.view){for(var o=a.view,r=o.state,i=r.selection.$from,s=-1,l=i.depth;l>0;l-=1)if("details"===i.node(l).type.name){s=l;break}if(!(s<0)){var u=i.node(s),c=i.before(s)+u.nodeSize,d=i.after(),m=d>=c-1;if(console.log("[Details Plugin] Inside details:",{key:t.key,isAtEnd:m,afterPara:d,nodeEnd:c,parentType:i.parent.type.name,parentEmpty:0===i.parent.content.size}),m){var g=r.tr,p=r.schema,y=r.selection.constructor;if("Enter"===t.key){if("paragraph"!==i.parent.type.name||0!==i.parent.content.size)return;var f=i.index(s),v=u.child(f-1);if(!v||"paragraph"!==v.type.name||0!==v.content.size)return void console.log("[Details Plugin] Allowing one empty line. Not exiting yet.");console.log("[Details Plugin] EXITING via Double Enter (Two empty paras)!"),t.preventDefault();var k=i.before();g.delete(k,d);var h=p.nodes.paragraph.createAndFill(),b=c-(d-k);g.insert(b,h),y.near&&g.setSelection(y.near(g.doc.resolve(b+1))),o.dispatch(g)}if("ArrowDown"===t.key){if(i.parentOffset!==i.parent.content.size)return;console.log("[Details Plugin] EXITING via ArrowDown!"),t.preventDefault(),c<r.doc.content.size?y.near&&(g.setSelection(y.near(g.doc.resolve(c))),o.dispatch(g)):(h=p.nodes.paragraph.createAndFill(),g.insert(c,h),y.near&&g.setSelection(y.near(g.doc.resolve(c+1))),o.dispatch(g))}}}}else console.log("[Details Plugin] No editor view found")}else console.log("[Details Plugin] No instance found")}(e,n))})),{toHTMLRenderers:{htmlBlock:{renderer:function(e){return/<(\/)?(details|summary)/i.test(e.literal||"")?[{type:"html",content:e.literal||""}]:[{type:"openTag",tagName:"div",outerNewLine:!0},{type:"html",content:e.literal||""},{type:"closeTag",tagName:"div",outerNewLine:!0}]}}},markdownCommands:{details:function(e,t,n){var o=t.tr,r=t.selection,i=t.schema,s=r.content(),l=s.content.textBetween(0,s.content.size,"\n")||a.get("content"),u=""+("<details>\n<summary>"+a.get("summary")+"</summary>\n")+l+"\n</details>";return o.replaceSelectionWith(i.text(u)),n(o),!0}},wysiwygNodeViews:{details:function(e,t,a){var o=document.createElement("details"),r=e.attrs&&e.attrs.htmlAttrs||{};return(!0===e.attrs.open||r&&null!==r.open&&void 0!==r.open)&&(o.open=!0),o.addEventListener("click",(function(o){var i=o.target;if("SUMMARY"===i.tagName||i.closest("summary")){o.preventDefault();var s=a();if("number"==typeof s){var l=null!==r.open&&void 0!==r.open,u=n({},r);l?delete u.open:u.open="";var c=n(n({},e.attrs),{htmlAttrs:u});t.dispatch(t.state.tr.setNodeMarkup(s,null,c))}}})),{dom:o,contentDOM:o}}},wysiwygCommands:{details:function(e,t,n){var o=t.tr,r=t.schema,i=a.get("summary"),s=a.get("content"),l=r.nodes,u=l.details,c=l.summary,d=l.paragraph;if(u&&c&&d){var m=c.create({},r.text(i)),g=d.create({},r.text(s)),p=u.create({},[m,g]);return o.replaceSelectionWith(p),n(o),!0}return!1}},toolbarItems:[{groupIndex:4,itemIndex:3,item:r}]}}return t=t.default}()}));
@@ -0,0 +1,42 @@
1
+ /*!
2
+ * TOAST UI Editor : Text Align Plugin
3
+ * @version 3.2.10 | Thu Jan 08 2026
4
+ * @author NHN Cloud FE Development Lab <dl_javascript@nhn.com>
5
+ * @license MIT
6
+ */
7
+ /* Light Mode */
8
+ .toastui-editor-toolbar-icons.details {
9
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%23333%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z%27/%3E%3Cpath d=%27M9 10L12 13 15 10%27 stroke=%27%23333%27/%3E%3C/svg%3E") !important;
10
+ background-size: 24px 24px !important;
11
+ background-repeat: no-repeat !important;
12
+ background-position: center center !important;
13
+ text-indent: -9999px !important;
14
+ }
15
+
16
+ details {
17
+ display: block;
18
+ background-color: #f7f7f7;
19
+ border: 1px solid #e1e1e1;
20
+ border-radius: 4px;
21
+ padding: 10px;
22
+ margin: 10px 0;
23
+ }
24
+
25
+ summary {
26
+ cursor: pointer;
27
+ font-weight: bold;
28
+ margin-bottom: 5px;
29
+ outline: none;
30
+ }
31
+
32
+ /* Dark Mode */
33
+ .toastui-editor-dark .toastui-editor-toolbar-icons.details {
34
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%23eee%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z%27/%3E%3Cpath d=%27M9 10L12 13 15 10%27 stroke=%27%23eee%27/%3E%3C/svg%3E") !important;
35
+ }
36
+
37
+ .toastui-editor-dark details {
38
+ background-color: #282a36;
39
+ /* Darker background for contrast against editor bg */
40
+ border-color: #44475a;
41
+ color: #f8f8f2;
42
+ }
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * TOAST UI Editor : Text Align Plugin
3
- * @version 1.0.2 | Sun Jan 04 2026
3
+ * @version 3.2.10 | Thu Jan 08 2026
4
4
  * @author NHN Cloud FE Development Lab <dl_javascript@nhn.com>
5
5
  * @license MIT
6
6
  */
@@ -455,16 +455,117 @@ function addLangs(i18n) {
455
455
  summary: 'Summary header',
456
456
  content: 'Hidden content',
457
457
  });
458
+ i18n.setLanguage(['ar', 'ar-AR'], {
459
+ details: 'كتلة قابلة للطي',
460
+ summary: 'عنوان الملخص',
461
+ content: 'المحتوى المخفي',
462
+ });
463
+ i18n.setLanguage(['cs', 'cs-CZ'], {
464
+ details: 'Sbalitelný blok',
465
+ summary: 'Záhlaví shrnutí',
466
+ content: 'Skrytý obsah',
467
+ });
458
468
  i18n.setLanguage(['de', 'de-DE'], {
459
469
  details: 'Aufklappbarer Block',
460
470
  summary: 'Klick mich zum Aufklappen',
461
471
  content: 'Hier steht der versteckte Inhalt',
462
472
  });
473
+ i18n.setLanguage(['es', 'es-ES'], {
474
+ details: 'Bloque plegable',
475
+ summary: 'Encabezado del resumen',
476
+ content: 'Contenido oculto',
477
+ });
478
+ i18n.setLanguage(['fi', 'fi-FI'], {
479
+ details: 'Kokoontaitettava lohko',
480
+ summary: 'Yhteenveto',
481
+ content: 'Piilotettu sisältö',
482
+ });
483
+ i18n.setLanguage(['fr', 'fr-FR'], {
484
+ details: 'Bloc pliable',
485
+ summary: 'Titre du résumé',
486
+ content: 'Contenu masqué',
487
+ });
488
+ i18n.setLanguage(['gl', 'gl-ES'], {
489
+ details: 'Bloque pregable',
490
+ summary: 'Cabeceira do resumo',
491
+ content: 'Contido oculto',
492
+ });
493
+ i18n.setLanguage(['hr', 'hr-HR'], {
494
+ details: 'Sklopivi blok',
495
+ summary: 'Naslov sažetka',
496
+ content: 'Skriveni sadržaj',
497
+ });
498
+ i18n.setLanguage(['it', 'it-IT'], {
499
+ details: 'Blocco comprimibile',
500
+ summary: 'Intestazione riepilogo',
501
+ content: 'Contenuto nascosto',
502
+ });
503
+ i18n.setLanguage(['ja', 'ja-JP'], {
504
+ details: '折りたたみブロック',
505
+ summary: '要約ヘッダー',
506
+ content: '隠しコンテンツ',
507
+ });
508
+ i18n.setLanguage(['ko', 'ko-KR'], {
509
+ details: '접이식 블록',
510
+ summary: '요약 헤더',
511
+ content: '숨겨진 콘텐츠',
512
+ });
513
+ i18n.setLanguage(['nb', 'nb-NO'], {
514
+ details: 'Sammenleggbar blokk',
515
+ summary: 'Sammendrag overskrift',
516
+ content: 'Skjult innhold',
517
+ });
518
+ i18n.setLanguage(['nl', 'nl-NL'], {
519
+ details: 'Inklapbaar blok',
520
+ summary: 'Samenvatting kop',
521
+ content: 'Verborgen inhoud',
522
+ });
523
+ i18n.setLanguage(['pl', 'pl-PL'], {
524
+ details: 'Blok zwijany',
525
+ summary: 'Nagłówek podsumowania',
526
+ content: 'Ukryta treść',
527
+ });
528
+ i18n.setLanguage(['pt', 'pt-BR'], {
529
+ details: 'Bloco recolhível',
530
+ summary: 'Cabeçalho de resumo',
531
+ content: 'Conteúdo oculto',
532
+ });
533
+ i18n.setLanguage(['ru', 'ru-RU'], {
534
+ details: 'Сворачиваемый блок',
535
+ summary: 'Заголовок резюме',
536
+ content: 'Скрытый контент',
537
+ });
538
+ i18n.setLanguage(['sv', 'sv-SE'], {
539
+ details: 'Hopfällbart block',
540
+ summary: 'Sammanfattning rubrik',
541
+ content: 'Dolt innehåll',
542
+ });
543
+ i18n.setLanguage(['tr', 'tr-TR'], {
544
+ details: 'Daraltılabilir blok',
545
+ summary: 'Özet başlığı',
546
+ content: 'Gizli içerik',
547
+ });
548
+ i18n.setLanguage(['uk', 'uk-UA'], {
549
+ details: 'Згортаємий блок',
550
+ summary: 'Заголовок резюме',
551
+ content: 'Прихований вміст',
552
+ });
553
+ i18n.setLanguage(['zh', 'zh-CN'], {
554
+ details: '可折叠块',
555
+ summary: '摘要标题',
556
+ content: '隐藏内容',
557
+ });
558
+ i18n.setLanguage(['zh-TW'], {
559
+ details: '可折疊區塊',
560
+ summary: '摘要標題',
561
+ content: '隱藏內容',
562
+ });
463
563
  }
464
564
 
465
565
  ;// CONCATENATED MODULE: ./src/index.ts
466
566
 
467
567
 
568
+
468
569
  var PREFIX = 'toastui-editor-';
469
570
  function createToolbarItemOption(i18n) {
470
571
  return {
@@ -524,9 +625,23 @@ function handleDetailsExit(context, event) {
524
625
  if (!isEmptyPara) {
525
626
  return;
526
627
  }
527
- console.log('[Details Plugin] EXITING via Enter!');
628
+ // Check previous node to see if it's also an empty paragraph
629
+ // $from.index(0) gives the index in the parent details node
630
+ var index = $from.index(detailsDepth);
631
+ var prevNode = detailsNode.child(index - 1);
632
+ var isPrevEmptyPara = prevNode && prevNode.type.name === 'paragraph' && prevNode.content.size === 0;
633
+ if (!isPrevEmptyPara) {
634
+ console.log('[Details Plugin] Allowing one empty line. Not exiting yet.');
635
+ return;
636
+ }
637
+ console.log('[Details Plugin] EXITING via Double Enter (Two empty paras)!');
528
638
  event.preventDefault();
529
639
  var beforePara = $from.before();
640
+ // delete the current empty para and the previous one?
641
+ // Usually "Exit" means: Lift the current empty para out.
642
+ // But if we have TWO empty paras, maybe we want to keep one inside?
643
+ // Let's stick to: If two empty paras, we treat it as an exit signal.
644
+ // We should probably remove the *current* empty para and move the cursor out.
530
645
  tr.delete(beforePara, afterPara);
531
646
  var p = schema.nodes.paragraph.createAndFill();
532
647
  var insertPos = nodeEnd - (afterPara - beforePara);
@@ -577,6 +692,20 @@ function detailsPlugin(context, options) {
577
692
  handleDetailsExit(context, event);
578
693
  });
579
694
  return {
695
+ toHTMLRenderers: {
696
+ htmlBlock: {
697
+ renderer: function (node) {
698
+ var isDetails = /<(\/)?(details|summary)/i.test(node.literal || '');
699
+ return isDetails
700
+ ? [{ type: 'html', content: node.literal || '' }]
701
+ : [
702
+ { type: 'openTag', tagName: 'div', outerNewLine: true },
703
+ { type: 'html', content: node.literal || '' },
704
+ { type: 'closeTag', tagName: 'div', outerNewLine: true },
705
+ ];
706
+ },
707
+ },
708
+ },
580
709
  markdownCommands: {
581
710
  details: function (payload, _a, dispatch) {
582
711
  var tr = _a.tr, selection = _a.selection, schema = _a.schema;
@@ -591,25 +720,14 @@ function detailsPlugin(context, options) {
591
720
  return true;
592
721
  },
593
722
  },
594
- toHTMLRenderers: {
595
- htmlBlock: {
596
- details: function (node) { return [
597
- { type: 'openTag', tagName: 'details', outerNewLine: true },
598
- { type: 'html', content: node.childrenHTML || '' },
599
- { type: 'closeTag', tagName: 'details', outerNewLine: true },
600
- ]; },
601
- summary: function (node) { return [
602
- { type: 'openTag', tagName: 'summary', outerNewLine: true },
603
- { type: 'html', content: node.childrenHTML || '' },
604
- { type: 'closeTag', tagName: 'summary', outerNewLine: true },
605
- ]; },
606
- },
607
- },
608
723
  wysiwygNodeViews: {
609
724
  details: function (node, view, getPos) {
610
725
  var dom = document.createElement('details');
611
- var htmlAttrs = node.attrs.htmlAttrs;
612
- if (htmlAttrs.open) {
726
+ var htmlAttrs = (node.attrs && node.attrs.htmlAttrs) || {};
727
+ var isOpenAttr = node.attrs.open; // Core definition uses direct attribute
728
+ // Check both direct attribute (priority) and htmlAttrs (legacy/plugin)
729
+ var isOpen = isOpenAttr === true || (htmlAttrs && htmlAttrs.open !== null && typeof htmlAttrs.open !== 'undefined');
730
+ if (isOpen) {
613
731
  dom.open = true;
614
732
  }
615
733
  dom.addEventListener('click', function (e) {
@@ -618,7 +736,15 @@ function detailsPlugin(context, options) {
618
736
  e.preventDefault();
619
737
  var pos = getPos();
620
738
  if (typeof pos === 'number') {
621
- var newAttrs = __assign(__assign({}, node.attrs), { htmlAttrs: __assign(__assign({}, htmlAttrs), { open: !htmlAttrs.open }) });
739
+ var isOpen_1 = htmlAttrs.open !== null && typeof htmlAttrs.open !== 'undefined';
740
+ var newHtmlAttrs = __assign({}, htmlAttrs);
741
+ if (isOpen_1) {
742
+ delete newHtmlAttrs.open;
743
+ }
744
+ else {
745
+ newHtmlAttrs.open = '';
746
+ }
747
+ var newAttrs = __assign(__assign({}, node.attrs), { htmlAttrs: newHtmlAttrs });
622
748
  view.dispatch(view.state.tr.setNodeMarkup(pos, null, newAttrs));
623
749
  }
624
750
  }
@@ -645,8 +771,8 @@ function detailsPlugin(context, options) {
645
771
  },
646
772
  toolbarItems: [
647
773
  {
648
- groupIndex: 0,
649
- itemIndex: 4,
774
+ groupIndex: 4,
775
+ itemIndex: 3,
650
776
  item: toolbarItem,
651
777
  },
652
778
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@licium/editor-plugin-details",
3
- "version": "1.0.3",
3
+ "version": "3.2.10",
4
4
  "description": "Details/Summary plugin for Toast UI Editor",
5
5
  "keywords": [
6
6
  "nhn",
@@ -47,5 +47,6 @@
47
47
  },
48
48
  "publishConfig": {
49
49
  "access": "public"
50
- }
51
- }
50
+ },
51
+ "gitHead": "f5c5e17205373f73a607b05d989c4b06768752dd"
52
+ }
package/src/i18n/langs.ts CHANGED
@@ -7,9 +7,129 @@ export function addLangs(i18n: I18n) {
7
7
  content: 'Hidden content',
8
8
  });
9
9
 
10
+ i18n.setLanguage(['ar', 'ar-AR'], {
11
+ details: 'كتلة قابلة للطي',
12
+ summary: 'عنوان الملخص',
13
+ content: 'المحتوى المخفي',
14
+ });
15
+
16
+ i18n.setLanguage(['cs', 'cs-CZ'], {
17
+ details: 'Sbalitelný blok',
18
+ summary: 'Záhlaví shrnutí',
19
+ content: 'Skrytý obsah',
20
+ });
21
+
10
22
  i18n.setLanguage(['de', 'de-DE'], {
11
23
  details: 'Aufklappbarer Block',
12
24
  summary: 'Klick mich zum Aufklappen',
13
25
  content: 'Hier steht der versteckte Inhalt',
14
26
  });
27
+
28
+ i18n.setLanguage(['es', 'es-ES'], {
29
+ details: 'Bloque plegable',
30
+ summary: 'Encabezado del resumen',
31
+ content: 'Contenido oculto',
32
+ });
33
+
34
+ i18n.setLanguage(['fi', 'fi-FI'], {
35
+ details: 'Kokoontaitettava lohko',
36
+ summary: 'Yhteenveto',
37
+ content: 'Piilotettu sisältö',
38
+ });
39
+
40
+ i18n.setLanguage(['fr', 'fr-FR'], {
41
+ details: 'Bloc pliable',
42
+ summary: 'Titre du résumé',
43
+ content: 'Contenu masqué',
44
+ });
45
+
46
+ i18n.setLanguage(['gl', 'gl-ES'], {
47
+ details: 'Bloque pregable',
48
+ summary: 'Cabeceira do resumo',
49
+ content: 'Contido oculto',
50
+ });
51
+
52
+ i18n.setLanguage(['hr', 'hr-HR'], {
53
+ details: 'Sklopivi blok',
54
+ summary: 'Naslov sažetka',
55
+ content: 'Skriveni sadržaj',
56
+ });
57
+
58
+ i18n.setLanguage(['it', 'it-IT'], {
59
+ details: 'Blocco comprimibile',
60
+ summary: 'Intestazione riepilogo',
61
+ content: 'Contenuto nascosto',
62
+ });
63
+
64
+ i18n.setLanguage(['ja', 'ja-JP'], {
65
+ details: '折りたたみブロック',
66
+ summary: '要約ヘッダー',
67
+ content: '隠しコンテンツ',
68
+ });
69
+
70
+ i18n.setLanguage(['ko', 'ko-KR'], {
71
+ details: '접이식 블록',
72
+ summary: '요약 헤더',
73
+ content: '숨겨진 콘텐츠',
74
+ });
75
+
76
+ i18n.setLanguage(['nb', 'nb-NO'], {
77
+ details: 'Sammenleggbar blokk',
78
+ summary: 'Sammendrag overskrift',
79
+ content: 'Skjult innhold',
80
+ });
81
+
82
+ i18n.setLanguage(['nl', 'nl-NL'], {
83
+ details: 'Inklapbaar blok',
84
+ summary: 'Samenvatting kop',
85
+ content: 'Verborgen inhoud',
86
+ });
87
+
88
+ i18n.setLanguage(['pl', 'pl-PL'], {
89
+ details: 'Blok zwijany',
90
+ summary: 'Nagłówek podsumowania',
91
+ content: 'Ukryta treść',
92
+ });
93
+
94
+ i18n.setLanguage(['pt', 'pt-BR'], {
95
+ details: 'Bloco recolhível',
96
+ summary: 'Cabeçalho de resumo',
97
+ content: 'Conteúdo oculto',
98
+ });
99
+
100
+ i18n.setLanguage(['ru', 'ru-RU'], {
101
+ details: 'Сворачиваемый блок',
102
+ summary: 'Заголовок резюме',
103
+ content: 'Скрытый контент',
104
+ });
105
+
106
+ i18n.setLanguage(['sv', 'sv-SE'], {
107
+ details: 'Hopfällbart block',
108
+ summary: 'Sammanfattning rubrik',
109
+ content: 'Dolt innehåll',
110
+ });
111
+
112
+ i18n.setLanguage(['tr', 'tr-TR'], {
113
+ details: 'Daraltılabilir blok',
114
+ summary: 'Özet başlığı',
115
+ content: 'Gizli içerik',
116
+ });
117
+
118
+ i18n.setLanguage(['uk', 'uk-UA'], {
119
+ details: 'Згортаємий блок',
120
+ summary: 'Заголовок резюме',
121
+ content: 'Прихований вміст',
122
+ });
123
+
124
+ i18n.setLanguage(['zh', 'zh-CN'], {
125
+ details: '可折叠块',
126
+ summary: '摘要标题',
127
+ content: '隐藏内容',
128
+ });
129
+
130
+ i18n.setLanguage(['zh-TW'], {
131
+ details: '可折疊區塊',
132
+ summary: '摘要標題',
133
+ content: '隱藏內容',
134
+ });
15
135
  }
package/src/index.ts CHANGED
@@ -1,6 +1,8 @@
1
- import type { PluginContext, PluginInfo, I18n } from '@licium/editor';
1
+ /* eslint-disable */
2
+ import type { PluginContext, PluginInfo, I18n, MdNode } from '@licium/editor';
2
3
  import { PluginOptions } from '@t/index';
3
4
  import { addLangs } from './i18n/langs';
5
+ import './css/plugin.css';
4
6
 
5
7
  const PREFIX = 'toastui-editor-';
6
8
 
@@ -77,11 +79,28 @@ function handleDetailsExit(context: PluginContext, event: KeyboardEvent): void {
77
79
  return;
78
80
  }
79
81
 
80
- console.log('[Details Plugin] EXITING via Enter!');
82
+ // Check previous node to see if it's also an empty paragraph
83
+ // $from.index(0) gives the index in the parent details node
84
+ const index = $from.index(detailsDepth);
85
+ const prevNode = detailsNode.child(index - 1);
86
+ const isPrevEmptyPara = prevNode && prevNode.type.name === 'paragraph' && prevNode.content.size === 0;
87
+
88
+ if (!isPrevEmptyPara) {
89
+ console.log('[Details Plugin] Allowing one empty line. Not exiting yet.');
90
+ return;
91
+ }
92
+
93
+ console.log('[Details Plugin] EXITING via Double Enter (Two empty paras)!');
81
94
  event.preventDefault();
82
95
 
83
96
  const beforePara = $from.before();
84
97
 
98
+ // delete the current empty para and the previous one?
99
+ // Usually "Exit" means: Lift the current empty para out.
100
+ // But if we have TWO empty paras, maybe we want to keep one inside?
101
+ // Let's stick to: If two empty paras, we treat it as an exit signal.
102
+ // We should probably remove the *current* empty para and move the cursor out.
103
+
85
104
  tr.delete(beforePara, afterPara);
86
105
 
87
106
  const p = schema.nodes.paragraph.createAndFill()!;
@@ -152,6 +171,20 @@ export default function detailsPlugin(
152
171
  });
153
172
 
154
173
  return {
174
+ toHTMLRenderers: {
175
+ htmlBlock: {
176
+ renderer: (node) => {
177
+ const isDetails = /<(\/)?(details|summary)/i.test(node.literal || '');
178
+ return isDetails
179
+ ? [{ type: 'html', content: node.literal || '' }]
180
+ : [
181
+ { type: 'openTag', tagName: 'div', outerNewLine: true },
182
+ { type: 'html', content: node.literal || '' },
183
+ { type: 'closeTag', tagName: 'div', outerNewLine: true },
184
+ ];
185
+ },
186
+ },
187
+ },
155
188
  markdownCommands: {
156
189
  details: (payload, { tr, selection, schema }, dispatch) => {
157
190
  const slice = selection.content();
@@ -168,26 +201,18 @@ export default function detailsPlugin(
168
201
  return true;
169
202
  },
170
203
  },
171
- toHTMLRenderers: {
172
- htmlBlock: {
173
- details: (node) => [
174
- { type: 'openTag', tagName: 'details', outerNewLine: true },
175
- { type: 'html', content: node.childrenHTML || '' },
176
- { type: 'closeTag', tagName: 'details', outerNewLine: true },
177
- ],
178
- summary: (node) => [
179
- { type: 'openTag', tagName: 'summary', outerNewLine: true },
180
- { type: 'html', content: node.childrenHTML || '' },
181
- { type: 'closeTag', tagName: 'summary', outerNewLine: true },
182
- ],
183
- },
184
- },
204
+
205
+
185
206
  wysiwygNodeViews: {
186
207
  details: (node, view, getPos) => {
187
208
  const dom = document.createElement('details');
188
- const { htmlAttrs } = node.attrs;
209
+ const htmlAttrs = (node.attrs && node.attrs.htmlAttrs) || {};
210
+ const isOpenAttr = node.attrs.open; // Core definition uses direct attribute
211
+
212
+ // Check both direct attribute (priority) and htmlAttrs (legacy/plugin)
213
+ const isOpen = isOpenAttr === true || (htmlAttrs && htmlAttrs.open !== null && typeof htmlAttrs.open !== 'undefined');
189
214
 
190
- if (htmlAttrs.open) {
215
+ if (isOpen) {
191
216
  dom.open = true;
192
217
  }
193
218
 
@@ -200,9 +225,18 @@ export default function detailsPlugin(
200
225
  const pos = getPos();
201
226
 
202
227
  if (typeof pos === 'number') {
228
+ const isOpen = htmlAttrs.open !== null && typeof htmlAttrs.open !== 'undefined';
229
+ const newHtmlAttrs = { ...htmlAttrs };
230
+
231
+ if (isOpen) {
232
+ delete newHtmlAttrs.open;
233
+ } else {
234
+ newHtmlAttrs.open = '';
235
+ }
236
+
203
237
  const newAttrs = {
204
238
  ...node.attrs,
205
- htmlAttrs: { ...htmlAttrs, open: !htmlAttrs.open },
239
+ htmlAttrs: newHtmlAttrs,
206
240
  };
207
241
 
208
242
  view.dispatch(view.state.tr.setNodeMarkup(pos, null, newAttrs));
@@ -235,8 +269,8 @@ export default function detailsPlugin(
235
269
  },
236
270
  toolbarItems: [
237
271
  {
238
- groupIndex: 0,
239
- itemIndex: 4,
272
+ groupIndex: 4,
273
+ itemIndex: 3,
240
274
  item: toolbarItem,
241
275
  },
242
276
  ],