ds-one 0.2.0-alpha.2 → 0.2.5-alpha.1

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 (247) hide show
  1. package/DS1/{utils/language.ts → 0-face/i18n.ts} +197 -2
  2. package/DS1/0-face/preferences.ts +23 -0
  3. package/DS1/0-face/pricing.ts +57 -0
  4. package/DS1/1-root/one.css +1 -1
  5. package/DS1/2-core/ds-button.ts +1 -1
  6. package/DS1/2-core/ds-cycle.ts +17 -18
  7. package/DS1/2-core/{ds-year.ts → ds-date.ts} +4 -4
  8. package/DS1/2-core/ds-input.ts +1 -0
  9. package/DS1/2-core/ds-text.ts +1 -1
  10. package/DS1/2-core/ds-tooltip.ts +4 -3
  11. package/DS1/3-unit/doublenav-v1.ts +105 -0
  12. package/DS1/3-unit/{ds-doublenav.ts → ds-portfolio-doublenav.ts} +4 -5
  13. package/DS1/3-unit/ds-portfolio-panel.ts +27 -0
  14. package/DS1/3-unit/ds-portfolio-singlenav.ts +79 -0
  15. package/DS1/3-unit/list-v1.ts +24 -0
  16. package/DS1/3-unit/{ds-panel.ts → panel-v1.ts} +2 -3
  17. package/DS1/3-unit/row-v1.ts +52 -0
  18. package/DS1/3-unit/{ds-singlenav.ts → singlenav-v1.ts} +4 -5
  19. package/DS1/4-page/ds-grid.ts +1 -1
  20. package/DS1/index.ts +39 -37
  21. package/README.md +3 -3
  22. package/dist/0-face/{2025-04-23-device.d.ts → device.d.ts} +1 -1
  23. package/dist/0-face/device.d.ts.map +1 -0
  24. package/dist/{utils/language.d.ts → 0-face/i18n.d.ts} +1 -1
  25. package/dist/0-face/i18n.d.ts.map +1 -0
  26. package/dist/{utils/language.js → 0-face/i18n.js} +141 -2
  27. package/dist/0-face/preferences.d.ts +9 -0
  28. package/dist/0-face/preferences.d.ts.map +1 -0
  29. package/dist/0-face/preferences.js +14 -0
  30. package/dist/0-face/pricing.d.ts +15 -0
  31. package/dist/0-face/pricing.d.ts.map +1 -0
  32. package/dist/0-face/pricing.js +46 -0
  33. package/dist/0-face/theme.d.ts.map +1 -0
  34. package/dist/2-core/ds-button.js +1 -1
  35. package/dist/2-core/ds-cycle.js +15 -15
  36. package/dist/2-core/{ds-year.d.ts → ds-date.d.ts} +4 -4
  37. package/dist/2-core/ds-date.d.ts.map +1 -0
  38. package/dist/2-core/{ds-year.js → ds-date.js} +4 -4
  39. package/dist/2-core/ds-input.d.ts +1 -0
  40. package/dist/2-core/ds-input.d.ts.map +1 -0
  41. package/dist/2-core/ds-input.js +1 -0
  42. package/dist/2-core/ds-text.js +1 -1
  43. package/dist/2-core/ds-tooltip.d.ts.map +1 -1
  44. package/dist/2-core/ds-tooltip.js +4 -2
  45. package/dist/3-unit/{ds-doublenav.d.ts → ds-portfolio-doublenav.d.ts} +4 -4
  46. package/dist/3-unit/ds-portfolio-doublenav.d.ts.map +1 -0
  47. package/dist/3-unit/{ds-doublenav.js → ds-portfolio-doublenav.js} +4 -4
  48. package/dist/3-unit/{ds-panel.d.ts → ds-portfolio-panel.d.ts} +3 -3
  49. package/dist/3-unit/ds-portfolio-panel.d.ts.map +1 -0
  50. package/dist/3-unit/{ds-panel.js → ds-portfolio-panel.js} +3 -3
  51. package/dist/3-unit/{ds-singlenav.d.ts → ds-portfolio-singlenav.d.ts} +4 -4
  52. package/dist/3-unit/ds-portfolio-singlenav.d.ts.map +1 -0
  53. package/dist/3-unit/{ds-singlenav.js → ds-portfolio-singlenav.js} +4 -4
  54. package/dist/4-page/ds-grid.js +1 -1
  55. package/dist/ds-one.bundle.js +2087 -3806
  56. package/dist/ds-one.bundle.js.map +4 -4
  57. package/dist/ds-one.bundle.min.js +74 -740
  58. package/dist/ds-one.bundle.min.js.map +4 -4
  59. package/dist/index.d.ts +16 -26
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +32 -34
  62. package/package.json +2 -2
  63. package/DS1/0-face/2025-04-23-language.ts +0 -4
  64. package/DS1/2-core/ds-article.ts +0 -454
  65. package/DS1/2-core/ds-attributes.ts +0 -155
  66. package/DS1/2-core/ds-downloadcv.ts +0 -146
  67. package/DS1/2-core/ds-header.ts +0 -82
  68. package/DS1/2-core/ds-home.ts +0 -168
  69. package/DS1/2-core/ds-link.ts +0 -121
  70. package/DS1/2-core/ds-markdown.ts +0 -252
  71. package/DS1/2-core/ds-price.ts +0 -108
  72. package/DS1/2-core/ds-squarecircle.ts +0 -155
  73. package/DS1/2-core/ds-title.ts +0 -139
  74. package/DS1/2-core/ds-viewtoggle.ts +0 -83
  75. package/DS1/utils/cdn-loader.ts +0 -232
  76. package/DS1/utils/keys.json +0 -41
  77. package/DS1/utils/pricing.ts +0 -24
  78. package/DS1/utils/scroll.ts +0 -184
  79. package/DS1/utils/settings.ts +0 -23
  80. package/DS1/utils/viewMode.ts +0 -55
  81. package/dist/0-face/2025-04-23-device.d.ts.map +0 -1
  82. package/dist/0-face/2025-04-23-language.d.ts +0 -1
  83. package/dist/0-face/2025-04-23-language.d.ts.map +0 -1
  84. package/dist/0-face/2025-04-23-language.js +0 -3
  85. package/dist/2-core/article-v1.d.ts +0 -129
  86. package/dist/2-core/article-v1.d.ts.map +0 -1
  87. package/dist/2-core/article-v1.js +0 -361
  88. package/dist/2-core/attributes-v1.d.ts +0 -47
  89. package/dist/2-core/attributes-v1.d.ts.map +0 -1
  90. package/dist/2-core/attributes-v1.js +0 -128
  91. package/dist/2-core/cycle-v1.d.ts +0 -66
  92. package/dist/2-core/cycle-v1.d.ts.map +0 -1
  93. package/dist/2-core/cycle-v1.js +0 -586
  94. package/dist/2-core/downloadcv-v1.d.ts +0 -58
  95. package/dist/2-core/downloadcv-v1.d.ts.map +0 -1
  96. package/dist/2-core/downloadcv-v1.js +0 -119
  97. package/dist/2-core/ds-article.d.ts +0 -129
  98. package/dist/2-core/ds-article.d.ts.map +0 -1
  99. package/dist/2-core/ds-article.js +0 -361
  100. package/dist/2-core/ds-attributes.d.ts +0 -47
  101. package/dist/2-core/ds-attributes.d.ts.map +0 -1
  102. package/dist/2-core/ds-attributes.js +0 -128
  103. package/dist/2-core/ds-button.figma.d.ts +0 -2
  104. package/dist/2-core/ds-button.figma.d.ts.map +0 -1
  105. package/dist/2-core/ds-button.figma.js +0 -6
  106. package/dist/2-core/ds-downloadcv.d.ts +0 -58
  107. package/dist/2-core/ds-downloadcv.d.ts.map +0 -1
  108. package/dist/2-core/ds-downloadcv.js +0 -119
  109. package/dist/2-core/ds-header.d.ts +0 -28
  110. package/dist/2-core/ds-header.d.ts.map +0 -1
  111. package/dist/2-core/ds-header.js +0 -66
  112. package/dist/2-core/ds-home.d.ts +0 -26
  113. package/dist/2-core/ds-home.d.ts.map +0 -1
  114. package/dist/2-core/ds-home.js +0 -148
  115. package/dist/2-core/ds-link.d.ts +0 -35
  116. package/dist/2-core/ds-link.d.ts.map +0 -1
  117. package/dist/2-core/ds-link.js +0 -85
  118. package/dist/2-core/ds-markdown.d.ts +0 -7
  119. package/dist/2-core/ds-markdown.d.ts.map +0 -1
  120. package/dist/2-core/ds-markdown.js +0 -240
  121. package/dist/2-core/ds-price.d.ts +0 -46
  122. package/dist/2-core/ds-price.d.ts.map +0 -1
  123. package/dist/2-core/ds-price.js +0 -72
  124. package/dist/2-core/ds-squarecircle.d.ts +0 -50
  125. package/dist/2-core/ds-squarecircle.d.ts.map +0 -1
  126. package/dist/2-core/ds-squarecircle.js +0 -133
  127. package/dist/2-core/ds-title.d.ts +0 -50
  128. package/dist/2-core/ds-title.d.ts.map +0 -1
  129. package/dist/2-core/ds-title.js +0 -103
  130. package/dist/2-core/ds-viewtoggle.d.ts +0 -27
  131. package/dist/2-core/ds-viewtoggle.d.ts.map +0 -1
  132. package/dist/2-core/ds-viewtoggle.js +0 -49
  133. package/dist/2-core/ds-year.d.ts.map +0 -1
  134. package/dist/2-core/header-v1.d.ts +0 -28
  135. package/dist/2-core/header-v1.d.ts.map +0 -1
  136. package/dist/2-core/header-v1.js +0 -66
  137. package/dist/2-core/home-v1.d.ts +0 -26
  138. package/dist/2-core/home-v1.d.ts.map +0 -1
  139. package/dist/2-core/home-v1.js +0 -148
  140. package/dist/2-core/icon-v1.d.ts +0 -28
  141. package/dist/2-core/icon-v1.d.ts.map +0 -1
  142. package/dist/2-core/icon-v1.js +0 -297
  143. package/dist/2-core/link-v1.d.ts +0 -35
  144. package/dist/2-core/link-v1.d.ts.map +0 -1
  145. package/dist/2-core/link-v1.js +0 -85
  146. package/dist/2-core/markdown-v1.d.ts +0 -7
  147. package/dist/2-core/markdown-v1.d.ts.map +0 -1
  148. package/dist/2-core/markdown-v1.js +0 -240
  149. package/dist/2-core/price-v1.d.ts +0 -46
  150. package/dist/2-core/price-v1.d.ts.map +0 -1
  151. package/dist/2-core/price-v1.js +0 -72
  152. package/dist/2-core/squarecircle-v1.d.ts +0 -50
  153. package/dist/2-core/squarecircle-v1.d.ts.map +0 -1
  154. package/dist/2-core/squarecircle-v1.js +0 -133
  155. package/dist/2-core/text-v1.d.ts +0 -48
  156. package/dist/2-core/text-v1.d.ts.map +0 -1
  157. package/dist/2-core/text-v1.js +0 -83
  158. package/dist/2-core/title-v1.d.ts +0 -50
  159. package/dist/2-core/title-v1.d.ts.map +0 -1
  160. package/dist/2-core/title-v1.js +0 -103
  161. package/dist/2-core/tooltip-v1.d.ts +0 -39
  162. package/dist/2-core/tooltip-v1.d.ts.map +0 -1
  163. package/dist/2-core/tooltip-v1.js +0 -145
  164. package/dist/2-core/viewtoggle-v1.d.ts +0 -27
  165. package/dist/2-core/viewtoggle-v1.d.ts.map +0 -1
  166. package/dist/2-core/viewtoggle-v1.js +0 -49
  167. package/dist/2-core/year-v1.d.ts +0 -16
  168. package/dist/2-core/year-v1.d.ts.map +0 -1
  169. package/dist/2-core/year-v1.js +0 -21
  170. package/dist/3-unit/ds-doublenav.d.ts.map +0 -1
  171. package/dist/3-unit/ds-panel.d.ts.map +0 -1
  172. package/dist/3-unit/ds-singlenav.d.ts.map +0 -1
  173. package/dist/utils/cdn-loader.d.ts +0 -19
  174. package/dist/utils/cdn-loader.d.ts.map +0 -1
  175. package/dist/utils/cdn-loader.js +0 -160
  176. package/dist/utils/keys.json +0 -41
  177. package/dist/utils/language.d.ts.map +0 -1
  178. package/dist/utils/pricing.d.ts +0 -8
  179. package/dist/utils/pricing.d.ts.map +0 -1
  180. package/dist/utils/pricing.js +0 -14
  181. package/dist/utils/scroll.d.ts +0 -34
  182. package/dist/utils/scroll.d.ts.map +0 -1
  183. package/dist/utils/scroll.js +0 -140
  184. package/dist/utils/settings.d.ts +0 -9
  185. package/dist/utils/settings.d.ts.map +0 -1
  186. package/dist/utils/settings.js +0 -14
  187. package/dist/utils/theme.d.ts.map +0 -1
  188. package/dist/utils/viewMode.d.ts +0 -14
  189. package/dist/utils/viewMode.d.ts.map +0 -1
  190. package/dist/utils/viewMode.js +0 -46
  191. /package/DS1/0-face/{2025-04-23-device.ts → device.ts} +0 -0
  192. /package/DS1/{utils → 0-face}/theme.ts +0 -0
  193. /package/DS1/{x Icon → x-icon}/1x.svg +0 -0
  194. /package/DS1/{x Icon → x-icon}/1xdots.svg +0 -0
  195. /package/DS1/{x Icon → x-icon}/1xgrid.svg +0 -0
  196. /package/DS1/{x Icon → x-icon}/1xlines.svg +0 -0
  197. /package/DS1/{x Icon → x-icon}/2x.svg +0 -0
  198. /package/DS1/{x Icon → x-icon}/2xdots.svg +0 -0
  199. /package/DS1/{x Icon → x-icon}/2xgrid.svg +0 -0
  200. /package/DS1/{x Icon → x-icon}/2xlines.svg +0 -0
  201. /package/DS1/{x Icon → x-icon}/big.svg +0 -0
  202. /package/DS1/{x Icon → x-icon}/blank.svg +0 -0
  203. /package/DS1/{x Icon → x-icon}/check.svg +0 -0
  204. /package/DS1/{x Icon → x-icon}/close.svg +0 -0
  205. /package/DS1/{x Icon → x-icon}/collapse.svg +0 -0
  206. /package/DS1/{x Icon → x-icon}/color.svg +0 -0
  207. /package/DS1/{x Icon → x-icon}/column.svg +0 -0
  208. /package/DS1/{x Icon → x-icon}/default.svg +0 -0
  209. /package/DS1/{x Icon → x-icon}/delete.svg +0 -0
  210. /package/DS1/{x Icon → x-icon}/do.svg +0 -0
  211. /package/DS1/{x Icon → x-icon}/down.svg +0 -0
  212. /package/DS1/{x Icon → x-icon}/duplicate.svg +0 -0
  213. /package/DS1/{x Icon → x-icon}/email.svg +0 -0
  214. /package/DS1/{x Icon → x-icon}/expand.svg +0 -0
  215. /package/DS1/{x Icon → x-icon}/gallery.svg +0 -0
  216. /package/DS1/{x Icon → x-icon}/group.svg +0 -0
  217. /package/DS1/{x Icon → x-icon}/head.svg +0 -0
  218. /package/DS1/{x Icon → x-icon}/icon.svg +0 -0
  219. /package/DS1/{x Icon → x-icon}/left.svg +0 -0
  220. /package/DS1/{x Icon → x-icon}/lock.svg +0 -0
  221. /package/DS1/{x Icon → x-icon}/mic.svg +0 -0
  222. /package/DS1/{x Icon → x-icon}/minimize.svg +0 -0
  223. /package/DS1/{x Icon → x-icon}/more.svg +0 -0
  224. /package/DS1/{x Icon → x-icon}/note.svg +0 -0
  225. /package/DS1/{x Icon → x-icon}/open.svg +0 -0
  226. /package/DS1/{x Icon → x-icon}/page.svg +0 -0
  227. /package/DS1/{x Icon → x-icon}/plus.svg +0 -0
  228. /package/DS1/{x Icon → x-icon}/rewind.svg +0 -0
  229. /package/DS1/{x Icon → x-icon}/right.svg +0 -0
  230. /package/DS1/{x Icon → x-icon}/row..svg +0 -0
  231. /package/DS1/{x Icon → x-icon}/search.svg +0 -0
  232. /package/DS1/{x Icon → x-icon}/see.svg +0 -0
  233. /package/DS1/{x Icon → x-icon}/star.svg +0 -0
  234. /package/DS1/{x Icon → x-icon}/title.svg +0 -0
  235. /package/DS1/{x Icon → x-icon}/undo.svg +0 -0
  236. /package/DS1/{x Icon → x-icon}/ungroup.svg +0 -0
  237. /package/DS1/{x Icon → x-icon}/unhead.svg +0 -0
  238. /package/DS1/{x Icon → x-icon}/unicon.svg +0 -0
  239. /package/DS1/{x Icon → x-icon}/unlock.svg +0 -0
  240. /package/DS1/{x Icon → x-icon}/unmic.svg +0 -0
  241. /package/DS1/{x Icon → x-icon}/unsee.svg +0 -0
  242. /package/DS1/{x Icon → x-icon}/unstar.svg +0 -0
  243. /package/DS1/{x Icon → x-icon}/untitle.svg +0 -0
  244. /package/DS1/{x Icon → x-icon}/up.svg +0 -0
  245. /package/dist/0-face/{2025-04-23-device.js → device.js} +0 -0
  246. /package/dist/{utils → 0-face}/theme.d.ts +0 -0
  247. /package/dist/{utils → 0-face}/theme.js +0 -0
