create-starlight-cms 0.9.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 (228) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -0
  3. package/THIRD_PARTY_NOTICES.md +4 -0
  4. package/bin/index.js +147 -0
  5. package/package.json +46 -0
  6. package/template/.nvmrc +1 -0
  7. package/template/LICENSE +21 -0
  8. package/template/README.md +68 -0
  9. package/template/THIRD_PARTY_NOTICES.md +12 -0
  10. package/template/_gitignore +14 -0
  11. package/template/astro.config.mjs +26 -0
  12. package/template/migrations/0001_schema.sql +73 -0
  13. package/template/migrations/0002_publish_delivery.sql +16 -0
  14. package/template/package-lock.json.template +9123 -0
  15. package/template/package.json +89 -0
  16. package/template/scripts/build-admin.mjs +38 -0
  17. package/template/scripts/check-linux-bindings.mjs +18 -0
  18. package/template/scripts/dev.mjs +94 -0
  19. package/template/src/admin/admin.scss +182 -0
  20. package/template/src/admin/app.ts +130 -0
  21. package/template/src/admin/client.tsx +684 -0
  22. package/template/src/admin/components/tiptap-extension/node-background-extension.ts +150 -0
  23. package/template/src/admin/components/tiptap-icons/align-center-icon.tsx +38 -0
  24. package/template/src/admin/components/tiptap-icons/align-justify-icon.tsx +38 -0
  25. package/template/src/admin/components/tiptap-icons/align-left-icon.tsx +38 -0
  26. package/template/src/admin/components/tiptap-icons/align-right-icon.tsx +38 -0
  27. package/template/src/admin/components/tiptap-icons/arrow-left-icon.tsx +24 -0
  28. package/template/src/admin/components/tiptap-icons/arrow-right-icon.tsx +26 -0
  29. package/template/src/admin/components/tiptap-icons/ban-icon.tsx +26 -0
  30. package/template/src/admin/components/tiptap-icons/blockquote-icon.tsx +44 -0
  31. package/template/src/admin/components/tiptap-icons/bold-icon.tsx +26 -0
  32. package/template/src/admin/components/tiptap-icons/case-sensitive-icon.tsx +32 -0
  33. package/template/src/admin/components/tiptap-icons/check-icon.tsx +26 -0
  34. package/template/src/admin/components/tiptap-icons/chevron-down-icon.tsx +26 -0
  35. package/template/src/admin/components/tiptap-icons/chevron-up-icon.tsx +22 -0
  36. package/template/src/admin/components/tiptap-icons/close-icon.tsx +24 -0
  37. package/template/src/admin/components/tiptap-icons/code-block-icon.tsx +38 -0
  38. package/template/src/admin/components/tiptap-icons/code2-icon.tsx +32 -0
  39. package/template/src/admin/components/tiptap-icons/corner-down-left-icon.tsx +26 -0
  40. package/template/src/admin/components/tiptap-icons/external-link-icon.tsx +28 -0
  41. package/template/src/admin/components/tiptap-icons/heading-five-icon.tsx +28 -0
  42. package/template/src/admin/components/tiptap-icons/heading-four-icon.tsx +28 -0
  43. package/template/src/admin/components/tiptap-icons/heading-icon.tsx +24 -0
  44. package/template/src/admin/components/tiptap-icons/heading-one-icon.tsx +28 -0
  45. package/template/src/admin/components/tiptap-icons/heading-six-icon.tsx +30 -0
  46. package/template/src/admin/components/tiptap-icons/heading-three-icon.tsx +36 -0
  47. package/template/src/admin/components/tiptap-icons/heading-two-icon.tsx +28 -0
  48. package/template/src/admin/components/tiptap-icons/highlighter-icon.tsx +26 -0
  49. package/template/src/admin/components/tiptap-icons/image-plus-icon.tsx +26 -0
  50. package/template/src/admin/components/tiptap-icons/italic-icon.tsx +24 -0
  51. package/template/src/admin/components/tiptap-icons/link-icon.tsx +28 -0
  52. package/template/src/admin/components/tiptap-icons/list-icon.tsx +56 -0
  53. package/template/src/admin/components/tiptap-icons/list-ordered-icon.tsx +56 -0
  54. package/template/src/admin/components/tiptap-icons/list-todo-icon.tsx +50 -0
  55. package/template/src/admin/components/tiptap-icons/moon-star-icon.tsx +30 -0
  56. package/template/src/admin/components/tiptap-icons/redo2-icon.tsx +26 -0
  57. package/template/src/admin/components/tiptap-icons/search-icon.tsx +26 -0
  58. package/template/src/admin/components/tiptap-icons/strike-icon.tsx +28 -0
  59. package/template/src/admin/components/tiptap-icons/subscript-icon.tsx +38 -0
  60. package/template/src/admin/components/tiptap-icons/sun-icon.tsx +58 -0
  61. package/template/src/admin/components/tiptap-icons/superscript-icon.tsx +38 -0
  62. package/template/src/admin/components/tiptap-icons/trash-icon.tsx +26 -0
  63. package/template/src/admin/components/tiptap-icons/underline-icon.tsx +26 -0
  64. package/template/src/admin/components/tiptap-icons/undo2-icon.tsx +26 -0
  65. package/template/src/admin/components/tiptap-icons/whole-word-icon.tsx +36 -0
  66. package/template/src/admin/components/tiptap-node/blockquote-node/blockquote-node.scss +37 -0
  67. package/template/src/admin/components/tiptap-node/code-block-node/code-block-node.scss +54 -0
  68. package/template/src/admin/components/tiptap-node/heading-node/heading-node.scss +45 -0
  69. package/template/src/admin/components/tiptap-node/horizontal-rule-node/horizontal-rule-node-extension.ts +14 -0
  70. package/template/src/admin/components/tiptap-node/horizontal-rule-node/horizontal-rule-node.scss +25 -0
  71. package/template/src/admin/components/tiptap-node/image-node/image-node.scss +35 -0
  72. package/template/src/admin/components/tiptap-node/image-upload-node/image-upload-node-extension.ts +161 -0
  73. package/template/src/admin/components/tiptap-node/image-upload-node/image-upload-node.scss +249 -0
  74. package/template/src/admin/components/tiptap-node/image-upload-node/image-upload-node.tsx +554 -0
  75. package/template/src/admin/components/tiptap-node/image-upload-node/index.tsx +1 -0
  76. package/template/src/admin/components/tiptap-node/list-node/list-node.scss +208 -0
  77. package/template/src/admin/components/tiptap-node/paragraph-node/paragraph-node.scss +217 -0
  78. package/template/src/admin/components/tiptap-templates/simple/NOTICE.md +14 -0
  79. package/template/src/admin/components/tiptap-templates/simple/data/content.json +477 -0
  80. package/template/src/admin/components/tiptap-templates/simple/simple-editor.scss +97 -0
  81. package/template/src/admin/components/tiptap-templates/simple/simple-editor.tsx +348 -0
  82. package/template/src/admin/components/tiptap-templates/simple/theme-toggle.tsx +44 -0
  83. package/template/src/admin/components/tiptap-ui/blockquote-button/blockquote-button.tsx +122 -0
  84. package/template/src/admin/components/tiptap-ui/blockquote-button/index.tsx +2 -0
  85. package/template/src/admin/components/tiptap-ui/blockquote-button/use-blockquote.ts +273 -0
  86. package/template/src/admin/components/tiptap-ui/code-block-button/code-block-button.tsx +122 -0
  87. package/template/src/admin/components/tiptap-ui/code-block-button/index.tsx +2 -0
  88. package/template/src/admin/components/tiptap-ui/code-block-button/use-code-block.ts +283 -0
  89. package/template/src/admin/components/tiptap-ui/color-highlight-button/color-highlight-button.scss +49 -0
  90. package/template/src/admin/components/tiptap-ui/color-highlight-button/color-highlight-button.tsx +170 -0
  91. package/template/src/admin/components/tiptap-ui/color-highlight-button/index.tsx +2 -0
  92. package/template/src/admin/components/tiptap-ui/color-highlight-button/use-color-highlight.ts +380 -0
  93. package/template/src/admin/components/tiptap-ui/color-highlight-popover/color-highlight-popover.tsx +231 -0
  94. package/template/src/admin/components/tiptap-ui/color-highlight-popover/index.tsx +1 -0
  95. package/template/src/admin/components/tiptap-ui/heading-button/heading-button.tsx +124 -0
  96. package/template/src/admin/components/tiptap-ui/heading-button/index.tsx +2 -0
  97. package/template/src/admin/components/tiptap-ui/heading-button/use-heading.ts +348 -0
  98. package/template/src/admin/components/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx +128 -0
  99. package/template/src/admin/components/tiptap-ui/heading-dropdown-menu/index.tsx +2 -0
  100. package/template/src/admin/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts +132 -0
  101. package/template/src/admin/components/tiptap-ui/image-upload-button/image-upload-button.tsx +130 -0
  102. package/template/src/admin/components/tiptap-ui/image-upload-button/index.tsx +2 -0
  103. package/template/src/admin/components/tiptap-ui/image-upload-button/use-image-upload.ts +197 -0
  104. package/template/src/admin/components/tiptap-ui/link-popover/index.tsx +2 -0
  105. package/template/src/admin/components/tiptap-ui/link-popover/link-popover.scss +16 -0
  106. package/template/src/admin/components/tiptap-ui/link-popover/link-popover.tsx +315 -0
  107. package/template/src/admin/components/tiptap-ui/link-popover/use-link-popover.ts +297 -0
  108. package/template/src/admin/components/tiptap-ui/list-button/index.tsx +2 -0
  109. package/template/src/admin/components/tiptap-ui/list-button/list-button.tsx +120 -0
  110. package/template/src/admin/components/tiptap-ui/list-button/use-list.ts +353 -0
  111. package/template/src/admin/components/tiptap-ui/list-dropdown-menu/index.tsx +2 -0
  112. package/template/src/admin/components/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx +126 -0
  113. package/template/src/admin/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts +220 -0
  114. package/template/src/admin/components/tiptap-ui/mark-button/index.tsx +2 -0
  115. package/template/src/admin/components/tiptap-ui/mark-button/mark-button.tsx +122 -0
  116. package/template/src/admin/components/tiptap-ui/mark-button/use-mark.ts +219 -0
  117. package/template/src/admin/components/tiptap-ui/search-and-replace/index.tsx +2 -0
  118. package/template/src/admin/components/tiptap-ui/search-and-replace/search-and-replace.scss +300 -0
  119. package/template/src/admin/components/tiptap-ui/search-and-replace/search-and-replace.tsx +718 -0
  120. package/template/src/admin/components/tiptap-ui/search-and-replace/use-search-and-replace.ts +528 -0
  121. package/template/src/admin/components/tiptap-ui/text-align-button/index.tsx +2 -0
  122. package/template/src/admin/components/tiptap-ui/text-align-button/text-align-button.tsx +144 -0
  123. package/template/src/admin/components/tiptap-ui/text-align-button/use-text-align.ts +237 -0
  124. package/template/src/admin/components/tiptap-ui/undo-redo-button/index.tsx +2 -0
  125. package/template/src/admin/components/tiptap-ui/undo-redo-button/undo-redo-button.tsx +125 -0
  126. package/template/src/admin/components/tiptap-ui/undo-redo-button/use-undo-redo.ts +200 -0
  127. package/template/src/admin/components/tiptap-ui-primitive/badge/badge-colors.scss +395 -0
  128. package/template/src/admin/components/tiptap-ui-primitive/badge/badge-group.scss +16 -0
  129. package/template/src/admin/components/tiptap-ui-primitive/badge/badge.scss +99 -0
  130. package/template/src/admin/components/tiptap-ui-primitive/badge/badge.tsx +44 -0
  131. package/template/src/admin/components/tiptap-ui-primitive/badge/index.tsx +1 -0
  132. package/template/src/admin/components/tiptap-ui-primitive/button/button-colors.scss +613 -0
  133. package/template/src/admin/components/tiptap-ui-primitive/button/button.scss +390 -0
  134. package/template/src/admin/components/tiptap-ui-primitive/button/button.tsx +158 -0
  135. package/template/src/admin/components/tiptap-ui-primitive/button/index.tsx +1 -0
  136. package/template/src/admin/components/tiptap-ui-primitive/button-group/button-group.scss +89 -0
  137. package/template/src/admin/components/tiptap-ui-primitive/button-group/button-group.tsx +72 -0
  138. package/template/src/admin/components/tiptap-ui-primitive/button-group/index.tsx +1 -0
  139. package/template/src/admin/components/tiptap-ui-primitive/card/card.scss +74 -0
  140. package/template/src/admin/components/tiptap-ui-primitive/card/card.tsx +79 -0
  141. package/template/src/admin/components/tiptap-ui-primitive/card/index.tsx +1 -0
  142. package/template/src/admin/components/tiptap-ui-primitive/dropdown-menu/dropdown-menu.scss +152 -0
  143. package/template/src/admin/components/tiptap-ui-primitive/dropdown-menu/dropdown-menu.tsx +262 -0
  144. package/template/src/admin/components/tiptap-ui-primitive/dropdown-menu/index.tsx +1 -0
  145. package/template/src/admin/components/tiptap-ui-primitive/input/index.tsx +1 -0
  146. package/template/src/admin/components/tiptap-ui-primitive/input/input.scss +41 -0
  147. package/template/src/admin/components/tiptap-ui-primitive/input/input.tsx +22 -0
  148. package/template/src/admin/components/tiptap-ui-primitive/input-group/index.tsx +1 -0
  149. package/template/src/admin/components/tiptap-ui-primitive/input-group/input-group.scss +225 -0
  150. package/template/src/admin/components/tiptap-ui-primitive/input-group/input-group.tsx +135 -0
  151. package/template/src/admin/components/tiptap-ui-primitive/popover/index.tsx +1 -0
  152. package/template/src/admin/components/tiptap-ui-primitive/popover/popover.scss +64 -0
  153. package/template/src/admin/components/tiptap-ui-primitive/popover/popover.tsx +35 -0
  154. package/template/src/admin/components/tiptap-ui-primitive/separator/index.tsx +1 -0
  155. package/template/src/admin/components/tiptap-ui-primitive/separator/separator.scss +23 -0
  156. package/template/src/admin/components/tiptap-ui-primitive/separator/separator.tsx +28 -0
  157. package/template/src/admin/components/tiptap-ui-primitive/spacer/index.tsx +1 -0
  158. package/template/src/admin/components/tiptap-ui-primitive/spacer/spacer.tsx +24 -0
  159. package/template/src/admin/components/tiptap-ui-primitive/switch/index.tsx +1 -0
  160. package/template/src/admin/components/tiptap-ui-primitive/switch/switch.scss +86 -0
  161. package/template/src/admin/components/tiptap-ui-primitive/switch/switch.tsx +29 -0
  162. package/template/src/admin/components/tiptap-ui-primitive/textarea/index.tsx +1 -0
  163. package/template/src/admin/components/tiptap-ui-primitive/textarea/textarea.scss +42 -0
  164. package/template/src/admin/components/tiptap-ui-primitive/textarea/textarea.tsx +16 -0
  165. package/template/src/admin/components/tiptap-ui-primitive/toolbar/index.tsx +1 -0
  166. package/template/src/admin/components/tiptap-ui-primitive/toolbar/toolbar.scss +98 -0
  167. package/template/src/admin/components/tiptap-ui-primitive/toolbar/toolbar.tsx +203 -0
  168. package/template/src/admin/components/tiptap-ui-primitive/tooltip/index.tsx +1 -0
  169. package/template/src/admin/components/tiptap-ui-primitive/tooltip/tooltip.scss +43 -0
  170. package/template/src/admin/components/tiptap-ui-primitive/tooltip/tooltip.tsx +241 -0
  171. package/template/src/admin/hooks/use-composed-ref.ts +46 -0
  172. package/template/src/admin/hooks/use-cursor-visibility.ts +69 -0
  173. package/template/src/admin/hooks/use-element-rect.ts +166 -0
  174. package/template/src/admin/hooks/use-is-breakpoint.ts +35 -0
  175. package/template/src/admin/hooks/use-menu-navigation.ts +204 -0
  176. package/template/src/admin/hooks/use-scrolling.ts +75 -0
  177. package/template/src/admin/hooks/use-throttled-callback.ts +48 -0
  178. package/template/src/admin/hooks/use-tiptap-editor.ts +69 -0
  179. package/template/src/admin/hooks/use-unmount.ts +21 -0
  180. package/template/src/admin/hooks/use-window-size.ts +93 -0
  181. package/template/src/admin/html.ts +17 -0
  182. package/template/src/admin/lib/tiptap-utils.ts +652 -0
  183. package/template/src/admin/navigation-tree.tsx +82 -0
  184. package/template/src/admin/preview.ts +73 -0
  185. package/template/src/admin/styles/_cms-theme.scss +58 -0
  186. package/template/src/admin/styles/_keyframe-animations.scss +91 -0
  187. package/template/src/admin/styles/_variables.scss +298 -0
  188. package/template/src/assets/favicon.svg +1 -0
  189. package/template/src/assets/logo.svg +1 -0
  190. package/template/src/astro-env.d.ts +1 -0
  191. package/template/src/content.config.ts +12 -0
  192. package/template/src/db/bootstrap.ts +36 -0
  193. package/template/src/db/client.ts +7 -0
  194. package/template/src/db/schema.ts +45 -0
  195. package/template/src/documents/service.ts +178 -0
  196. package/template/src/documents/validation.ts +9 -0
  197. package/template/src/env.ts +9 -0
  198. package/template/src/index.ts +10 -0
  199. package/template/src/locales.ts +10 -0
  200. package/template/src/media/service.ts +120 -0
  201. package/template/src/navigation/service.ts +171 -0
  202. package/template/src/pages/[locale]/cms-preview-shell.astro +12 -0
  203. package/template/src/pages/cms-preview-shell.astro +5 -0
  204. package/template/src/publish/deploy-hook.ts +42 -0
  205. package/template/src/publish/record.ts +20 -0
  206. package/template/src/publish/service.ts +135 -0
  207. package/template/src/scss.d.ts +5 -0
  208. package/template/src/site.config.ts +7 -0
  209. package/template/src/sql.d.ts +4 -0
  210. package/template/src/starlight/cms-origin.ts +4 -0
  211. package/template/src/starlight/loader.ts +21 -0
  212. package/template/src/starlight/preview-render.ts +162 -0
  213. package/template/src/starlight/preview-shell.astro +14 -0
  214. package/template/src/starlight/render.ts +59 -0
  215. package/template/src/starlight/schema.ts +34 -0
  216. package/template/src/starlight/sidebar.ts +35 -0
  217. package/template/src/starlight/snapshot.ts +47 -0
  218. package/template/src/starlight/source.ts +26 -0
  219. package/template/src/starlight/tiptap.ts +29 -0
  220. package/template/src/styles/starlight.css +12 -0
  221. package/template/test-files/content.test.template.ts +82 -0
  222. package/template/test-files/deploy-hook.test.template.ts +32 -0
  223. package/template/test-files/media.test.template.ts +39 -0
  224. package/template/test-files/publishing.integration.test.template.ts +156 -0
  225. package/template/tsconfig.admin.json +18 -0
  226. package/template/tsconfig.json +23 -0
  227. package/template/worker-configuration.d.ts +15389 -0
  228. package/template/wrangler.jsonc +26 -0
