testio-tailwind 3.21.0 → 3.22.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.
Files changed (134) hide show
  1. package/AI_DESIGN_SYSTEM_REFERENCE.md +2407 -0
  2. package/package.json +1 -1
  3. package/src/_data/navigation.json +4 -0
  4. package/src/_includes/header.njk +8 -8
  5. package/src/_includes/page-with-sidebar.njk +10 -1
  6. package/src/assets/scripts/app.js +27 -0
  7. package/src/assets/stylesheets/components/designsystem/designsystem-styles.css +4 -2
  8. package/src/assets/stylesheets/components/tables.css +4 -0
  9. package/src/pages/buttons/block.haml +1 -0
  10. package/src/pages/buttons/button_group.haml +1 -0
  11. package/src/pages/buttons/buttons-lg.haml +1 -0
  12. package/src/pages/buttons/buttons-round.haml +1 -0
  13. package/src/pages/buttons/buttons-sm.haml +1 -0
  14. package/src/pages/buttons/buttons-xl.haml +1 -0
  15. package/src/pages/buttons/buttons.haml +1 -0
  16. package/src/pages/buttons/buttons_input.haml +4 -0
  17. package/src/pages/buttons/dropdown-menu.haml +2 -11
  18. package/src/pages/buttons/square-buttons.haml +15 -0
  19. package/src/pages/components/alerts.haml +1 -0
  20. package/src/pages/components/banner_cards.haml +1 -0
  21. package/src/pages/components/card_badges.haml +8 -7
  22. package/src/pages/components/cards.haml +15 -22
  23. package/src/pages/components/cards_customer.haml +1 -0
  24. package/src/pages/components/cards_fixedwidth.haml +7 -0
  25. package/src/pages/components/cards_iconheader.haml +1 -0
  26. package/src/pages/components/cards_tester.haml +1 -0
  27. package/src/pages/components/descriptionlist.haml +12 -3
  28. package/src/pages/components/devices.haml +1 -0
  29. package/src/pages/components/drawer.haml +1 -0
  30. package/src/pages/components/drawer_filter.haml +1 -0
  31. package/src/pages/components/emptystate.haml +2 -0
  32. package/src/pages/{buttons → components}/info_popover.haml +2 -1
  33. package/src/pages/components/listitem_with_actionbar.haml +1 -0
  34. package/src/pages/components/listitem_with_footer.haml +1 -0
  35. package/src/pages/components/listitems.haml +1 -0
  36. package/src/pages/components/listitems_badge.haml +1 -0
  37. package/src/pages/components/listitems_collapsable.haml +1 -0
  38. package/src/pages/components/listitems_nested.haml +1 -0
  39. package/src/pages/components/listitems_selectable.haml +1 -0
  40. package/src/pages/components/loading_spinner.haml +1 -0
  41. package/src/pages/components/metasidebar.haml +1 -0
  42. package/src/pages/components/modal_details.haml +3 -1
  43. package/src/pages/components/notifications.haml +2 -1
  44. package/src/pages/components/user_item.haml +1 -0
  45. package/src/pages/forms/attachments.pug +71 -0
  46. package/src/pages/forms/checkboxes.haml +1 -0
  47. package/src/pages/forms/checkboxes_devices.haml +1 -0
  48. package/src/pages/forms/date-time.haml +1 -0
  49. package/src/pages/forms/dropzone.pug +38 -0
  50. package/src/pages/forms/flatpickr.haml +1 -1
  51. package/src/pages/forms/form-addon.haml +1 -0
  52. package/src/pages/forms/form-card.haml +1 -0
  53. package/src/pages/forms/form_grid.haml +1 -0
  54. package/src/pages/forms/form_hint.haml +1 -0
  55. package/src/pages/forms/forms.haml +1 -0
  56. package/src/pages/forms/radiobuttons.haml +1 -0
  57. package/src/pages/forms/rating_scale.haml +1 -0
  58. package/src/pages/forms/search.haml +1 -0
  59. package/src/pages/forms/selectable_token.haml +1 -0
  60. package/src/pages/forms/selectable_token_browsers.haml +1 -0
  61. package/src/pages/forms/selectable_token_lg.haml +1 -0
  62. package/src/pages/forms/selectable_token_xl.haml +1 -0
  63. package/src/pages/forms/textarea.haml +4 -0
  64. package/src/pages/forms/toggle-buttons.haml +1 -0
  65. package/src/pages/forms/toggle-switch.haml +1 -0
  66. package/src/pages/forms/trix_editor.pug +1 -0
  67. package/src/pages/icons/bug-icons.haml +1 -0
  68. package/src/pages/icons/index.njk +18 -14
  69. package/src/pages/icons/status-icons.haml +1 -0
  70. package/src/pages/layout/app_layout.haml +2 -0
  71. package/src/pages/layout/margins.haml +1 -0
  72. package/src/pages/layout/max_width.haml +2 -1
  73. package/src/pages/layout/paddings.haml +1 -0
  74. package/src/pages/layout/spacing.haml +1 -0
  75. package/src/pages/{examples → layouts}/agenticqa_splitview.haml +1 -1
  76. package/src/pages/layouts/layout-actionbar.haml +45 -0
  77. package/src/pages/{examples → layouts}/layout-basic.haml +3 -2
  78. package/src/pages/{examples → layouts}/layout-chat.haml +3 -2
  79. package/src/pages/{examples/layout-sidebar-actionbar-metasidebar.haml → layouts/layout-chatwindow.haml} +3 -2
  80. package/src/pages/{examples → layouts}/layout-customer.haml +3 -2
  81. package/src/pages/{examples → layouts}/layout-form-sidebar-actionbar-metasidebar.haml +5 -228
  82. package/src/pages/{examples → layouts}/layout-manager.haml +3 -2
  83. package/src/pages/{examples → layouts}/layout-metasidebar.haml +3 -2
  84. package/src/pages/{examples → layouts}/layout-sidebar.haml +3 -2
  85. package/src/pages/{examples → layouts}/layout-tester.haml +3 -2
  86. package/src/pages/{examples → layouts}/splitview-metasidebar.haml +3 -3
  87. package/src/pages/{examples → layouts}/splitview-testcases.haml +2 -2
  88. package/src/pages/{examples → layouts}/splitview.haml +3 -3
  89. package/src/pages/navigation/header-manager.haml +1 -0
  90. package/src/pages/navigation/header-tester.haml +1 -0
  91. package/src/pages/navigation/header.haml +1 -0
  92. package/src/pages/navigation/header_customer.haml +1 -0
  93. package/src/pages/navigation/header_tester_epam.haml +1 -0
  94. package/src/pages/navigation/product_dropdown.haml +2 -0
  95. package/src/pages/navigation/radio_tabs.haml +1 -0
  96. package/src/pages/navigation/sidebar-manager.haml +2 -1
  97. package/src/pages/navigation/sidebar-tester-elements.haml +1 -0
  98. package/src/pages/navigation/sidebar-tester-seatlimitation.haml +1 -1
  99. package/src/pages/navigation/sidebar-tester.haml +1 -1
  100. package/src/pages/navigation/sidebar.haml +1 -0
  101. package/src/pages/navigation/sidebar_collapsables.haml +1 -0
  102. package/src/pages/navigation/sidebar_customer.haml +1 -0
  103. package/src/pages/navigation/tabnavigation.haml +1 -0
  104. package/src/pages/navigation/tabnavigation_actions.haml +1 -0
  105. package/src/pages/navigation/tabnavigation_pills.haml +1 -0
  106. package/src/pages/navigation/tabnavigation_sm.haml +1 -0
  107. package/src/pages/navigation/test-header-tester.haml +2 -0
  108. package/src/pages/tables/index.njk +7 -0
  109. package/src/pages/tables/tables-cellstyle.haml +38 -0
  110. package/src/pages/tables/tables-grid.haml +31 -0
  111. package/src/pages/tables/tables.haml +16 -0
  112. package/src/pages/tables/tables_alternating.haml +27 -0
  113. package/src/pages/tables/tables_borders.haml +22 -0
  114. package/src/pages/tables/tables_cells.haml +50 -0
  115. package/src/pages/tables/tables_footer.haml +27 -0
  116. package/src/pages/tables/tables_formrow.haml +31 -0
  117. package/src/pages/tables/tables_header.haml +22 -0
  118. package/src/pages/{components → tables}/tables_linked.haml +11 -10
  119. package/src/pages/typography/link_with_icon.haml +10 -2
  120. package/src/pages/typography/linked_icon.haml +6 -0
  121. package/src/pages/typography/section_header.haml +1 -0
  122. package/src/pages/typography/section_header_actions.haml +1 -0
  123. package/src/pages/typography/text_with_icon.haml +3 -2
  124. package/utils/filters.js +161 -0
  125. package/CLAUDE_DESIGN_SYSTEM_REFERENCE.md +0 -1978
  126. package/src/pages/buttons/link-with-icon.haml +0 -13
  127. package/src/pages/components/tables-cellstyle.pug +0 -285
  128. package/src/pages/components/tables-grid.pug +0 -258
  129. package/src/pages/components/tables.haml +0 -57
  130. package/src/pages/components/tables_cells.pug +0 -57
  131. package/src/pages/components/tables_formrow.haml +0 -55
  132. package/src/pages/examples/layout-actionbar.haml +0 -268
  133. package/src/pages/examples/layout-sidebar-actionbar.haml +0 -308
  134. package/src/pages/forms/uploads.pug +0 -101
