tailjng 0.0.62 → 0.1.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 (54) hide show
  1. package/README.md +186 -32
  2. package/cli/component-manager.js +71 -20
  3. package/cli/execute/init-app.js +251 -0
  4. package/cli/file-operations.js +45 -29
  5. package/cli/index.js +66 -15
  6. package/cli/settings/components-list.js +4 -151
  7. package/cli/settings/header-generator.js +9 -10
  8. package/cli/settings/lib-utils.js +89 -0
  9. package/cli/settings/overwrite-policy.js +18 -0
  10. package/cli/settings/path-utils.js +14 -29
  11. package/cli/settings/project-utils.js +220 -0
  12. package/cli/settings/prompt-utils.js +66 -5
  13. package/cli/templates/app.generator.js +382 -0
  14. package/fesm2022/tailjng.mjs +232 -66
  15. package/fesm2022/tailjng.mjs.map +1 -1
  16. package/lib/services/static/colors.service.d.ts +17 -0
  17. package/lib/services/transformer/transform.service.d.ts +3 -3
  18. package/package.json +1 -1
  19. package/public-api.d.ts +2 -0
  20. package/registry/components.json +164 -0
  21. package/src/lib/components/alert/alert-dialog/dialog-alert.component.css +17 -0
  22. package/src/lib/components/alert/alert-dialog/dialog-alert.component.html +83 -51
  23. package/src/lib/components/alert/alert-dialog/dialog-alert.component.ts +85 -53
  24. package/src/lib/components/alert/alert-toast/toast-alert.component.css +38 -4
  25. package/src/lib/components/alert/alert-toast/toast-alert.component.html +72 -40
  26. package/src/lib/components/alert/alert-toast/toast-alert.component.ts +84 -19
  27. package/src/lib/components/badge/badge.component.ts +1 -2
  28. package/src/lib/components/button/button.component.css +14 -0
  29. package/src/lib/components/button/button.component.html +17 -17
  30. package/src/lib/components/button/button.component.ts +139 -48
  31. package/src/lib/components/card/card-crud-complete/complete-crud-card.component.ts +5 -1
  32. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.html +1 -1
  33. package/src/lib/components/filter/filter-complete/complete-filter.component.html +1 -1
  34. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.html +8 -5
  35. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.scss +12 -0
  36. package/src/lib/components/select/select-dropdown/dropdown-select.component.css +4 -0
  37. package/src/lib/components/select/select-dropdown/dropdown-select.component.html +1 -1
  38. package/src/lib/components/select/select-dropdown/dropdown-select.component.ts +3 -3
  39. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.css +4 -0
  40. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.html +1 -1
  41. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.ts +30 -20
  42. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.html +504 -170
  43. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.scss +92 -0
  44. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.ts +139 -5
  45. package/src/lib/components/table/table-crud-complete/expand-grid/table-expand-grid.builder.ts +116 -0
  46. package/src/lib/components/table/table-crud-complete/expand-grid/table-expand-grid.helper.ts +43 -0
  47. package/src/lib/components/table/table-crud-complete/expand-grid/table-expand-grid.types.ts +39 -0
  48. package/src/lib/components/table/table-crud-complete/index.ts +3 -0
  49. package/src/lib/components/toggle-radio/toggle-radio.component.css +4 -0
  50. package/src/lib/components/toggle-radio/toggle-radio.component.html +4 -4
  51. package/src/lib/components/toggle-radio/toggle-radio.component.ts +15 -6
  52. package/src/lib/components/tooltip/tooltip.service.ts +0 -30
  53. package/tailjng-0.1.0.tgz +0 -0
  54. package/src/lib/components/color/colors.service.ts +0 -187
