includio-cms 0.1.3 → 0.5.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 (313) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/ROADMAP.md +23 -13
  3. package/dist/admin/api/accept-invite.js +1 -5
  4. package/dist/admin/api/invite.js +7 -16
  5. package/dist/admin/client/account/account-page.svelte +20 -50
  6. package/dist/admin/client/account/lang.d.ts +15 -23
  7. package/dist/admin/client/account/lang.js +51 -67
  8. package/dist/admin/client/account/preferences-section.svelte +26 -84
  9. package/dist/admin/client/account/profile-section.svelte +60 -40
  10. package/dist/admin/client/account/schema.d.ts +11 -3
  11. package/dist/admin/client/account/schema.js +25 -16
  12. package/dist/admin/client/account/security-section.svelte +139 -105
  13. package/dist/admin/client/account/sessions-section.svelte +35 -34
  14. package/dist/admin/client/admin/admin-after-login-layout-content.svelte +3 -5
  15. package/dist/admin/client/admin/admin-layout.svelte +3 -2
  16. package/dist/admin/client/admin/admin-preloader.svelte +36 -0
  17. package/dist/admin/client/admin/admin-preloader.svelte.d.ts +18 -0
  18. package/dist/admin/client/admin/dashboard-page.svelte +55 -41
  19. package/dist/admin/client/collection/a11y-score-cell.svelte +45 -0
  20. package/dist/admin/client/collection/a11y-score-cell.svelte.d.ts +6 -0
  21. package/dist/admin/client/collection/bulk-actions-bar.svelte +83 -0
  22. package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +9 -0
  23. package/dist/admin/client/collection/collection-entries.svelte +255 -256
  24. package/dist/admin/client/collection/collection-view.svelte.d.ts +4 -3
  25. package/dist/admin/client/collection/collection-view.svelte.js +9 -5
  26. package/dist/admin/client/collection/collection.svelte +22 -12
  27. package/dist/admin/client/collection/data-table.svelte +50 -39
  28. package/dist/admin/client/collection/data-table.svelte.d.ts +1 -0
  29. package/dist/admin/client/collection/date-cell.svelte +7 -5
  30. package/dist/admin/client/collection/date-cell.svelte.d.ts +1 -1
  31. package/dist/admin/client/collection/empty-state.svelte +28 -0
  32. package/dist/admin/client/collection/empty-state.svelte.d.ts +9 -0
  33. package/dist/admin/client/collection/entry-link.svelte +10 -4
  34. package/dist/admin/client/collection/entry-link.svelte.d.ts +1 -0
  35. package/dist/admin/client/collection/grid-view.svelte +21 -23
  36. package/dist/admin/client/collection/grid-view.svelte.d.ts +1 -2
  37. package/dist/admin/client/collection/row-actions.svelte +60 -0
  38. package/dist/admin/client/collection/row-actions.svelte.d.ts +9 -0
  39. package/dist/admin/client/collection/status-badge.svelte +7 -8
  40. package/dist/admin/client/collection/table-pagination.svelte +122 -79
  41. package/dist/admin/client/collection/table-pagination.svelte.d.ts +1 -0
  42. package/dist/admin/client/collection/table-toolbar.svelte +108 -88
  43. package/dist/admin/client/collection/table-toolbar.svelte.d.ts +8 -9
  44. package/dist/admin/client/entry/entry-form.svelte +109 -1
  45. package/dist/admin/client/entry/entry-header.svelte +96 -37
  46. package/dist/admin/client/entry/entry-header.svelte.d.ts +5 -0
  47. package/dist/admin/client/entry/entry.svelte +171 -60
  48. package/dist/admin/client/entry/header/a11y-validator.d.ts +46 -0
  49. package/dist/admin/client/entry/header/a11y-validator.js +311 -0
  50. package/dist/admin/client/entry/header/publish-panel.svelte +373 -131
  51. package/dist/admin/client/entry/header/publish-panel.svelte.d.ts +4 -0
  52. package/dist/admin/client/entry/header/save-indicator.svelte +33 -23
  53. package/dist/admin/client/entry/header/schedule-popover.svelte +1 -1
  54. package/dist/admin/client/entry/header/status-badge.svelte +25 -118
  55. package/dist/admin/client/entry/header/version-history-sheet.svelte +314 -98
  56. package/dist/admin/client/form/form-submission/form-submission.svelte +271 -83
  57. package/dist/admin/client/form/form-submission/submission-field.svelte +12 -12
  58. package/dist/admin/client/form/form-submissions.svelte +421 -139
  59. package/dist/admin/client/form/submission-link.svelte +8 -2
  60. package/dist/admin/client/form/submission-link.svelte.d.ts +1 -0
  61. package/dist/admin/client/form/submission-status-badge.svelte +18 -4
  62. package/dist/admin/client/form/submission-status-badge.svelte.d.ts +1 -0
  63. package/dist/admin/client/login/lang.d.ts +32 -0
  64. package/dist/admin/client/login/lang.js +66 -2
  65. package/dist/admin/client/login/login-form.svelte +237 -95
  66. package/dist/admin/client/login/login-form.svelte.d.ts +2 -17
  67. package/dist/admin/client/login/login-page.svelte +34 -98
  68. package/dist/admin/client/login/reset-password-page.svelte +235 -0
  69. package/dist/admin/client/login/reset-password-page.svelte.d.ts +4 -0
  70. package/dist/admin/client/login/schema.d.ts +15 -0
  71. package/dist/admin/client/login/schema.js +21 -0
  72. package/dist/admin/client/users/accept-invite-page.svelte +166 -37
  73. package/dist/admin/client/users/create-user-dialog.svelte +15 -7
  74. package/dist/admin/client/users/delete-user-dialog.svelte +81 -16
  75. package/dist/admin/client/users/delete-user-dialog.svelte.d.ts +4 -1
  76. package/dist/admin/client/users/edit-user-dialog.svelte +3 -0
  77. package/dist/admin/client/users/invite-user-dialog.svelte +16 -3
  78. package/dist/admin/client/users/lang.d.ts +27 -0
  79. package/dist/admin/client/users/lang.js +64 -10
  80. package/dist/admin/client/users/pending-invitations.svelte +59 -23
  81. package/dist/admin/client/users/users-page.svelte +471 -72
  82. package/dist/admin/components/accessibility/accessibility-overview.svelte +2 -7
  83. package/dist/admin/components/dashboard/a11y-gauge.svelte +90 -0
  84. package/dist/admin/components/dashboard/a11y-gauge.svelte.d.ts +18 -0
  85. package/dist/admin/components/dashboard/accessibility-hub.svelte +13 -12
  86. package/dist/admin/components/dashboard/form-submissions-widget.svelte +71 -113
  87. package/dist/admin/components/dashboard/index.d.ts +4 -2
  88. package/dist/admin/components/dashboard/index.js +4 -2
  89. package/dist/admin/components/dashboard/recent-activity.svelte +53 -75
  90. package/dist/admin/components/dashboard/recent-entries.svelte +94 -0
  91. package/dist/admin/components/dashboard/recent-entries.svelte.d.ts +18 -0
  92. package/dist/admin/components/dashboard/stat-card.svelte +2 -2
  93. package/dist/admin/components/dashboard/tip-of-the-day.svelte +109 -0
  94. package/dist/admin/components/dashboard/tip-of-the-day.svelte.d.ts +3 -0
  95. package/dist/admin/components/dashboard/welcome-header.svelte +45 -0
  96. package/dist/admin/components/dashboard/welcome-header.svelte.d.ts +3 -0
  97. package/dist/admin/components/fields/{array-field.svelte → blocks-field.svelte} +4 -4
  98. package/dist/admin/components/fields/{array-field.svelte.d.ts → blocks-field.svelte.d.ts} +5 -5
  99. package/dist/admin/components/fields/content-field.svelte +27 -0
  100. package/dist/admin/components/fields/content-field.svelte.d.ts +31 -0
  101. package/dist/admin/components/fields/field-renderer.svelte +9 -7
  102. package/dist/admin/components/fields/image-field.svelte +2 -2
  103. package/dist/admin/components/fields/media-field.svelte +2 -2
  104. package/dist/admin/components/fields/seo-field.svelte +205 -25
  105. package/dist/admin/components/fields/simple-array-field.svelte +289 -0
  106. package/dist/admin/components/fields/simple-array-field.svelte.d.ts +30 -0
  107. package/dist/admin/components/fields/slug-field.svelte +3 -2
  108. package/dist/admin/components/fields/standalone-field-renderer.svelte +148 -0
  109. package/dist/admin/components/fields/standalone-field-renderer.svelte.d.ts +9 -0
  110. package/dist/admin/components/fields/text-field-wrapper.svelte +13 -1
  111. package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
  112. package/dist/admin/components/fields/url-field.svelte +5 -4
  113. package/dist/admin/components/layout/app-sidebar.svelte +27 -24
  114. package/dist/admin/components/layout/lang.d.ts +6 -0
  115. package/dist/admin/components/layout/lang.js +13 -1
  116. package/dist/admin/components/layout/layout-renderer.svelte +352 -0
  117. package/dist/admin/components/layout/layout-renderer.svelte.d.ts +14 -0
  118. package/dist/admin/components/layout/nav-breadcrumbs.svelte +4 -4
  119. package/dist/admin/components/layout/nav-collections.svelte +65 -36
  120. package/dist/admin/components/layout/nav-footer.svelte +31 -0
  121. package/dist/admin/components/layout/nav-footer.svelte.d.ts +18 -0
  122. package/dist/admin/components/layout/nav-forms.svelte +55 -30
  123. package/dist/admin/components/layout/nav-main.svelte +14 -52
  124. package/dist/admin/components/layout/nav-search.svelte +4 -3
  125. package/dist/admin/components/layout/nav-singletons.svelte +59 -17
  126. package/dist/admin/components/layout/nav-singletons.svelte.d.ts +17 -8
  127. package/dist/admin/components/layout/site-header.svelte +74 -13
  128. package/dist/admin/components/media/alt-input.svelte +32 -22
  129. package/dist/admin/components/media/bulk-action-bar.svelte +139 -150
  130. package/dist/admin/components/media/file/file-details.svelte +299 -217
  131. package/dist/admin/components/media/file/file-miniature.svelte +54 -41
  132. package/dist/admin/components/media/file/file-miniature.svelte.d.ts +1 -0
  133. package/dist/admin/components/media/file/file-preview.svelte +1 -1
  134. package/dist/admin/components/media/file-upload.svelte +24 -26
  135. package/dist/admin/components/media/files-list.svelte +112 -40
  136. package/dist/admin/components/media/files-list.svelte.d.ts +2 -0
  137. package/dist/admin/components/media/focal-point-input.svelte +122 -26
  138. package/dist/admin/components/media/media-library.svelte +127 -70
  139. package/dist/admin/components/media/media-search.svelte +6 -6
  140. package/dist/admin/components/media/media-sort.svelte +3 -1
  141. package/dist/admin/components/media/multi-file-summary.svelte +88 -68
  142. package/dist/admin/components/media/tag-combobox.svelte +141 -66
  143. package/dist/admin/components/media/tag-combobox.svelte.d.ts +1 -0
  144. package/dist/admin/components/media/tag-sidebar.svelte +139 -121
  145. package/dist/admin/components/tiptap/FigureNodeView.svelte +144 -15
  146. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +254 -0
  147. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte.d.ts +4 -0
  148. package/dist/admin/components/tiptap/SlashCommandPopup.svelte +212 -0
  149. package/dist/admin/components/tiptap/SlashCommandPopup.svelte.d.ts +8 -0
  150. package/dist/admin/components/tiptap/content-editor.svelte +280 -0
  151. package/dist/admin/components/tiptap/content-editor.svelte.d.ts +9 -0
  152. package/dist/admin/components/tiptap/editor-toolbar.svelte +230 -0
  153. package/dist/admin/components/tiptap/editor-toolbar.svelte.d.ts +16 -0
  154. package/dist/admin/components/tiptap/heading-a11y-plugin.d.ts +2 -0
  155. package/dist/admin/components/tiptap/heading-a11y-plugin.js +67 -0
  156. package/dist/admin/components/tiptap/image-dialog.svelte +172 -11
  157. package/dist/admin/components/tiptap/inline-block-node.d.ts +19 -0
  158. package/dist/admin/components/tiptap/inline-block-node.js +98 -0
  159. package/dist/admin/components/tiptap/link-dialog.svelte +9 -4
  160. package/dist/admin/components/tiptap/slash-command.d.ts +17 -0
  161. package/dist/admin/components/tiptap/slash-command.js +181 -0
  162. package/dist/admin/components/tiptap/structured-content-utils.d.ts +21 -0
  163. package/dist/admin/components/tiptap/structured-content-utils.js +150 -0
  164. package/dist/admin/components/tiptap/tiptap-editor.svelte +18 -190
  165. package/dist/admin/email/invite-template.d.ts +8 -0
  166. package/dist/admin/email/invite-template.js +99 -0
  167. package/dist/admin/email/reset-password-template.d.ts +7 -0
  168. package/dist/admin/email/reset-password-template.js +96 -0
  169. package/dist/admin/remote/ai.remote.d.ts +1 -0
  170. package/dist/admin/remote/ai.remote.js +4 -1
  171. package/dist/admin/remote/entry.remote.d.ts +8 -0
  172. package/dist/admin/remote/entry.remote.js +53 -4
  173. package/dist/admin/remote/preview.remote.js +2 -1
  174. package/dist/admin/shared/password-schema.d.ts +5 -0
  175. package/dist/admin/shared/password-schema.js +10 -0
  176. package/dist/admin/styles/admin.css +1530 -151
  177. package/dist/admin/utils/formatDate.d.ts +1 -0
  178. package/dist/admin/utils/formatDate.js +8 -0
  179. package/dist/admin/utils/roleLabel.d.ts +2 -0
  180. package/dist/admin/utils/roleLabel.js +13 -0
  181. package/dist/ai-claude/index.d.ts +2 -0
  182. package/dist/ai-claude/index.js +56 -0
  183. package/dist/cms/runtime/api.d.ts +6 -1
  184. package/dist/cms/runtime/api.js +3 -0
  185. package/dist/cms/runtime/schemas.d.ts +9 -1
  186. package/dist/cms/runtime/schemas.js +8 -0
  187. package/dist/cms/runtime/types.d.ts +82 -10
  188. package/dist/cms/runtime/types.js +4 -0
  189. package/dist/components/ui/accordion/accordion.stories.svelte +39 -0
  190. package/dist/components/ui/accordion/accordion.stories.svelte.d.ts +27 -0
  191. package/dist/components/ui/alert/alert.stories.svelte +53 -0
  192. package/dist/components/ui/alert/alert.stories.svelte.d.ts +27 -0
  193. package/dist/components/ui/alert/alert.svelte +5 -0
  194. package/dist/components/ui/alert/alert.svelte.d.ts +9 -0
  195. package/dist/components/ui/avatar/avatar.stories.svelte +16 -0
  196. package/dist/components/ui/avatar/avatar.stories.svelte.d.ts +27 -0
  197. package/dist/components/ui/badge/badge.stories.svelte +33 -0
  198. package/dist/components/ui/badge/badge.stories.svelte.d.ts +27 -0
  199. package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte +33 -0
  200. package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte.d.ts +27 -0
  201. package/dist/components/ui/button/button.stories.svelte +43 -0
  202. package/dist/components/ui/button/button.stories.svelte.d.ts +27 -0
  203. package/dist/components/ui/button/button.svelte +1 -2
  204. package/dist/components/ui/button/button.svelte.d.ts +0 -3
  205. package/dist/components/ui/button-group/button-group-separator.svelte.d.ts +1 -1
  206. package/dist/components/ui/card/card.stories.svelte +42 -0
  207. package/dist/components/ui/card/card.stories.svelte.d.ts +27 -0
  208. package/dist/components/ui/command/command.stories.svelte +51 -0
  209. package/dist/components/ui/command/command.stories.svelte.d.ts +27 -0
  210. package/dist/components/ui/dialog/dialog.stories.svelte +29 -0
  211. package/dist/components/ui/dialog/dialog.stories.svelte.d.ts +27 -0
  212. package/dist/components/ui/field/field-label.svelte.d.ts +1 -1
  213. package/dist/components/ui/field/field.stories.svelte +21 -0
  214. package/dist/components/ui/field/field.stories.svelte.d.ts +27 -0
  215. package/dist/components/ui/input/input.stories.svelte +40 -0
  216. package/dist/components/ui/input/input.stories.svelte.d.ts +27 -0
  217. package/dist/components/ui/input/input.svelte +2 -4
  218. package/dist/components/ui/item/item-separator.svelte.d.ts +1 -1
  219. package/dist/components/ui/label/label.stories.svelte +20 -0
  220. package/dist/components/ui/label/label.stories.svelte.d.ts +27 -0
  221. package/dist/components/ui/popover/popover.stories.svelte +29 -0
  222. package/dist/components/ui/popover/popover.stories.svelte.d.ts +27 -0
  223. package/dist/components/ui/select/select-group-heading.svelte.d.ts +1 -1
  224. package/dist/components/ui/select/select.stories.svelte +23 -0
  225. package/dist/components/ui/select/select.stories.svelte.d.ts +27 -0
  226. package/dist/components/ui/separator/separator.stories.svelte +24 -0
  227. package/dist/components/ui/separator/separator.stories.svelte.d.ts +27 -0
  228. package/dist/components/ui/sheet/sheet.stories.svelte +29 -0
  229. package/dist/components/ui/sheet/sheet.stories.svelte.d.ts +27 -0
  230. package/dist/components/ui/sidebar/sidebar-group.svelte +3 -3
  231. package/dist/components/ui/sidebar/sidebar-group.svelte.d.ts +2 -2
  232. package/dist/components/ui/sidebar/sidebar-menu-button.svelte +28 -30
  233. package/dist/components/ui/sidebar/sidebar-menu-button.svelte.d.ts +7 -7
  234. package/dist/components/ui/sidebar/sidebar-separator.svelte.d.ts +1 -1
  235. package/dist/components/ui/sidebar/sidebar-trigger.svelte +4 -4
  236. package/dist/components/ui/sonner/sonner.stories.svelte +22 -0
  237. package/dist/components/ui/sonner/sonner.stories.svelte.d.ts +26 -0
  238. package/dist/components/ui/sonner/sonner.svelte +8 -2
  239. package/dist/components/ui/sonner/toast-demo.svelte +29 -0
  240. package/dist/components/ui/sonner/toast-demo.svelte.d.ts +6 -0
  241. package/dist/components/ui/textarea/textarea.stories.svelte +22 -0
  242. package/dist/components/ui/textarea/textarea.stories.svelte.d.ts +27 -0
  243. package/dist/components/ui/textarea/textarea.svelte +0 -2
  244. package/dist/components/ui/toggle/toggle.stories.svelte +22 -0
  245. package/dist/components/ui/toggle/toggle.stories.svelte.d.ts +27 -0
  246. package/dist/components/ui/toggle-group/toggle-group.stories.svelte +17 -0
  247. package/dist/components/ui/toggle-group/toggle-group.stories.svelte.d.ts +27 -0
  248. package/dist/components/ui/tooltip/tooltip.stories.svelte +26 -0
  249. package/dist/components/ui/tooltip/tooltip.stories.svelte.d.ts +27 -0
  250. package/dist/core/fields/fieldSchemaToTs.d.ts +1 -0
  251. package/dist/core/fields/fieldSchemaToTs.js +133 -1
  252. package/dist/core/fields/layoutUtils.d.ts +17 -0
  253. package/dist/core/fields/layoutUtils.js +149 -0
  254. package/dist/core/fields/structuredToHtml.d.ts +9 -0
  255. package/dist/core/fields/structuredToHtml.js +161 -0
  256. package/dist/core/server/entries/operations/create.js +2 -1
  257. package/dist/core/server/entries/operations/get.js +8 -6
  258. package/dist/core/server/entries/operations/update.d.ts +3 -0
  259. package/dist/core/server/entries/operations/update.js +30 -2
  260. package/dist/core/server/fields/queryStructuredContent.d.ts +15 -0
  261. package/dist/core/server/fields/queryStructuredContent.js +65 -0
  262. package/dist/core/server/fields/resolveImageFields.js +51 -2
  263. package/dist/core/server/fields/resolveRelationFields.js +2 -2
  264. package/dist/core/server/fields/resolveRichtextLinks.js +80 -13
  265. package/dist/core/server/fields/resolveUrlFields.js +57 -6
  266. package/dist/core/server/fields/slugResolver.d.ts +10 -0
  267. package/dist/core/server/fields/slugResolver.js +34 -0
  268. package/dist/core/server/generator/fields.js +15 -4
  269. package/dist/core/server/generator/generator.js +3 -2
  270. package/dist/files-local/index.js +126 -64
  271. package/dist/paraglide/.prettierignore +3 -0
  272. package/dist/paraglide/messages/_index.d.ts +36 -0
  273. package/dist/paraglide/messages/_index.js +72 -0
  274. package/dist/paraglide/messages/en.d.ts +5 -0
  275. package/dist/paraglide/messages/en.js +14 -0
  276. package/dist/paraglide/messages/pl.d.ts +5 -0
  277. package/dist/paraglide/messages/pl.js +14 -0
  278. package/dist/paraglide/messages.d.ts +2 -0
  279. package/dist/paraglide/messages.js +4 -0
  280. package/dist/paraglide/registry.d.ts +21 -0
  281. package/dist/paraglide/registry.js +31 -0
  282. package/dist/paraglide/runtime.d.ts +583 -0
  283. package/dist/paraglide/runtime.js +1402 -0
  284. package/dist/paraglide/server.d.ts +67 -0
  285. package/dist/paraglide/server.js +175 -0
  286. package/dist/server/auth.d.ts +5 -0
  287. package/dist/server/auth.js +12 -1
  288. package/dist/sveltekit/components/structured-content.svelte +204 -0
  289. package/dist/sveltekit/components/structured-content.svelte.d.ts +21 -0
  290. package/dist/sveltekit/config.d.ts +13 -3
  291. package/dist/sveltekit/index.d.ts +3 -0
  292. package/dist/sveltekit/index.js +3 -0
  293. package/dist/sveltekit/server/handle.js +1 -0
  294. package/dist/types/config.d.ts +3 -0
  295. package/dist/types/fields.d.ts +19 -2
  296. package/dist/types/index.d.ts +2 -0
  297. package/dist/types/index.js +2 -0
  298. package/dist/types/layout.d.ts +54 -0
  299. package/dist/types/layout.js +6 -0
  300. package/dist/types/structured-content.d.ts +63 -0
  301. package/dist/types/structured-content.js +1 -0
  302. package/dist/updates/0.1.4/index.d.ts +2 -0
  303. package/dist/updates/0.1.4/index.js +11 -0
  304. package/dist/updates/0.1.5/index.d.ts +2 -0
  305. package/dist/updates/0.1.5/index.js +18 -0
  306. package/dist/updates/0.2.0/index.d.ts +2 -0
  307. package/dist/updates/0.2.0/index.js +11 -0
  308. package/dist/updates/0.2.2/index.d.ts +2 -0
  309. package/dist/updates/0.2.2/index.js +13 -0
  310. package/dist/updates/0.5.0/index.d.ts +2 -0
  311. package/dist/updates/0.5.0/index.js +14 -0
  312. package/dist/updates/index.js +6 -1
  313. package/package.json +17 -10