@@ -0,0 +1,16 @@
1
+ ---
2
+ tags: tables
3
+ title: Standard table
4
+ ---
5
+
6
+ %p.mb-heading A standard table with basic styling and a cell with buttons.
7
+ %table.table
8
+ %tr
9
+ %td Cell content
10
+ %td Cell content
11
+ %td Cell content
12
+ %tr
13
+ %td Cell content Lorem ipsum dolor sit amet, consetetur sadipscing.
14
+ %td Cell content
15
+ %td.cell-with-buttons
16
+ %button.btn.btn-primary action
@@ -0,0 +1,27 @@
1
+ ---
2
+ tags: tables
3
+ title: Striped rows
4
+ ---
5
+
6
+ %p.mb-heading Table with alternating background colors for rows
7
+ %table.table.table-striped
8
+ %tr
9
+ %td Cell content
10
+ %td Cell content
11
+ %td.cell-with-buttons
12
+ %button.btn.btn-primary action
13
+ %tr
14
+ %td Cell content
15
+ %td Cell content
16
+ %td.cell-with-buttons
17
+ %button.btn.btn-primary action
18
+ %tr
19
+ %td Cell content Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
20
+ %td Cell content
21
+ %td.cell-with-buttons
22
+ %button.btn.btn-primary action
23
+ %tr
24
+ %td Cell content
25
+ %td Cell content
26
+ %td.cell-with-buttons
27
+ %button.btn.btn-primary action
@@ -0,0 +1,22 @@
1
+ ---
2
+ tags: tables
3
+ title: Borders
4
+ ---
5
+
6
+ %p.mb-heading Table with borders
7
+ %table.table.table-bordered
8
+ %tr
9
+ %td Cell content
10
+ %td Cell content
11
+ %td.cell-with-buttons
12
+ %button.btn.btn-primary action
13
+ %tr
14
+ %td Cell content
15
+ %td Cell content
16
+ %td.cell-with-buttons
17
+ %button.btn.btn-primary action
18
+ %tr
19
+ %td Cell content Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
20
+ %td Cell content
21
+ %td.cell-with-buttons
22
+ %button.btn.btn-primary action
@@ -0,0 +1,50 @@
1
+ ---
2
+ tags: tables
3
+ title: Cell behavior
4
+ ---
5
+
6
+ %p.mb-heading
7
+ Using the class
8
+ %code.tag table-standard
9
+ will make the last cell in a row take up all the available space. All previous cells will be as wide as their content.
10
+
11
+ %table.table-standard.table-cellstyle
12
+ %tbody
13
+ %tr
14
+ %td A cell with some lorem ipsum
15
+ %td Cell content
16
+ %td A small cell
17
+ %tr
18
+ %td This cell will take as much space as it's content
19
+ %td Cell content
20
+ %td The last cell will take up all the free space
21
+ .py-sm
22
+ %table.table-standard.table-cellstyle
23
+ %tbody
24
+ %tr
25
+ %td.cell-full-width
26
+ Add
27
+ %code.tag.bg-gray-light .cell-full-width
28
+ to let a cell take all the available space.
29
+ %td.wrap.min-w-12
30
+ Add
31
+ %code.tag.bg-gray-light .wrap
32
+ to allow line breaks within a cell.
33
+ %td Another cell content
34
+ %td € 40.00
35
+ %tr
36
+ %td.cell-full-width.pre
37
+ %p Add
38
+ %code.tag.bg-gray-light .pre
39
+ %br
40
+ to show preformatted white space.
41
+ | Multi
42
+ | Line
43
+ | textbox
44
+ | Cat and dog
45
+ %td.wrap.min-w-spacing-2xl
46
+ Wrapping cells need a
47
+ %code.tag.bg-gray-light .min-w
48
+ rule to not be squished.
49
+ %td Another cell content
50
+ %td € 999.00
@@ -0,0 +1,27 @@
1
+ ---
2
+ tags: tables
3
+ title: Footer
4
+ ---
5
+
6
+ %p.mb-heading Table with header and footer
7
+ %table.table.table-bordered.table-striped
8
+ %thead
9
+ %th Table header
10
+ %th Table header
11
+ %th Table header
12
+ %tbody
13
+ %tr
14
+ %td Cell content Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
15
+ %td Cell content
16
+ %td.cell-with-buttons
17
+ %button.btn.btn-primary action
18
+ %tr
19
+ %td Cell content
20
+ %td Cell content Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
21
+ %td.cell-with-buttons
22
+ %button.btn.btn-primary action
23
+ %tfoot
24
+ %tr
25
+ %td Table footer
26
+ %td Table footer
27
+ %td Table footer
@@ -0,0 +1,31 @@
1
+ ---
2
+ tags: tables
3
+ title: Table within form
4
+ ---
5
+
6
+ %p.mb-xs Tables cannot contain form elements, instead wrap the whole table with a form element.
7
+
8
+ %form
9
+ %table.table.table-bordered.align-top
10
+ %tbody
11
+ %tr
12
+ %td
13
+ %select.tom-select.min-w-spacing-3xl{placeholder: "Please select"}
14
+ %option{value: ""}
15
+ %option{value:"Option 1"} Option 1
16
+ %option{value:"Option 2"} Option 2
17
+ %option{value:"Option 3"} Option 3
18
+ %option{value:"Option 4"} ABC
19
+ %option{value:"Option 5"} DEF
20
+ %option{value:"Option 6"} WHY
21
+ %option{value:"Option 7"} XYZ
22
+ %td
23
+ %input.form-control.min-w-spacing-3xl{type:'text', placeholder:"Add min-w to avoid squishing"}
24
+ %td
25
+ %textarea.form-control.min-w-spacing-3xl{rows:'1', placeholder:"Text area with min-w"}
26
+ %td.text-right
27
+ .btn-group.ml-auto
28
+ %button.btn.btn-success.btn-square
29
+ .icon.icon-check
30
+ %button.btn.btn-danger.btn-square
31
+ .icon.icon-cross
@@ -0,0 +1,22 @@
1
+ ---
2
+ tags: tables
3
+ title: Header
4
+ ---
5
+
6
+ %p.mb-heading Table with header
7
+ %table.table.table-bordered.table-striped
8
+ %thead
9
+ %th Table header
10
+ %th Table header
11
+ %th Table header
12
+ %tbody
13
+ %tr
14
+ %td Cell content Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
15
+ %td Cell content
16
+ %td
17
+ %button.btn.btn-primary action
18
+ %tr
19
+ %td Cell content
20
+ %td Cell content Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
21
+ %td
22
+ %button.btn.btn-primary action
@@ -1,23 +1,24 @@
1
1
  ---
