@origostudio/origo-design 0.0.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 (209) hide show
  1. package/.editorconfig +13 -0
  2. package/.gitattributes +22 -0
  3. package/.perf-baseline.json +12 -0
  4. package/.prettierignore +13 -0
  5. package/.prettierrc +10 -0
  6. package/.vscode/extensions.json +3 -0
  7. package/BRANCH-PROTECTION.md +25 -0
  8. package/CHANGELOG.md +292 -0
  9. package/LICENSE +21 -0
  10. package/README.md +11 -0
  11. package/commitlint.config.js +6 -0
  12. package/dist/packages/cli/CHANGELOG.md +92 -0
  13. package/dist/packages/cli/README.md +11 -0
  14. package/dist/packages/cli/package.json +16 -0
  15. package/dist/packages/cli/package.json.bak +16 -0
  16. package/dist/packages/cli/src/commands/generate/entity.d.ts +2 -0
  17. package/dist/packages/cli/src/commands/generate/entity.js +24 -0
  18. package/dist/packages/cli/src/commands/generate/entity.js.map +1 -0
  19. package/dist/packages/cli/src/commands/generate/index.d.ts +2 -0
  20. package/dist/packages/cli/src/commands/generate/index.js +30 -0
  21. package/dist/packages/cli/src/commands/generate/index.js.map +1 -0
  22. package/dist/packages/cli/src/commands/init.d.ts +5 -0
  23. package/dist/packages/cli/src/commands/init.js +27 -0
  24. package/dist/packages/cli/src/commands/init.js.map +1 -0
  25. package/dist/packages/cli/src/commands/new.d.ts +2 -0
  26. package/dist/packages/cli/src/commands/new.js +24 -0
  27. package/dist/packages/cli/src/commands/new.js.map +1 -0
  28. package/dist/packages/cli/src/commands/validate.d.ts +2 -0
  29. package/dist/packages/cli/src/commands/validate.js +50 -0
  30. package/dist/packages/cli/src/commands/validate.js.map +1 -0
  31. package/dist/packages/cli/src/index.d.ts +11 -0
  32. package/dist/packages/cli/src/index.js +15 -0
  33. package/dist/packages/cli/src/index.js.map +1 -0
  34. package/dist/packages/cli/src/lib/cli.d.ts +1 -0
  35. package/dist/packages/cli/src/lib/cli.js +7 -0
  36. package/dist/packages/cli/src/lib/cli.js.map +1 -0
  37. package/dist/packages/cli/src/lib/generator.d.ts +16 -0
  38. package/dist/packages/cli/src/lib/generator.js +165 -0
  39. package/dist/packages/cli/src/lib/generator.js.map +1 -0
  40. package/dist/packages/cli/src/lib/scaffolding.d.ts +5 -0
  41. package/dist/packages/cli/src/lib/scaffolding.js +87 -0
  42. package/dist/packages/cli/src/lib/scaffolding.js.map +1 -0
  43. package/dist/packages/cli/src/lib/validation.d.ts +4 -0
  44. package/dist/packages/cli/src/lib/validation.js +191 -0
  45. package/dist/packages/cli/src/lib/validation.js.map +1 -0
  46. package/dist/packages/cli/src/main.d.ts +4 -0
  47. package/dist/packages/cli/src/main.js +38 -0
  48. package/dist/packages/cli/src/main.js.map +1 -0
  49. package/dist/packages/cli/src/templates/entity.json +6 -0
  50. package/dist/packages/cli/src/templates/extension.json +8 -0
  51. package/dist/packages/cli/src/templates/index.d.ts +29 -0
  52. package/dist/packages/cli/src/templates/index.js +61 -0
  53. package/dist/packages/cli/src/templates/index.js.map +1 -0
  54. package/dist/packages/cli/src/templates/origo.json +16 -0
  55. package/dist/packages/cli/src/utils/errors.d.ts +15 -0
  56. package/dist/packages/cli/src/utils/errors.js +58 -0
  57. package/dist/packages/cli/src/utils/errors.js.map +1 -0
  58. package/dist/packages/core/CHANGELOG.md +275 -0
  59. package/dist/packages/core/README.md +11 -0
  60. package/dist/packages/core/package.json +21 -0
  61. package/dist/packages/core/package.json.bak +21 -0
  62. package/dist/packages/core/src/extension-api/extension-manager.d.ts +35 -0
  63. package/dist/packages/core/src/extension-api/extension-manager.js +300 -0
  64. package/dist/packages/core/src/extension-api/extension-manager.js.map +1 -0
  65. package/dist/packages/core/src/extension-api/index.d.ts +2 -0
  66. package/dist/packages/core/src/extension-api/index.js +6 -0
  67. package/dist/packages/core/src/extension-api/index.js.map +1 -0
  68. package/dist/packages/core/src/extension-api/types.d.ts +34 -0
  69. package/dist/packages/core/src/extension-api/types.js +3 -0
  70. package/dist/packages/core/src/extension-api/types.js.map +1 -0
  71. package/dist/packages/core/src/index.d.ts +3 -0
  72. package/dist/packages/core/src/index.js +7 -0
  73. package/dist/packages/core/src/index.js.map +1 -0
  74. package/dist/packages/core/src/schemas/capability.schema.json +104 -0
  75. package/dist/packages/core/src/schemas/contract.schema.json +52 -0
  76. package/dist/packages/core/src/schemas/domain.schema.json +47 -0
  77. package/dist/packages/core/src/schemas/entity.schema.json +72 -0
  78. package/dist/packages/core/src/schemas/extension.schema.json +39 -0
  79. package/dist/packages/core/src/schemas/permission.schema.json +21 -0
  80. package/dist/packages/core/src/types/ast.d.ts +17 -0
  81. package/dist/packages/core/src/types/ast.js +3 -0
  82. package/dist/packages/core/src/types/ast.js.map +1 -0
  83. package/dist/packages/core/src/types/domain.d.ts +101 -0
  84. package/dist/packages/core/src/types/domain.js +8 -0
  85. package/dist/packages/core/src/types/domain.js.map +1 -0
  86. package/dist/packages/core/src/types/validation.d.ts +12 -0
  87. package/dist/packages/core/src/types/validation.js +16 -0
  88. package/dist/packages/core/src/types/validation.js.map +1 -0
  89. package/dist/packages/core/src/validator/ast-validator.d.ts +11 -0
  90. package/dist/packages/core/src/validator/ast-validator.js +388 -0
  91. package/dist/packages/core/src/validator/ast-validator.js.map +1 -0
  92. package/dist/packages/core/src/validator/index.d.ts +23 -0
  93. package/dist/packages/core/src/validator/index.js +143 -0
  94. package/dist/packages/core/src/validator/index.js.map +1 -0
  95. package/dist/packages/core/src/validator/serializer.d.ts +11 -0
  96. package/dist/packages/core/src/validator/serializer.js +99 -0
  97. package/dist/packages/core/src/validator/serializer.js.map +1 -0
  98. package/eslint.config.js +114 -0
  99. package/generate_prompts.js +19 -0
  100. package/jest.config.ts +5 -0
  101. package/jest.preset.js +3 -0
  102. package/nx.json +169 -0
  103. package/package.json +115 -0
  104. package/packages/angular-renderer/CHANGELOG.md +274 -0
  105. package/packages/angular-renderer/README.md +11 -0
  106. package/packages/angular-renderer/package.json +20 -0
  107. package/packages/angular-renderer/playwright-ct.config.ts +23 -0
  108. package/packages/angular-renderer/src/adapters/web/adapter.ts +145 -0
  109. package/packages/angular-renderer/src/adapters/web/experience-adapter.service.ts +21 -0
  110. package/packages/angular-renderer/src/components/primitives/button/button.component.html +9 -0
  111. package/packages/angular-renderer/src/components/primitives/button/button.component.pw.ts +19 -0
  112. package/packages/angular-renderer/src/components/primitives/button/button.component.scss +31 -0
  113. package/packages/angular-renderer/src/components/primitives/button/button.component.ts +67 -0
  114. package/packages/angular-renderer/src/components/primitives/primitives.a11y.pw.ts +33 -0
  115. package/packages/angular-renderer/src/components/primitives/text-input/text-input.component.html +10 -0
  116. package/packages/angular-renderer/src/components/primitives/text-input/text-input.component.pw.ts +29 -0
  117. package/packages/angular-renderer/src/components/primitives/text-input/text-input.component.scss +42 -0
  118. package/packages/angular-renderer/src/components/primitives/text-input/text-input.component.ts +84 -0
  119. package/packages/angular-renderer/src/components/primitives/vbox/vbox.component.html +2 -0
  120. package/packages/angular-renderer/src/components/primitives/vbox/vbox.component.pw.ts +19 -0
  121. package/packages/angular-renderer/src/components/primitives/vbox/vbox.component.scss +5 -0
  122. package/packages/angular-renderer/src/components/primitives/vbox/vbox.component.ts +74 -0
  123. package/packages/angular-renderer/src/index.ts +7 -0
  124. package/packages/angular-renderer/src/lib/renderer.component.ts +118 -0
  125. package/packages/angular-renderer/src/lib/renderer.tokens.ts +9 -0
  126. package/packages/angular-renderer/src/lib/test-utils.ts +26 -0
  127. package/packages/angular-renderer/src/lib/theme.provider.ts +83 -0
  128. package/packages/angular-renderer/src/test-setup.ts +8 -0
  129. package/packages/cli/CHANGELOG.md +92 -0
  130. package/packages/cli/README.md +11 -0
  131. package/packages/cli/package.json +16 -0
  132. package/packages/cli/src/commands/generate/entity.ts +20 -0
  133. package/packages/cli/src/commands/generate/index.ts +28 -0
  134. package/packages/cli/src/commands/init.ts +28 -0
  135. package/packages/cli/src/commands/new.ts +22 -0
  136. package/packages/cli/src/commands/validate.ts +47 -0
  137. package/packages/cli/src/index.ts +11 -0
  138. package/packages/cli/src/lib/cli.ts +3 -0
  139. package/packages/cli/src/lib/generator.ts +181 -0
  140. package/packages/cli/src/lib/scaffolding.ts +104 -0
  141. package/packages/cli/src/lib/validation.ts +218 -0
  142. package/packages/cli/src/main.ts +38 -0
  143. package/packages/cli/src/templates/entity.json +6 -0
  144. package/packages/cli/src/templates/extension.json +8 -0
  145. package/packages/cli/src/templates/index.ts +94 -0
  146. package/packages/cli/src/templates/origo.json +16 -0
  147. package/packages/cli/src/utils/errors.ts +80 -0
  148. package/packages/core/CHANGELOG.md +275 -0
  149. package/packages/core/README.md +11 -0
  150. package/packages/core/package-lock.json +1035 -0
  151. package/packages/core/package.json +21 -0
  152. package/packages/core/scripts/generate-types.ts +23 -0
  153. package/packages/core/src/extension-api/extension-manager.ts +378 -0
  154. package/packages/core/src/extension-api/index.ts +2 -0
  155. package/packages/core/src/extension-api/types.ts +37 -0
  156. package/packages/core/src/index.ts +3 -0
  157. package/packages/core/src/schemas/__fixtures__/target-page.json +222 -0
  158. package/packages/core/src/schemas/capability.schema.json +104 -0
  159. package/packages/core/src/schemas/contract.schema.json +52 -0
  160. package/packages/core/src/schemas/domain.schema.json +47 -0
  161. package/packages/core/src/schemas/entity.schema.json +72 -0
  162. package/packages/core/src/schemas/extension.schema.json +39 -0
  163. package/packages/core/src/schemas/permission.schema.json +21 -0
  164. package/packages/core/src/types/ast.ts +20 -0
  165. package/packages/core/src/types/domain.ts +105 -0
  166. package/packages/core/src/types/validation.ts +29 -0
  167. package/packages/core/src/validator/ast-validator.ts +406 -0
  168. package/packages/core/src/validator/index.ts +207 -0
  169. package/packages/core/src/validator/serializer.ts +120 -0
  170. package/packages/core/test/fixtures/target-page.json +87 -0
  171. package/packages/design-tokens/CHANGELOG.md +273 -0
  172. package/packages/design-tokens/README.md +11 -0
  173. package/packages/design-tokens/package.json +25 -0
  174. package/packages/design-tokens/src/build.ts +97 -0
  175. package/packages/design-tokens/src/compile.ts +60 -0
  176. package/packages/design-tokens/src/index.ts +7 -0
  177. package/packages/design-tokens/src/runtime/index.ts +2 -0
  178. package/packages/design-tokens/src/runtime/theme-fetcher.ts +90 -0
  179. package/packages/design-tokens/src/runtime/theme-injector.ts +149 -0
  180. package/packages/design-tokens/src/schemas/base-tokens.schema.json +127 -0
  181. package/packages/design-tokens/src/schemas/semantic-tokens.schema.json +52 -0
  182. package/packages/design-tokens/src/tokens/base.json +46 -0
  183. package/packages/design-tokens/src/tokens/semantic.json +33 -0
  184. package/packages/playground/CHANGELOG.md +12 -0
  185. package/packages/playground/package.json +10 -0
  186. package/packages/playground/public/favicon.ico +0 -0
  187. package/packages/playground/src/app/app.component.html +1 -0
  188. package/packages/playground/src/app/app.component.scss +4 -0
  189. package/packages/playground/src/app/app.component.ts +13 -0
  190. package/packages/playground/src/app/app.config.ts +9 -0
  191. package/packages/playground/src/editor/badl-editor.component.html +1 -0
  192. package/packages/playground/src/editor/badl-editor.component.scss +10 -0
  193. package/packages/playground/src/editor/badl-editor.component.ts +57 -0
  194. package/packages/playground/src/editor/editor.worker.ts +1 -0
  195. package/packages/playground/src/editor/json.worker.ts +1 -0
  196. package/packages/playground/src/editor/monaco-environment.ts +11 -0
  197. package/packages/playground/src/editor/schema-registry.ts +30 -0
  198. package/packages/playground/src/index.html +13 -0
  199. package/packages/playground/src/main.ts +5 -0
  200. package/packages/playground/src/schemas/capability.schema.json +104 -0
  201. package/packages/playground/src/schemas/contract.schema.json +52 -0
  202. package/packages/playground/src/schemas/domain.schema.json +47 -0
  203. package/packages/playground/src/schemas/entity.schema.json +72 -0
  204. package/packages/playground/src/schemas/extension.schema.json +39 -0
  205. package/packages/playground/src/schemas/permission.schema.json +21 -0
  206. package/packages/playground/src/styles.scss +8 -0
  207. package/packages/playground/src/test-setup.ts +146 -0
  208. package/packages/playground/vite.config.ts +12 -0
  209. package/packages/playground/vitest.config.mjs +24 -0
