snice 4.10.0 → 4.12.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 (269) hide show
  1. package/adapters/react/carousel.d.ts +1 -0
  2. package/adapters/react/carousel.d.ts.map +1 -1
  3. package/adapters/react/carousel.js +1 -1
  4. package/adapters/react/carousel.js.map +1 -1
  5. package/adapters/react/carousel.tsx +2 -1
  6. package/bin/templates/CLAUDE.md +169 -113
  7. package/bin/templates/pwa/README.md +72 -94
  8. package/bin/templates/pwa/src/components/app-header.ts +218 -0
  9. package/bin/templates/pwa/src/components/notification-badge.ts +68 -0
  10. package/bin/templates/pwa/src/components/search-bar.ts +99 -0
  11. package/bin/templates/pwa/src/controllers/notification-controller.ts +42 -0
  12. package/bin/templates/pwa/src/guards/auth.ts +1 -1
  13. package/bin/templates/pwa/src/main.ts +19 -0
  14. package/bin/templates/pwa/src/pages/dashboard.ts +124 -23
  15. package/bin/templates/pwa/src/pages/data.ts +329 -0
  16. package/bin/templates/pwa/src/pages/notifications.ts +112 -30
  17. package/bin/templates/pwa/src/pages/profile.ts +86 -52
  18. package/bin/templates/pwa/src/pages/settings.ts +291 -0
  19. package/bin/templates/pwa/src/styles/global.css +12 -3
  20. package/dist/cdn/accordion/snice-accordion.js +1 -1
  21. package/dist/cdn/accordion/snice-accordion.min.js +1 -1
  22. package/dist/cdn/alert/snice-alert.js +1 -1
  23. package/dist/cdn/alert/snice-alert.min.js +1 -1
  24. package/dist/cdn/app-tiles/snice-app-tiles.js +1 -1
  25. package/dist/cdn/app-tiles/snice-app-tiles.min.js +1 -1
  26. package/dist/cdn/audio-recorder/snice-audio-recorder.js +1 -1
  27. package/dist/cdn/audio-recorder/snice-audio-recorder.min.js +1 -1
  28. package/dist/cdn/avatar/snice-avatar.js +1 -1
  29. package/dist/cdn/avatar/snice-avatar.min.js +1 -1
  30. package/dist/cdn/badge/snice-badge.js +1 -1
  31. package/dist/cdn/badge/snice-badge.min.js +1 -1
  32. package/dist/cdn/banner/snice-banner.js +1 -1
  33. package/dist/cdn/banner/snice-banner.min.js +1 -1
  34. package/dist/cdn/book/snice-book.js +1 -1
  35. package/dist/cdn/book/snice-book.min.js +1 -1
  36. package/dist/cdn/breadcrumbs/snice-breadcrumbs.js +1 -1
  37. package/dist/cdn/breadcrumbs/snice-breadcrumbs.min.js +1 -1
  38. package/dist/cdn/button/snice-button.js +1 -1
  39. package/dist/cdn/button/snice-button.min.js +1 -1
  40. package/dist/cdn/calendar/snice-calendar.js +1 -1
  41. package/dist/cdn/calendar/snice-calendar.min.js +1 -1
  42. package/dist/cdn/camera/README.md +1 -1
  43. package/dist/cdn/camera/snice-camera.js +1 -1
  44. package/dist/cdn/camera/snice-camera.min.js +1 -1
  45. package/dist/cdn/camera-annotate/snice-camera-annotate.js +1 -1
  46. package/dist/cdn/camera-annotate/snice-camera-annotate.min.js +1 -1
  47. package/dist/cdn/candlestick/snice-candlestick.js +1 -1
  48. package/dist/cdn/candlestick/snice-candlestick.min.js +1 -1
  49. package/dist/cdn/card/snice-card.js +1 -1
  50. package/dist/cdn/card/snice-card.min.js +1 -1
  51. package/dist/cdn/carousel/README.md +2 -2
  52. package/dist/cdn/carousel/snice-carousel.js +9 -3
  53. package/dist/cdn/carousel/snice-carousel.js.map +1 -1
  54. package/dist/cdn/carousel/snice-carousel.min.js +3 -3
  55. package/dist/cdn/carousel/snice-carousel.min.js.map +1 -1
  56. package/dist/cdn/chart/snice-chart.js +1 -1
  57. package/dist/cdn/chart/snice-chart.min.js +1 -1
  58. package/dist/cdn/chat/snice-chat.js +1 -1
  59. package/dist/cdn/chat/snice-chat.min.js +1 -1
  60. package/dist/cdn/checkbox/snice-checkbox.js +1 -1
  61. package/dist/cdn/checkbox/snice-checkbox.min.js +1 -1
  62. package/dist/cdn/chip/snice-chip.js +1 -1
  63. package/dist/cdn/chip/snice-chip.min.js +1 -1
  64. package/dist/cdn/code-block/snice-code-block.js +1 -1
  65. package/dist/cdn/code-block/snice-code-block.min.js +1 -1
  66. package/dist/cdn/color-display/snice-color-display.js +1 -1
  67. package/dist/cdn/color-display/snice-color-display.min.js +1 -1
  68. package/dist/cdn/color-picker/snice-color-picker.js +1 -1
  69. package/dist/cdn/color-picker/snice-color-picker.min.js +1 -1
  70. package/dist/cdn/command-palette/snice-command-palette.js +1 -1
  71. package/dist/cdn/command-palette/snice-command-palette.min.js +1 -1
  72. package/dist/cdn/comments/snice-comments.js +1 -1
  73. package/dist/cdn/comments/snice-comments.min.js +1 -1
  74. package/dist/cdn/countdown/snice-countdown.js +1 -1
  75. package/dist/cdn/countdown/snice-countdown.min.js +1 -1
  76. package/dist/cdn/cropper/snice-cropper.js +1 -1
  77. package/dist/cdn/cropper/snice-cropper.min.js +1 -1
  78. package/dist/cdn/date-picker/snice-date-picker.js +1 -1
  79. package/dist/cdn/date-picker/snice-date-picker.min.js +1 -1
  80. package/dist/cdn/diff/snice-diff.js +1 -1
  81. package/dist/cdn/diff/snice-diff.min.js +1 -1
  82. package/dist/cdn/divider/snice-divider.js +1 -1
  83. package/dist/cdn/divider/snice-divider.min.js +1 -1
  84. package/dist/cdn/doc/snice-doc.js +1 -1
  85. package/dist/cdn/doc/snice-doc.min.js +1 -1
  86. package/dist/cdn/draw/snice-draw.js +1 -1
  87. package/dist/cdn/draw/snice-draw.min.js +1 -1
  88. package/dist/cdn/drawer/snice-drawer.js +1 -1
  89. package/dist/cdn/drawer/snice-drawer.min.js +1 -1
  90. package/dist/cdn/empty-state/snice-empty-state.js +1 -1
  91. package/dist/cdn/empty-state/snice-empty-state.min.js +1 -1
  92. package/dist/cdn/file-gallery/snice-file-gallery.js +1 -1
  93. package/dist/cdn/file-gallery/snice-file-gallery.min.js +1 -1
  94. package/dist/cdn/file-upload/snice-file-upload.js +1 -1
  95. package/dist/cdn/file-upload/snice-file-upload.min.js +1 -1
  96. package/dist/cdn/flip-card/snice-flip-card.js +1 -1
  97. package/dist/cdn/flip-card/snice-flip-card.min.js +1 -1
  98. package/dist/cdn/flow/snice-flow.js +1 -1
  99. package/dist/cdn/flow/snice-flow.min.js +1 -1
  100. package/dist/cdn/funnel/snice-funnel.js +1 -1
  101. package/dist/cdn/funnel/snice-funnel.min.js +1 -1
  102. package/dist/cdn/gantt/README.md +1 -1
  103. package/dist/cdn/gantt/snice-gantt.js +1 -1
  104. package/dist/cdn/gantt/snice-gantt.min.js +1 -1
  105. package/dist/cdn/gauge/snice-gauge.js +1 -1
  106. package/dist/cdn/gauge/snice-gauge.min.js +1 -1
  107. package/dist/cdn/heatmap/snice-heatmap.js +1 -1
  108. package/dist/cdn/heatmap/snice-heatmap.min.js +1 -1
  109. package/dist/cdn/image/snice-image.js +1 -1
  110. package/dist/cdn/image/snice-image.min.js +1 -1
  111. package/dist/cdn/input/snice-input.js +1 -1
  112. package/dist/cdn/input/snice-input.min.js +1 -1
  113. package/dist/cdn/kanban/snice-kanban.js +1 -1
  114. package/dist/cdn/kanban/snice-kanban.min.js +1 -1
  115. package/dist/cdn/kpi/snice-kpi.js +1 -1
  116. package/dist/cdn/kpi/snice-kpi.min.js +1 -1
  117. package/dist/cdn/layout/snice-layout.js +1 -1
  118. package/dist/cdn/layout/snice-layout.min.js +1 -1
  119. package/dist/cdn/link/snice-link.js +1 -1
  120. package/dist/cdn/link/snice-link.min.js +1 -1
  121. package/dist/cdn/link-preview/snice-link-preview.js +1 -1
  122. package/dist/cdn/link-preview/snice-link-preview.min.js +1 -1
  123. package/dist/cdn/list/snice-list.js +1 -1
  124. package/dist/cdn/list/snice-list.min.js +1 -1
  125. package/dist/cdn/location/snice-location.js +1 -1
  126. package/dist/cdn/location/snice-location.min.js +1 -1
  127. package/dist/cdn/login/snice-login.js +1 -1
  128. package/dist/cdn/login/snice-login.min.js +1 -1
  129. package/dist/cdn/map/snice-map.js +1 -1
  130. package/dist/cdn/map/snice-map.min.js +1 -1
  131. package/dist/cdn/markdown/README.md +1 -1
  132. package/dist/cdn/markdown/snice-markdown.js +2 -2
  133. package/dist/cdn/markdown/snice-markdown.js.map +1 -1
  134. package/dist/cdn/markdown/snice-markdown.min.js +2 -2
  135. package/dist/cdn/markdown/snice-markdown.min.js.map +1 -1
  136. package/dist/cdn/masonry/snice-masonry.js +1 -1
  137. package/dist/cdn/masonry/snice-masonry.min.js +1 -1
  138. package/dist/cdn/menu/snice-menu.js +1 -1
  139. package/dist/cdn/menu/snice-menu.min.js +1 -1
  140. package/dist/cdn/modal/snice-modal.js +1 -1
  141. package/dist/cdn/modal/snice-modal.min.js +1 -1
  142. package/dist/cdn/music-player/snice-music-player.js +1 -1
  143. package/dist/cdn/music-player/snice-music-player.min.js +1 -1
  144. package/dist/cdn/nav/snice-nav.js +1 -1
  145. package/dist/cdn/nav/snice-nav.min.js +1 -1
  146. package/dist/cdn/network-graph/snice-network-graph.js +1 -1
  147. package/dist/cdn/network-graph/snice-network-graph.min.js +1 -1
  148. package/dist/cdn/notification-center/snice-notification-center.js +1 -1
  149. package/dist/cdn/notification-center/snice-notification-center.min.js +1 -1
  150. package/dist/cdn/org-chart/snice-org-chart.js +1 -1
  151. package/dist/cdn/org-chart/snice-org-chart.min.js +1 -1
  152. package/dist/cdn/pagination/snice-pagination.js +1 -1
  153. package/dist/cdn/pagination/snice-pagination.min.js +1 -1
  154. package/dist/cdn/paint/snice-paint.js +1 -1
  155. package/dist/cdn/paint/snice-paint.min.js +1 -1
  156. package/dist/cdn/pdf-viewer/README.md +2 -2
  157. package/dist/cdn/pdf-viewer/snice-pdf-viewer.js +72 -78
  158. package/dist/cdn/pdf-viewer/snice-pdf-viewer.js.map +1 -1
  159. package/dist/cdn/pdf-viewer/snice-pdf-viewer.min.js +3 -3
  160. package/dist/cdn/pdf-viewer/snice-pdf-viewer.min.js.map +1 -1
  161. package/dist/cdn/podcast-player/README.md +1 -1
  162. package/dist/cdn/podcast-player/snice-podcast-player.js +2 -2
  163. package/dist/cdn/podcast-player/snice-podcast-player.js.map +1 -1
  164. package/dist/cdn/podcast-player/snice-podcast-player.min.js +2 -2
  165. package/dist/cdn/podcast-player/snice-podcast-player.min.js.map +1 -1
  166. package/dist/cdn/pricing-table/snice-pricing-table.js +1 -1
  167. package/dist/cdn/pricing-table/snice-pricing-table.min.js +1 -1
  168. package/dist/cdn/progress/snice-progress.js +1 -1
  169. package/dist/cdn/progress/snice-progress.min.js +1 -1
  170. package/dist/cdn/qr-code/snice-qr-code.js +1 -1
  171. package/dist/cdn/qr-code/snice-qr-code.min.js +1 -1
  172. package/dist/cdn/qr-reader/snice-qr-reader.js +1 -1
  173. package/dist/cdn/qr-reader/snice-qr-reader.min.js +1 -1
  174. package/dist/cdn/radio/snice-radio.js +1 -1
  175. package/dist/cdn/radio/snice-radio.min.js +1 -1
  176. package/dist/cdn/rating/snice-rating.js +1 -1
  177. package/dist/cdn/rating/snice-rating.min.js +1 -1
  178. package/dist/cdn/recipe/snice-recipe.js +1 -1
  179. package/dist/cdn/recipe/snice-recipe.min.js +1 -1
  180. package/dist/cdn/runtime/README.md +1 -1
  181. package/dist/cdn/runtime/snice-runtime.esm.js +10 -3
  182. package/dist/cdn/runtime/snice-runtime.esm.js.map +1 -1
  183. package/dist/cdn/runtime/snice-runtime.esm.min.js +4 -4
  184. package/dist/cdn/runtime/snice-runtime.esm.min.js.map +1 -1
  185. package/dist/cdn/runtime/snice-runtime.js +10 -3
  186. package/dist/cdn/runtime/snice-runtime.js.map +1 -1
  187. package/dist/cdn/runtime/snice-runtime.min.js +4 -4
  188. package/dist/cdn/runtime/snice-runtime.min.js.map +1 -1
  189. package/dist/cdn/sankey/snice-sankey.js +1 -1
  190. package/dist/cdn/sankey/snice-sankey.min.js +1 -1
  191. package/dist/cdn/select/snice-select.js +1 -1
  192. package/dist/cdn/select/snice-select.min.js +1 -1
  193. package/dist/cdn/skeleton/snice-skeleton.js +1 -1
  194. package/dist/cdn/skeleton/snice-skeleton.min.js +1 -1
  195. package/dist/cdn/slider/snice-slider.js +1 -1
  196. package/dist/cdn/slider/snice-slider.min.js +1 -1
  197. package/dist/cdn/sortable/snice-sortable.js +1 -1
  198. package/dist/cdn/sortable/snice-sortable.min.js +1 -1
  199. package/dist/cdn/sparkline/snice-sparkline.js +1 -1
  200. package/dist/cdn/sparkline/snice-sparkline.min.js +1 -1
  201. package/dist/cdn/spinner/snice-spinner.js +1 -1
  202. package/dist/cdn/spinner/snice-spinner.min.js +1 -1
  203. package/dist/cdn/split-pane/snice-split-pane.js +1 -1
  204. package/dist/cdn/split-pane/snice-split-pane.min.js +1 -1
  205. package/dist/cdn/spotlight/snice-spotlight.js +1 -1
  206. package/dist/cdn/spotlight/snice-spotlight.min.js +1 -1
  207. package/dist/cdn/spreadsheet/snice-spreadsheet.js +1 -1
  208. package/dist/cdn/spreadsheet/snice-spreadsheet.min.js +1 -1
  209. package/dist/cdn/stepper/snice-stepper.js +1 -1
  210. package/dist/cdn/stepper/snice-stepper.min.js +1 -1
  211. package/dist/cdn/switch/snice-switch.js +1 -1
  212. package/dist/cdn/switch/snice-switch.min.js +1 -1
  213. package/dist/cdn/table/snice-table.js +1 -1
  214. package/dist/cdn/table/snice-table.min.js +1 -1
  215. package/dist/cdn/tabs/snice-tabs.js +1 -1
  216. package/dist/cdn/tabs/snice-tabs.min.js +1 -1
  217. package/dist/cdn/tag-input/snice-tag-input.js +1 -1
  218. package/dist/cdn/tag-input/snice-tag-input.min.js +1 -1
  219. package/dist/cdn/terminal/snice-terminal.js +1 -1
  220. package/dist/cdn/terminal/snice-terminal.min.js +1 -1
  221. package/dist/cdn/testimonial/snice-testimonial.js +1 -1
  222. package/dist/cdn/testimonial/snice-testimonial.min.js +1 -1
  223. package/dist/cdn/textarea/snice-textarea.js +1 -1
  224. package/dist/cdn/textarea/snice-textarea.min.js +1 -1
  225. package/dist/cdn/time-range-picker/snice-time-range-picker.js +1 -1
  226. package/dist/cdn/time-range-picker/snice-time-range-picker.min.js +1 -1
  227. package/dist/cdn/timeline/snice-timeline.js +1 -1
  228. package/dist/cdn/timeline/snice-timeline.min.js +1 -1
  229. package/dist/cdn/timer/snice-timer.js +1 -1
  230. package/dist/cdn/timer/snice-timer.min.js +1 -1
  231. package/dist/cdn/toast/snice-toast.js +1 -1
  232. package/dist/cdn/toast/snice-toast.min.js +1 -1
  233. package/dist/cdn/tooltip/snice-tooltip.js +1 -1
  234. package/dist/cdn/tooltip/snice-tooltip.min.js +1 -1
  235. package/dist/cdn/tree/snice-tree.js +1 -1
  236. package/dist/cdn/tree/snice-tree.min.js +1 -1
  237. package/dist/cdn/treemap/snice-treemap.js +1 -1
  238. package/dist/cdn/treemap/snice-treemap.min.js +1 -1
  239. package/dist/cdn/video-player/snice-video-player.js +1 -1
  240. package/dist/cdn/video-player/snice-video-player.min.js +1 -1
  241. package/dist/cdn/virtual-scroller/snice-virtual-scroller.js +1 -1
  242. package/dist/cdn/virtual-scroller/snice-virtual-scroller.min.js +1 -1
  243. package/dist/cdn/waterfall/snice-waterfall.js +1 -1
  244. package/dist/cdn/waterfall/snice-waterfall.min.js +1 -1
  245. package/dist/cdn/weather/snice-weather.js +1 -1
  246. package/dist/cdn/weather/snice-weather.min.js +1 -1
  247. package/dist/components/carousel/snice-carousel.js +8 -2
  248. package/dist/components/carousel/snice-carousel.js.map +1 -1
  249. package/dist/components/code-block/grammars/html.json +48 -0
  250. package/dist/components/markdown/snice-markdown.js +1 -1
  251. package/dist/components/markdown/snice-markdown.js.map +1 -1
  252. package/dist/components/pdf-viewer/snice-pdf-viewer.d.ts +9 -8
  253. package/dist/components/pdf-viewer/snice-pdf-viewer.js +71 -77
  254. package/dist/components/pdf-viewer/snice-pdf-viewer.js.map +1 -1
  255. package/dist/components/podcast-player/snice-podcast-player.js +1 -1
  256. package/dist/components/podcast-player/snice-podcast-player.js.map +1 -1
  257. package/dist/index.cjs +8 -1
  258. package/dist/index.cjs.map +1 -1
  259. package/dist/index.esm.js +8 -1
  260. package/dist/index.esm.js.map +1 -1
  261. package/dist/index.iife.js +8 -1
  262. package/dist/index.iife.js.map +1 -1
  263. package/dist/symbols.cjs +1 -1
  264. package/dist/symbols.esm.js +1 -1
  265. package/dist/transitions.cjs +1 -1
  266. package/dist/transitions.esm.js +1 -1
  267. package/docs/ai/patterns.md +1 -1
  268. package/docs/routing.md +2 -2
  269. package/package.json +4 -2