2
- tags: components
3
- title: Tables with linked content
2
+ tags: tables
3
+ title: Linked cells and rows
4
4
  ---
5
5
 
6
6
  %p.mb-heading
7
- Individual cells can be linked with JS. Add the class .linked-cell to highlight it on hover.
8
- %br
9
- If you want to highlight a whole row, but still have action buttons, use the class .highlight-parent-row.
10
- %table.table.table-bordered.table-striped
11
- %thead
12
- %th Table header
13
- %th Table header
14
- %th Table header
7
+ %span Individual cells can be linked with Javascript. Add the class
8
+ %code.tag .linked-cell
9
+ %span to highlight it on hover.
10
+ %table.table.table-bordered
15
11
  %tbody
16
12
  %tr
17
13
  %td.linked-cell{onclick: "location.href='https://test.io'"} Linked cell
18
14
  %td.linked-cell{onclick: "location.href='https://test.io'"} Linked cell
19
15
  %td.cell-with-buttons
20
16
  %a.btn.btn-primary{href:"#"} action
17
+ %p.mb-heading
18
+ %span If you want to highlight a whole row, but still have action buttons, use the class
19
+ %code.tag .highlight-parent-row.
20
+ %table.table.table-bordered
21
+ %tbody
21
22
  %tr
22
23
  %td.highlight-parent-row{onclick: "location.href='https://test.io'"} Highlight parent