@@ -0,0 +1,390 @@
1
+ .tiptap-button {
2
+ font-size: 0.875rem;
3
+ font-weight: 500;
4
+ font-feature-settings:
5
+ "salt" on,
6
+ "cv01" on;
7
+ line-height: 1.15;
8
+ height: 2rem;
9
+ min-width: 2rem;
10
+ border: none;
11
+ padding: 0.5rem;
12
+ gap: 0.25rem;
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ border-radius: var(--tt-radius-lg, 0.75rem);
17
+ transition-property: background, color, opacity;
18
+ transition-duration: var(--tt-transition-duration-default);
19
+ transition-timing-function: var(--tt-transition-easing-default);
20
+
21
+ // focus-visible
22
+ &:focus-visible {
23
+ outline: none;
24
+ }
25
+
26
+ &[data-highlighted="true"],
27
+ &[data-focus-visible="true"] {
28
+ background-color: var(--tt-button-hover-bg-color);
29
+ color: var(--tt-button-hover-text-color);
30
+ // outline: 2px solid var(--tt-button-active-icon-color);
31
+ }
32
+
33
+ &[data-weight="small"] {
34
+ width: 1.5rem;
35
+ min-width: 1.5rem;
36
+ padding-right: 0;
37
+ padding-left: 0;
38
+ }
39
+
40
+ /* button size large */
41
+ &[data-size="large"] {
42
+ font-size: 0.9375rem;
43
+ height: 2.375rem;
44
+ min-width: 2.375rem;
45
+ padding: 0.625rem;
46
+ }
47
+
48
+ /* button size small */
49
+ &[data-size="small"] {
50
+ font-size: 0.75rem;
51
+ line-height: 1.2;
52
+ height: 1.5rem;
53
+ min-width: 1.5rem;
54
+ padding: 0.3125rem;
55
+ border-radius: var(--tt-radius-md, 0.5rem);
56
+ }
57
+
58
+ /* trim / expand text of the button */
59
+ .tiptap-button-text {
60
+ padding: 0 0.125rem;
61
+ flex-grow: 1;
62
+ text-align: left;
63
+ line-height: 1.5rem;
64
+ }
65
+
66
+ &[data-text-trim="on"] {
67
+ .tiptap-button-text {
68
+ text-overflow: ellipsis;
69
+ overflow: hidden;
70
+ }
71
+ }
72
+
73
+ /* global icon settings */
74
+ .tiptap-button-icon,
75
+ .tiptap-button-icon-sub,
76
+ .tiptap-button-dropdown-arrows,
77
+ .tiptap-button-dropdown-small {
78
+ flex-shrink: 0;
79
+ }
80
+
81
+ /* standard icon, what is used */
82
+ .tiptap-button-icon {
83
+ width: 1rem;
84
+ height: 1rem;
85
+ }
86
+
87
+ &[data-size="large"] .tiptap-button-icon {
88
+ width: 1.125rem;
89
+ height: 1.125rem;
90
+ }
91
+
92
+ &[data-size="small"] .tiptap-button-icon {
93
+ width: 0.875rem;
94
+ height: 0.875rem;
95
+ }
96
+
97
+ /* if 2 icons are used and this icon should be more subtle */
98
+ .tiptap-button-icon-sub {
99
+ width: 1rem;
100
+ height: 1rem;
101
+ }
102
+
103
+ &[data-size="large"] .tiptap-button-icon-sub {
104
+ width: 1.125rem;
105
+ height: 1.125rem;
106
+ }
107
+
108
+ &[data-size="small"] .tiptap-button-icon-sub {
109
+ width: 0.875rem;
110
+ height: 0.875rem;
111
+ }
112
+
113
+ /* dropdown menus or arrows that are slightly smaller */
114
+ .tiptap-button-dropdown-arrows {
115
+ width: 0.75rem;
116
+ height: 0.75rem;
117
+ }
118
+
119
+ &[data-size="large"] .tiptap-button-dropdown-arrows {
120
+ width: 0.875rem;
121
+ height: 0.875rem;
122
+ }
123
+
124
+ &[data-size="small"] .tiptap-button-dropdown-arrows {
125
+ width: 0.625rem;
126
+ height: 0.625rem;
127
+ }
128
+
129
+ /* dropdown menu for icon buttons only */
130
+ .tiptap-button-dropdown-small {
131
+ width: 0.625rem;
132
+ height: 0.625rem;
133
+ }
134
+
135
+ &[data-size="large"] .tiptap-button-dropdown-small {
136
+ width: 0.75rem;
137
+ height: 0.75rem;
138
+ }
139
+
140
+ &[data-size="small"] .tiptap-button-dropdown-small {
141
+ width: 0.5rem;
142
+ height: 0.5rem;
143
+ }
144
+
145
+ /* button only has icons */
146
+ &:has(> svg):not(:has(> :not(svg))) {
147
+ gap: 0.125rem;
148
+
149
+ &[data-size="large"],
150
+ &[data-size="small"] {
151
+ gap: 0.125rem;
152
+ }
153
+ }
154
+
155
+ /* button only has 2 icons and one of them is dropdown small */
156
+ &:has(> svg:nth-of-type(2)):has(> .tiptap-button-dropdown-small):not(
157
+ :has(> svg:nth-of-type(3))
158
+ ):not(:has(> .tiptap-button-text)) {
159
+ gap: 0;
160
+ padding-right: 0.25rem;
161
+
162
+ &[data-size="large"] {
163
+ padding-right: 0.375rem;
164
+ }
165
+
166
+ &[data-size="small"] {
167
+ padding-right: 0.25rem;
168
+ }
169
+ }
170
+
171
+ /* Emoji is used in a button */
172
+ .tiptap-button-emoji {
173
+ width: 1rem;
174
+ display: flex;
175
+ justify-content: center;
176
+ }
177
+
178
+ &[data-size="large"] .tiptap-button-emoji {
179
+ width: 1.125rem;
180
+ }
181
+
182
+ &[data-size="small"] .tiptap-button-emoji {
183
+ width: 0.875rem;
184
+ }
185
+
186
+ /* Checkbox-style toggle button */
187
+ &[data-variant="check"] {
188
+ width: 100%;
189
+ justify-content: flex-start;
190
+ cursor: pointer;
191
+
192
+ .tiptap-button-text {
193
+ padding: 0;
194
+ }
195
+
196
+ .tiptap-button-check {
197
+ display: grid;
198
+ place-content: center;
199
+ flex-shrink: 0;
200
+ width: 1rem;
201
+ height: 1rem;
202
+ margin-left: auto;
203
+ border: 1px solid var(--tt-button-check-border-color);
204
+ border-radius: 0.25rem;
205
+ background-color: var(--tt-button-check-bg-color);
206
+ color: transparent;
207
+ transition-property: background-color, border-color, color;
208
+ transition-duration: var(--tt-transition-duration-default);
209
+ transition-timing-function: var(--tt-transition-easing-default);
210
+
211
+ > svg {
212
+ width: 0.875rem;
213
+ height: 0.875rem;
214
+ }
215
+ }
216
+
217
+ &:hover:not([disabled]):not([aria-checked="true"]):not(
218
+ [aria-checked="mixed"]
219
+ )
220
+ .tiptap-button-check {
221
+ border-color: var(--tt-button-check-hover-border-color);
222
+ background-color: var(--tt-button-check-hover-bg-color);
223
+ }
224
+
225
+ &[aria-checked="true"],
226
+ &[aria-checked="mixed"] {
227
+ color: var(--tt-button-check-checked-text-color);
228
+
229
+ .tiptap-button-check {
230
+ border-color: var(--tt-button-check-checked-border-color);
231
+ background-color: var(--tt-button-check-checked-bg-color);
232
+ color: var(--tt-button-check-checked-icon-color);
233
+ }
234
+ }
235
+
236
+ &[aria-checked="mixed"] {
237
+ .tiptap-button-check {
238
+ &::after {
239
+ width: 0.5rem;
240
+ height: 0.125rem;
241
+ border-radius: 999px;
242
+ background-color: currentColor;
243
+ content: "";
244
+ }
245
+
246
+ > svg {
247
+ display: none;
248
+ }
249
+ }
250
+ }
251
+
252
+ &:focus-visible {
253
+ box-shadow: 0 0 0 3px var(--tt-button-check-focus-ring-color);
254
+ }
255
+
256
+ &:disabled {
257
+ cursor: not-allowed;
258
+ opacity: 0.5;
259
+ }
260
+ }
261
+ }
262
+
263
+ /* --------------------------------------------
264
+ ----------- BUTTON COLOR SETTINGS -------------
265
+ -------------------------------------------- */
266
+
267
+ .tiptap-button {
268
+ background-color: var(--tt-button-default-bg-color);
269
+ color: var(--tt-button-default-text-color);
270
+
271
+ .tiptap-button-icon {
272
+ color: var(--tt-button-default-icon-color);
273
+ }
274
+
275
+ .tiptap-button-icon-sub {
276
+ color: var(--tt-button-default-icon-sub-color);
277
+ }
278
+
279
+ .tiptap-button-dropdown-arrows {
280
+ color: var(--tt-button-default-dropdown-arrows-color);
281
+ }
282
+
283
+ .tiptap-button-dropdown-small {
284
+ color: var(--tt-button-default-dropdown-arrows-color);
285
+ }
286
+
287
+ /* hover state of a button */
288
+ &:hover:not([data-active-item="true"]):not([disabled]),
289
+ &[data-active-item="true"]:not([disabled]),
290
+ &[data-highlighted]:not([disabled]):not([data-highlighted="false"]) {
291
+ background-color: var(--tt-button-hover-bg-color);
292
+ color: var(--tt-button-hover-text-color);
293
+
294
+ .tiptap-button-icon {
295
+ color: var(--tt-button-hover-icon-color);
296
+ }
297
+
298
+ .tiptap-button-icon-sub {
299
+ color: var(--tt-button-hover-icon-sub-color);
300
+ }
301
+
302
+ .tiptap-button-dropdown-arrows,
303
+ .tiptap-button-dropdown-small {
304
+ color: var(--tt-button-hover-dropdown-arrows-color);
305
+ }
306
+ }
307
+
308
+ /* Active state of a button */
309
+ &[data-active-state="on"]:not([disabled]),
310
+ &[data-state="open"]:not([disabled]) {
311
+ background-color: var(--tt-button-active-bg-color);
312
+ color: var(--tt-button-active-text-color);
313
+
314
+ .tiptap-button-icon {
315
+ color: var(--tt-button-active-icon-color);
316
+ }
317
+
318
+ .tiptap-button-icon-sub {
319
+ color: var(--tt-button-active-icon-sub-color);
320
+ }
321
+
322
+ .tiptap-button-dropdown-arrows,
323
+ .tiptap-button-dropdown-small {
324
+ color: var(--tt-button-active-dropdown-arrows-color);
325
+ }
326
+
327
+ &:hover {
328
+ background-color: var(--tt-button-active-hover-bg-color);
329
+ }
330
+
331
+ /* Emphasized */
332
+ &[data-appearance="emphasized"] {
333
+ background-color: var(--tt-button-active-bg-color-emphasized);
334
+ color: var(--tt-button-active-text-color-emphasized);
335
+
336
+ .tiptap-button-icon {
337
+ color: var(--tt-button-active-icon-color-emphasized);
338
+ }
339
+
340
+ .tiptap-button-icon-sub {
341
+ color: var(--tt-button-active-icon-sub-color-emphasized);
342
+ }
343
+
344
+ .tiptap-button-dropdown-arrows,
345
+ .tiptap-button-dropdown-small {
346
+ color: var(--tt-button-active-dropdown-arrows-color-emphasized);
347
+ }
348
+
349
+ &:hover {
350
+ background-color: var(--tt-button-active-hover-bg-color-emphasized);
351
+ }
352
+ }
353
+
354
+ /* Subdued */
355
+ &[data-appearance="subdued"] {
356
+ background-color: var(--tt-button-active-bg-color-subdued);
357
+ color: var(--tt-button-active-text-color-subdued);
358
+
359
+ .tiptap-button-icon {
360
+ color: var(--tt-button-active-icon-color-subdued);
361
+ }
362
+
363
+ .tiptap-button-icon-sub {
364
+ color: var(--tt-button-active-icon-sub-color-subdued);
365
+ }
366
+
367
+ .tiptap-button-dropdown-arrows,
368
+ .tiptap-button-dropdown-small {
369
+ color: var(--tt-button-active-dropdown-arrows-color-subdued);
370
+ }
371
+
372
+ &:hover {
373
+ background-color: var(--tt-button-active-hover-bg-color-subdued);
374
+
375
+ .tiptap-button-icon {
376
+ color: var(--tt-button-active-icon-color-subdued);
377
+ }
378
+ }
379
+ }
380
+ }
381
+
382
+ &:disabled {
383
+ background-color: var(--tt-button-disabled-bg-color);
384
+ color: var(--tt-button-disabled-text-color);
385
+
386
+ .tiptap-button-icon {
387
+ color: var(--tt-button-disabled-icon-color);
388
+ }
389
+ }
390
+ }
@@ -0,0 +1,158 @@
1
+ import { forwardRef, Fragment, useMemo } from "react"
2
+
3
+ // --- Tiptap UI Primitive ---
4
+ import {
5
+ Tooltip,
6
+ TooltipContent,
7
+ TooltipTrigger,
8
+ } from "@/components/tiptap-ui-primitive/tooltip"
9
+
10
+ // --- Icons ---
11
+ import { CheckIcon } from "@/components/tiptap-icons/check-icon"
12
+
13
+ // --- Lib ---
14
+ import { cn, parseShortcutKeys } from "@/lib/tiptap-utils"
15
+
16
+ import "@/components/tiptap-ui-primitive/button/button-colors.scss"
17
+ import "@/components/tiptap-ui-primitive/button/button.scss"
18
+
19
+ export type ButtonStyle =
20
+ "ghost" | "primary" | "secondary" | "tertiary" | "subtle"
21
+ export type ButtonVariant = ButtonStyle | "check"
22
+ export type ButtonSize = "small" | "default" | "large"
23
+
24
+ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
25
+ showTooltip?: boolean
26
+ tooltip?: React.ReactNode
27
+ shortcutKeys?: string
28
+ /**
29
+ * Visual treatment or feature variant.
30
+ *
31
+ * Style values map to `data-style` and pull their colors from
32
+ * `button-colors.scss`: `subtle` is the neutral default (a faint gray
33
+ * fill), `ghost` is transparent, `primary` is the brand fill, `secondary`
34
+ * is a solid neutral (dark fill in light mode, light fill in dark mode),
35
+ * and `tertiary` currently mirrors `ghost`.
36
+ *
37
+ * The `check` variant is a checkbox-style toggle button. It defaults to
38
+ * the ghost style and small size, supplies `role="checkbox"`, and renders
39
+ * its own check indicator. Set `aria-checked` to control its state.
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * <Button
44
+ * variant="check"
45
+ * aria-checked={checked}
46
+ * onClick={() => setChecked((value) => !value)}
47
+ * >
48
+ * <FilterIcon className="tiptap-button-icon" />
49
+ * <span className="tiptap-button-text">Only active items</span>
50
+ * </Button>
51
+ * ```
52
+ */
53
+ variant?: ButtonVariant
54
+ size?: ButtonSize
55
+ }
56
+
57
+ export const ShortcutDisplay: React.FC<{ shortcuts: string[] }> = ({
58
+ shortcuts,
59
+ }) => {
60
+ if (shortcuts.length === 0) return null
61
+
62
+ return (
63
+ <div>
64
+ {shortcuts.map((key, index) => (
65
+ <Fragment key={index}>
66
+ {index > 0 && <kbd>+</kbd>}
67
+ <kbd>{key}</kbd>
68
+ </Fragment>
69
+ ))}
70
+ </div>
71
+ )
72
+ }
73
+
74
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
75
+ (
76
+ {
77
+ className,
78
+ children,
79
+ tooltip,
80
+ showTooltip = true,
81
+ shortcutKeys,
82
+ variant,
83
+ size,
84
+ role,
85
+ "aria-checked": ariaChecked,
86
+ ...props
87
+ },
88
+ ref
89
+ ) => {
90
+ const isCheckVariant = variant === "check"
91
+ const buttonStyle: ButtonStyle | undefined = isCheckVariant
92
+ ? "ghost"
93
+ : variant
94
+ const buttonSize = isCheckVariant ? (size ?? "small") : size
95
+ const buttonRole = isCheckVariant ? (role ?? "checkbox") : role
96
+ const buttonAriaChecked = isCheckVariant
97
+ ? (ariaChecked ?? false)
98
+ : ariaChecked
99
+ const shortcuts = useMemo<string[]>(
100
+ () => parseShortcutKeys({ shortcutKeys }),
101
+ [shortcutKeys]
102
+ )
103
+ const content = (
104
+ <>
105
+ {children}
106
+ {isCheckVariant && (
107
+ <span className="tiptap-button-check" aria-hidden="true">
108
+ <CheckIcon />
109
+ </span>
110
+ )}
111
+ </>
112
+ )
113
+
114
+ if (!tooltip || !showTooltip) {
115
+ return (
116
+ <button
117
+ data-slot="tiptap-button"
118
+ className={cn("tiptap-button", className)}
119
+ ref={ref}
120
+ data-style={buttonStyle}
121
+ data-size={buttonSize}
122
+ data-variant={isCheckVariant ? "check" : undefined}
123
+ role={buttonRole}
124
+ aria-checked={buttonAriaChecked}
125
+ {...props}
126
+ >
127
+ {content}
128
+ </button>
129
+ )
130
+ }
131
+
132
+ return (
133
+ <Tooltip delay={200}>
134
+ <TooltipTrigger
135
+ data-slot="tiptap-button"
136
+ className={cn("tiptap-button", className)}
137
+ ref={ref}
138
+ data-style={buttonStyle}
139
+ data-size={buttonSize}
140
+ data-variant={isCheckVariant ? "check" : undefined}
141
+ role={buttonRole}
142
+ aria-checked={buttonAriaChecked}
143
+ {...props}
144
+ >
145
+ {content}
146
+ </TooltipTrigger>
147
+ <TooltipContent>
148
+ {tooltip}
149
+ <ShortcutDisplay shortcuts={shortcuts} />
150
+ </TooltipContent>
151
+ </Tooltip>
152
+ )
153
+ }
154
+ )
155
+
156
+ Button.displayName = "Button"
157
+
158
+ export default Button
@@ -0,0 +1 @@
1
+ export * from "./button"
@@ -0,0 +1,89 @@
1
+ .tiptap-button-group {
2
+ display: flex;
3
+ width: fit-content;
4
+ align-items: stretch;
5
+
6
+ /* nested tiptap-button-group gets a gap */
7
+ &:has(> [data-slot="tiptap-button-group"]) {
8
+ gap: 0.125rem;
9
+ }
10
+
11
+ /* focus-visible children stack correctly */
12
+ & > *:focus-visible {
13
+ position: relative;
14
+ z-index: 10;
15
+ }
16
+
17
+ /* select trigger without explicit width becomes fit-content */
18
+ & > [data-slot="select-trigger"]:not([class*="w-"]) {
19
+ width: fit-content;
20
+ }
21
+
22
+ /* input children fill remaining space */
23
+ & > input {
24
+ flex: 1;
25
+ }
26
+ }
27
+
28
+ /* Corners are only squared where two children meet, so first/last children
29
+ keep their own size-appropriate radius (e.g. small buttons use
30
+ --tt-radius-md, default buttons --tt-radius-lg, input-group buttons a
31
+ pill) instead of a hardcoded group radius. */
32
+
33
+ /* ─── Horizontal ────────────────────────────────────────── */
34
+ .tiptap-button-group-horizontal {
35
+ /* [data-slot] children followed by another: remove right radius */
36
+ & > [data-slot]:has(~ [data-slot]) {
37
+ border-top-right-radius: 0;
38
+ border-bottom-right-radius: 0;
39
+ }
40
+
41
+ /* non-first [data-slot] children: remove left radius + left border */
42
+ & > [data-slot] ~ [data-slot] {
43
+ border-top-left-radius: 0;
44
+ border-bottom-left-radius: 0;
45
+ border-left-width: 0;
46
+ }
47
+ }
48
+
49
+ /* ─── Vertical ──────────────────────────────────────────── */
50
+ .tiptap-button-group-vertical {
51
+ flex-direction: column;
52
+
53
+ /* [data-slot] children followed by another: remove bottom radius */
54
+ & > [data-slot]:has(~ [data-slot]) {
55
+ border-bottom-left-radius: 0;
56
+ border-bottom-right-radius: 0;
57
+ }
58
+
59
+ /* non-first [data-slot] children: remove top radius + top border */
60
+ & > [data-slot] ~ [data-slot] {
61
+ border-top-left-radius: 0;
62
+ border-top-right-radius: 0;
63
+ border-top-width: 0;
64
+ }
65
+ }
66
+
67
+ /* ─── Separator ──────────────────────────────────────────── */
68
+ .tiptap-button-group-separator {
69
+ position: relative;
70
+ align-self: stretch;
71
+ }
72
+
73
+ .tiptap-button-group-separator[data-orientation="horizontal"] {
74
+ margin-left: 1px;
75
+ margin-right: 1px;
76
+ }
77
+
78
+ .tiptap-button-group-separator[data-orientation="horizontal"] {
79
+ width: auto;
80
+ }
81
+
82
+ .tiptap-button-group-separator[data-orientation="vertical"] {
83
+ margin-top: 1px;
84
+ margin-bottom: 1px;
85
+ }
86
+
87
+ .tiptap-button-group-separator[data-orientation="vertical"] {
88
+ height: auto;
89
+ }
@@ -0,0 +1,72 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props"
2
+ import { useRender } from "@base-ui/react/use-render"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+ import { cn } from "@/lib/tiptap-utils"
5
+ import { Separator } from "@/components/tiptap-ui-primitive/separator"
6
+ import "./button-group.scss"
7
+
8
+ const buttonGroupVariants = cva("tiptap-button-group", {
9
+ variants: {
10
+ orientation: {
11
+ horizontal: "tiptap-button-group-horizontal",
12
+ vertical: "tiptap-button-group-vertical",
13
+ },
14
+ },
15
+ defaultVariants: {
16
+ orientation: "horizontal",
17
+ },
18
+ })
19
+
20
+ function ButtonGroup({
21
+ className,
22
+ orientation,
23
+ ...props
24
+ }: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
25
+ return (
26
+ <div
27
+ role="group"
28
+ data-slot="tiptap-button-group"
29
+ data-orientation={orientation}
30
+ className={cn(buttonGroupVariants({ orientation }), className)}
31
+ {...props}
32
+ />
33
+ )
34
+ }
35
+
36
+ function ButtonGroupText({
37
+ className,
38
+ render,
39
+ ...props
40
+ }: useRender.ComponentProps<"div">) {
41
+ return useRender({
42
+ defaultTagName: "div",
43
+ props: mergeProps<"div">(
44
+ { className: cn("tiptap-button-group-text", className) },
45
+ props
46
+ ),
47
+ render,
48
+ state: { slot: "tiptap-button-group-text" },
49
+ })
50
+ }
51
+
52
+ function ButtonGroupSeparator({
53
+ className,
54
+ orientation = "vertical",
55
+ ...props
56
+ }: React.ComponentProps<typeof Separator>) {
57
+ return (
58
+ <Separator
59
+ data-slot="tiptap-button-group-separator"
60
+ orientation={orientation}
61
+ className={cn("tiptap-button-group-separator", className)}
62
+ {...props}
63
+ />
64
+ )
65
+ }
66
+
67
+ export {
68
+ ButtonGroup,
69
+ ButtonGroupSeparator,
70
+ ButtonGroupText,
71
+ buttonGroupVariants,
72
+ }
@@ -0,0 +1 @@
1
+ export * from "./button-group"