@@ -0,0 +1,7 @@
1
+ import baseTokensSchema from './schemas/base-tokens.schema.json';
2
+ import semanticTokensSchema from './schemas/semantic-tokens.schema.json';
3
+ import baseTokens from './tokens/base.json';
4
+ import semanticTokens from './tokens/semantic.json';
5
+
6
+ export * from './build';
7
+ export { baseTokensSchema, semanticTokensSchema, baseTokens, semanticTokens };
@@ -0,0 +1,2 @@
1
+ export * from './theme-injector';
2
+ export * from './theme-fetcher';
@@ -0,0 +1,90 @@
1
+ import { injectTheme } from './theme-injector';
2
+
3
+ /**
4
+ * Fetches a theme.json file from the specified URL.
5
+ *
6
+ * - Gracefully handles SSR environments by returning an empty object.
7
+ * - Handles network errors by catching them and returning an empty object.
8
+ *
9
+ * @param url The URL to fetch the theme JSON from.
10
+ * @returns A promise that resolves to the parsed theme JSON object, or an empty object on failure/SSR.
11
+ */
12
+ export async function fetchTheme(url: string): Promise<Record<string, unknown>> {
13
+ // SSR guard: fetch might not exist, or we are in a server environment
14
+ if (typeof fetch === 'undefined' || typeof document === 'undefined') {
15
+ return {};
16
+ }
17
+
18
+ try {
19
+ const response = await fetch(url);
20
+ if (!response.ok) {
21
+ console.warn(
22
+ `[origo-design] Failed to fetch theme from ${url}: ${response.status} ${response.statusText}`
23
+ );
24
+ return {};
25
+ }
26
+ const data = await response.json();
27
+ return data;
28
+ } catch (error) {
29
+ console.warn(`[origo-design] Error fetching theme from ${url}:`, error);
30
+ return {};
31
+ }
32
+ }
33
+
34
+ const activeLoads = new Map<unknown, number>();
35
+ let globalLoadCounter = 0;
36
+
37
+ /**
38
+ * Fetches a theme.json file and injects it into the DOM.
39
+ * Measures the time taken using the Performance API to ensure NFR-PERF-005 limits.
40
+ *
41
+ * @param url The URL to fetch the theme JSON from.
42
+ * @param target The target HTMLElement to scope the injected styles to. Defaults to documentElement.
43
+ * @returns A promise that resolves to a teardown function to remove the injected style tag.
44
+ */
45
+ export async function loadAndInjectTheme(
46
+ url: string,
47
+ target?: HTMLElement | null
48
+ ): Promise<() => void> {
49
+ const loadId = ++globalLoadCounter;
50
+ const loadKey = target || 'default';
51
+ activeLoads.set(loadKey, loadId);
52
+ const perfMarkStart = `theme-load-start-${url}`;
53
+ const perfMarkEnd = `theme-load-end-${url}`;
54
+ const perfMeasure = `theme-load-measure-${url}`;
55
+
56
+ if (typeof performance !== 'undefined' && performance.mark) {
57
+ performance.mark(perfMarkStart);
58
+ }
59
+
60
+ let teardown: () => void = () => {
61
+ // default no-op teardown
62
+ };
63
+ try {
64
+ const themeJson = await fetchTheme(url);
65
+ // Concurrency Lock: abort injection if a newer call has superseded this one for the same target
66
+ if (activeLoads.get(loadKey) !== loadId) {
67
+ return teardown;
68
+ }
69
+ const injected = injectTheme(themeJson, target);
70
+ if (typeof injected === 'function') {
71
+ teardown = injected;
72
+ }
73
+ } catch (error) {
74
+ console.warn('[origo-design] Error in loadAndInjectTheme', error);
75
+ } finally {
76
+ if (typeof performance !== 'undefined' && performance.mark && performance.measure) {
77
+ performance.mark(perfMarkEnd);
78
+ try {
79
+ performance.measure(perfMeasure, perfMarkStart, perfMarkEnd);
80
+ } catch {
81
+ // Ignore measure errors
82
+ }
83
+ performance.clearMarks(perfMarkStart);
84
+ performance.clearMarks(perfMarkEnd);
85
+ performance.clearMeasures(perfMeasure);
86
+ }
87
+ }
88
+
89
+ return teardown;
90
+ }
@@ -0,0 +1,149 @@
1
+ /** CSS custom property name allowlist — alphanumeric, hyphens, underscores only */
2
+ const CSS_SAFE_KEY = /^[a-zA-Z0-9_-]+$/;
3
+
4
+ /** Keys that must never be set on a plain object to prevent prototype pollution */
5
+ const BLOCKED_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
6
+
7
+ /**
8
+ * Allowed CSS value patterns for semantic token overrides.
9
+ * Validates concrete formats rather than relying solely on a denylist.
10
+ */
11
+ const SAFE_VALUE_PATTERNS: RegExp[] = [
12
+ /^#[0-9a-fA-F]{3,8}$/, // hex colors: #fff, #ffffff, #ffffffaa
13
+ /^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+(?:\s*,\s*[\d.]+)?\s*\)$/, // rgb / rgba
14
+ /^hsla?\(\s*[\d.]+\s*,\s*[\d.]+%\s*,\s*[\d.]+%(?:\s*,\s*[\d.]+)?\s*\)$/, // hsl / hsla
15
+ /^[\d.]+(?:px|rem|em|%|vh|vw|vmin|vmax|pt|ch|ex|fr|deg|rad|turn|ms|s)$/, // numeric with units
16
+ /^[\d.]+$/, // plain numbers (opacity, z-index, line-height)
17
+ /^[a-zA-Z][-a-zA-Z0-9]*$/, // CSS named values: red, transparent, inherit, currentColor, etc.
18
+ /^var\(--[a-zA-Z0-9_-]+\)$/, // CSS variables
19
+ /^calc\([^)]+\)$/, // CSS calc
20
+ /^clamp\([^)]+\)$/, // CSS clamp
21
+ /^(?:linear|radial|conic)-gradient\([^)]+\)$/, // CSS gradients
22
+ ];
23
+
24
+ function isSafeValue(value: string): boolean {
25
+ return SAFE_VALUE_PATTERNS.some(pattern => pattern.test(value.trim()));
26
+ }
27
+
28
+ /**
29
+ * Parses and sanitises a flat or single-level-nested theme JSON object into
30
+ * a safe map of `token-key → CSS value`.
31
+ *
32
+ * - Keys are validated against a CSS identifier allowlist.
33
+ * - Values are validated against explicit CSS format patterns.
34
+ * - Prototype-poisoning keys (`__proto__`, `constructor`, `prototype`) are blocked.
35
+ * - Arrays are rejected at every nesting level.
36
+ */
37
+ export function parseTheme(themeJson: unknown): Record<string, string> {
38
+ if (!themeJson || typeof themeJson !== 'object' || Array.isArray(themeJson)) {
39
+ return {};
40
+ }
41
+
42
+ // Object.create(null) avoids prototype setter side-effects for any key written
43
+ const result = Object.create(null) as Record<string, string>;
44
+
45
+ const flatten = (
46
+ obj: Record<string, unknown>,
47
+ prefix = '',
48
+ depth = 0,
49
+ visited = new WeakSet()
50
+ ) => {
51
+ if (depth > 1) return; // Prevent arbitrary depth beyond single-level
52
+ if (visited.has(obj)) return; // Prevent circular reference crashes
53
+ visited.add(obj);
54
+
55
+ for (const key of Object.keys(obj)) {
56
+ if (BLOCKED_KEYS.has(key) || !CSS_SAFE_KEY.test(key)) continue;
57
+
58
+ const value = obj[key];
59
+
60
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
61
+ flatten(value as Record<string, unknown>, `${prefix}${key}-`, depth + 1, visited);
62
+ } else if (typeof value === 'string' || typeof value === 'number') {
63
+ const strVal = String(value).trim();
64
+ const fullKey = `${prefix}${key}`;
65
+
66
+ // Block base token overrides, enforce semantic overrides
67
+ if (!fullKey.startsWith('base-') && isSafeValue(strVal)) {
68
+ result[fullKey] = strVal;
69
+ }
70
+ }
71
+ }
72
+ };
73
+
74
+ flatten(themeJson as Record<string, unknown>);
75
+ return result;
76
+ }
77
+
78
+ /**
79
+ * Applies a parsed theme to the DOM by injecting a `<style>` tag into
80
+ * `document.head` scoped to the target element.
81
+ *
82
+ * - When `target` is `document.documentElement` (the default), the style block
83
+ * targets `:root` for global override.
84
+ * - When `target` is any other element, the element receives a stable
85
+ * `data-origo-theme-id` attribute and the style block is scoped to that
86
+ * attribute selector, enabling per-subtree theme isolation.
87
+ * - Passing an empty or fully-invalid theme removes the previously injected
88
+ * style tag for that target rather than leaving stale rules.
89
+ * - Safe to call in SSR environments — returns immediately if `document` is
90
+ * not defined.
91
+ */
92
+ let nextTagId = 0;
93
+
94
+ export function injectTheme(themeJson: unknown, target?: HTMLElement | null): () => void {
95
+ // SSR guard — DOM APIs are not available in Node.js environments
96
+ if (typeof document === 'undefined') return () => undefined;
97
+
98
+ const actualTarget = target === undefined ? document.documentElement : target;
99
+ if (!actualTarget) return () => undefined;
100
+
101
+ const head = document.head || document.documentElement;
102
+ if (!head) return () => undefined;
103
+
104
+ const parsed = parseTheme(themeJson);
105
+
106
+ // Determine the CSS selector and the style-tag attribute for this target
107
+ let selector: string;
108
+ let tagId: string;
109
+
110
+ if (actualTarget === document.documentElement) {
111
+ selector = ':root';
112
+ tagId = 'runtime';
113
+ } else {
114
+ // Assign a stable per-element ID so repeated calls reuse the same tag
115
+ if (!actualTarget.dataset['origoThemeId']) {
116
+ actualTarget.dataset['origoThemeId'] = `t${++nextTagId}`;
117
+ }
118
+ tagId = actualTarget.dataset['origoThemeId'] as string;
119
+ selector = `[data-origo-theme-id="${CSS.escape(tagId)}"]`;
120
+ }
121
+
122
+ let styleTag = head.querySelector<HTMLStyleElement>(
123
+ `style[data-origo-theme="${CSS.escape(tagId)}"]`
124
+ );
125
+
126
+ // Empty theme → remove the style tag to prevent stale overrides
127
+ if (Object.keys(parsed).length === 0) {
128
+ styleTag?.remove();
129
+ return () => undefined;
130
+ }
131
+
132
+ if (!styleTag) {
133
+ styleTag = document.createElement('style');
134
+ styleTag.setAttribute('data-origo-theme', tagId);
135
+ head.appendChild(styleTag);
136
+ }
137
+
138
+ let cssVariables = '';
139
+ for (const [key, value] of Object.entries(parsed)) {
140
+ cssVariables += ` --origo-${key}: ${value};\n`;
141
+ }
142
+
143
+ styleTag.textContent = `${selector} {\n${cssVariables}}\n`;
144
+
145
+ // Return a teardown function to clean up scoped injections
146
+ return () => {
147
+ styleTag?.remove();
148
+ };
149
+ }
@@ -0,0 +1,127 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://origo.dev/schemas/base-tokens.schema.json",
4
+ "title": "Origo Base Tokens Schema",
5
+ "description": "JSON Schema for Origo Base Tokens",
6
+ "type": "object",
7
+ "patternProperties": {
8
+ "^[^$][a-zA-Z0-9_\\-]*$": {
9
+ "$ref": "#/$defs/tokenOrGroup"
10
+ }
11
+ },
12
+ "additionalProperties": false,
13
+ "$defs": {
14
+ "tokenOrGroup": {
15
+ "anyOf": [{ "$ref": "#/$defs/token" }, { "$ref": "#/$defs/tokenGroup" }]
16
+ },
17
+ "token": {
18
+ "type": "object",
19
+ "properties": {
20
+ "$value": {
21
+ "type": ["string", "number", "object", "array", "boolean"]
22
+ },
23
+ "$type": {
24
+ "type": "string",
25
+ "enum": [
26
+ "color",
27
+ "dimension",
28
+ "fontFamily",
29
+ "fontWeight",
30
+ "duration",
31
+ "cubicBezier",
32
+ "number",
33
+ "shadow",
34
+ "strokeStyle",
35
+ "border",
36
+ "transition",
37
+ "typography",
38
+ "spacing",
39
+ "borderRadius",
40
+ "elevation",
41
+ "animation",
42
+ "motion",
43
+ "breakpoints",
44
+ "opacity",
45
+ "density"
46
+ ]
47
+ },
48
+ "$description": {
49
+ "type": "string"
50
+ }
51
+ },
52
+ "required": ["$value"],
53
+ "additionalProperties": false,
54
+ "allOf": [
55
+ {
56
+ "if": { "properties": { "$type": { "const": "typography" } }, "required": ["$type"] },
57
+ "then": { "properties": { "$value": { "$ref": "#/$defs/typographyValue" } } }
58
+ },
59
+ {
60
+ "if": { "properties": { "$type": { "const": "shadow" } }, "required": ["$type"] },
61
+ "then": { "properties": { "$value": { "$ref": "#/$defs/shadowValue" } } }
62
+ },
63
+ {
64
+ "if": { "properties": { "$type": { "const": "border" } }, "required": ["$type"] },
65
+ "then": { "properties": { "$value": { "$ref": "#/$defs/borderValue" } } }
66
+ }
67
+ ]
68
+ },
69
+ "tokenGroup": {
70
+ "type": "object",
71
+ "properties": {
72
+ "$type": {
73
+ "type": "string"
74
+ },
75
+ "$description": {
76
+ "type": "string"
77
+ }
78
+ },
79
+ "patternProperties": {
80
+ "^[^$][a-zA-Z0-9_\\-]*$": {
81
+ "$ref": "#/$defs/tokenOrGroup"
82
+ }
83
+ },
84
+ "additionalProperties": false
85
+ },
86
+ "typographyValue": {
87
+ "type": "object",
88
+ "properties": {
89
+ "fontFamily": { "type": "string" },
90
+ "fontSize": { "type": ["string", "number"] },
91
+ "fontWeight": { "type": ["string", "number"] },
92
+ "letterSpacing": { "type": ["string", "number"] },
93
+ "lineHeight": { "type": ["string", "number"] }
94
+ },
95
+ "required": ["fontFamily", "fontSize"],
96
+ "additionalProperties": false
97
+ },
98
+ "shadowValue": {
99
+ "anyOf": [
100
+ { "$ref": "#/$defs/singleShadowValue" },
101
+ { "type": "array", "items": { "$ref": "#/$defs/singleShadowValue" } }
102
+ ]
103
+ },
104
+ "singleShadowValue": {
105
+ "type": "object",
106
+ "properties": {
107
+ "color": { "type": "string" },
108
+ "offsetX": { "type": ["string", "number"] },
109
+ "offsetY": { "type": ["string", "number"] },
110
+ "blur": { "type": ["string", "number"] },
111
+ "spread": { "type": ["string", "number"] }
112
+ },
113
+ "required": ["color", "offsetX", "offsetY"],
114
+ "additionalProperties": false
115
+ },
116
+ "borderValue": {
117
+ "type": "object",
118
+ "properties": {
119
+ "color": { "type": "string" },
120
+ "width": { "type": ["string", "number"] },
121
+ "style": { "type": "string" }
122
+ },
123
+ "required": ["color", "width", "style"],
124
+ "additionalProperties": false
125
+ }
126
+ }
127
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://origo.dev/schemas/semantic-tokens.schema.json",
4
+ "title": "Origo Semantic Tokens Schema",
5
+ "description": "JSON Schema for Origo Semantic Tokens (ensures references)",
6
+ "type": "object",
7
+ "patternProperties": {
8
+ "^[^$][a-zA-Z0-9_\\-]*$": {
9
+ "$ref": "#/$defs/tokenOrGroup"
10
+ }
11
+ },
12
+ "additionalProperties": false,
13
+ "$defs": {
14
+ "tokenOrGroup": {
15
+ "anyOf": [{ "$ref": "#/$defs/token" }, { "$ref": "#/$defs/tokenGroup" }]
16
+ },
17
+ "token": {
18
+ "type": "object",
19
+ "properties": {
20
+ "$value": {
21
+ "type": "string",
22
+ "pattern": "^.*\\{[a-zA-Z0-9_\\-\\.]+\\}.*$"
23
+ },
24
+ "$type": {
25
+ "type": "string"
26
+ },
27
+ "$description": {
28
+ "type": "string"
29
+ }
30
+ },
31
+ "required": ["$value"],
32
+ "additionalProperties": false
33
+ },
34
+ "tokenGroup": {
35
+ "type": "object",
36
+ "properties": {
37
+ "$type": {
38
+ "type": "string"
39
+ },
40
+ "$description": {
41
+ "type": "string"
42
+ }
43
+ },
44
+ "patternProperties": {
45
+ "^[^$][a-zA-Z0-9_\\-]*$": {
46
+ "$ref": "#/$defs/tokenOrGroup"
47
+ }
48
+ },
49
+ "additionalProperties": false
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "color": {
3
+ "base": {
4
+ "blue": {
5
+ "100": {
6
+ "$value": "#E6F0FF",
7
+ "$type": "color"
8
+ },
9
+ "500": {
10
+ "$value": "#0066FF",
11
+ "$type": "color"
12
+ },
13
+ "900": {
14
+ "$value": "#002966",
15
+ "$type": "color"
16
+ }
17
+ },
18
+ "neutral": {
19
+ "100": {
20
+ "$value": "#F5F5F5",
21
+ "$type": "color"
22
+ },
23
+ "900": {
24
+ "$value": "#1A1A1A",
25
+ "$type": "color"
26
+ }
27
+ }
28
+ }
29
+ },
30
+ "spacing": {
31
+ "base": {
32
+ "1": {
33
+ "$value": "4px",
34
+ "$type": "dimension"
35
+ },
36
+ "2": {
37
+ "$value": "8px",
38
+ "$type": "dimension"
39
+ },
40
+ "4": {
41
+ "$value": "16px",
42
+ "$type": "dimension"
43
+ }
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "color": {
3
+ "surface": {
4
+ "primary": {
5
+ "$value": "{color.base.blue.500}",
6
+ "$type": "color",
7
+ "$description": "Primary surface color for main actions"
8
+ },
9
+ "background": {
10
+ "$value": "{color.base.neutral.100}",
11
+ "$type": "color"
12
+ }
13
+ },
14
+ "text": {
15
+ "primary": {
16
+ "$value": "{color.base.neutral.900}",
17
+ "$type": "color"
18
+ },
19
+ "inverse": {
20
+ "$value": "{color.base.neutral.100}",
21
+ "$type": "color"
22
+ }
23
+ }
24
+ },
25
+ "spacing": {
26
+ "container": {
27
+ "padding": {
28
+ "$value": "{spacing.base.4}",
29
+ "$type": "dimension"
30
+ }
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,12 @@
1
+ ## 0.0.22 (2026-09-04)
2
+
3
+ ### 🚀 Features
4
+
5
+ - add tsconfig.app.json to playground package for application-specific build configuration ([3a9d8e4](https://github.com/OrigoStudio/origo-design/commit/3a9d8e4))
6
+ - add test setup configuration for vitest and browser environment mocks ([e75ad98](https://github.com/OrigoStudio/origo-design/commit/e75ad98))
7
+ - scaffold playground application and e2e testing suite with initial editor component and schema registry ([f097894](https://github.com/OrigoStudio/origo-design/commit/f097894))
8
+ - add GitHub Actions workflow to automate library releases using Nx Release ([3d6651f](https://github.com/OrigoStudio/origo-design/commit/3d6651f))
9
+
10
+ ### ❤️ Thank You
11
+
12
+ - Hardik Patel @hardikpatelse
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@origo/playground",
3
+ "version": "0.0.22",
4
+ "private": true,
5
+ "dependencies": {},
6
+ "peerDependencies": {
7
+ "@angular/common": "^18.0.0",
8
+ "@angular/core": "^18.0.0"
9
+ }
10
+ }
@@ -0,0 +1 @@
1
+ <origo-badl-editor [initialValue]="'{}'"></origo-badl-editor>
@@ -0,0 +1,4 @@
1
+ :host {
2
+ display: block;
3
+ height: 100%;
4
+ }
@@ -0,0 +1,13 @@
1
+ import { Component } from '@angular/core';
2
+ import { BadlEditorComponent } from '../editor/badl-editor.component';
3
+
4
+ @Component({
5
+ standalone: true,
6
+ imports: [BadlEditorComponent],
7
+ selector: 'origo-root',
8
+ templateUrl: './app.component.html',
9
+ styleUrl: './app.component.scss',
10
+ })
11
+ export class AppComponent {
12
+ protected title = 'playground';
13
+ }
@@ -0,0 +1,9 @@
1
+ import {
2
+ ApplicationConfig,
3
+ provideBrowserGlobalErrorListeners,
4
+ provideZonelessChangeDetection,
5
+ } from '@angular/core';
6
+
7
+ export const appConfig: ApplicationConfig = {
8
+ providers: [provideBrowserGlobalErrorListeners(), provideZonelessChangeDetection()],
9
+ };
@@ -0,0 +1 @@
1
+ <div class="editor-container" #editorContainer></div>
@@ -0,0 +1,10 @@
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ height: 100%;
5
+ }
6
+
7
+ .editor-container {
8
+ width: 100%;
9
+ height: 100%;
10
+ }
@@ -0,0 +1,57 @@
1
+ import {
2
+ Component,
3
+ AfterViewInit,
4
+ OnDestroy,
5
+ ElementRef,
6
+ ViewChild,
7
+ input,
8
+ NgZone,
9
+ inject,
10
+ } from '@angular/core';
11
+ import * as monaco from 'monaco-editor';
12
+ import './monaco-environment'; // MUST be first monaco import — side-effect only
13
+ import { registerBadlSchema } from './schema-registry';
14
+
15
+ @Component({
16
+ selector: 'origo-badl-editor',
17
+ standalone: true,
18
+ templateUrl: './badl-editor.component.html',
19
+ styleUrls: ['./badl-editor.component.scss'],
20
+ })
21
+ export class BadlEditorComponent implements AfterViewInit, OnDestroy {
22
+ @ViewChild('editorContainer') editorContainer!: ElementRef<HTMLDivElement>;
23
+
24
+ readonly initialValue = input<string>('');
25
+ readonly theme = input<string>('vs-dark');
26
+ readonly readOnly = input<boolean>(false);
27
+
28
+ private editor: monaco.editor.IStandaloneCodeEditor | null = null;
29
+ private zone = inject(NgZone);
30
+
31
+ ngAfterViewInit(): void {
32
+ registerBadlSchema();
33
+ this.zone.runOutsideAngular(() => {
34
+ if (!this.editorContainer?.nativeElement) return;
35
+ try {
36
+ this.editor = monaco.editor.create(this.editorContainer.nativeElement, {
37
+ model: monaco.editor.createModel(
38
+ this.initialValue(),
39
+ 'json',
40
+ monaco.Uri.parse('inmemory://model/domain.json')
41
+ ),
42
+ theme: this.theme(),
43
+ readOnly: this.readOnly(),
44
+ automaticLayout: true, // Required for split-pane resize in Story 7.2
45
+ minimap: { enabled: false },
46
+ });
47
+ } catch (e) {
48
+ console.error('Failed to initialize Monaco Editor', e);
49
+ }
50
+ });
51
+ }
52
+
53
+ ngOnDestroy(): void {
54
+ this.editor?.dispose(); // CRITICAL — prevents Monaco timer memory leak
55
+ this.editor = null;
56
+ }
57
+ }
@@ -0,0 +1 @@
1
+ import 'monaco-editor/esm/vs/editor/editor.worker';
@@ -0,0 +1 @@
1
+ import 'monaco-editor/esm/vs/language/json/json.worker';
@@ -0,0 +1,11 @@
1
+ // packages/playground/src/editor/monaco-environment.ts
2
+ // This file MUST be imported before ANY other monaco-editor import (side-effect)
3
+
4
+ (window as Window & typeof globalThis & { MonacoEnvironment: unknown }).MonacoEnvironment = {
5
+ getWorker(_: string, label: string) {
6
+ if (label === 'json') {
7
+ return new Worker(new URL('./json.worker', import.meta.url), { type: 'module' });
8
+ }
9
+ return new Worker(new URL('./editor.worker', import.meta.url), { type: 'module' });
10
+ },
11
+ };