23
24
  %td.highlight-parent-row{onclick: "location.href='https://test.io'"} Highlight parent
@@ -3,10 +3,18 @@ tags: typography
3
3
  title: Link with icon
4
4
  ---
5
5
 
6
+ %p.mb-heading This component provides a solution for the common usecase to have links with icons.
6
7
  %a.link-with-icon{href:""}
7
8
  .icon.icon-exclamation-circle
8
- All approved testers must complete this course to achieve bronze level and above.
9
+ Link text
9
10
  .pt-xs
10
11
  %a.link-with-icon{href:""}
11
12
  .icon.icon-exclamation-circle
12
- All approved testers will have a one-month transition period to complete it. If they do not pass, even Diamond testers will be downgraded to copper level.
13
+ A longer link text that might wrap onto multiple lines to demonstrate proper alignment of the icon with the text content.
14
+ %p.my-heading As an alternative consider the use of ghost buttons.
15
+ %a.btn.btn-ghost{href:""}
16
+ .icon.icon-exclamation-circle.mr-icon-spacing
17
+ Button
18
+ %a.btn.btn-ghost.btn-sm{href:""}
19
+ .icon.icon-exclamation-circle.mr-icon-spacing
20
+ Button SM
@@ -3,8 +3,14 @@ tags: typography
3
3
  title: Linked icon