@@ -3,11 +3,11 @@
3
3
  import * as Command from '../../../components/ui/command/index.js';
4
4
  import Label from '../../../components/ui/label/label.svelte';
5
5
  import type { MediaTag } from '../../../types/media.js';
6
- import { tick } from 'svelte';
7
6
  import Button from '../../../components/ui/button/button.svelte';
8
7
  import SelectorIcon from '@tabler/icons-svelte/icons/selector';
9
8
  import { cn } from '../../../utils.js';
10
9
  import CheckIcon from '@tabler/icons-svelte/icons/check';
10
+ import X from '@tabler/icons-svelte/icons/x';
11
11
  import type { InterfaceLanguage } from '../../../types/languages.js';
12
12
  import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
13
13
 
@@ -26,6 +26,7 @@
26
26
  noTags: string;
27
27
  create: string;
28
28
  searchTags: string;
29
+ addTagPlaceholder: string;
29
30
  }
30
31
  > = {
31
32
  pl: {
@@ -34,7 +35,8 @@
34
35
  empty: 'Brak tagów',
35
36
  noTags: 'Brak tagów.',
36
37
  create: 'Utwórz',
37
- searchTags: 'Szukaj tagów...'
38
+ searchTags: 'Szukaj tagów...',
39
+ addTagPlaceholder: 'Dodaj tag...'
38
40
  },
39
41
  en: {
40
42
  label: 'Tags',
@@ -42,7 +44,8 @@
42
44
  empty: 'No tags',
43
45
  noTags: 'No tags found.',
44
46
  create: 'Create',
45
- searchTags: 'Search tags...'
47
+ searchTags: 'Search tags...',
48
+ addTagPlaceholder: 'Add tag...'
46
49
  }
47
50
  };
