@bookklik/senangstart-css 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.
- package/README.md +209 -0
- package/package.json +41 -0
- package/src/cdn/jit.js +503 -0
- package/src/cli/commands/build.js +169 -0
- package/src/cli/commands/dev.js +75 -0
- package/src/cli/commands/init.js +64 -0
- package/src/cli/index.js +36 -0
- package/src/compiler/generators/ai-context.js +128 -0
- package/src/compiler/generators/css.js +344 -0
- package/src/compiler/generators/typescript.js +125 -0
- package/src/compiler/index.js +50 -0
- package/src/compiler/parser.js +67 -0
- package/src/compiler/tokenizer.js +142 -0
- package/src/config/defaults.js +137 -0
- package/src/utils/logger.js +48 -0
- package/templates/senangstart.config.js +35 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SenangStart CSS - TypeScript Definition Generator
|
|
3
|
+
* Generates type definitions for React/Vue/Svelte
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate TypeScript definitions
|
|
8
|
+
* @param {Object} config - Configuration object
|
|
9
|
+
* @returns {string} - TypeScript definition content
|
|
10
|
+
*/
|
|
11
|
+
export function generateTypeScript(config) {
|
|
12
|
+
const { theme } = config;
|
|
13
|
+
|
|
14
|
+
// Generate spacing unions
|
|
15
|
+
const spacingKeys = Object.keys(theme.spacing);
|
|
16
|
+
const paddingUnions = spacingKeys.map(k => `'p:${k}'`).join(' | ');
|
|
17
|
+
const marginUnions = spacingKeys.map(k => `'m:${k}'`).join(' | ');
|
|
18
|
+
const gapUnions = spacingKeys.map(k => `'g:${k}'`).join(' | ');
|
|
19
|
+
|
|
20
|
+
// Generate radius unions
|
|
21
|
+
const radiusKeys = Object.keys(theme.radius);
|
|
22
|
+
const roundedUnions = radiusKeys.map(k => `'rounded:${k}'`).join(' | ');
|
|
23
|
+
|
|
24
|
+
// Generate shadow unions
|
|
25
|
+
const shadowKeys = Object.keys(theme.shadow);
|
|
26
|
+
const shadowUnions = shadowKeys.map(k => `'shadow:${k}'`).join(' | ');
|
|
27
|
+
|
|
28
|
+
// Generate color unions
|
|
29
|
+
const colorKeys = Object.keys(theme.colors);
|
|
30
|
+
const bgUnions = colorKeys.map(k => `'bg:${k}'`).join(' | ');
|
|
31
|
+
const textColorUnions = colorKeys.map(k => `'text:${k}'`).join(' | ');
|
|
32
|
+
|
|
33
|
+
// Generate font size unions
|
|
34
|
+
const fontSizeKeys = Object.keys(theme.fontSize);
|
|
35
|
+
const textSizeUnions = fontSizeKeys.map(k => `'text-size:${k}'`).join(' | ');
|
|
36
|
+
|
|
37
|
+
// Generate font weight unions
|
|
38
|
+
const fontWeightKeys = Object.keys(theme.fontWeight);
|
|
39
|
+
const fontUnions = fontWeightKeys.map(k => `'font:${k}'`).join(' | ');
|
|
40
|
+
|
|
41
|
+
return `/**
|
|
42
|
+
* SenangStart CSS - TypeScript Definitions
|
|
43
|
+
* Auto-generated from configuration
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
// Layout attribute values
|
|
47
|
+
type LayoutDisplay = 'flex' | 'grid' | 'block' | 'inline' | 'hidden';
|
|
48
|
+
type LayoutDirection = 'row' | 'col' | 'row-reverse' | 'col-reverse';
|
|
49
|
+
type LayoutAlignment = 'center' | 'start' | 'end' | 'between' | 'around' | 'evenly';
|
|
50
|
+
type LayoutWrap = 'wrap' | 'nowrap';
|
|
51
|
+
type LayoutPosition = 'absolute' | 'relative' | 'fixed' | 'sticky';
|
|
52
|
+
type LayoutZIndex = 'z:base' | 'z:low' | 'z:mid' | 'z:high' | 'z:top';
|
|
53
|
+
|
|
54
|
+
type LayoutValue =
|
|
55
|
+
| LayoutDisplay
|
|
56
|
+
| LayoutDirection
|
|
57
|
+
| LayoutAlignment
|
|
58
|
+
| LayoutWrap
|
|
59
|
+
| LayoutPosition
|
|
60
|
+
| LayoutZIndex
|
|
61
|
+
| \`\${LayoutDisplay} \${LayoutDirection}\`
|
|
62
|
+
| \`\${LayoutDisplay} \${LayoutAlignment}\`
|
|
63
|
+
| \`\${LayoutDisplay} \${LayoutDirection} \${LayoutAlignment}\`
|
|
64
|
+
| string;
|
|
65
|
+
|
|
66
|
+
// Space attribute values
|
|
67
|
+
type SpacingScale = ${spacingKeys.map(k => `'${k}'`).join(' | ')};
|
|
68
|
+
type Breakpoint = 'mob' | 'tab' | 'lap' | 'desk';
|
|
69
|
+
|
|
70
|
+
type PaddingValue = ${paddingUnions} | \`p-t:\${SpacingScale}\` | \`p-r:\${SpacingScale}\` | \`p-b:\${SpacingScale}\` | \`p-l:\${SpacingScale}\` | \`p-x:\${SpacingScale}\` | \`p-y:\${SpacingScale}\`;
|
|
71
|
+
type MarginValue = ${marginUnions} | \`m-t:\${SpacingScale}\` | \`m-r:\${SpacingScale}\` | \`m-b:\${SpacingScale}\` | \`m-l:\${SpacingScale}\` | \`m-x:\${SpacingScale}\` | \`m-y:\${SpacingScale}\` | 'm-x:auto';
|
|
72
|
+
type GapValue = ${gapUnions} | \`g-x:\${SpacingScale}\` | \`g-y:\${SpacingScale}\`;
|
|
73
|
+
type SizeValue = \`w:[\${string}]\` | \`h:[\${string}]\` | \`min-w:[\${string}]\` | \`max-w:[\${string}]\` | \`min-h:[\${string}]\` | \`max-h:[\${string}]\`;
|
|
74
|
+
|
|
75
|
+
type SpaceValue = PaddingValue | MarginValue | GapValue | SizeValue | \`\${Breakpoint}:\${PaddingValue}\` | string;
|
|
76
|
+
|
|
77
|
+
// Visual attribute values
|
|
78
|
+
type ColorKey = ${colorKeys.map(k => `'${k}'`).join(' | ')};
|
|
79
|
+
type RadiusKey = ${radiusKeys.map(k => `'${k}'`).join(' | ')};
|
|
80
|
+
type ShadowKey = ${shadowKeys.map(k => `'${k}'`).join(' | ')};
|
|
81
|
+
type FontSizeKey = ${fontSizeKeys.map(k => `'${k}'`).join(' | ')};
|
|
82
|
+
type FontWeightKey = ${fontWeightKeys.map(k => `'${k}'`).join(' | ')};
|
|
83
|
+
|
|
84
|
+
type BgValue = ${bgUnions} | \`bg:[\${string}]\`;
|
|
85
|
+
type TextColorValue = ${textColorUnions};
|
|
86
|
+
type TextSizeValue = ${textSizeUnions};
|
|
87
|
+
type FontValue = ${fontUnions};
|
|
88
|
+
type RoundedValue = ${roundedUnions};
|
|
89
|
+
type ShadowValue = ${shadowUnions};
|
|
90
|
+
type TextAlignValue = 'text:left' | 'text:center' | 'text:right';
|
|
91
|
+
|
|
92
|
+
type VisualValue = BgValue | TextColorValue | TextSizeValue | FontValue | RoundedValue | ShadowValue | TextAlignValue | string;
|
|
93
|
+
|
|
94
|
+
// React JSX attribute extensions
|
|
95
|
+
declare module 'react' {
|
|
96
|
+
interface HTMLAttributes<T> {
|
|
97
|
+
layout?: LayoutValue;
|
|
98
|
+
space?: SpaceValue;
|
|
99
|
+
visual?: VisualValue;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Vue attribute extensions
|
|
104
|
+
declare module 'vue' {
|
|
105
|
+
interface HTMLAttributes {
|
|
106
|
+
layout?: string;
|
|
107
|
+
space?: string;
|
|
108
|
+
visual?: string;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Svelte attribute extensions
|
|
113
|
+
declare namespace svelteHTML {
|
|
114
|
+
interface HTMLAttributes<T> {
|
|
115
|
+
'layout'?: string;
|
|
116
|
+
'space'?: string;
|
|
117
|
+
'visual'?: string;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export {};
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default { generateTypeScript };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SenangStart CSS - Main Compiler Orchestrator
|
|
3
|
+
* Coordinates parsing, tokenizing, and generating output
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { parseSource, parseMultipleSources } from './parser.js';
|
|
7
|
+
import { tokenizeAll } from './tokenizer.js';
|
|
8
|
+
import { generateCSS, minifyCSS } from './generators/css.js';
|
|
9
|
+
import { generateAIContext } from './generators/ai-context.js';
|
|
10
|
+
import { generateTypeScript } from './generators/typescript.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Compile a single source string
|
|
14
|
+
* @param {string} content - Source content
|
|
15
|
+
* @param {Object} config - Configuration
|
|
16
|
+
* @returns {Object} - Compilation results
|
|
17
|
+
*/
|
|
18
|
+
export function compileSource(content, config) {
|
|
19
|
+
const parsed = parseSource(content);
|
|
20
|
+
const tokens = tokenizeAll(parsed);
|
|
21
|
+
const css = generateCSS(tokens, config);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
tokens,
|
|
25
|
+
css,
|
|
26
|
+
minifiedCSS: config.output?.minify ? minifyCSS(css) : null
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Compile multiple source files
|
|
32
|
+
* @param {Array<{path: string, content: string}>} files - Source files
|
|
33
|
+
* @param {Object} config - Configuration
|
|
34
|
+
* @returns {Object} - Compilation results
|
|
35
|
+
*/
|
|
36
|
+
export function compileMultiple(files, config) {
|
|
37
|
+
const parsed = parseMultipleSources(files);
|
|
38
|
+
const tokens = tokenizeAll(parsed);
|
|
39
|
+
const css = generateCSS(tokens, config);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
tokens,
|
|
43
|
+
css,
|
|
44
|
+
minifiedCSS: config.output?.minify ? minifyCSS(css) : null,
|
|
45
|
+
aiContext: generateAIContext(config),
|
|
46
|
+
typescript: generateTypeScript(config)
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default { compileSource, compileMultiple };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SenangStart CSS - HTML/JSX Parser
|
|
3
|
+
* Extracts layout, space, and visual attributes from source files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Regex patterns for attribute extraction
|
|
7
|
+
const ATTRIBUTE_PATTERNS = {
|
|
8
|
+
layout: /layout\s*=\s*["']([^"']+)["']/g,
|
|
9
|
+
space: /space\s*=\s*["']([^"']+)["']/g,
|
|
10
|
+
visual: /visual\s*=\s*["']([^"']+)["']/g
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse a source file and extract all SenangStart attributes
|
|
15
|
+
* @param {string} content - File content to parse
|
|
16
|
+
* @returns {Object} - Extracted attributes by type
|
|
17
|
+
*/
|
|
18
|
+
export function parseSource(content) {
|
|
19
|
+
const results = {
|
|
20
|
+
layout: new Set(),
|
|
21
|
+
space: new Set(),
|
|
22
|
+
visual: new Set()
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
for (const [attr, pattern] of Object.entries(ATTRIBUTE_PATTERNS)) {
|
|
26
|
+
// Reset regex lastIndex for each new content
|
|
27
|
+
pattern.lastIndex = 0;
|
|
28
|
+
|
|
29
|
+
let match;
|
|
30
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
31
|
+
const value = match[1].trim();
|
|
32
|
+
// Split by whitespace to get individual tokens
|
|
33
|
+
value.split(/\s+/).forEach(token => {
|
|
34
|
+
if (token) {
|
|
35
|
+
results[attr].add(token);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse multiple source files
|
|
46
|
+
* @param {Array<{path: string, content: string}>} files - Array of file objects
|
|
47
|
+
* @returns {Object} - Combined extracted attributes
|
|
48
|
+
*/
|
|
49
|
+
export function parseMultipleSources(files) {
|
|
50
|
+
const combined = {
|
|
51
|
+
layout: new Set(),
|
|
52
|
+
space: new Set(),
|
|
53
|
+
visual: new Set()
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
for (const file of files) {
|
|
57
|
+
const parsed = parseSource(file.content);
|
|
58
|
+
|
|
59
|
+
parsed.layout.forEach(token => combined.layout.add(token));
|
|
60
|
+
parsed.space.forEach(token => combined.space.add(token));
|
|
61
|
+
parsed.visual.forEach(token => combined.visual.add(token));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return combined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default { parseSource, parseMultipleSources };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SenangStart CSS - Tokenizer
|
|
3
|
+
* Parses attribute values into structured tokens
|
|
4
|
+
*
|
|
5
|
+
* Syntax: [breakpoint]:[property]:[value]
|
|
6
|
+
* Examples:
|
|
7
|
+
* - p:medium → { property: 'p', value: 'medium' }
|
|
8
|
+
* - tab:p:big → { breakpoint: 'tab', property: 'p', value: 'big' }
|
|
9
|
+
* - w:[350px] → { property: 'w', value: '350px', isArbitrary: true }
|
|
10
|
+
* - hover:bg:primary → { state: 'hover', property: 'bg', value: 'primary' }
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Breakpoint prefixes
|
|
14
|
+
const BREAKPOINTS = ['mob', 'tab', 'lap', 'desk'];
|
|
15
|
+
|
|
16
|
+
// State prefixes
|
|
17
|
+
const STATES = ['hover', 'focus', 'active', 'disabled'];
|
|
18
|
+
|
|
19
|
+
// Layout keywords (no colon syntax)
|
|
20
|
+
const LAYOUT_KEYWORDS = [
|
|
21
|
+
'flex', 'grid', 'block', 'inline', 'hidden',
|
|
22
|
+
'row', 'col', 'row-reverse', 'col-reverse',
|
|
23
|
+
'center', 'start', 'end', 'between', 'around', 'evenly',
|
|
24
|
+
'wrap', 'nowrap',
|
|
25
|
+
'absolute', 'relative', 'fixed', 'sticky'
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Tokenize a single attribute value string
|
|
30
|
+
* @param {string} raw - Raw token string (e.g., "tab:p:big")
|
|
31
|
+
* @param {string} attrType - Attribute type: 'layout', 'space', or 'visual'
|
|
32
|
+
* @returns {Object} - Parsed token object
|
|
33
|
+
*/
|
|
34
|
+
export function tokenize(raw, attrType) {
|
|
35
|
+
const token = {
|
|
36
|
+
raw,
|
|
37
|
+
breakpoint: null,
|
|
38
|
+
state: null,
|
|
39
|
+
property: null,
|
|
40
|
+
value: null,
|
|
41
|
+
isArbitrary: false,
|
|
42
|
+
attrType
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Handle layout keywords (simple words like 'flex', 'center')
|
|
46
|
+
if (attrType === 'layout') {
|
|
47
|
+
// Check for z-index syntax (z:top, z:base)
|
|
48
|
+
if (raw.startsWith('z:')) {
|
|
49
|
+
token.property = 'z';
|
|
50
|
+
token.value = raw.substring(2);
|
|
51
|
+
return token;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check for overflow syntax
|
|
55
|
+
if (raw.startsWith('overflow:')) {
|
|
56
|
+
token.property = 'overflow';
|
|
57
|
+
token.value = raw.substring(9);
|
|
58
|
+
return token;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Simple layout keyword
|
|
62
|
+
if (LAYOUT_KEYWORDS.includes(raw)) {
|
|
63
|
+
token.property = raw;
|
|
64
|
+
token.value = raw;
|
|
65
|
+
return token;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check for responsive layout (e.g., tab:row)
|
|
69
|
+
const parts = raw.split(':');
|
|
70
|
+
if (parts.length === 2 && BREAKPOINTS.includes(parts[0])) {
|
|
71
|
+
token.breakpoint = parts[0];
|
|
72
|
+
token.property = parts[1];
|
|
73
|
+
token.value = parts[1];
|
|
74
|
+
return token;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Handle space and visual attributes with colon syntax
|
|
79
|
+
const parts = raw.split(':');
|
|
80
|
+
|
|
81
|
+
if (parts.length === 1) {
|
|
82
|
+
// Single value (shouldn't happen for space/visual, but handle it)
|
|
83
|
+
token.property = raw;
|
|
84
|
+
token.value = raw;
|
|
85
|
+
return token;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let idx = 0;
|
|
89
|
+
|
|
90
|
+
// Check for breakpoint prefix
|
|
91
|
+
if (BREAKPOINTS.includes(parts[0])) {
|
|
92
|
+
token.breakpoint = parts[0];
|
|
93
|
+
idx++;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check for state prefix
|
|
97
|
+
if (STATES.includes(parts[idx])) {
|
|
98
|
+
token.state = parts[idx];
|
|
99
|
+
idx++;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Property
|
|
103
|
+
if (idx < parts.length) {
|
|
104
|
+
token.property = parts[idx];
|
|
105
|
+
idx++;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Value
|
|
109
|
+
if (idx < parts.length) {
|
|
110
|
+
let value = parts.slice(idx).join(':');
|
|
111
|
+
|
|
112
|
+
// Check for arbitrary value in brackets
|
|
113
|
+
const arbitraryMatch = value.match(/^\[(.+)\]$/);
|
|
114
|
+
if (arbitraryMatch) {
|
|
115
|
+
token.value = arbitraryMatch[1].replace(/_/g, ' ');
|
|
116
|
+
token.isArbitrary = true;
|
|
117
|
+
} else {
|
|
118
|
+
token.value = value;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return token;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Tokenize all values from parsed attributes
|
|
127
|
+
* @param {Object} parsed - Output from parser { layout: Set, space: Set, visual: Set }
|
|
128
|
+
* @returns {Array} - Array of token objects
|
|
129
|
+
*/
|
|
130
|
+
export function tokenizeAll(parsed) {
|
|
131
|
+
const tokens = [];
|
|
132
|
+
|
|
133
|
+
for (const [attrType, values] of Object.entries(parsed)) {
|
|
134
|
+
for (const raw of values) {
|
|
135
|
+
tokens.push(tokenize(raw, attrType));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return tokens;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export default { tokenize, tokenizeAll };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SenangStart CSS - Default Configuration
|
|
3
|
+
* The "Natural Object" Scale using intuitive adjectives
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const defaultConfig = {
|
|
7
|
+
// Input files to scan for attributes
|
|
8
|
+
content: [
|
|
9
|
+
'./**/*.html',
|
|
10
|
+
'./src/**/*.{html,jsx,tsx,vue,svelte}',
|
|
11
|
+
'./pages/**/*.{html,jsx,tsx}',
|
|
12
|
+
'./components/**/*.{html,jsx,tsx}'
|
|
13
|
+
],
|
|
14
|
+
|
|
15
|
+
// Output configuration
|
|
16
|
+
output: {
|
|
17
|
+
css: './public/senangstart.css',
|
|
18
|
+
minify: false,
|
|
19
|
+
aiContext: './.cursorrules',
|
|
20
|
+
typescript: './types/senang.d.ts'
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
theme: {
|
|
24
|
+
// 1. SPACING: The "Natural Object" Scale
|
|
25
|
+
// Logic: How big is the object/gap physically?
|
|
26
|
+
spacing: {
|
|
27
|
+
'none': '0px', // No space
|
|
28
|
+
'tiny': '4px', // Pebble (Borders, offsets)
|
|
29
|
+
'small': '8px', // Matchbox (Grouping inside components)
|
|
30
|
+
'medium': '16px', // Smartphone (Standard default)
|
|
31
|
+
'big': '32px', // Laptop (Separation between groups)
|
|
32
|
+
'giant': '64px', // Door (Layout sections)
|
|
33
|
+
'vast': '128px' // House (Hero sections)
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// 2. RADIUS: Tactile Feel
|
|
37
|
+
radius: {
|
|
38
|
+
'none': '0px', // Sharp corners
|
|
39
|
+
'small': '4px', // Subtle nudge
|
|
40
|
+
'medium': '8px', // Soft corner
|
|
41
|
+
'big': '16px', // Distinct curve
|
|
42
|
+
'round': '9999px' // Pill/Circle
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// 3. SHADOWS: Depth Perception
|
|
46
|
+
shadow: {
|
|
47
|
+
'none': 'none',
|
|
48
|
+
'small': '0 1px 2px rgba(0,0,0,0.05)',
|
|
49
|
+
'medium': '0 4px 6px rgba(0,0,0,0.1)',
|
|
50
|
+
'big': '0 10px 15px rgba(0,0,0,0.15)',
|
|
51
|
+
'giant': '0 25px 50px rgba(0,0,0,0.25)'
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// 4. FONT SIZES: Reading Scale
|
|
55
|
+
fontSize: {
|
|
56
|
+
'tiny': '12px',
|
|
57
|
+
'small': '14px',
|
|
58
|
+
'medium': '16px',
|
|
59
|
+
'big': '20px',
|
|
60
|
+
'giant': '32px',
|
|
61
|
+
'vast': '48px'
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// 5. FONT WEIGHTS
|
|
65
|
+
fontWeight: {
|
|
66
|
+
'normal': '400',
|
|
67
|
+
'medium': '500',
|
|
68
|
+
'bold': '700'
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// 6. BREAKPOINTS: Device Intent
|
|
72
|
+
screens: {
|
|
73
|
+
'mob': '480px', // Mobile
|
|
74
|
+
'tab': '768px', // Tablet
|
|
75
|
+
'lap': '1024px', // Laptop
|
|
76
|
+
'desk': '1280px' // Desktop
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// 7. COLORS: SenangStart Brand Palette
|
|
80
|
+
colors: {
|
|
81
|
+
'white': '#FFFFFF',
|
|
82
|
+
'black': '#000000',
|
|
83
|
+
'grey': '#6B7280',
|
|
84
|
+
'dark': '#3E4A5D', // Brand dark
|
|
85
|
+
'light': '#DBEAFE', // Brand light/secondary
|
|
86
|
+
'primary': '#2563EB', // Brand primary
|
|
87
|
+
'secondary': '#DBEAFE', // Brand secondary
|
|
88
|
+
'success': '#10B981',
|
|
89
|
+
'warning': '#F59E0B',
|
|
90
|
+
'danger': '#EF4444'
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// 8. Z-INDEX: Stacking Order
|
|
94
|
+
zIndex: {
|
|
95
|
+
'base': '0',
|
|
96
|
+
'low': '10',
|
|
97
|
+
'mid': '50',
|
|
98
|
+
'high': '100',
|
|
99
|
+
'top': '9999'
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
// Extend or override defaults
|
|
104
|
+
extend: {}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Merge user config with defaults
|
|
109
|
+
*/
|
|
110
|
+
export function mergeConfig(userConfig = {}) {
|
|
111
|
+
const merged = { ...defaultConfig };
|
|
112
|
+
|
|
113
|
+
if (userConfig.content) {
|
|
114
|
+
merged.content = userConfig.content;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (userConfig.output) {
|
|
118
|
+
merged.output = { ...merged.output, ...userConfig.output };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (userConfig.theme) {
|
|
122
|
+
merged.theme = {
|
|
123
|
+
spacing: { ...merged.theme.spacing, ...userConfig.theme?.spacing },
|
|
124
|
+
radius: { ...merged.theme.radius, ...userConfig.theme?.radius },
|
|
125
|
+
shadow: { ...merged.theme.shadow, ...userConfig.theme?.shadow },
|
|
126
|
+
fontSize: { ...merged.theme.fontSize, ...userConfig.theme?.fontSize },
|
|
127
|
+
fontWeight: { ...merged.theme.fontWeight, ...userConfig.theme?.fontWeight },
|
|
128
|
+
screens: { ...merged.theme.screens, ...userConfig.theme?.screens },
|
|
129
|
+
colors: { ...merged.theme.colors, ...userConfig.theme?.colors },
|
|
130
|
+
zIndex: { ...merged.theme.zIndex, ...userConfig.theme?.zIndex }
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return merged;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default defaultConfig;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SenangStart CSS - Console Logger with Colors
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const colors = {
|
|
6
|
+
reset: '\x1b[0m',
|
|
7
|
+
bright: '\x1b[1m',
|
|
8
|
+
dim: '\x1b[2m',
|
|
9
|
+
|
|
10
|
+
// Foreground colors
|
|
11
|
+
red: '\x1b[31m',
|
|
12
|
+
green: '\x1b[32m',
|
|
13
|
+
yellow: '\x1b[33m',
|
|
14
|
+
blue: '\x1b[34m',
|
|
15
|
+
magenta: '\x1b[35m',
|
|
16
|
+
cyan: '\x1b[36m',
|
|
17
|
+
white: '\x1b[37m'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const prefix = `${colors.magenta}${colors.bright}[senang]${colors.reset}`;
|
|
21
|
+
|
|
22
|
+
export const logger = {
|
|
23
|
+
info: (msg) => {
|
|
24
|
+
console.log(`${prefix} ${colors.blue}ℹ${colors.reset} ${msg}`);
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
success: (msg) => {
|
|
28
|
+
console.log(`${prefix} ${colors.green}✓${colors.reset} ${msg}`);
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
warn: (msg) => {
|
|
32
|
+
console.log(`${prefix} ${colors.yellow}⚠${colors.reset} ${msg}`);
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
error: (msg) => {
|
|
36
|
+
console.log(`${prefix} ${colors.red}✗${colors.reset} ${msg}`);
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
build: (msg) => {
|
|
40
|
+
console.log(`${prefix} ${colors.cyan}⚡${colors.reset} ${msg}`);
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
watch: (msg) => {
|
|
44
|
+
console.log(`${prefix} ${colors.green}👁${colors.reset} ${msg}`);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default logger;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SenangStart CSS Configuration
|
|
3
|
+
* @see https://senangstart.dev/docs/configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
// Files to scan for SenangStart attributes
|
|
8
|
+
content: [
|
|
9
|
+
'./**/*.html',
|
|
10
|
+
'./src/**/*.{html,jsx,tsx,vue,svelte}',
|
|
11
|
+
'./components/**/*.{html,jsx,tsx}'
|
|
12
|
+
],
|
|
13
|
+
|
|
14
|
+
// Output configuration
|
|
15
|
+
output: {
|
|
16
|
+
css: './public/senangstart.css',
|
|
17
|
+
minify: true,
|
|
18
|
+
aiContext: './.cursorrules',
|
|
19
|
+
typescript: './types/senang.d.ts'
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
// Theme customization
|
|
23
|
+
theme: {
|
|
24
|
+
// Override or extend the default spacing scale
|
|
25
|
+
// spacing: {
|
|
26
|
+
// 'huge': '256px' // Add custom scale
|
|
27
|
+
// },
|
|
28
|
+
|
|
29
|
+
// Add custom colors
|
|
30
|
+
// colors: {
|
|
31
|
+
// 'brand': '#8B5CF6',
|
|
32
|
+
// 'accent': '#EC4899'
|
|
33
|
+
// }
|
|
34
|
+
}
|
|
35
|
+
}
|