4
4
  ---
5
5
 
6
+ %p.mb-heading This component provides a solution for the common usecase to have linked icons.
6
7
  %a.linked-icon{href:""}
7
8
  .icon.icon-exclamation-circle
8
9
  .pt-xs
9
10
  %a.linked-icon{href:""}
10
11
  .icon.icon-exclamation-circle-filled
12
+ %p.my-heading As an alternative consider the use of square ghost buttons. They offer a larger clickable area.
13
+ %a.btn.btn-square.btn-ghost{href:""}
14
+ .icon.icon-exclamation-circle
15
+ %a.btn.btn-square.btn-ghost.btn-sm{href:""}
16
+ .icon.icon-exclamation-circle
@@ -3,6 +3,7 @@ tags: typography
3
3
  title: Section header
4
4
  ---
5
5
 
6
+ %p.mb-heading This component is used to separate different sections of a page with a header and an optional label.
6
7
  %section
7
8
  .section-header
8
9
  %h1.section-title Section header with h1
@@ -3,6 +3,7 @@ tags: typography
3
3
  title: Section header with actions
4
4
  ---
5
5
 
6
+ %p.mb-heading This component is used to separate different sections of a page with a header and action links or icons.
6
7
  %section
7
8
  .section-header
8
9
  %h1.section-title Section with action h1
