@once-ui-system/core 1.7.12 → 1.7.13

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 (80) hide show
  1. package/AGENTS.md +53 -0
  2. package/README.md +27 -0
  3. package/agent/once-ui-codegen.mdc +27 -0
  4. package/ai/catalog.json +2 -2
  5. package/ai/components/ContextMenu.json +2 -2
  6. package/ai/components/Table.json +17 -2
  7. package/ai/manifest.json +2 -2
  8. package/ai/spec.json +22 -7
  9. package/dist/AGENTS.md +53 -0
  10. package/dist/README.md +27 -0
  11. package/dist/agent/once-ui-codegen.mdc +27 -0
  12. package/dist/ai/catalog.json +2 -2
  13. package/dist/ai/components/ContextMenu.json +2 -2
  14. package/dist/ai/components/Table.json +17 -2
  15. package/dist/ai/manifest.json +2 -2
  16. package/dist/ai/spec.json +22 -7
  17. package/dist/components/ContextMenu.d.ts +2 -2
  18. package/dist/components/ContextMenu.d.ts.map +1 -1
  19. package/dist/components/ContextMenu.js +3 -3
  20. package/dist/components/ContextMenu.js.map +1 -1
  21. package/dist/components/ElementType.d.ts.map +1 -1
  22. package/dist/components/ElementType.js +6 -4
  23. package/dist/components/ElementType.js.map +1 -1
  24. package/dist/components/Media.d.ts.map +1 -1
  25. package/dist/components/Media.js +6 -3
  26. package/dist/components/Media.js.map +1 -1
  27. package/dist/components/MediaVideoPlayer.d.ts +12 -0
  28. package/dist/components/MediaVideoPlayer.d.ts.map +1 -0
  29. package/dist/components/MediaVideoPlayer.js +246 -0
  30. package/dist/components/MediaVideoPlayer.js.map +1 -0
  31. package/dist/components/MediaVideoPlayer.module.scss +90 -0
  32. package/dist/components/Table.d.ts +34 -6
  33. package/dist/components/Table.d.ts.map +1 -1
  34. package/dist/components/Table.js +114 -30
  35. package/dist/components/Table.js.map +1 -1
  36. package/dist/components/Table.module.scss +174 -3
  37. package/dist/components/ThemeInit.d.ts.map +1 -1
  38. package/dist/components/ThemeInit.js +2 -1
  39. package/dist/components/ThemeInit.js.map +1 -1
  40. package/dist/components/Timeline.d.ts +1 -1
  41. package/dist/components/Timeline.d.ts.map +1 -1
  42. package/dist/components/Timeline.js +16 -52
  43. package/dist/components/Timeline.js.map +1 -1
  44. package/dist/data/emoji-data.json +8 -0
  45. package/dist/icons.d.ts.map +1 -1
  46. package/dist/icons.js +7 -2
  47. package/dist/icons.js.map +1 -1
  48. package/dist/index.d.ts +0 -1
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +0 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/modules/code/CodeBlock.impl.d.ts.map +1 -1
  53. package/dist/modules/code/CodeBlock.impl.js +11 -5
  54. package/dist/modules/code/CodeBlock.impl.js.map +1 -1
  55. package/dist/modules/seo/Schema.d.ts.map +1 -1
  56. package/dist/modules/seo/Schema.js +3 -4
  57. package/dist/modules/seo/Schema.js.map +1 -1
  58. package/dist/package.json +22 -3
  59. package/dist/server/og-url-validation.d.ts +10 -0
  60. package/dist/server/og-url-validation.d.ts.map +1 -0
  61. package/dist/server/og-url-validation.js +234 -0
  62. package/dist/server/og-url-validation.js.map +1 -0
  63. package/dist/server/og-utils.d.ts +5 -2
  64. package/dist/server/og-utils.d.ts.map +1 -1
  65. package/dist/server/og-utils.js +39 -14
  66. package/dist/server/og-utils.js.map +1 -1
  67. package/dist/utils/index.d.ts +1 -0
  68. package/dist/utils/index.d.ts.map +1 -1
  69. package/dist/utils/index.js +1 -0
  70. package/dist/utils/index.js.map +1 -1
  71. package/dist/utils/safe-html.d.ts +19 -0
  72. package/dist/utils/safe-html.d.ts.map +1 -0
  73. package/dist/utils/safe-html.js +43 -0
  74. package/dist/utils/safe-html.js.map +1 -0
  75. package/package.json +23 -3
  76. package/scripts/copy-files.js +103 -0
  77. package/scripts/generate-ai-spec.js +464 -0
  78. package/scripts/generate-emoji-data.js +128 -0
  79. package/scripts/init-agent.js +156 -0
  80. package/scripts/validate-ai-code.js +149 -0
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Scaffold agent harness pointers in a consumer project.
4
+ * Usage: npx once-ui-init-agent [--force] [--dry-run]
5
+ */
6
+
7
+ const fs = require("fs");
8
+ const path = require("path");
9
+
10
+ const PACKAGE_ROOT = path.join(__dirname, "..");
11
+ const MARKER_START = "<!-- once-ui-agent-harness:start -->";
12
+ const MARKER_END = "<!-- once-ui-agent-harness:end -->";
13
+
14
+ const PROJECT_AGENTS_SECTION = `${MARKER_START}
15
+ ## Once UI codegen harness
16
+
17
+ Before generating Once UI code, load the harness from your installed package (not full doc pages):
18
+
19
+ 1. Read \`node_modules/@once-ui-system/core/ai/manifest.json\`
20
+ 2. Load bootstrap: \`ai/rules.compact.md\` + \`ai/catalog.json\`
21
+ 3. Match intent via \`ai/tasks/index.json\` → fetch task bundle + component slices
22
+ 4. Validate: \`npx once-ui-validate-ai-code path/to/file.tsx\`
23
+
24
+ npm exports: \`@once-ui-system/core/ai/manifest.json\`
25
+
26
+ Remote fallback: https://docs.once-ui.com/ai/manifest.json
27
+ ${MARKER_END}
28
+ `;
29
+
30
+ function parseArgs(argv) {
31
+ return {
32
+ force: argv.includes("--force"),
33
+ dryRun: argv.includes("--dry-run"),
34
+ };
35
+ }
36
+
37
+ function findProjectRoot(startDir = process.cwd()) {
38
+ let dir = path.resolve(startDir);
39
+
40
+ while (true) {
41
+ const pkgPath = path.join(dir, "package.json");
42
+ if (fs.existsSync(pkgPath)) {
43
+ try {
44
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
45
+ if (pkg.name !== "@once-ui-system/core") {
46
+ return dir;
47
+ }
48
+ } catch {
49
+ // keep walking
50
+ }
51
+ }
52
+
53
+ const parent = path.dirname(dir);
54
+ if (parent === dir) {
55
+ break;
56
+ }
57
+ dir = parent;
58
+ }
59
+
60
+ return path.resolve(startDir);
61
+ }
62
+
63
+ function upsertMarkedSection(existing, section, force) {
64
+ if (existing.includes(MARKER_START) && existing.includes(MARKER_END)) {
65
+ if (!force) {
66
+ return { content: existing, changed: false, reason: "AGENTS.md already contains Once UI harness section" };
67
+ }
68
+
69
+ const start = existing.indexOf(MARKER_START);
70
+ const end = existing.indexOf(MARKER_END) + MARKER_END.length;
71
+ return {
72
+ content: `${existing.slice(0, start)}${section}${existing.slice(end)}`,
73
+ changed: true,
74
+ };
75
+ }
76
+
77
+ if (existing.trim().length === 0) {
78
+ return {
79
+ content: `# AGENTS.md\n\n${section}\n`,
80
+ changed: true,
81
+ };
82
+ }
83
+
84
+ return {
85
+ content: `${existing.trimEnd()}\n\n${section}\n`,
86
+ changed: true,
87
+ };
88
+ }
89
+
90
+ function writeFile(root, relativePath, content, { dryRun, force }) {
91
+ const target = path.join(root, relativePath);
92
+ const exists = fs.existsSync(target);
93
+
94
+ if (exists && !force) {
95
+ const current = fs.readFileSync(target, "utf8");
96
+ if (current === content) {
97
+ return { path: target, status: "unchanged" };
98
+ }
99
+ return { path: target, status: "skipped", reason: "already exists (use --force)" };
100
+ }
101
+
102
+ if (dryRun) {
103
+ return { path: target, status: "would-write" };
104
+ }
105
+
106
+ fs.mkdirSync(path.dirname(target), { recursive: true });
107
+ fs.writeFileSync(target, content, "utf8");
108
+ return { path: target, status: exists ? "updated" : "created" };
109
+ }
110
+
111
+ function main() {
112
+ const options = parseArgs(process.argv.slice(2));
113
+ const projectRoot = findProjectRoot();
114
+ const cursorRuleTemplate = fs.readFileSync(
115
+ path.join(PACKAGE_ROOT, "agent", "once-ui-codegen.mdc"),
116
+ "utf8",
117
+ );
118
+
119
+ const agentsPath = path.join(projectRoot, "AGENTS.md");
120
+ let agentsContent = "";
121
+ if (fs.existsSync(agentsPath)) {
122
+ agentsContent = fs.readFileSync(agentsPath, "utf8");
123
+ }
124
+
125
+ const agentsResult = upsertMarkedSection(agentsContent, PROJECT_AGENTS_SECTION, options.force);
126
+ const results = [];
127
+
128
+ if (agentsResult.changed) {
129
+ results.push(
130
+ writeFile(projectRoot, "AGENTS.md", agentsResult.content, options),
131
+ );
132
+ } else {
133
+ results.push({
134
+ path: agentsPath,
135
+ status: "unchanged",
136
+ reason: agentsResult.reason,
137
+ });
138
+ }
139
+
140
+ results.push(
141
+ writeFile(projectRoot, ".cursor/rules/once-ui-codegen.mdc", cursorRuleTemplate, options),
142
+ );
143
+
144
+ console.log(`Once UI agent harness setup (${options.dryRun ? "dry run" : "applied"})`);
145
+ console.log(`Project root: ${projectRoot}`);
146
+ for (const result of results) {
147
+ const suffix = result.reason ? ` (${result.reason})` : "";
148
+ console.log(`- ${result.status}: ${path.relative(projectRoot, result.path)}${suffix}`);
149
+ }
150
+
151
+ if (!options.dryRun) {
152
+ console.log("\nNext: open a .tsx file and ask your agent to build Once UI — it should load the harness from node_modules/@once-ui-system/core/ai/");
153
+ }
154
+ }
155
+
156
+ main();
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Mechanical Once UI codegen validator.
4
+ * Usage: node scripts/validate-ai-code.js <file.tsx> [--fix]
5
+ * node scripts/validate-ai-code.js --stdin < file.tsx
6
+ */
7
+
8
+ const fs = require("fs");
9
+ const path = require("path");
10
+
11
+ const ROOT = path.join(__dirname, "..");
12
+ const SPEC_PATH = path.join(ROOT, "ai", "spec.json");
13
+
14
+ function loadSpec() {
15
+ if (!fs.existsSync(SPEC_PATH)) return { components: {}, tokens: {} };
16
+ return JSON.parse(fs.readFileSync(SPEC_PATH, "utf8"));
17
+ }
18
+
19
+ function parseIconNames(spec) {
20
+ const raw = spec.tokens?.IconName || "";
21
+ const matches = raw.match(/"([^"]+)"/g) || [];
22
+ return new Set(matches.map((m) => m.slice(1, -1)));
23
+ }
24
+
25
+ function readInput(argv) {
26
+ const fileArg = argv.find((a) => !a.startsWith("-"));
27
+ if (argv.includes("--stdin")) {
28
+ return { source: fs.readFileSync(0, "utf8"), file: "<stdin>" };
29
+ }
30
+ if (!fileArg) {
31
+ console.error("Usage: validate-ai-code.js <file.tsx> [--fix] | --stdin");
32
+ process.exit(2);
33
+ }
34
+ return { source: fs.readFileSync(fileArg, "utf8"), file: fileArg };
35
+ }
36
+
37
+ function validate(source, spec, iconNames) {
38
+ const issues = [];
39
+
40
+ const add = (rule, message, fix) => issues.push({ rule, message, fix });
41
+
42
+ if (/fillWidth[\s\S]{0,80}fillHeight|<Flex[^>]*direction="column"|<Flex[^>]*direction="row"/.test(source)) {
43
+ if (/fillWidth[\s\S]{0,120}fillHeight/.test(source)) {
44
+ add("layout.shorthands", "Use fill instead of fillWidth + fillHeight", "fill");
45
+ }
46
+ if (/<Flex[^>]*direction="column"/.test(source)) {
47
+ add("layout.shorthands", "Use Column instead of Flex direction=\"column\"", "Column");
48
+ }
49
+ if (/<Flex[^>]*direction="row"/.test(source)) {
50
+ add("layout.shorthands", "Use Row instead of Flex direction=\"row\"", "Row");
51
+ }
52
+ }
53
+
54
+ if (/horizontal="center"[\s\S]{0,80}vertical="center"/.test(source)) {
55
+ add("layout.shorthands", "Use center instead of horizontal=\"center\" vertical=\"center\"", "center");
56
+ }
57
+
58
+ if (/position="relative"/.test(source)) {
59
+ add("layout.defaults", "position=\"relative\" is the default — omit it");
60
+ }
61
+
62
+ if (/<Button[^>]*variant="primary"/.test(source)) {
63
+ add("layout.defaults", "variant=\"primary\" is the Button default — omit it");
64
+ }
65
+
66
+ if (/#[0-9a-fA-F]{3,8}\b|rgb\s*\(|rgba\s*\(/.test(source)) {
67
+ add("color.tokens", "Use semantic color tokens, not hex/rgb");
68
+ }
69
+
70
+ if (/<img[\s>/]|<a[\s>/]|<button[\s>/]/.test(source)) {
71
+ add("components.primitives", "Use Once UI components (Media, SmartLink, Button) instead of raw HTML");
72
+ }
73
+
74
+ const cardMatches = source.match(/<Card\b[^>]*>/g) || [];
75
+ for (const tag of cardMatches) {
76
+ if (!/\b(href|onClick)=/.test(tag)) {
77
+ add("Card.interactive", "Card without href/onClick — use Column + surface recipe for static panels");
78
+ break;
79
+ }
80
+ }
81
+
82
+ const iconPropRe = /(?:prefixIcon|suffixIcon|name)=["']([^"']+)["']/g;
83
+ let iconMatch;
84
+ while ((iconMatch = iconPropRe.exec(source)) !== null) {
85
+ const name = iconMatch[1];
86
+ if (iconNames.size && !iconNames.has(name)) {
87
+ add("Icon.names", `Unknown icon name "${name}" — use IconName from spec.json`);
88
+ }
89
+ }
90
+
91
+ if (/delay=\{[^}]*\*\s*80/.test(source)) {
92
+ add("RevealFx.delay", "RevealFx delay is in seconds — use index * 0.1, not index * 80");
93
+ }
94
+
95
+ if (/<Fade[^>]*>[\s\S]*<(Column|Row)[^>]*overflowY/.test(source)) {
96
+ add("Fade.edge", "Fade wrapping scroll content — use as absolute edge strip instead");
97
+ }
98
+
99
+ if (/style=\{\{[^}]*(gap|padding|margin|color|background):/.test(source)) {
100
+ add("spacing.tokens", "Use token props (gap, padding, background) instead of style={{}}");
101
+ }
102
+
103
+ return issues;
104
+ }
105
+
106
+ function applyFixes(source, issues) {
107
+ let out = source;
108
+ for (const issue of issues) {
109
+ if (issue.fix === "fill") {
110
+ out = out.replace(/\bfillWidth\b\s*\n?\s*\bfillHeight\b/g, "fill");
111
+ }
112
+ if (issue.fix === "center") {
113
+ out = out.replace(/\bhorizontal="center"\s*\n?\s*vertical="center"/g, "center");
114
+ }
115
+ }
116
+ return out;
117
+ }
118
+
119
+ function main() {
120
+ const spec = loadSpec();
121
+ const iconNames = parseIconNames(spec);
122
+ const fix = process.argv.includes("--fix");
123
+ const { source, file } = readInput(process.argv.slice(2));
124
+
125
+ const issues = validate(source, spec, iconNames);
126
+
127
+ if (issues.length === 0) {
128
+ console.log(`✓ ${file}: no mechanical issues`);
129
+ process.exit(0);
130
+ }
131
+
132
+ console.log(`✗ ${file}: ${issues.length} issue(s)`);
133
+ for (const issue of issues) {
134
+ console.log(` [${issue.rule}] ${issue.message}`);
135
+ }
136
+
137
+ if (fix) {
138
+ const fixed = applyFixes(source, issues);
139
+ if (fixed !== source && !process.argv.includes("--stdin")) {
140
+ const fileArg = process.argv.find((a) => a.endsWith(".tsx") || a.endsWith(".jsx"));
141
+ fs.writeFileSync(fileArg, fixed);
142
+ console.log(`Applied auto-fixes to ${fileArg}`);
143
+ }
144
+ }
145
+
146
+ process.exit(1);
147
+ }
148
+
149
+ main();