@@ -0,0 +1,218 @@
1
+ import { element, property, render, styles, context, on, dispatch, html, css, watch, query } from 'snice';
2
+ import type { Context } from 'snice';
3
+ import type { Principal, User } from '../types/auth';
4
+ import { logout } from '../services/auth';
5
+
6
+ @element('app-header')
7
+ export class AppHeader extends HTMLElement {
8
+ @property() userName = '';
9
+ @property() userAvatar = '';
10
+ @property({ type: Boolean }) authenticated = false;
11
+ @property({ type: Boolean }) menuOpen = false;
12
+
13
+ @query('.user-menu') $menu!: HTMLElement;
14
+
15
+ @context()
16
+ handleContext(ctx: Context) {
17
+ const principal = ctx.application.principal as Principal | undefined;
18
+ const user = principal?.user;
19
+ this.authenticated = principal?.isAuthenticated || false;
20
+ this.userName = user?.name || '';
21
+ this.userAvatar = user?.avatar || '';
22
+ }
23
+
24
+ @watch('menuOpen')
25
+ onMenuToggle() {
26
+ if (this.$menu) {
27
+ this.$menu.style.display = this.menuOpen ? 'block' : 'none';
28
+ }
29
+ }
30
+
31
+ toggleMenu() {
32
+ this.menuOpen = !this.menuOpen;
33
+ }
34
+
35
+ @on('keydown:Escape')
36
+ closeMenu() {
37
+ this.menuOpen = false;
38
+ }
39
+
40
+ @dispatch('header-logout')
41
+ async handleLogout() {
42
+ await logout();
43
+ window.location.hash = '#/login';
44
+ return { loggedOut: true };
45
+ }
46
+
47
+ @render()
48
+ renderContent() {
49
+ return html`
50
+ <header>
51
+ <div class="brand">
52
+ <a href="#/">{{projectName}}</a>
53
+ </div>
54
+
55
+ <if ${this.authenticated}>
56
+ <nav class="nav-links">
57
+ <a href="#/dashboard">Dashboard</a>
58
+ <a href="#/data">Data</a>
59
+ <a href="#/notifications">
60
+ <notification-badge></notification-badge>
61
+ Notifications
62
+ </a>
63
+ </nav>
64
+
65
+ <div class="user-section">
66
+ <button class="user-btn" @click=${this.toggleMenu}>
67
+ <if ${this.userAvatar}>
68
+ <snice-avatar
69
+ src="${this.userAvatar}"
70
+ name="${this.userName}"
71
+ size="small"
72
+ ></snice-avatar>
73
+ </if>
74
+ <if ${!this.userAvatar}>
75
+ <snice-avatar
76
+ name="${this.userName}"
77
+ size="small"
78
+ ></snice-avatar>
79
+ </if>
80
+ <span class="user-name">${this.userName}</span>
81
+ </button>
82
+
83
+ <div class="user-menu" style="display: none">
84
+ <a href="#/profile" @click=${() => this.menuOpen = false}>Profile</a>
85
+ <a href="#/settings" @click=${() => this.menuOpen = false}>Settings</a>
86
+ <snice-divider></snice-divider>
87
+ <button @click=${this.handleLogout}>Sign Out</button>
88
+ </div>
89
+ </div>
90
+ </if>
91
+
92
+ <if ${!this.authenticated}>
93
+ <a href="#/login" class="login-link">Sign In</a>
94
+ </if>
95
+ </header>
96
+ `;
97
+ }
98
+
99
+ @styles()
100
+ componentStyles() {
101
+ return css`
102
+ :host {
103
+ display: block;
104
+ }
105
+
106
+ header {
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: space-between;
110
+ padding: 0 1.5rem;
111
+ height: 60px;
112
+ background: var(--bg-primary);
113
+ border-bottom: 1px solid var(--border-color);
114
+ }
115
+
116
+ .brand a {
117
+ font-size: 1.25rem;
118
+ font-weight: 700;
119
+ color: var(--primary-color);
120
+ text-decoration: none;
121
+ }
122
+
123
+ .nav-links {
124
+ display: flex;
125
+ gap: 1.5rem;
126
+ align-items: center;
127
+ }
128
+
129
+ .nav-links a {
130
+ color: var(--text-light);
131
+ text-decoration: none;
132
+ font-size: 0.875rem;
133
+ font-weight: 500;
134
+ display: flex;
135
+ align-items: center;
136
+ gap: 0.375rem;
137
+ }
138
+
139
+ .nav-links a:hover {
140
+ color: var(--primary-color);
141
+ }
142
+
143
+ .user-section {
144
+ position: relative;
145
+ }
146
+
147
+ .user-btn {
148
+ display: flex;
149
+ align-items: center;
150
+ gap: 0.5rem;
151
+ background: none;
152
+ border: none;
153
+ cursor: pointer;
154
+ padding: 0.375rem 0.5rem;
155
+ border-radius: var(--radius-md);
156
+ color: var(--text-color);
157
+ }
158
+
159
+ .user-btn:hover {
160
+ background: var(--bg-secondary);
161
+ }
162
+
163
+ .user-name {
164
+ font-size: 0.875rem;
165
+ font-weight: 500;
166
+ }
167
+
168
+ .user-menu {
169
+ position: absolute;
170
+ right: 0;
171
+ top: 100%;
172
+ margin-top: 0.5rem;
173
+ min-width: 180px;
174
+ background: var(--bg-primary);
175
+ border: 1px solid var(--border-color);
176
+ border-radius: var(--radius-md);
177
+ box-shadow: var(--shadow-lg);
178
+ padding: 0.5rem 0;
179
+ z-index: 100;
180
+ }
181
+
182
+ .user-menu a,
183
+ .user-menu button {
184
+ display: block;
185
+ width: 100%;
186
+ padding: 0.5rem 1rem;
187
+ color: var(--text-color);
188
+ text-decoration: none;
189
+ background: none;
190
+ border: none;
191
+ cursor: pointer;
192
+ text-align: left;
193
+ font-size: 0.875rem;
194
+ }
195
+
196
+ .user-menu a:hover,
197
+ .user-menu button:hover {
198
+ background: var(--bg-secondary);
199
+ color: var(--primary-color);
200
+ }
201
+
202
+ .login-link {
203
+ color: var(--primary-color);
204
+ text-decoration: none;
205
+ font-weight: 500;
206
+ }
207
+
208
+ @media (max-width: 640px) {
209
+ .nav-links {
210
+ display: none;
211
+ }
212
+ .user-name {
213
+ display: none;
214
+ }
215
+ }
216
+ `;
217
+ }
218
+ }
@@ -0,0 +1,68 @@
1
+ import { element, property, render, styles, ready, dispose, html, css, watch, query } from 'snice';
2
+ import { getNotificationsDaemon } from '../daemons/notifications';
3
+
4
+ @element('notification-badge')
5
+ export class NotificationBadge extends HTMLElement {
6
+ @property({ type: Number }) count = 0;
7
+
8
+ private unsubscribe: (() => void) | null = null;
9
+
10
+ @query('.badge') $badge!: HTMLElement;
11
+
12
+ @ready()
13
+ initialize() {
14
+ const daemon = getNotificationsDaemon();
15
+ this.unsubscribe = daemon.subscribe(() => {
16
+ this.count++;
17
+ });
18
+ }
19
+
20
+ @dispose()
21
+ cleanup() {
22
+ if (this.unsubscribe) {
23
+ this.unsubscribe();
24
+ this.unsubscribe = null;
25
+ }
26
+ }
27
+
28
+ @watch('count')
29
+ onCountChange() {
30
+ if (this.$badge) {
31
+ this.$badge.textContent = this.count > 99 ? '99+' : String(this.count);
32
+ this.$badge.style.display = this.count > 0 ? 'flex' : 'none';
33
+ }
34
+ }
35
+
36
+ @render({ once: true })
37
+ renderContent() {
38
+ const display = this.count > 0 ? 'flex' : 'none';
39
+ return html`
40
+ <span class="badge" style="display: ${display}">${this.count}</span>
41
+ `;
42
+ }
43
+
44
+ @styles()
45
+ componentStyles() {
46
+ return css`
47
+ :host {
48
+ display: inline-flex;
49
+ position: relative;
50
+ }
51
+
52
+ .badge {
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ min-width: 18px;
57
+ height: 18px;
58
+ padding: 0 4px;
59
+ background: var(--danger-color);
60
+ color: white;
61
+ font-size: 0.6875rem;
62
+ font-weight: 600;
63
+ border-radius: 9px;
64
+ line-height: 1;
65
+ }
66
+ `;
67
+ }
68
+ }
@@ -0,0 +1,99 @@
1
+ import { element, property, render, styles, on, dispatch, html, css } from 'snice';
2
+
3
+ @element('search-bar')
4
+ export class SearchBar extends HTMLElement {
5
+ @property() value = '';
6
+ @property() placeholder = 'Search...';
7
+
8
+ @on('input', 'input', { debounce: 300 })
9
+ handleInput(e: Event) {
10
+ this.value = (e.target as HTMLInputElement).value;
11
+ this.dispatchSearch();
12
+ }
13
+
14
+ @on('keydown:Escape', 'input')
15
+ clearSearch(e: Event) {
16
+ this.value = '';
17
+ (e.target as HTMLInputElement).value = '';
18
+ this.dispatchSearch();
19
+ }
20
+
21
+ @dispatch('search')
22
+ dispatchSearch() {
23
+ return { query: this.value };
24
+ }
25
+
26
+ @render()
27
+ renderContent() {
28
+ return html`
29
+ <div class="search-container">
30
+ <span class="icon">&#128269;</span>
31
+ <input
32
+ type="text"
33
+ .value=${this.value}
34
+ placeholder="${this.placeholder}"
35
+ />
36
+ <if ${this.value.length > 0}>
37
+ <button class="clear" @click=${() => { this.value = ''; this.dispatchSearch(); }}>&#10005;</button>
38
+ </if>
39
+ </div>
40
+ `;
41
+ }
42
+
43
+ @styles()
44
+ componentStyles() {
45
+ return css`
46
+ :host {
47
+ display: block;
48
+ }
49
+
50
+ .search-container {
51
+ display: flex;
52
+ align-items: center;
53
+ gap: 0.5rem;
54
+ padding: 0.5rem 0.75rem;
55
+ background: var(--bg-primary);
56
+ border: 1px solid var(--border-color);
57
+ border-radius: var(--radius-md);
58
+ }
59
+
60
+ .search-container:focus-within {
61
+ border-color: var(--primary-color);
62
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 15%, transparent);
63
+ }
64
+
65
+ .icon {
66
+ color: var(--text-light);
67
+ font-size: 0.875rem;
68
+ }
69
+
70
+ input {
71
+ flex: 1;
72
+ border: none;
73
+ outline: none;
74
+ background: transparent;
75
+ font-size: 0.875rem;
76
+ color: var(--text-color);
77
+ }
78
+
79
+ input::placeholder {
80
+ color: var(--text-light);
81
+ }
82
+
83
+ .clear {
84
+ background: none;
85
+ border: none;
86
+ cursor: pointer;
87
+ color: var(--text-light);
88
+ font-size: 0.75rem;
89
+ padding: 0.125rem 0.25rem;
90
+ border-radius: var(--radius-sm);
91
+ }
92
+
93
+ .clear:hover {
94
+ background: var(--bg-secondary);
95
+ color: var(--text-color);
96
+ }
97
+ `;
98
+ }
99
+ }
@@ -0,0 +1,42 @@
1
+ import { controller, respond } from 'snice';
2
+ import { getNotificationsDaemon } from '../daemons/notifications';
3
+ import type { Notification } from '../types/notifications';
4
+
5
+ @controller('notification-controller')
6
+ export class NotificationController {
7
+ element!: HTMLElement;
8
+ private notifications: Notification[] = [];
9
+ private unsubscribe: (() => void) | null = null;
10
+
11
+ async attach(el: HTMLElement) {
12
+ this.element = el;
13
+ const daemon = getNotificationsDaemon();
14
+ this.unsubscribe = daemon.subscribe((notification) => {
15
+ this.notifications = [notification, ...this.notifications];
16
+ });
17
+ }
18
+
19
+ async detach() {
20
+ if (this.unsubscribe) {
21
+ this.unsubscribe();
22
+ this.unsubscribe = null;
23
+ }
24
+ }
25
+
26
+ @respond('get-notifications')
27
+ handleGetNotifications() {
28
+ return this.notifications;
29
+ }
30
+
31
+ @respond('clear-notifications')
32
+ handleClearNotifications() {
33
+ this.notifications = [];
34
+ return { cleared: true };
35
+ }
36
+
37
+ @respond('dismiss-notification')
38
+ handleDismissNotification(payload: { id: string }) {
39
+ this.notifications = this.notifications.filter(n => n.id !== payload.id);
40
+ return { dismissed: true };
41
+ }
42
+ }
@@ -1,7 +1,7 @@
1
1
  import type { AppContext } from 'snice';