@@ -3,10 +3,11 @@ tags: typography
3
3
  title: Text with icon
4
4
  ---
5
5
 
6
+ %p.mb-heading This component provides a solution for the common usecase to have text with icons.
6
7
  .text-with-icon
7
8
  .icon.icon-exclamation-circle
8
- All approved testers must complete this course to achieve bronze level and above.
9
+ Example text
9
10
  .pt-xs
10
11
  .text-with-icon
11
12
  .icon.icon-exclamation-circle
12
- All approved testers will have a one-month transition period to complete it. If they do not pass, even Diamond testers will be downgraded to copper level.
13
+ A longer example text that might wrap onto multiple lines to demonstrate proper alignment of the icon with the text content.
package/utils/filters.js CHANGED
@@ -1,11 +1,172 @@
1
1
 
2
2
  const { DateTime } = require('luxon')
3
3
 
4
+ // HTML to HAML converter with proper indentation
5
+ function htmlToHaml(html) {
6
+ let haml = '';
7
+ let currentIndent = 0;
8
+
9
+ // Remove leading/trailing whitespace
10
+ html = html.trim();
11
+
12
+ // Tokenize HTML
13
+ const tokens = tokenizeHtml(html);
14
+
15
+ // Process tokens
16
+ for (let i = 0; i < tokens.length; i++) {
17
+ const token = tokens[i];
18
+
19
+ if (token.type === 'openTag') {
20
+ const hamlTag = tagToHaml(token.tag, token.attrs);
21
+ haml += getIndent(currentIndent) + hamlTag;
22
+
23
+ // Check if this is a self-closing or void element
24
+ if (token.selfClosing || isVoidElement(token.tag)) {
25
+ haml += '\n';
26
+ } else {
27
+ haml += '\n';
28
+ currentIndent++;
29
+ }
30
+ } else if (token.type === 'closeTag') {
31
+ currentIndent = Math.max(0, currentIndent - 1);
32
+ } else if (token.type === 'text') {
33
+ const text = token.content.trim();
34
+ if (text) {
35
+ haml += getIndent(currentIndent) + text + '\n';
36
+ }
37
+ }
38
+ }
39
+
40
+ return haml.trim();
41
+ }
42
+
43
+ // Tokenize HTML into an array of tokens
44
+ function tokenizeHtml(html) {
45
+ const tokens = [];
46
+ let i = 0;
47
+
48
+ while (i < html.length) {
49
+ if (html[i] === '<') {
50
+ const tagEnd = html.indexOf('>', i);
51
+ if (tagEnd === -1) break;
52
+
53
+ const tagContent = html.substring(i + 1, tagEnd);
54
+
55
+ if (tagContent.startsWith('/')) {
56
+ // Closing tag
57
+ const tagName = tagContent.substring(1).trim();
58
+ tokens.push({ type: 'closeTag', tag: tagName });
59
+ } else if (tagContent.startsWith('!')) {
60
+ // Comment or doctype - skip
61
+ i = tagEnd + 1;
62
+ continue;
63
+ } else {
64
+ // Opening tag
65
+ const selfClosing = tagContent.endsWith('/');
66
+ const cleanContent = selfClosing ? tagContent.slice(0, -1) : tagContent;
67
+ const parts = cleanContent.trim().split(/\s+/);
68
+ const tagName = parts[0];
69
+ const attrString = cleanContent.substring(tagName.length).trim();
70
+
71
+ tokens.push({
72
+ type: 'openTag',
73
+ tag: tagName,
74
+ attrs: attrString,
75
+ selfClosing: selfClosing
76
+ });
77
+ }
78
+
79
+ i = tagEnd + 1;
80
+ } else {
81
+ // Text content
82
+ const nextTag = html.indexOf('<', i);
83
+ const textEnd = nextTag === -1 ? html.length : nextTag;
84
+ const text = html.substring(i, textEnd);
85
+
86
+ if (text.trim()) {
87
+ tokens.push({ type: 'text', content: text });
88
+ }
89
+
90
+ i = textEnd;
91
+ }
92
+ }
93
+
94
+ return tokens;
95
+ }
96
+
97
+ // Convert a tag and its attributes to HAML format
98
+ function tagToHaml(tag, attrString) {
99
+ let result = '%' + tag;
100
+
101
+ if (!attrString) {
102
+ return result;
103
+ }
104
+
105
+ const classes = [];
106
+ const ids = [];
107
+ const attrs = {};
108
+
109
+ // Parse attributes
110
+ const attrRegex = /(\w+(?:-\w+)*(?::\w+)*)\s*=\s*["']([^"']*)["']|(\w+(?:-\w+)*(?::\w+)*)/g;
111
+ let match;
112
+
113
+ while ((match = attrRegex.exec(attrString)) !== null) {
114
+ if (match[1]) {
115
+ const name = match[1];
116
+ const value = match[2];
117
+
118
+ if (name === 'class') {
119
+ classes.push(...value.split(/\s+/));
120
+ } else if (name === 'id') {
121
+ ids.push(value);
122
+ } else if (name !== 'type' || tag !== 'input') {
123
+ // Skip type attribute for input tags as they're implicit
124
+ attrs[name] = value;
125
+ }
126
+ }
127
+ }
128
+
129
+ // Build HAML tag with classes and IDs
130
+ if (ids.length > 0) {
131
+ result += '#' + ids.join('.');
132
+ }
133
+
134
+ if (classes.length > 0) {
135
+ result += '.' + classes.join('.');
136
+ }
137
+
138
+ // Add other attributes
139
+ if (Object.keys(attrs).length > 0) {
140
+ const attrPairs = Object.entries(attrs)
141
+ .map(([k, v]) => `${k}:'${v}'`)
142
+ .join(', ');
143
+ result += `{${attrPairs}}`;
144
+ }
145
+
146
+ return result;
147
+ }
148
+
149
+ // Check if tag is a void element (self-closing)
150
+ function isVoidElement(tag) {
151
+ const voidElements = ['br', 'hr', 'img', 'input', 'meta', 'link', 'area', 'base', 'col', 'embed', 'source', 'track', 'wbr'];
152
+ return voidElements.includes(tag.toLowerCase());
153
+ }
154
+
155
+ // Get indentation string
156
+ function getIndent(level) {
157
+ return ' '.repeat(level);
158
+ }
159
+
4
160
  module.exports = {
5
161
  // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
6
162
  htmlDateString: (dateObj) => {
7
163
  return DateTime.fromJSDate(dateObj, {
8
164
  zone: 'utc'
9
165
  }).toFormat('yyyy-LL-dd');
166
+ },
167
+
168
+ // Convert HTML to HAML format
169
+ convertToHaml: (html) => {
170
+ return htmlToHaml(html);
10
171
  }
11
172
  }