48
51
 
@@ -54,9 +57,10 @@
54
57
  onchange: (tagIds: string[]) => void;
55
58
  showLabel?: boolean;
56
59
  onCreate?: (name: string, color: string) => Promise<void>;
60
+ variant?: 'dropdown' | 'inline';
57
61
  };
58
62
 
59
- let { tags, selectedTagIds, onchange, showLabel = true, onCreate }: Props = $props();
63
+ let { tags, selectedTagIds, onchange, showLabel = true, onCreate, variant = 'dropdown' }: Props = $props();
60
64
 
61
65
  let open = $state(false);
62
66
  let triggerRef = $state<HTMLButtonElement>(null!);
@@ -74,6 +78,10 @@
74
78
  onchange(newIds);
75
79
  }
76
80
 
81
+ function removeTag(tagId: string) {
82
+ onchange(selectedTagIds.filter((id) => id !== tagId));
83
+ }
84
+
77
85
  function getNextColor(): string {
78
86
  const usedColors = tags.map((t) => t.color);
79
87
  const available = TAG_COLORS.filter((c) => !usedColors.includes(c));
@@ -87,69 +95,136 @@
87
95
  }
88
96
  </script>
89
97
 
90
- <div class="grid grid-cols-1 gap-2">
91
- {#if showLabel}
92
- <Label>{lang[interfaceLanguage.current].label}</Label>
93
- {/if}
94
- <Popover.Root bind:open>
95
- <Popover.Trigger bind:ref={triggerRef}>
96
- {#snippet child({ props })}
97
- <Button
98
- {...props}
99
- variant="outline"
100
- class="w-full justify-between"
101
- role="combobox"
102
- aria-expanded={open}
98
+ {#if variant === 'inline'}
99
+ <!-- Inline variant: chip container with colored dots + text input -->
100
+ <div class="flex flex-wrap items-center gap-1 rounded-lg bg-muted/60 border border-transparent px-2 py-1.5 min-h-[36px] transition-all focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/10 focus-within:bg-card">
101
+ {#each selectedTags as tag (tag.id)}
102
+ <span class="inline-flex items-center gap-1 rounded-full bg-card border border-border px-2 py-0.5 text-xs font-medium text-muted-foreground">
103
+ <span class="h-1.5 w-1.5 rounded-full shrink-0" style="background-color: {tag.color}"></span>
104
+ {tag.name}
105
+ <button
106
+ type="button"
107
+ class="flex items-center justify-center w-3.5 h-3.5 ml-0.5 rounded-full text-text-light transition-colors hover:bg-error-bg hover:text-destructive"
108
+ onclick={() => removeTag(tag.id)}
109
+ aria-label="Usuń tag {tag.name}"
103
110
  >
104
- {#if selectedTags.length > 0}
105
- <span class="flex items-center gap-1 truncate">
106
- {#each selectedTags.slice(0, 3) as tag}
107
- <span class="inline-flex items-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-xs">
108
- <span class="h-2 w-2 rounded-full" style="background-color: {tag.color}"></span>
109
- {tag.name}
110
- </span>
111
+ <X class="h-2.5 w-2.5" />
112
+ </button>
113
+ </span>
114
+ {/each}
115
+ <!-- Inline input to search/add -->
116
+ <Popover.Root bind:open>
117
+ <Popover.Trigger>
118
+ {#snippet child({ props })}
119
+ <input
120
+ {...props}
121
+ type="text"
122
+ class="flex-1 min-w-[60px] border-none bg-transparent text-xs text-foreground placeholder:text-text-light outline-none py-0.5"
123
+ placeholder={lang[interfaceLanguage.current].addTagPlaceholder}
124
+ bind:value={searchValue}
125
+ onfocus={() => (open = true)}
126
+ />
127
+ {/snippet}
128
+ </Popover.Trigger>
129
+ <Popover.Content class="w-[220px] p-0" align="start">
130
+ <Command.Root shouldFilter={true}>
131
+ <Command.Input placeholder={lang[interfaceLanguage.current].searchTags} bind:value={searchValue} />
132
+ <Command.List>
133
+ <Command.Empty>{lang[interfaceLanguage.current].noTags}</Command.Empty>
134
+ <Command.Group>
135
+ {#each tags as tag (tag.id)}
136
+ <Command.Item
137
+ value={tag.name}
138
+ onSelect={() => toggleTag(tag.id)}
139
+ >
140
+ <CheckIcon class={cn('mr-2 shrink-0', !selectedTagIds.includes(tag.id) && 'text-transparent')} />
141
+ <span class="mr-2 h-3 w-3 shrink-0 rounded-full" style="background-color: {tag.color}"></span>
142
+ <span class="flex-1 truncate">{tag.name}</span>
143
+ </Command.Item>
111
144
  {/each}
112
- {#if selectedTags.length > 3}
113
- <span class="text-xs text-muted-foreground">+{selectedTags.length - 3}</span>
114
- {/if}
115
- </span>
116
- {:else}
117
- {lang[interfaceLanguage.current].placeholder}
118
- {/if}
119
- <SelectorIcon class="opacity-50" />
120
- </Button>
121
- {/snippet}
122
- </Popover.Trigger>
123
- <Popover.Content class="w-[220px] p-0">
124
- <Command.Root shouldFilter={true}>
125
- <Command.Input placeholder={lang[interfaceLanguage.current].searchTags} bind:value={searchValue} />
126
- <Command.List>
127
- <Command.Empty>{lang[interfaceLanguage.current].noTags}</Command.Empty>
128
- <Command.Group>
129
- {#each tags as tag (tag.id)}
130
- <Command.Item
131
- value={tag.name}
132
- onSelect={() => toggleTag(tag.id)}
133
- >
134
- <CheckIcon class={cn('mr-2 shrink-0', !selectedTagIds.includes(tag.id) && 'text-transparent')} />
135
- <span class="mr-2 h-3 w-3 shrink-0 rounded-full" style="background-color: {tag.color}"></span>
136
- <span class="flex-1 truncate">{tag.name}</span>
137
- </Command.Item>
138
- {/each}
139
- </Command.Group>
140
- {#if onCreate && searchValue.trim() && !hasExactMatch}
145
+ </Command.Group>
146
+ {#if onCreate && searchValue.trim() && !hasExactMatch}
147
+ <Command.Group>
148
+ <Command.Item
149
+ value="__create__{searchValue.trim()}"
150
+ onSelect={handleCreateInline}
151
+ >
152
+ <span class="mr-2 text-muted-foreground">+</span>
153
+ {lang[interfaceLanguage.current].create} "{searchValue.trim()}"
154
+ </Command.Item>
155
+ </Command.Group>
156
+ {/if}
157
+ </Command.List>
158
+ </Command.Root>
159
+ </Popover.Content>
160
+ </Popover.Root>
161
+ </div>
162
+ {:else}
163
+ <!-- Dropdown variant (default) -->
164
+ <div class="grid grid-cols-1 gap-2">
165
+ {#if showLabel}
166
+ <Label>{lang[interfaceLanguage.current].label}</Label>
167
+ {/if}
168
+ <Popover.Root bind:open>
169
+ <Popover.Trigger bind:ref={triggerRef}>
170
+ {#snippet child({ props })}
171
+ <Button
172
+ {...props}
173
+ variant="outline"
174
+ class="w-full justify-between"
175
+ role="combobox"
176
+ aria-expanded={open}
177
+ >
178
+ {#if selectedTags.length > 0}
179
+ <span class="flex items-center gap-1 truncate">
180
+ {#each selectedTags.slice(0, 3) as tag}
181
+ <span class="inline-flex items-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-xs">
182
+ <span class="h-2 w-2 rounded-full" style="background-color: {tag.color}"></span>
183
+ {tag.name}
184
+ </span>
185
+ {/each}
186
+ {#if selectedTags.length > 3}
187
+ <span class="text-xs text-muted-foreground">+{selectedTags.length - 3}</span>
188
+ {/if}
189
+ </span>
190
+ {:else}
191
+ {lang[interfaceLanguage.current].placeholder}
192
+ {/if}
193
+ <SelectorIcon class="opacity-50" />
194
+ </Button>
195
+ {/snippet}
196
+ </Popover.Trigger>
197
+ <Popover.Content class="w-[220px] p-0">
198
+ <Command.Root shouldFilter={true}>
199
+ <Command.Input placeholder={lang[interfaceLanguage.current].searchTags} bind:value={searchValue} />
200
+ <Command.List>
201
+ <Command.Empty>{lang[interfaceLanguage.current].noTags}</Command.Empty>
141
202
  <Command.Group>
142
- <Command.Item
143
- value="__create__{searchValue.trim()}"
144
- onSelect={handleCreateInline}
145
- >
146
- <span class="mr-2 text-muted-foreground">+</span>
147
- {lang[interfaceLanguage.current].create} "{searchValue.trim()}"
148
- </Command.Item>
203
+ {#each tags as tag (tag.id)}
204
+ <Command.Item
205
+ value={tag.name}
206
+ onSelect={() => toggleTag(tag.id)}
207
+ >
208
+ <CheckIcon class={cn('mr-2 shrink-0', !selectedTagIds.includes(tag.id) && 'text-transparent')} />
209
+ <span class="mr-2 h-3 w-3 shrink-0 rounded-full" style="background-color: {tag.color}"></span>
210
+ <span class="flex-1 truncate">{tag.name}</span>
211
+ </Command.Item>
212
+ {/each}
149
213
  </Command.Group>
150
- {/if}
151
- </Command.List>
152
- </Command.Root>
153
- </Popover.Content>
154
- </Popover.Root>
155
- </div>
214
+ {#if onCreate && searchValue.trim() && !hasExactMatch}
215
+ <Command.Group>
216
+ <Command.Item
217
+ value="__create__{searchValue.trim()}"
218
+ onSelect={handleCreateInline}
219
+ >
220
+ <span class="mr-2 text-muted-foreground">+</span>
221
+ {lang[interfaceLanguage.current].create} "{searchValue.trim()}"
222
+ </Command.Item>
223
+ </Command.Group>
224
+ {/if}
225
+ </Command.List>
226
+ </Command.Root>
227
+ </Popover.Content>
228
+ </Popover.Root>
229
+ </div>
230
+ {/if}
@@ -5,6 +5,7 @@ type Props = {
5
5
  onchange: (tagIds: string[]) => void;
6
6
  showLabel?: boolean;
7
7
  onCreate?: (name: string, color: string) => Promise<void>;
8
+ variant?: 'dropdown' | 'inline';
8
9
  };
9
10
  declare const TagCombobox: import("svelte").Component<Props, {}, "">;
10
11
  type TagCombobox = ReturnType<typeof TagCombobox>;
@@ -2,7 +2,6 @@
2
2
  import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
3
3
  import type { InterfaceLanguage } from '../../../types/languages.js';
4
4
  import type { MediaFile, MediaTag } from '../../../types/media.js';
5
- import Button from '../../../components/ui/button/button.svelte';
6
5
  import Input from '../../../components/ui/input/input.svelte';
7
6
  import * as Popover from '../../../components/ui/popover/index.js';
8
7
  import * as AlertDialog from '../../../components/ui/alert-dialog/index.js';
@@ -67,7 +66,7 @@
67
66
  type Props = {
68
67
  tags: MediaTag[];
69
68
  files: MediaFile[];
70
- activeFilter: string | null; // null = all, 'untagged', or tag id
69
+ activeFilter: string | null;
71
70
  onFilterChange: (filter: string | null) => void;
72
71
  onCreateTag: (name: string, color: string) => Promise<void>;
73
72
  onUpdateTag: (id: string, name: string, color: string) => Promise<void>;
@@ -132,146 +131,165 @@
132
131
  }
133
132
  </script>
134
133
 
135
- <div class="flex h-full flex-col gap-0.5">
136
- <!-- All files -->
137
- <button
138
- type="button"
139
- class={cn(
140
- 'flex items-center gap-2 rounded-lg px-3 py-1.5 text-left text-sm transition-colors',
141
- activeFilter === null
142
- ? 'bg-[#2D4A77]/10 text-[#2D4A77] dark:bg-[#4975AE]/15 dark:text-[#4975AE] font-medium'
143
- : 'text-muted-foreground hover:bg-accent/50 hover:text-foreground'
144
- )}
145
- onclick={() => onFilterChange(null)}
146
- >
147
- <Tag class="h-4 w-4 shrink-0" />
148
- <span class="flex-1">{lang[interfaceLanguage.current].all}</span>
149
- <span class="text-xs tabular-nums opacity-80">{files.length}</span>
150
- </button>
151
-
152
- <!-- Untagged -->
153
- <button
154
- type="button"
155
- class={cn(
156
- 'flex items-center gap-2 rounded-lg px-3 py-1.5 text-left text-sm transition-colors',
157
- activeFilter === 'untagged'
158
- ? 'bg-[#2D4A77]/10 text-[#2D4A77] dark:bg-[#4975AE]/15 dark:text-[#4975AE] font-medium'
159
- : 'text-muted-foreground hover:bg-accent/50 hover:text-foreground'
160
- )}
161
- onclick={() => onFilterChange('untagged')}
162
- >
163
- <TagOff class="h-4 w-4 shrink-0" />
164
- <span class="flex-1">{lang[interfaceLanguage.current].untagged}</span>
165
- <span class="text-xs tabular-nums opacity-80">{untaggedCount}</span>
166
- </button>
167
-
168
- <div class="my-1.5 border-t"></div>
134
+ <div class="flex h-full flex-col">
135
+ <div class="flex-1 overflow-y-auto py-2 scrollbar-thin" role="listbox" aria-label="Filtry tagów">
136
+ <ul class="flex flex-col gap-px px-1.5">
137
+ <!-- All files -->
138
+ <li>
139
+ <button
140
+ type="button"
141
+ role="option"
142
+ aria-selected={activeFilter === null}
143
+ class={cn(
144
+ 'flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-[13px] font-medium transition-colors',
145
+ activeFilter === null
146
+ ? 'bg-primary/8 text-primary font-semibold'
147
+ : 'text-muted-foreground hover:bg-muted hover:text-foreground'
148
+ )}
149
+ onclick={() => onFilterChange(null)}
150
+ >
151
+ <span class="flex h-[18px] w-[18px] shrink-0 items-center justify-center">
152
+ <Tag class="h-4 w-4" />
153
+ </span>
154
+ <span class="flex-1 truncate">{lang[interfaceLanguage.current].all}</span>
155
+ <span class="text-[11px] font-semibold tabular-nums {activeFilter === null ? 'text-primary' : 'text-text-light'}">{files.length}</span>
156
+ </button>
157
+ </li>
169
158
 
170
- <!-- Tags list -->
171
- <div class="flex-1 space-y-0.5 overflow-y-auto">
172
- {#each tags as tag (tag.id)}
173
- {#if editingTagId === tag.id}
174
- <form
175
- class="flex items-center gap-1 px-1"
176
- onsubmit={(e) => { e.preventDefault(); handleRename(tag); }}
159
+ <!-- Untagged -->
160
+ <li>
161
+ <button
162
+ type="button"
163
+ role="option"
164
+ aria-selected={activeFilter === 'untagged'}
165
+ class={cn(
166
+ 'flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-[13px] font-medium transition-colors',
167
+ activeFilter === 'untagged'
168
+ ? 'bg-primary/8 text-primary font-semibold'
169
+ : 'text-muted-foreground hover:bg-muted hover:text-foreground'
170
+ )}
171
+ onclick={() => onFilterChange('untagged')}
177
172
  >
178
- <span class="h-3 w-3 shrink-0 rounded-full" style="background-color: {tag.color}"></span>
179
- <Input
180
- class="h-7 text-xs"
181
- bind:value={editingTagName}
182
- autofocus
183
- onblur={() => handleRename(tag)}
184
- onkeydown={(e) => e.key === 'Escape' && (editingTagId = null)}
185
- />
186
- </form>
187
- {:else}
188
- <div class="group relative flex items-center">
189
- <button
190
- type="button"
191
- class={cn(
192
- 'flex flex-1 items-center gap-2 rounded-lg px-3 py-1.5 text-left text-sm transition-colors',
193
- activeFilter === tag.id
194
- ? 'bg-[#2D4A77]/10 text-[#2D4A77] dark:bg-[#4975AE]/15 dark:text-[#4975AE] font-medium'
195
- : 'text-muted-foreground hover:bg-accent/50 hover:text-foreground'
196
- )}
197
- onclick={() => onFilterChange(tag.id)}
198
- >
199
- <span class="h-3 w-3 shrink-0 rounded-full" style="background-color: {tag.color}"></span>
200
- <span class="flex-1 truncate">{tag.name}</span>
201
- <span class="text-xs tabular-nums opacity-80">{getTagFileCount(tag.id)}</span>
202
- </button>
173
+ <span class="flex h-[18px] w-[18px] shrink-0 items-center justify-center">
174
+ <TagOff class="h-4 w-4" />
175
+ </span>
176
+ <span class="flex-1 truncate">{lang[interfaceLanguage.current].untagged}</span>
177
+ <span class="text-[11px] font-semibold tabular-nums {activeFilter === 'untagged' ? 'text-primary' : 'text-text-light'}">{untaggedCount}</span>
178
+ </button>
179
+ </li>
203
180
 
204
- <!-- Context menu -->
205
- <Popover.Root bind:open={
206
- () => menuOpenTagId === tag.id,
207
- (v) => { menuOpenTagId = v ? tag.id : null; }
208
- }>
209
- <Popover.Trigger>
210
- {#snippet child({ props })}
211
- <button
212
- {...props}
213
- type="button"
214
- class="absolute right-1 rounded p-1 opacity-0 transition-opacity hover:bg-accent group-hover:opacity-100"
215
- >
216
- <Dots class="h-3.5 w-3.5" />
217
- </button>
218
- {/snippet}
219
- </Popover.Trigger>
220
- <Popover.Content class="w-40 p-1" align="start">
181
+ <!-- Separator -->
182
+ <li aria-hidden="true" class="my-1.5 mx-2.5 h-px bg-border"></li>
183
+
184
+ <!-- User tags -->
185
+ {#each tags as tag (tag.id)}
186
+ <li>
187
+ {#if editingTagId === tag.id}
188
+ <form
189
+ class="flex items-center gap-1 px-1"
190
+ onsubmit={(e) => { e.preventDefault(); handleRename(tag); }}
191
+ >
192
+ <span class="h-2 w-2 shrink-0 rounded-full" style="background-color: {tag.color}"></span>
193
+ <Input
194
+ class="h-7 text-xs"
195
+ bind:value={editingTagName}
196
+ autofocus
197
+ onblur={() => handleRename(tag)}
198
+ onkeydown={(e) => e.key === 'Escape' && (editingTagId = null)}
199
+ />
200
+ </form>
201
+ {:else}
202
+ <div class="group relative flex items-center">
221
203
  <button
222
204
  type="button"
223
- class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-xs hover:bg-accent"
224
- onclick={() => startRename(tag)}
205
+ role="option"
206
+ aria-selected={activeFilter === tag.id}
207
+ class={cn(
208
+ 'flex w-full flex-1 items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-[13px] font-medium transition-colors',
209
+ activeFilter === tag.id
210
+ ? 'bg-primary/8 text-primary font-semibold'
211
+ : 'text-muted-foreground hover:bg-muted hover:text-foreground'
212
+ )}
213
+ onclick={() => onFilterChange(tag.id)}
225
214
  >
226
- {lang[interfaceLanguage.current].rename}
215
+ <span class="h-2 w-2 shrink-0 rounded-full" style="background-color: {tag.color}"></span>
216
+ <span class="flex-1 truncate">{tag.name}</span>
217
+ <span class="text-[11px] font-semibold tabular-nums {activeFilter === tag.id ? 'text-primary' : 'text-text-light'}">{getTagFileCount(tag.id)}</span>
227
218
  </button>
219
+
220
+ <!-- Context menu -->
228
221
  <Popover.Root bind:open={
229
- () => colorPickerTagId === tag.id,
230
- (v) => { colorPickerTagId = v ? tag.id : null; }
222
+ () => menuOpenTagId === tag.id,
223
+ (v) => { menuOpenTagId = v ? tag.id : null; }
231
224
  }>
232
225
  <Popover.Trigger>
233
226
  {#snippet child({ props })}
234
227
  <button
235
228
  {...props}
236
229
  type="button"
237
- class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-xs hover:bg-accent"
230
+ class="absolute right-1 rounded p-1 opacity-0 transition-opacity hover:bg-accent group-hover:opacity-100"
238
231
  >
239
- {lang[interfaceLanguage.current].changeColor}
232
+ <Dots class="h-3.5 w-3.5" />
240
233
  </button>
241
234
  {/snippet}
242
235
  </Popover.Trigger>
243
- <Popover.Content class="w-auto p-2" side="right">
244
- <div class="grid grid-cols-6 gap-1">
245
- {#each TAG_COLORS as color}
246
- <button
247
- type="button"
248
- class={cn(
249
- 'h-6 w-6 rounded-full transition-transform hover:scale-110',
250
- tag.color === color && 'ring-2 ring-foreground ring-offset-2'
251
- )}
252
- style="background-color: {color}"
253
- onclick={() => handleColorChange(tag, color)}
254
- ></button>
255
- {/each}
256
- </div>
236
+ <Popover.Content class="w-40 p-1" align="start">
237
+ <button
238
+ type="button"
239
+ class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-xs hover:bg-accent"
240
+ onclick={() => startRename(tag)}
241
+ >
242
+ {lang[interfaceLanguage.current].rename}
243
+ </button>
244
+ <Popover.Root bind:open={
245
+ () => colorPickerTagId === tag.id,
246
+ (v) => { colorPickerTagId = v ? tag.id : null; }
247
+ }>
248
+ <Popover.Trigger>
249
+ {#snippet child({ props })}
250
+ <button
251
+ {...props}
252
+ type="button"
253
+ class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-xs hover:bg-accent"
254
+ >
255
+ {lang[interfaceLanguage.current].changeColor}
256
+ </button>
257
+ {/snippet}
258
+ </Popover.Trigger>
259
+ <Popover.Content class="w-auto p-2" side="right">
260
+ <div class="grid grid-cols-6 gap-1">
261
+ {#each TAG_COLORS as color}
262
+ <button
263
+ type="button"
264
+ class={cn(
265
+ 'h-6 w-6 rounded-full transition-transform hover:scale-110',
266
+ tag.color === color && 'ring-2 ring-foreground ring-offset-2'
267
+ )}
268
+ style="background-color: {color}"
269
+ onclick={() => handleColorChange(tag, color)}
270
+ ></button>
271
+ {/each}
272
+ </div>
273
+ </Popover.Content>
274
+ </Popover.Root>
275
+ <button
276
+ type="button"
277
+ class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-xs text-destructive hover:bg-destructive/10"
278
+ onclick={() => { deleteTagId = tag.id; menuOpenTagId = null; }}
279
+ >
280
+ {lang[interfaceLanguage.current].deleteTag}
281
+ </button>
257
282
  </Popover.Content>
258
283
  </Popover.Root>
259
- <button
260
- type="button"
261
- class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-xs text-destructive hover:bg-destructive/10"
262
- onclick={() => { deleteTagId = tag.id; menuOpenTagId = null; }}
263
- >
264
- {lang[interfaceLanguage.current].deleteTag}
265
- </button>
266
- </Popover.Content>
267
- </Popover.Root>
268
- </div>
269
- {/if}
270
- {/each}
284
+ </div>
285
+ {/if}
286
+ </li>
287
+ {/each}
288
+ </ul>
271
289
  </div>
272
290
 
273
- <!-- Add tag -->
274
- <div class="mt-2 border-t pt-2">
291
+ <!-- Add tag footer -->
292
+ <div class="border-t p-1.5">
275
293
  {#if isAdding}
276
294
  <form
277
295
  class="flex items-center gap-1 px-1"
@@ -289,7 +307,7 @@
289
307
  {:else}
290
308
  <button
291
309
  type="button"
292
- class="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-xs text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground"
310
+ class="flex w-full items-center gap-1.5 rounded-md border border-dashed border-border px-2.5 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:border-lavender hover:bg-lavender-lighter hover:text-primary"
293
311
  onclick={() => (isAdding = true)}
294
312
  >
295
313
  <Plus class="h-3.5 w-3.5" />