@@ -8,8 +8,9 @@ type TranslationData = {
8
8
 
9
9
  type TranslationMap = Record<string, TranslationData>;
10
10
 
11
- // Import the JSON directly to ensure it's bundled
12
- import translationKeys from "./keys.json";
11
+ // Bundled translations (keys.json may not exist - will use external translations if not available)
12
+ // This is a fallback for when external translations aren't loaded
13
+ let translationKeys: TranslationMap = {};
13
14
 
14
15
  // Primary language list – prioritise the 10 requested languages when cycling
15
16
  const LANGUAGE_PRIORITY_ORDER = [
@@ -67,6 +68,187 @@ declare global {
67
68
  }
68
69
  }
69
70
 
71
+ // CDN Loader: Automatically detects and loads translation JSON files
72
+ // for CDN users who want to use external translations
73
+
74
+ const DEFAULT_TRANSLATION_FILE = "./translations.json";
75
+ let loadAttempted = false;
76
+
77
+ function normalizeCandidate(path: string): string | null {
78
+ if (!path) {
79
+ return null;
80
+ }
81
+ const trimmed = path.trim();
82
+ if (!trimmed) {
83
+ return null;
84
+ }
85
+
86
+ if (
87
+ trimmed.startsWith("./") ||
88
+ trimmed.startsWith("../") ||
89
+ trimmed.startsWith("/") ||
90
+ /^https?:\/\//i.test(trimmed)
91
+ ) {
92
+ return trimmed;
93
+ }
94
+
95
+ return `./${trimmed}`;
96
+ }
97
+
98
+ function findAttributeCandidate(): string | null {
99
+ if (typeof document === "undefined") {
100
+ return null;
101
+ }
102
+
103
+ const scriptWithAttribute = document.querySelector(
104
+ "script[data-ds-one-translations]"
105
+ );
106
+ const scriptCandidate = scriptWithAttribute?.getAttribute(
107
+ "data-ds-one-translations"
108
+ );
109
+ if (scriptCandidate) {
110
+ return scriptCandidate;
111
+ }
112
+
113
+ const metaCandidate = document
114
+ .querySelector('meta[name="ds-one:translations"]')
115
+ ?.getAttribute("content");
116
+ if (metaCandidate) {
117
+ return metaCandidate;
118
+ }
119
+
120
+ const linkCandidate = document
121
+ .querySelector('link[rel="ds-one-translations"]')
122
+ ?.getAttribute("href");
123
+ if (linkCandidate) {
124
+ return linkCandidate;
125
+ }
126
+
127
+ return null;
128
+ }
129
+
130
+ function resolveTranslationSources(): string[] {
131
+ const candidates: string[] = [];
132
+
133
+ const windowCandidate =
134
+ typeof window !== "undefined" ? window.DS_ONE_TRANSLATIONS_FILE : null;
135
+ const attributeCandidate = findAttributeCandidate();
136
+
137
+ // Only use explicitly configured paths, or the single default
138
+ const windowNormalized = normalizeCandidate(windowCandidate ?? "");
139
+ if (windowNormalized) {
140
+ candidates.push(windowNormalized);
141
+ }
142
+
143
+ const attrNormalized = normalizeCandidate(attributeCandidate ?? "");
144
+ if (attrNormalized && !candidates.includes(attrNormalized)) {
145
+ candidates.push(attrNormalized);
146
+ }
147
+
148
+ // Only try default if no explicit path was configured
149
+ if (candidates.length === 0) {
150
+ candidates.push(DEFAULT_TRANSLATION_FILE);
151
+ }
152
+
153
+ return candidates;
154
+ }
155
+
156
+ function validateTranslationMap(
157
+ candidate: unknown
158
+ ): candidate is TranslationMap {
159
+ if (!candidate || typeof candidate !== "object") {
160
+ return false;
161
+ }
162
+
163
+ return Object.values(candidate).every(
164
+ (entry) => entry && typeof entry === "object"
165
+ );
166
+ }
167
+
168
+ async function fetchTranslationFile(
169
+ source: string
170
+ ): Promise<TranslationMap | null> {
171
+ try {
172
+ const response = await fetch(source);
173
+
174
+ if (!response.ok) {
175
+ // 404 is expected if no translations file exists - don't log as error
176
+ return null;
177
+ }
178
+
179
+ const translations = await response.json();
180
+
181
+ if (!validateTranslationMap(translations)) {
182
+ console.warn(
183
+ `[DS one] Invalid translation format in ${source}. Expected object with language codes as keys.`
184
+ );
185
+ return null;
186
+ }
187
+
188
+ const languages = Object.keys(translations);
189
+ if (languages.length === 0) {
190
+ console.warn(`[DS one] No languages found in ${source}`);
191
+ return null;
192
+ }
193
+
194
+ return translations;
195
+ } catch {
196
+ // Silently fail - file likely doesn't exist or isn't valid JSON
197
+ return null;
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Attempts to load translations from a JSON file in the same directory
203
+ */
204
+ async function loadExternalTranslations(): Promise<boolean> {
205
+ // Only attempt once
206
+ if (loadAttempted) {
207
+ return false;
208
+ }
209
+ loadAttempted = true;
210
+
211
+ if (typeof window === "undefined") {
212
+ return false;
213
+ }
214
+
215
+ // Check if translations are already loaded (e.g., by the application)
216
+ if (
217
+ window.DS_ONE_TRANSLATIONS &&
218
+ Object.keys(window.DS_ONE_TRANSLATIONS).length > 0
219
+ ) {
220
+ console.log(
221
+ `[DS one] Translations already loaded (${Object.keys(window.DS_ONE_TRANSLATIONS).length} languages), skipping auto-load`
222
+ );
223
+ return true;
224
+ }
225
+
226
+ const sources = resolveTranslationSources();
227
+
228
+ for (const source of sources) {
229
+ const translations = await fetchTranslationFile(source);
230
+ if (!translations) {
231
+ continue;
232
+ }
233
+
234
+ window.DS_ONE_TRANSLATIONS = translations;
235
+
236
+ const languages = Object.keys(translations);
237
+ console.log(
238
+ `[DS one] External translations loaded from ${source}: ${languages.length} language(s) – ${languages.join(", ")}`
239
+ );
240
+
241
+ window.dispatchEvent(new CustomEvent("translations-ready"));
242
+ return true;
243
+ }
244
+
245
+ console.info(
246
+ `[DS one] No external translations found at ${sources[0] ?? DEFAULT_TRANSLATION_FILE}. Using bundled translations.`
247
+ );
248
+
249
+ return false;
250
+ }
251
+
70
252
  // Get translation data - prioritize external, fall back to bundled
71
253
  function getTranslationData(): TranslationMap {
72
254
  // Check for externally loaded translations first (CDN usage)
@@ -329,6 +511,19 @@ export const currentLanguage = {
329
511
  },
330
512
  };
331
513
 
514
+ // Auto-load translations when this module is imported (for CDN bundle)
515
+ if (typeof window !== "undefined") {
516
+ // Wait a bit to ensure the DOM is ready
517
+ if (document.readyState === "loading") {
518
+ document.addEventListener("DOMContentLoaded", () => {
519
+ loadExternalTranslations();
520
+ });
521
+ } else {
522
+ // DOM is already ready
523
+ loadExternalTranslations();
524
+ }
525
+ }
526
+
332
527
  // Listen for external translations being loaded
333
528
  if (typeof window !== "undefined") {
334
529
  window.addEventListener("translations-ready", () => {
@@ -0,0 +1,23 @@
1
+ import type { LanguageCode } from "./i18n";
2
+ import type { ThemeType } from "./theme";
3
+
4
+ export type Preferences = {
5
+ language?: LanguageCode;
6
+ theme?: ThemeType;
7
+ [key: string]: unknown;
8
+ };
9
+
10
+ export function savePreferences(preferences: Preferences): void {
11
+ if (typeof window === "undefined") {
12
+ return;
13
+ }
14
+
15
+ try {
16
+ const raw = window.localStorage?.getItem("ds-one:preferences");
17
+ const existing = raw ? (JSON.parse(raw) as Record<string, unknown>) : {};
18
+ const next = { ...existing, ...preferences };
19
+ window.localStorage?.setItem("ds-one:preferences", JSON.stringify(next));
20
+ } catch (error) {
21
+ console.warn("ds-one: unable to persist preferences", error);
22
+ }
23
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Currency label utilities for regional price display
3
+ *
4
+ * Note: This module provides currency symbols/labels based on language and region.
5
+ * Consider moving this functionality into i18n.ts as it's region/locale-related.
6
+ * Actual price values will be stored in a database or managed via Stripe.
7
+ */
8
+
9
+ import type { LanguageCode } from "./i18n";
10
+
11
+ type PriceLabelOptions = {
12
+ language: LanguageCode;
13
+ country?: string;
14
+ };
15
+
16
+ // Simple price label mapping based on language/country
17
+ const PRICE_LABELS: Record<string, string> = {
18
+ da: "kr.",
19
+ nb: "kr.",
20
+ sv: "kr.",
21
+ de: "€",
22
+ en: "$",
23
+ pt: "€",
24
+ es: "€",
25
+ zh: "¥",
26
+ ja: "¥",
27
+ ko: "₩",
28
+ };
29
+
30
+ export function getPriceLabel(options: PriceLabelOptions): string {
31
+ const { language, country } = options;
32
+
33
+ // If country is provided, try to map it to a currency
34
+ if (country) {
35
+ const countryUpper = country.toUpperCase();
36
+ // Add country-specific mappings if needed
37
+ if (countryUpper === "US" || countryUpper === "USA") {
38
+ return "$";
39
+ }
40
+ if (countryUpper === "GB" || countryUpper === "UK") {
41
+ return "£";
42
+ }
43
+ if (countryUpper === "JP" || countryUpper === "JPN") {
44
+ return "¥";
45
+ }
46
+ if (countryUpper === "CN" || countryUpper === "CHN") {
47
+ return "¥";
48
+ }
49
+ if (countryUpper === "KR" || countryUpper === "KOR") {
50
+ return "₩";
51
+ }
52
+ }
53
+
54
+ // Fall back to language-based mapping
55
+ const primaryLang = language.toLowerCase().split(/[-_]/)[0];
56
+ return PRICE_LABELS[primaryLang] || "$";
57
+ }
@@ -1,4 +1,4 @@
1
- /* version 0.2.0-alpha.2 */
1
+ /* version 0.2.0-alpha.3 */
2
2
 
3
3
  input {
4
4
  padding: 0;
@@ -2,7 +2,7 @@
2
2
  // Core button component
3
3
 
4
4
  import { LitElement, html, css, type PropertyValues } from "lit";
5
- import { getText, currentLanguage } from "../utils/language";
5
+ import { getText, currentLanguage } from "../0-face/i18n";
6
6
 
7
7
  export class Button extends LitElement {
8
8
  static properties = {
@@ -5,11 +5,11 @@ import {
5
5
  getAvailableLanguagesSync,
6
6
  getLanguageDisplayName,
7
7
  setLanguage,
8
- } from "../utils/language";
9
- import type { LanguageCode } from "../utils/language";
10
- import { currentTheme, setTheme } from "../utils/theme";
11
- import type { ThemeType } from "../utils/theme";
12
- import { saveSettings } from "../utils/settings";
8
+ } from "../0-face/i18n";
9
+ import type { LanguageCode } from "../0-face/i18n";
10
+ import { currentTheme, setTheme } from "../0-face/theme";
11
+ import type { ThemeType } from "../0-face/theme";
12
+ import { savePreferences } from "../0-face/preferences";
13
13
  import "./ds-button";
14
14
  import "./ds-icon";
15
15
 
@@ -362,8 +362,8 @@ export class Cycle extends LitElement {
362
362
  setLanguage(newLanguage as LanguageCode);
363
363
  }
364
364
 
365
- // Save settings
366
- saveSettings({ language: newLanguage as LanguageCode });
365
+ // Save preferences
366
+ savePreferences({ language: newLanguage as LanguageCode });
367
367
 
368
368
  // Dispatch language change event
369
369
  window.dispatchEvent(
@@ -383,8 +383,8 @@ export class Cycle extends LitElement {
383
383
  // Set the new theme using the shared helper
384
384
  setTheme(newTheme as ThemeType);
385
385
 
386
- // Save settings
387
- saveSettings({ theme: newTheme as ThemeType });
386
+ // Save preferences
387
+ savePreferences({ theme: newTheme as ThemeType });
388
388
 
389
389
  // Theme helper already emits the change event, so no manual dispatch here
390
390
  } else if (this.type === "accent-color") {
@@ -402,8 +402,8 @@ export class Cycle extends LitElement {
402
402
  // Apply the new accent color
403
403
  applyAccentColor();
404
404
 
405
- // Save settings
406
- saveSettings({ accentColor: newColor });
405
+ // Save preferences
406
+ savePreferences({ accentColor: newColor });
407
407
 
408
408
  // Dispatch accent color change event
409
409
  window.dispatchEvent(
@@ -423,8 +423,8 @@ export class Cycle extends LitElement {
423
423
  // Save the new notes style medium
424
424
  saveNotesStyleMedium(newStyle);
425
425
 
426
- // Save settings
427
- saveSettings({ notesStyleMedium: newStyle });
426
+ // Save preferences
427
+ savePreferences({ notesStyleMedium: newStyle });
428
428
 
429
429
  // Dispatch notes style medium change event
430
430
  window.dispatchEvent(
@@ -444,8 +444,8 @@ export class Cycle extends LitElement {
444
444
  // Save the new note behavior
445
445
  savePageStyle(newBehavior);
446
446
 
447
- // Save settings
448
- saveSettings({ pageStyle: newBehavior });
447
+ // Save preferences
448
+ savePreferences({ pageStyle: newBehavior });
449
449
 
450
450
  // Dispatch note behavior change event
451
451
  window.dispatchEvent(
@@ -465,8 +465,8 @@ export class Cycle extends LitElement {
465
465
  // Save the new page style
466
466
  savePageStyle(newIconOnlyValue);
467
467
 
468
- // Save settings
469
- saveSettings({ pageStyle: newIconOnlyValue });
468
+ // Save preferences
469
+ savePreferences({ pageStyle: newIconOnlyValue });
470
470
 
471
471
  // No label update for icon-only type
472
472
  this.label = "";
@@ -736,4 +736,3 @@ declare global {
736
736
  "ds-cycle": Cycle;
737
737
  }
738
738
  }
739
-
@@ -3,9 +3,9 @@ import { LitElement, html, css } from "lit";
3
3
  /**
4
4
  * A component for displaying the current year
5
5
  *
6
- * @element ds-year
6
+ * @element ds-date
7
7
  */
8
- export class Year extends LitElement {
8
+ export class DateComponent extends LitElement {
9
9
  static styles = css`
10
10
  :host {
11
11
  display: inline;
@@ -21,11 +21,11 @@ export class Year extends LitElement {
21
21
  }
22
22
  }
23
23
 
24
- customElements.define("ds-year", Year);
24
+ customElements.define("ds-date", DateComponent);
25
25
 
26
26
  declare global {
27
27
  interface HTMLElementTagNameMap {
28
- "ds-year": Year;
28
+ "ds-date": DateComponent;
29
29
  }
30
30
  }
31
31
 
@@ -0,0 +1 @@
1
+ // here should be an input component
@@ -1,5 +1,5 @@
1
1
  import { LitElement, html, css } from "lit";
2
- import { getText } from "../utils/language";
2
+ import { getText } from "../0-face/i18n";
3
3
 
4
4
  /**
5
5
  * A component for displaying text from translations
@@ -1,5 +1,5 @@
1
1
  import { LitElement, html, css } from "lit";
2
- import { translate, getNotionText } from "../utils/language";
2
+ import { translate, getNotionText } from "../0-face/i18n";
3
3
 
4
4
  export class Tooltip extends LitElement {
5
5
  static properties = {
@@ -84,7 +84,9 @@ export class Tooltip extends LitElement {
84
84
  pointer-events: none;
85
85
  height: calc(var(--08) * var(--scaling-factor));
86
86
  opacity: 0;
87
- transition: opacity 120ms ease, transform 120ms ease;
87
+ transition:
88
+ opacity 120ms ease,
89
+ transform 120ms ease;
88
90
  background-color: light-dark(var(--black), var(--white));
89
91
  color: light-dark(var(--white), var(--black));
90
92
  border-radius: 0;
@@ -192,4 +194,3 @@ declare global {
192
194
  "ds-tooltip": Tooltip;
193
195
  }
194
196
  }
195
-
@@ -0,0 +1,105 @@
1
+ import { LitElement, html, css } from "lit";
2
+
3
+ /**
4
+ * A component for double navigation (previous/next)
5
+ *
6
+ * @element doublenav-v1
7
+ * @prop {string} previous - URL for previous link
8
+ * @prop {string} next - URL for next link
9
+ * @prop {string} previousText - Text for previous link
10
+ * @prop {string} nextText - Text for next link
11
+ * @prop {string} overlay - Overlay color (blue, red, orange, green, yellow)
12
+ */
13
+ export class DoubleNav extends LitElement {
14
+ static get properties() {
15
+ return {
16
+ previous: { type: String, reflect: true },
17
+ next: { type: String, reflect: true },
18
+ previousText: { type: String, reflect: true, attribute: "previous-text" },
19
+ nextText: { type: String, reflect: true, attribute: "next-text" },
20
+ overlay: { type: String, reflect: true },
21
+ };
22
+ }
23
+
24
+ declare previous: string;
25
+ declare next: string;
26
+ declare previousText: string;
27
+ declare nextText: string;
28
+ declare overlay?: string;
29
+
30
+ constructor() {
31
+ super();
32
+ this.previous = "";
33
+ this.next = "";
34
+ this.previousText = "";
35
+ this.nextText = "";
36
+ this.overlay = "";
37
+ }
38
+
39
+ static styles = css`
40
+ :host {
41
+ display: flex;
42
+ justify-content: space-between;
43
+ gap: calc(5px * var(--scaling-factor));
44
+ padding: calc(2px * var(--scaling-factor));
45
+ align-items: center;
46
+ }
47
+
48
+ a {
49
+ display: inline-flex;
50
+ align-items: center;
51
+ gap: calc(5px * var(--scaling-factor));
52
+ text-decoration: none;
53
+ color: inherit;
54
+ }
55
+
56
+ .nav-previous {
57
+ justify-self: start;
58
+ }
59
+
60
+ .nav-next {
61
+ justify-self: end;
62
+ }
63
+
64
+ .nav-previous icon-v1 {
65
+ order: -1;
66
+ }
67
+
68
+ .nav-next icon-v1 {
69
+ padding-top: 3px;
70
+ }
71
+
72
+ .nav-previous icon-v1 {
73
+ padding-top: 3px;
74
+ }
75
+ `;
76
+
77
+ render() {
78
+ return html`
79
+ ${this.previous
80
+ ? html`
81
+ <a href="${this.previous}" class="nav-previous">
82
+ <icon-v1 type="left"></icon-v1>
83
+ <ds-text>${this.previousText || "Previous"}</ds-text>
84
+ </a>
85
+ `
86
+ : html`<div></div>`}
87
+ ${this.next
88
+ ? html`
89
+ <a href="${this.next}" class="nav-next">
90
+ <ds-text>${this.nextText || "Next"}</ds-text>
91
+ <icon-v1 type="right"></icon-v1>
92
+ </a>
93
+ `
94
+ : html`<div></div>`}
95
+ `;
96
+ }
97
+ }
98
+
99
+ customElements.define("doublenav-v1", DoubleNav);
100
+
101
+ declare global {
102
+ interface HTMLElementTagNameMap {
103
+ "doublenav-v1": DoubleNav;
104
+ }
105
+ }
@@ -3,14 +3,14 @@ import { LitElement, html, css } from "lit";
3
3
  /**
4
4
  * A component for double navigation (previous/next)
5
5
  *
6
- * @element ds-doublenav
6
+ * @element portfolio-doublenav
7
7
  * @prop {string} previous - URL for previous link
8
8
  * @prop {string} next - URL for next link
9
9
  * @prop {string} previousText - Text for previous link
10
10
  * @prop {string} nextText - Text for next link
11
11
  * @prop {string} overlay - Overlay color (blue, red, orange, green, yellow)
12
12
  */
13
- export class DoubleNav extends LitElement {
13
+ export class PortfolioDoubleNav extends LitElement {
14
14
  static get properties() {
15
15
  return {
16
16
  previous: { type: String, reflect: true },
@@ -96,11 +96,10 @@ export class DoubleNav extends LitElement {
96
96
  }
97
97
  }
98
98
 
99
- customElements.define("ds-doublenav", DoubleNav);
99
+ customElements.define("portfolio-doublenav", PortfolioDoubleNav);
100
100
 
101
101
  declare global {
102
102
  interface HTMLElementTagNameMap {
103
- "ds-doublenav": DoubleNav;
103
+ "portfolio-doublenav": PortfolioDoubleNav;
104
104
  }
105
105
  }
106
-
@@ -0,0 +1,27 @@
1
+
2
+ import { LitElement, html, css } from "lit";
3
+
4
+ export class PortfolioPanel extends LitElement {
5
+ static styles = css`
6
+ :host {
7
+ display: flex;
8
+ flex-direction: row;
9
+ height: var(--08);
10
+ align-items: end;
11
+ gap: var(--025);
12
+ }
13
+ `;
14
+
15
+ render() {
16
+ return html`<slot></slot>`;
17
+ }
18
+ }
19
+
20
+ customElements.define("portfolio-panel", PortfolioPanel);
21
+
22
+ declare global {
23
+ interface HTMLElementTagNameMap {
24
+ "portfolio-panel": PortfolioPanel;
25
+ }
26
+ }
27
+