2
2
  import type { Principal } from '../types/auth';
3
3
 
4
- export function authGuard(appContext: AppContext): boolean {
4
+ export function isAuthenticated(appContext: AppContext, _params: Record<string, string>): boolean {
5
5
  const principal = appContext.principal as Principal | undefined;
6
6
 
7
7
  if (!principal?.isAuthenticated) {
@@ -1,6 +1,10 @@
1
+ import { useNativeElementControllers } from 'snice';
1
2
  import { initialize } from './router';
2
3
  import './styles/global.css';
3
4
 
5
+ // Enable controllers on native HTML elements (div, form, etc.)
6
+ useNativeElementControllers();
7
+
4
8
  // Import snice layout and components
5
9
  import 'snice/components/layout/snice-layout';
6
10
  import 'snice/components/button/snice-button';
@@ -10,12 +14,27 @@ import 'snice/components/alert/snice-alert';
10
14
  import 'snice/components/avatar/snice-avatar';
11
15
  import 'snice/components/empty-state/snice-empty-state';
12
16
  import 'snice/components/spinner/snice-spinner';
17
+ import 'snice/components/badge/snice-badge';
18
+ import 'snice/components/switch/snice-switch';
19
+ import 'snice/components/divider/snice-divider';
20
+ import 'snice/components/tabs/snice-tabs';
21
+ import 'snice/components/tabs/snice-tab';
22
+
23
+ // Import components
24
+ import './components/app-header';
25
+ import './components/search-bar';
26
+ import './components/notification-badge';
27
+
28
+ // Import controllers
29
+ import './controllers/notification-controller';
13
30
 
14
31
  // Import pages
15
32
  import './pages/login';
16
33
  import './pages/dashboard';
17
34
  import './pages/profile';
18
35
  import './pages/notifications';
36
+ import './pages/settings';
37
+ import './pages/data';
19
38
 
20
39
  // Import and start daemons
21
40
  import { getNotificationsDaemon } from './daemons/notifications';