@@ -0,0 +1,220 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { COLORS } = require('./colors');
4
+
5
+ function findAngularWorkspace(startDir) {
6
+ let dir = path.resolve(startDir);
7
+ while (true) {
8
+ const angularJsonPath = path.join(dir, 'angular.json');
9
+ if (fs.existsSync(angularJsonPath)) {
10
+ return { workspaceRoot: dir, angularJsonPath };
11
+ }
12
+ const parent = path.dirname(dir);
13
+ if (parent === dir) return null;
14
+ dir = parent;
15
+ }
16
+ }
17
+
18
+ function readJson(filePath) {
19
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
20
+ }
21
+
22
+ function writeJson(filePath, data) {
23
+ fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
24
+ }
25
+
26
+ function getApplicationProjects(angularJson) {
27
+ return Object.entries(angularJson.projects || {})
28
+ .filter(([, project]) => project.projectType === 'application')
29
+ .map(([name, project]) => ({
30
+ name,
31
+ root: project.root || '',
32
+ sourceRoot: project.sourceRoot || 'src',
33
+ }));
34
+ }
35
+
36
+ function resolveAppPaths(workspaceRoot, appProject) {
37
+ const appRoot = path.join(workspaceRoot, appProject.root);
38
+ const srcRoot = path.join(workspaceRoot, appProject.sourceRoot);
39
+ return { appRoot, srcRoot };
40
+ }
41
+
42
+ function getBuildOptions(angularJson, projectName) {
43
+ return angularJson.projects?.[projectName]?.architect?.build?.options || {};
44
+ }
45
+
46
+ function detectStyleLanguage(buildOptions) {
47
+ if (buildOptions.inlineStyleLanguage === 'scss') return 'scss';
48
+ const styles = buildOptions.styles || [];
49
+ const first = styles.find((s) => typeof s === 'string');
50
+ if (first && first.endsWith('.scss')) return 'scss';
51
+ return 'css';
52
+ }
53
+
54
+ function getPrimaryStyleEntry(buildOptions) {
55
+ const styles = buildOptions.styles || [];
56
+ const first = styles.find((s) => typeof s === 'string');
57
+ return first || 'src/styles.css';
58
+ }
59
+
60
+ function fileExists(filePath) {
61
+ return fs.existsSync(filePath);
62
+ }
63
+
64
+ function writeFileSafe(filePath, content, overwrite = false) {
65
+ if (fs.existsSync(filePath) && !overwrite) {
66
+ return false;
67
+ }
68
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
69
+ fs.writeFileSync(filePath, content, 'utf8');
70
+ return true;
71
+ }
72
+
73
+ function ensureTailjngStylesInAngularJson(angularJsonPath, projectName, styleLanguage) {
74
+ const angularJson = readJson(angularJsonPath);
75
+ const buildOptions = getBuildOptions(angularJson, projectName);
76
+ const styles = buildOptions.styles || [];
77
+ const tailjngStyle = 'node_modules/tailjng/src/styles.css';
78
+
79
+ if (styles.includes(tailjngStyle)) {
80
+ return { changed: false, angularJson };
81
+ }
82
+
83
+ buildOptions.styles = [...styles, tailjngStyle];
84
+ angularJson.projects[projectName].architect.build.options = buildOptions;
85
+ writeJson(angularJsonPath, angularJson);
86
+ return { changed: true, angularJson };
87
+ }
88
+
89
+ function patchIndexHtml(indexPath) {
90
+ if (!fs.existsSync(indexPath)) return false;
91
+
92
+ let html = fs.readFileSync(indexPath, 'utf8');
93
+ let changed = false;
94
+
95
+ if (!html.includes('scroll-screen')) {
96
+ html = html.replace(
97
+ /<body([^>]*)>/i,
98
+ (match, attrs) => {
99
+ if (attrs.includes('class=')) {
100
+ return match.replace(
101
+ /class=(["'])(.*?)\1/i,
102
+ (_, q, classes) => `class=${q}${classes} bg-background dark:bg-dark-background transition duration-300 scroll-screen${q}`,
103
+ );
104
+ }
105
+ return '<body class="bg-background dark:bg-dark-background transition duration-300 scroll-screen">';
106
+ },
107
+ );
108
+ changed = true;
109
+ }
110
+
111
+ if (!html.includes('translate="no"')) {
112
+ html = html.replace(/<html([^>]*)>/i, '<html$1 translate="no">');
113
+ changed = true;
114
+ }
115
+
116
+ if (changed) {
117
+ fs.writeFileSync(indexPath, html, 'utf8');
118
+ }
119
+
120
+ return changed;
121
+ }
122
+
123
+ function insertLinesAfterImports(content, linesToInsert) {
124
+ const lines = content.split('\n');
125
+ let lastImport = -1;
126
+ for (let i = 0; i < lines.length; i += 1) {
127
+ if (lines[i].startsWith('import ')) lastImport = i;
128
+ }
129
+ const block = linesToInsert.filter(Boolean);
130
+ if (lastImport === -1) return `${block.join('\n')}\n${content}`;
131
+ lines.splice(lastImport + 1, 0, ...block);
132
+ return lines.join('\n');
133
+ }
134
+
135
+ function patchAppConfig(appConfigPath) {
136
+ if (!fs.existsSync(appConfigPath)) return { changed: false, created: false };
137
+
138
+ let content = fs.readFileSync(appConfigPath, 'utf8');
139
+ if (content.includes('tailjngProviders') || content.includes('TAILJNG_CONFIG')) {
140
+ return { changed: false, created: false };
141
+ }
142
+
143
+ content = insertLinesAfterImports(content, [
144
+ "import { tailjngProviders } from './config/tailjng.providers';",
145
+ ]);
146
+
147
+ if (content.includes('providers: [')) {
148
+ content = content.replace(/providers:\s*\[/, 'providers: [\n ...tailjngProviders,');
149
+ }
150
+
151
+ fs.writeFileSync(appConfigPath, content, 'utf8');
152
+ return { changed: true, created: false };
153
+ }
154
+
155
+ function patchAppComponentForAlerts(appComponentTsPath, appComponentHtmlPath) {
156
+ if (!fs.existsSync(appComponentTsPath) || !fs.existsSync(appComponentHtmlPath)) {
157
+ return { changed: false };
158
+ }
159
+
160
+ let ts = fs.readFileSync(appComponentTsPath, 'utf8');
161
+ let html = fs.readFileSync(appComponentHtmlPath, 'utf8');
162
+ let changed = false;
163
+
164
+ const tsImports = [
165
+ "import { JAlertDialogComponent } from './tailjng/alert/alert-dialog/dialog-alert.component';",
166
+ "import { JAlertToastComponent } from './tailjng/alert/alert-toast/toast-alert.component';",
167
+ ];
168
+
169
+ for (const imp of tsImports) {
170
+ if (!ts.includes(imp)) {
171
+ ts = insertLinesAfterImports(ts, [imp]);
172
+ changed = true;
173
+ }
174
+ }
175
+
176
+ if (ts.includes('imports: [') && !/imports:\s*\[[^\]]*JAlertDialogComponent/.test(ts)) {
177
+ ts = ts.replace(/imports:\s*\[/, 'imports: [JAlertDialogComponent, JAlertToastComponent, ');
178
+ changed = true;
179
+ }
180
+
181
+ const shell = `<!-- tailjng:shell -->
182
+ <JAlertDialog></JAlertDialog>
183
+ <JAlertToast></JAlertToast>
184
+ <!-- /tailjng:shell -->`;
185
+
186
+ if (!html.includes('tailjng:shell')) {
187
+ html = `${html.trim()}\n\n${shell}\n`;
188
+ changed = true;
189
+ }
190
+
191
+ if (changed) {
192
+ fs.writeFileSync(appComponentTsPath, ts, 'utf8');
193
+ fs.writeFileSync(appComponentHtmlPath, html, 'utf8');
194
+ }
195
+
196
+ return { changed };
197
+ }
198
+
199
+ function getMissingPackages(packageJson, packages) {
200
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
201
+ return Object.entries(packages).filter(([name]) => !deps[name]);
202
+ }
203
+
204
+ module.exports = {
205
+ findAngularWorkspace,
206
+ readJson,
207
+ writeJson,
208
+ getApplicationProjects,
209
+ resolveAppPaths,
210
+ getBuildOptions,
211
+ detectStyleLanguage,
212
+ getPrimaryStyleEntry,
213
+ writeFileSafe,
214
+ ensureTailjngStylesInAngularJson,
215
+ patchIndexHtml,
216
+ patchAppConfig,
217
+ patchAppComponentForAlerts,
218
+ getMissingPackages,
219
+ fileExists,
220
+ };
@@ -2,6 +2,7 @@
2
2
 
3
3
  const readline = require("readline")
4
4
  const { COLORS } = require("./colors")
5
+ const { getOverwritePolicy, setOverwritePolicy } = require("./overwrite-policy")
5
6
 
6
7
  function createReadlineInterface() {
7
8
  return readline.createInterface({
@@ -20,20 +21,80 @@ function askQuestion(question) {
20
21
  })
21
22
  }
22
23
 
24
+ /**
25
+ * Ask once how to handle components that already exist in the project.
26
+ */
27
+ async function askOverwriteStrategy(existingCount, componentNames = []) {
28
+ console.log("")
29
+ console.log(
30
+ `${COLORS.yellow}${COLORS.bright}⚠ [tailjng CLI]${COLORS.reset} ${COLORS.yellow}${existingCount} component(s) already exist in your project.${COLORS.reset}`,
31
+ )
32
+
33
+ if (componentNames.length > 0 && componentNames.length <= 8) {
34
+ console.log(`${COLORS.dim} ${componentNames.join(", ")}${COLORS.reset}`)
35
+ } else if (componentNames.length > 8) {
36
+ console.log(`${COLORS.dim} ${componentNames.slice(0, 8).join(", ")}... (+${componentNames.length - 8} more)${COLORS.reset}`)
37
+ }
38
+
39
+ console.log("")
40
+ console.log(`${COLORS.cyan}How do you want to handle existing components?${COLORS.reset}`)
41
+ console.log(`${COLORS.dim} [1] Ask for each one individually (default)${COLORS.reset}`)
42
+ console.log(`${COLORS.dim} [2] Overwrite all${COLORS.reset}`)
43
+ console.log(`${COLORS.dim} [3] Skip all (keep your versions)${COLORS.reset}`)
44
+ console.log("")
45
+
46
+ const answer = await askQuestion(`${COLORS.cyan}Choice (1/2/3) [1]: ${COLORS.reset}`)
47
+
48
+ if (answer === "2" || answer === "all" || answer === "a" || answer === "y") {
49
+ setOverwritePolicy("all")
50
+ console.log(`${COLORS.green} → Will overwrite all existing components.${COLORS.reset}\n`)
51
+ return
52
+ }
53
+
54
+ if (answer === "3" || answer === "skip" || answer === "s" || answer === "n") {
55
+ setOverwritePolicy("skip")
56
+ console.log(`${COLORS.yellow} → Will keep all your existing components.${COLORS.reset}\n`)
57
+ return
58
+ }
59
+
60
+ setOverwritePolicy("ask")
61
+ console.log(`${COLORS.cyan} → Will ask for each component individually.${COLORS.reset}\n`)
62
+ }
63
+
23
64
  async function askOverwrite(componentName, targetPath, isDependency = false) {
24
- const componentType = isDependency ? "dependency component" : "component"
65
+ const currentPolicy = getOverwritePolicy()
66
+
67
+ if (currentPolicy === "all") return true
68
+ if (currentPolicy === "skip") return false
69
+
70
+ const componentType = isDependency ? "dependency" : "component"
25
71
 
72
+ console.log("")
26
73
  console.log(
27
- `${COLORS.yellow}${COLORS.bright}⚠ [tailjng CLI] WARNING:${COLORS.reset} ${COLORS.yellow}${componentType} ${COLORS.bright}"${componentName}"${COLORS.reset} ${COLORS.yellow}already exists!${COLORS.reset}`,
74
+ `${COLORS.yellow}${COLORS.bright}⚠ [tailjng CLI]${COLORS.reset} ${COLORS.yellow}The ${componentType} ${COLORS.bright}"${componentName}"${COLORS.reset} ${COLORS.yellow}is already installed.${COLORS.reset}`,
28
75
  )
29
76
  console.log(`${COLORS.dim} Path: ${targetPath}${COLORS.reset}`)
30
77
  console.log(
31
- `${COLORS.yellow} This component may have been modified. Overwriting will replace your changes.${COLORS.reset}`,
78
+ `${COLORS.yellow} If you modified it, overwriting will replace your changes with the library version.${COLORS.reset}`,
32
79
  )
33
80
 
34
- const answer = await askQuestion(`${COLORS.cyan} Do you want to overwrite it? (y/N): ${COLORS.reset}`)
81
+ const answer = await askQuestion(
82
+ `${COLORS.cyan} Overwrite? (y/N/a=all remaining/s=skip remaining): ${COLORS.reset}`,
83
+ )
84
+
85
+ if (answer === "a" || answer === "all") {
86
+ setOverwritePolicy("all")
87
+ console.log(`${COLORS.green} → Overwriting this and all remaining existing components.${COLORS.reset}`)
88
+ return true
89
+ }
90
+
91
+ if (answer === "s" || answer === "skip") {
92
+ setOverwritePolicy("skip")
93
+ console.log(`${COLORS.yellow} → Keeping this and all remaining existing components.${COLORS.reset}`)
94
+ return false
95
+ }
35
96
 
36
97
  return answer === "y" || answer === "yes"
37
98
  }
38
99
 
39
- module.exports = { askOverwrite }
100
+ module.exports = { askOverwrite, askOverwriteStrategy }
@@ -0,0 +1,382 @@
1
+ function buildDefaultThemeBlock() {
2
+ return `@theme {
3
+ --color-background: #F0F1F4;
4
+ --color-foreground: #18191A;
5
+ --color-card: #E1E4E9;
6
+ --color-card-foreground: #242528;
7
+ --color-popover: #F0F1F4;
8
+ --color-popover-foreground: #011231;
9
+ --color-primary: #405883;
10
+ --color-primary-foreground: #FFFFFF;
11
+ --color-secondary: #A5AEBF;
12
+ --color-secondary-foreground: #000000;
13
+ --color-muted: #D2DFDF;
14
+ --color-muted-foreground: #60646B;
15
+ --color-accent: #C3D4D4;
16
+ --color-accent-foreground: #242528;
17
+ --color-destructive: #BF3F3F;
18
+ --color-destructive-foreground: #E4E5E6;
19
+ --color-border: #6C7D9D;
20
+ --color-input: #667799;
21
+ --color-ring: #405883;
22
+ --color-radius: 0.5rem;
23
+
24
+ --color-dark-background: #15181D;
25
+ --color-dark-foreground: #E4E5E6;
26
+ --color-dark-card: #15181D;
27
+ --color-dark-card-foreground: #E4E5E6;
28
+ --color-dark-popover: #0A0C0E;
29
+ --color-dark-popover-foreground: #E4E5E6;
30
+ --color-dark-primary: #2d3f5e;
31
+ --color-dark-primary-foreground: #FFFFFF;
32
+ --color-dark-secondary: #2A303B;
33
+ --color-dark-secondary-foreground: #FFFFFF;
34
+ --color-dark-muted: #344A4A;
35
+ --color-dark-muted-foreground: #93979E;
36
+ --color-dark-accent: #344A4A;
37
+ --color-dark-accent-foreground: #E4E5E6;
38
+ --color-dark-destructive: #BF3F3F;
39
+ --color-dark-destructive-foreground: #E4E5E6;
40
+ --color-dark-border: #667799;
41
+ --color-dark-input: #667799;
42
+ --color-dark-ring: #405883;
43
+ --color-dark-radius: 0.5rem;
44
+ }`;
45
+ }
46
+
47
+ function buildStylesCss() {
48
+ return `@import "tailwindcss";
49
+
50
+ html,
51
+ body {
52
+ font-family: 'Arial', sans-serif;
53
+ width: 100%;
54
+ min-height: 100%;
55
+ padding: 0;
56
+ margin: 0;
57
+ overflow-x: hidden;
58
+ }
59
+
60
+ app-root {
61
+ display: flex;
62
+ flex-direction: column;
63
+ min-height: 100vh;
64
+ width: 100%;
65
+ }
66
+
67
+ @custom-variant dark (&:where(.dark, .dark *));
68
+
69
+ ${buildDefaultThemeBlock()}
70
+
71
+ .dark {
72
+ --tw-bg-opacity: 1;
73
+ color: var(--white);
74
+ }
75
+ `;
76
+ }
77
+
78
+ function buildStylesScss() {
79
+ return `@use "tailwindcss";
80
+
81
+ @use "./scss/scroll";
82
+ @use "./scss/input";
83
+
84
+ html,
85
+ body {
86
+ font-family: 'Arial', sans-serif;
87
+ width: 100%;
88
+ min-height: 100%;
89
+ padding: 0;
90
+ margin: 0;
91
+ overflow-x: hidden;
92
+ }
93
+
94
+ app-root {
95
+ display: flex;
96
+ flex-direction: column;
97
+ min-height: 100vh;
98
+ width: 100%;
99
+ }
100
+
101
+ @custom-variant dark (&:where(.dark, .dark *));
102
+
103
+ ${buildDefaultThemeBlock()}
104
+
105
+ .dark {
106
+ --tw-bg-opacity: 1;
107
+ color: var(--white);
108
+ }
109
+ `;
110
+ }
111
+
112
+ function buildScrollScss() {
113
+ return `.scroll-element {
114
+ padding-right: 5px;
115
+
116
+ &::-webkit-scrollbar {
117
+ width: 5px;
118
+ height: 5px;
119
+ }
120
+
121
+ &::-webkit-scrollbar-track {
122
+ border-radius: 10px;
123
+ }
124
+
125
+ &::-webkit-scrollbar-thumb {
126
+ border-radius: 10px;
127
+ background: var(--color-primary);
128
+ transition: 0.5s;
129
+ }
130
+
131
+ &::-webkit-scrollbar-thumb:hover {
132
+ background: var(--color-dark-primary);
133
+ }
134
+
135
+ &::-webkit-scrollbar-button {
136
+ width: 0;
137
+ height: 0;
138
+ }
139
+ }
140
+
141
+ .scroll-screen {
142
+ &::-webkit-scrollbar {
143
+ width: 10px;
144
+ height: 10px;
145
+ }
146
+
147
+ &::-webkit-scrollbar-thumb {
148
+ background: var(--color-dark-primary);
149
+ border: 3px solid var(--color-primary);
150
+ transition: 0.5s;
151
+ }
152
+
153
+ &::-webkit-scrollbar-thumb:hover {
154
+ background: var(--color-primary);
155
+ }
156
+
157
+ &::-webkit-scrollbar-button {
158
+ width: 0;
159
+ height: 0;
160
+ }
161
+ }
162
+
163
+ .dark .scroll-screen {
164
+ &::-webkit-scrollbar-thumb {
165
+ background: var(--color-dark-primary);
166
+ border: 3px solid var(--color-primary);
167
+ transition: 0.5s;
168
+ }
169
+
170
+ &::-webkit-scrollbar-thumb:hover {
171
+ background: var(--color-primary);
172
+ }
173
+ }
174
+ `;
175
+ }
176
+
177
+ function buildInputScss() {
178
+ return `.input {
179
+ &:-webkit-autofill {
180
+ -webkit-text-fill-color: black !important;
181
+ -webkit-box-shadow: 0 0 0 1000px color-mix(in srgb, var(--color-primary) 15%, white) inset !important;
182
+ caret-color: var(--color-primary) !important;
183
+ }
184
+
185
+ &:-webkit-autofill:hover,
186
+ &:-webkit-autofill:focus,
187
+ &:-webkit-autofill:active {
188
+ -webkit-text-fill-color: black !important;
189
+ -webkit-box-shadow: 0 0 0 1000px color-mix(in srgb, var(--color-primary) 15%, white) inset !important;
190
+ caret-color: var(--color-primary) !important;
191
+ }
192
+ }
193
+
194
+ .dark .input {
195
+ &:-webkit-autofill {
196
+ -webkit-text-fill-color: white !important;
197
+ -webkit-box-shadow: 0 0 0 1000px color-mix(in srgb, var(--color-dark-background) 85%, black) inset !important;
198
+ caret-color: var(--color-dark-primary) !important;
199
+ }
200
+
201
+ &:-webkit-autofill:hover,
202
+ &:-webkit-autofill:focus,
203
+ &:-webkit-autofill:active {
204
+ -webkit-text-fill-color: white !important;
205
+ -webkit-box-shadow: 0 0 0 1000px color-mix(in srgb, var(--color-dark-background) 85%, black) inset !important;
206
+ caret-color: var(--color-dark-primary) !important;
207
+ }
208
+ }
209
+
210
+ .input[type="date"]::-webkit-calendar-picker-indicator,
211
+ .input[type="datetime-local"]::-webkit-calendar-picker-indicator {
212
+ filter: none !important;
213
+ opacity: 1 !important;
214
+ color: black !important;
215
+ background-color: transparent !important;
216
+ -webkit-filter: brightness(0) invert(0) !important;
217
+ }
218
+
219
+ .dark .input[type="date"]::-webkit-calendar-picker-indicator,
220
+ .dark .input[type="datetime-local"]::-webkit-calendar-picker-indicator {
221
+ filter: none !important;
222
+ opacity: 1 !important;
223
+ color: white !important;
224
+ background-color: transparent !important;
225
+ -webkit-filter: brightness(0) invert(1) !important;
226
+ }
227
+ `;
228
+ }
229
+
230
+ function buildEnvironmentTs(urlBase, socketUrl) {
231
+ return `export const environment = {
232
+ production: false,
233
+ urlBase: '${urlBase}',
234
+ socketUrl: '${socketUrl}',
235
+ };
236
+ `;
237
+ }
238
+
239
+ function buildTailjngProviders() {
240
+ return `import { CurrencyPipe } from '@angular/common';
241
+ import { provideHttpClient } from '@angular/common/http';
242
+ import { provideAnimations } from '@angular/platform-browser/animations';
243
+ import { TAILJNG_CONFIG } from 'tailjng';
244
+ import { environment } from '../../environment';
245
+
246
+ export const tailjngProviders = [
247
+ provideHttpClient(),
248
+ provideAnimations(),
249
+ CurrencyPipe,
250
+ {
251
+ provide: TAILJNG_CONFIG,
252
+ useValue: {
253
+ urlBase: environment.urlBase,
254
+ socketUrl: environment.socketUrl,
255
+ },
256
+ },
257
+ ];
258
+ `;
259
+ }
260
+
261
+ function buildAppConfigTs() {
262
+ return `import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
263
+ import { provideRouter } from '@angular/router';
264
+ import { routes } from './app.routes';
265
+ import { tailjngProviders } from './config/tailjng.providers';
266
+
267
+ export const appConfig: ApplicationConfig = {
268
+ providers: [
269
+ provideZoneChangeDetection({ eventCoalescing: true }),
270
+ provideRouter(routes),
271
+ ...tailjngProviders,
272
+ ],
273
+ };
274
+ `;
275
+ }
276
+
277
+ function buildPostcssRc() {
278
+ return JSON.stringify(
279
+ {
280
+ plugins: {
281
+ '@tailwindcss/postcss': {},
282
+ autoprefixer: {},
283
+ },
284
+ },
285
+ null,
286
+ 2,
287
+ );
288
+ }
289
+
290
+ function buildPathsJson(componentsPath) {
291
+ return JSON.stringify(
292
+ {
293
+ components: componentsPath,
294
+ stylesStrategy: 'angular-json-tailjng-styles',
295
+ },
296
+ null,
297
+ 2,
298
+ );
299
+ }
300
+
301
+ function buildPathsReadme() {
302
+ return `# Tailjng — configuración del proyecto
303
+
304
+ Este archivo define rutas usadas por el CLI de tailjng en este proyecto.
305
+
306
+ - \`components\`: carpeta donde se copian los componentes UI (\`npx tailjng add\`)
307
+ - Los servicios e interfaces se importan desde el paquete npm: \`import { ... } from 'tailjng'\`
308
+
309
+ Generado por \`npx tailjng init:app\`.
310
+ `;
311
+ }
312
+
313
+ function buildInitFiles(options) {
314
+ const {
315
+ styleLanguage,
316
+ primaryStylePath,
317
+ urlBase,
318
+ socketUrl,
319
+ componentsPath,
320
+ overwrite,
321
+ } = options;
322
+
323
+ const files = [
324
+ {
325
+ relativePath: '.postcssrc.json',
326
+ content: buildPostcssRc(),
327
+ appRootRelative: true,
328
+ },
329
+ {
330
+ relativePath: 'src/environment.ts',
331
+ content: buildEnvironmentTs(urlBase, socketUrl),
332
+ },
333
+ {
334
+ relativePath: 'src/app/config/tailjng.providers.ts',
335
+ content: buildTailjngProviders(),
336
+ },
337
+ {
338
+ relativePath: '.tailjng/paths.json',
339
+ content: buildPathsJson(componentsPath),
340
+ appRootRelative: true,
341
+ },
342
+ {
343
+ relativePath: '.tailjng/README.md',
344
+ content: buildPathsReadme(),
345
+ appRootRelative: true,
346
+ },
347
+ ];
348
+
349
+ if (styleLanguage === 'scss') {
350
+ files.push({
351
+ relativePath: primaryStylePath,
352
+ content: buildStylesScss(),
353
+ });
354
+ files.push({
355
+ relativePath: 'src/scss/_scroll.scss',
356
+ content: buildScrollScss(),
357
+ });
358
+ files.push({
359
+ relativePath: 'src/scss/_input.scss',
360
+ content: buildInputScss(),
361
+ });
362
+ } else {
363
+ files.push({
364
+ relativePath: primaryStylePath,
365
+ content: buildStylesCss(),
366
+ });
367
+ }
368
+
369
+ if (!options.hasAppConfig) {
370
+ files.push({
371
+ relativePath: 'src/app/app.config.ts',
372
+ content: buildAppConfigTs(),
373
+ });
374
+ }
375
+
376
+ return files.map((file) => ({ ...file, overwrite }));
377
+ }
378
+
379
+ module.exports = {
380
+ buildInitFiles,
381
+ buildAppConfigTs,
382
+ };