@seyuna/cli 1.0.0-canary.17 → 1.0.0-canary.19
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/esm/deno.d.ts +1 -0
- package/esm/deno.js +2 -1
- package/esm/src/config/init.d.ts +7 -0
- package/esm/src/config/init.js +68 -0
- package/esm/src/config/types.d.ts +24 -0
- package/esm/src/config/types.js +18 -0
- package/esm/src/helpers/cli.d.ts +44 -0
- package/esm/src/helpers/cli.js +72 -0
- package/esm/src/helpers/fs.d.ts +34 -0
- package/esm/src/helpers/fs.js +83 -0
- package/esm/src/helpers/styles.d.ts +53 -0
- package/esm/src/helpers/styles.js +71 -0
- package/esm/src/main.d.ts +10 -0
- package/esm/src/main.js +110 -0
- package/esm/src/ui/compile.d.ts +20 -0
- package/esm/src/ui/compile.js +193 -0
- package/esm/src/ui/default.d.ts +7 -0
- package/esm/src/ui/default.js +65 -0
- package/esm/src/ui/global.css.d.ts +10 -0
- package/esm/src/ui/global.css.js +104 -0
- package/esm/src/ui/types.d.ts +76 -0
- package/esm/src/ui/types.js +41 -0
- package/package.json +2 -2
package/esm/deno.d.ts
CHANGED
package/esm/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "@seyuna/cli",
|
|
3
|
-
"version": "1.0.0-canary.
|
|
3
|
+
"version": "1.0.0-canary.19",
|
|
4
4
|
"exports": "./src/main.ts",
|
|
5
5
|
"publish": {
|
|
6
6
|
"include": [
|
|
@@ -18,6 +18,7 @@ export default {
|
|
|
18
18
|
"@std/cli": "jsr:@std/cli@^1.0.25",
|
|
19
19
|
"@std/fs": "jsr:@std/fs@^1.0.0",
|
|
20
20
|
"@std/path": "jsr:@std/path@^1.0.0",
|
|
21
|
+
"@std/streams": "jsr:@std/streams@^1.0.8",
|
|
21
22
|
"@std/assert": "jsr:@std/assert@^1.0.0",
|
|
22
23
|
"@std/async": "jsr:@std/async@^1.0.0",
|
|
23
24
|
"@std/fmt": "jsr:@std/fmt@^1.0.0",
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initializes a new Seyuna project by creating a 'seyuna.json' configuration file.
|
|
3
|
+
* This function performs safety checks to avoid overwriting existing configurations.
|
|
4
|
+
*
|
|
5
|
+
* @returns A promise that resolves when initialization is complete.
|
|
6
|
+
*/
|
|
7
|
+
export declare function initConfig(): Promise<void>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { UI_CONFIGURATION } from "../ui/default.js";
|
|
3
|
+
import { SeyunaSpinner } from "../helpers/cli.js";
|
|
4
|
+
import { Brand } from "../helpers/styles.js";
|
|
5
|
+
import { Input, Select } from "@cliffy/prompt";
|
|
6
|
+
/**
|
|
7
|
+
* Initializes a new Seyuna project by creating a 'seyuna.json' configuration file.
|
|
8
|
+
* This function performs safety checks to avoid overwriting existing configurations.
|
|
9
|
+
*
|
|
10
|
+
* @returns A promise that resolves when initialization is complete.
|
|
11
|
+
*/
|
|
12
|
+
export async function initConfig() {
|
|
13
|
+
const licenseKey = await Input.prompt({
|
|
14
|
+
message: `${Brand.muted("›")} Enter your Seyuna license key (optional)`,
|
|
15
|
+
});
|
|
16
|
+
const outputDir = await Input.prompt({
|
|
17
|
+
message: `${Brand.muted("›")} Output directory for generated CSS`,
|
|
18
|
+
default: "src/styles",
|
|
19
|
+
}) || "src/styles";
|
|
20
|
+
const mode = await Select.prompt({
|
|
21
|
+
message: `${Brand.muted("›")} Default color appearance mode`,
|
|
22
|
+
default: "system",
|
|
23
|
+
options: [
|
|
24
|
+
{ name: "System", value: "system" },
|
|
25
|
+
{ name: "Light", value: "light" },
|
|
26
|
+
{ name: "Dark", value: "dark" },
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
const spinner = new SeyunaSpinner("Setting up your Seyuna project").start();
|
|
30
|
+
try {
|
|
31
|
+
/** @type {Config} Template for the initial project configuration */
|
|
32
|
+
const config = {
|
|
33
|
+
license: licenseKey || undefined,
|
|
34
|
+
ui: {
|
|
35
|
+
...UI_CONFIGURATION,
|
|
36
|
+
mode,
|
|
37
|
+
output_dir: outputDir,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
spinner.progress("Building configuration manifest");
|
|
41
|
+
const json = JSON.stringify(config, null, 2);
|
|
42
|
+
spinner.progress("Writing manifest to disk");
|
|
43
|
+
const fileName = "seyuna.json";
|
|
44
|
+
// Safety check: Avoid accidental overwrites of existing configurations
|
|
45
|
+
try {
|
|
46
|
+
const stats = await dntShim.Deno.stat(fileName);
|
|
47
|
+
if (stats.isFile) {
|
|
48
|
+
spinner.error(`Existing '${fileName}' found. Initialization aborted to prevent data loss.`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (!(error instanceof dntShim.Deno.errors.NotFound)) {
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
// File doesn't exist, proceed with initialization
|
|
57
|
+
}
|
|
58
|
+
await dntShim.Deno.writeTextFile(fileName, json);
|
|
59
|
+
spinner.done("Success! Your project is now powered by Seyuna.");
|
|
60
|
+
// Display helpful onboarding steps
|
|
61
|
+
console.log(`\n${Brand.muted("Next steps:")}`);
|
|
62
|
+
console.log(`${Brand.secondary("seyuna ui --compile")} ${Brand.muted("Build your initial styles")}`);
|
|
63
|
+
console.log(`${Brand.secondary("seyuna ui --watch")} ${Brand.muted("Start the development server")}`);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
spinner.error(`Failed to initialize: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { UI } from "../ui/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Root configuration structure for a Seyuna project.
|
|
4
|
+
* This interface represents the schema of the 'seyuna.json' file.
|
|
5
|
+
*/
|
|
6
|
+
export interface Config {
|
|
7
|
+
/**
|
|
8
|
+
* Optional license key for premium features.
|
|
9
|
+
*/
|
|
10
|
+
license?: string;
|
|
11
|
+
/**
|
|
12
|
+
* UI and styling engine configuration.
|
|
13
|
+
*/
|
|
14
|
+
ui?: UI;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Deeply merges two project configurations.
|
|
18
|
+
* Currently handles nested merging for the 'ui' property.
|
|
19
|
+
*
|
|
20
|
+
* @param base - The default or base configuration
|
|
21
|
+
* @param override - The user-provided or overriding configuration
|
|
22
|
+
* @returns A new configuration object with combined properties
|
|
23
|
+
*/
|
|
24
|
+
export declare function mergeConfig(base: Config, override: Config): Config;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { mergeUI } from "../ui/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Deeply merges two project configurations.
|
|
4
|
+
* Currently handles nested merging for the 'ui' property.
|
|
5
|
+
*
|
|
6
|
+
* @param base - The default or base configuration
|
|
7
|
+
* @param override - The user-provided or overriding configuration
|
|
8
|
+
* @returns A new configuration object with combined properties
|
|
9
|
+
*/
|
|
10
|
+
export function mergeConfig(base, override) {
|
|
11
|
+
return {
|
|
12
|
+
...base,
|
|
13
|
+
...override,
|
|
14
|
+
ui: base.ui && override.ui
|
|
15
|
+
? mergeUI(base.ui, override.ui)
|
|
16
|
+
: (override.ui ?? base.ui),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A branded spinner class that provides visual feedback for long-running CLI operations.
|
|
3
|
+
* It uses the Seyuna color palette and gradients for a premium experience.
|
|
4
|
+
*/
|
|
5
|
+
export declare class SeyunaSpinner {
|
|
6
|
+
private spinner;
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new branded spinner.
|
|
9
|
+
* @param message - The initial message to display next to the spinner.
|
|
10
|
+
*/
|
|
11
|
+
constructor(message: string);
|
|
12
|
+
/**
|
|
13
|
+
* Starts the spinner animation.
|
|
14
|
+
* @returns The spinner instance for chaining.
|
|
15
|
+
*/
|
|
16
|
+
start(): this;
|
|
17
|
+
/**
|
|
18
|
+
* Updates the message displayed next to the active spinner.
|
|
19
|
+
* @param message - The new progress message.
|
|
20
|
+
*/
|
|
21
|
+
progress(message: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Stops the spinner and displays a branded success message.
|
|
24
|
+
* @param message - The message to display upon completion.
|
|
25
|
+
*/
|
|
26
|
+
done(message: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Stops the spinner and displays a branded error message.
|
|
29
|
+
* @param message - The error message to display.
|
|
30
|
+
*/
|
|
31
|
+
error(message: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Stops the spinner and displays a branded warning message.
|
|
34
|
+
* @param message - The warning message to display.
|
|
35
|
+
*/
|
|
36
|
+
warning(message: string): void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convenience function to create and start a branded spinner in one call.
|
|
40
|
+
*
|
|
41
|
+
* @param message - The message to display.
|
|
42
|
+
* @returns An active SeyunaSpinner instance.
|
|
43
|
+
*/
|
|
44
|
+
export declare function startSpinner(message: string): SeyunaSpinner;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Spinner } from "@std/cli/unstable-spinner";
|
|
2
|
+
import { Brand, seyunaGradient, Symbols } from "./styles.js";
|
|
3
|
+
/**
|
|
4
|
+
* A branded spinner class that provides visual feedback for long-running CLI operations.
|
|
5
|
+
* It uses the Seyuna color palette and gradients for a premium experience.
|
|
6
|
+
*/
|
|
7
|
+
export class SeyunaSpinner {
|
|
8
|
+
/**
|
|
9
|
+
* Creates a new branded spinner.
|
|
10
|
+
* @param message - The initial message to display next to the spinner.
|
|
11
|
+
*/
|
|
12
|
+
constructor(message) {
|
|
13
|
+
Object.defineProperty(this, "spinner", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
configurable: true,
|
|
16
|
+
writable: true,
|
|
17
|
+
value: void 0
|
|
18
|
+
});
|
|
19
|
+
this.spinner = new Spinner({
|
|
20
|
+
message: seyunaGradient(message),
|
|
21
|
+
color: "cyan",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Starts the spinner animation.
|
|
26
|
+
* @returns The spinner instance for chaining.
|
|
27
|
+
*/
|
|
28
|
+
start() {
|
|
29
|
+
this.spinner.start();
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Updates the message displayed next to the active spinner.
|
|
34
|
+
* @param message - The new progress message.
|
|
35
|
+
*/
|
|
36
|
+
progress(message) {
|
|
37
|
+
this.spinner.message = seyunaGradient(message);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Stops the spinner and displays a branded success message.
|
|
41
|
+
* @param message - The message to display upon completion.
|
|
42
|
+
*/
|
|
43
|
+
done(message) {
|
|
44
|
+
this.spinner.stop();
|
|
45
|
+
console.log(`${Symbols.success} ${Brand.success(message)}`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Stops the spinner and displays a branded error message.
|
|
49
|
+
* @param message - The error message to display.
|
|
50
|
+
*/
|
|
51
|
+
error(message) {
|
|
52
|
+
this.spinner.stop();
|
|
53
|
+
console.log(`${Symbols.error} ${Brand.error(message)}`);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Stops the spinner and displays a branded warning message.
|
|
57
|
+
* @param message - The warning message to display.
|
|
58
|
+
*/
|
|
59
|
+
warning(message) {
|
|
60
|
+
this.spinner.stop();
|
|
61
|
+
console.log(`${Symbols.warning} ${Brand.warning(message)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Convenience function to create and start a branded spinner in one call.
|
|
66
|
+
*
|
|
67
|
+
* @param message - The message to display.
|
|
68
|
+
* @returns An active SeyunaSpinner instance.
|
|
69
|
+
*/
|
|
70
|
+
export function startSpinner(message) {
|
|
71
|
+
return new SeyunaSpinner(message).start();
|
|
72
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Config } from "../config/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Constructs an absolute or relative path from a filename and an output directory.
|
|
4
|
+
*
|
|
5
|
+
* @param fileName - The name of the file (e.g., "styles.css")
|
|
6
|
+
* @param outputDir - The target directory (e.g., "dist")
|
|
7
|
+
* @throws Error if fileName or outputDir are empty strings
|
|
8
|
+
* @returns The joined path string
|
|
9
|
+
*/
|
|
10
|
+
export declare function createPathFromFileName(fileName: string, outputDir: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Reads the text content of a file at the specified path.
|
|
13
|
+
*
|
|
14
|
+
* @param path - Path to the file to be read
|
|
15
|
+
* @throws Error with a descriptive message if the file is not found or cannot be read
|
|
16
|
+
* @returns The file content as a string
|
|
17
|
+
*/
|
|
18
|
+
export declare function readFile(path: string): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Saves content to a file, ensuring the parent directory exists.
|
|
21
|
+
*
|
|
22
|
+
* @param path - The target file path
|
|
23
|
+
* @param content - The content to write (string or Uint8Array)
|
|
24
|
+
* @returns A promise that resolves when the file is written
|
|
25
|
+
*/
|
|
26
|
+
export declare function saveFile(path: string, content: string | Uint8Array): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Loads and parses the 'seyuna.json' configuration file from the current directory.
|
|
29
|
+
* Merges the user configuration with the default UI settings.
|
|
30
|
+
*
|
|
31
|
+
* @throws Error if 'seyuna.json' is missing or contains invalid JSON
|
|
32
|
+
* @returns The merged configuration object
|
|
33
|
+
*/
|
|
34
|
+
export declare function loadSeyunaUserConfig(): Promise<Config>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { dirname, join } from "@std/path";
|
|
3
|
+
import { ensureDir } from "@std/fs";
|
|
4
|
+
import { mergeConfig } from "../config/types.js";
|
|
5
|
+
import { UI_CONFIGURATION } from "../ui/default.js";
|
|
6
|
+
/**
|
|
7
|
+
* Constructs an absolute or relative path from a filename and an output directory.
|
|
8
|
+
*
|
|
9
|
+
* @param fileName - The name of the file (e.g., "styles.css")
|
|
10
|
+
* @param outputDir - The target directory (e.g., "dist")
|
|
11
|
+
* @throws Error if fileName or outputDir are empty strings
|
|
12
|
+
* @returns The joined path string
|
|
13
|
+
*/
|
|
14
|
+
export function createPathFromFileName(fileName, outputDir) {
|
|
15
|
+
if (!fileName.trim()) {
|
|
16
|
+
throw new Error("File name cannot be empty");
|
|
17
|
+
}
|
|
18
|
+
if (!outputDir.trim()) {
|
|
19
|
+
throw new Error("Output directory cannot be empty");
|
|
20
|
+
}
|
|
21
|
+
return join(outputDir, fileName);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Reads the text content of a file at the specified path.
|
|
25
|
+
*
|
|
26
|
+
* @param path - Path to the file to be read
|
|
27
|
+
* @throws Error with a descriptive message if the file is not found or cannot be read
|
|
28
|
+
* @returns The file content as a string
|
|
29
|
+
*/
|
|
30
|
+
export async function readFile(path) {
|
|
31
|
+
try {
|
|
32
|
+
return await dntShim.Deno.readTextFile(path);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error instanceof dntShim.Deno.errors.NotFound) {
|
|
36
|
+
throw new Error(`File not found at path: ${path}`);
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`Failed to read file at ${path}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Saves content to a file, ensuring the parent directory exists.
|
|
43
|
+
*
|
|
44
|
+
* @param path - The target file path
|
|
45
|
+
* @param content - The content to write (string or Uint8Array)
|
|
46
|
+
* @returns A promise that resolves when the file is written
|
|
47
|
+
*/
|
|
48
|
+
export async function saveFile(path, content) {
|
|
49
|
+
const dir = dirname(path);
|
|
50
|
+
await ensureDir(dir);
|
|
51
|
+
const data = typeof content === "string"
|
|
52
|
+
? new TextEncoder().encode(content)
|
|
53
|
+
: content;
|
|
54
|
+
await dntShim.Deno.writeFile(path, data);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Loads and parses the 'seyuna.json' configuration file from the current directory.
|
|
58
|
+
* Merges the user configuration with the default UI settings.
|
|
59
|
+
*
|
|
60
|
+
* @throws Error if 'seyuna.json' is missing or contains invalid JSON
|
|
61
|
+
* @returns The merged configuration object
|
|
62
|
+
*/
|
|
63
|
+
export async function loadSeyunaUserConfig() {
|
|
64
|
+
try {
|
|
65
|
+
const content = await readFile("seyuna.json");
|
|
66
|
+
const userConfig = JSON.parse(content);
|
|
67
|
+
/** @type {Config} Default fallback configuration */
|
|
68
|
+
const defaultConfig = {
|
|
69
|
+
license: undefined,
|
|
70
|
+
ui: UI_CONFIGURATION,
|
|
71
|
+
};
|
|
72
|
+
return mergeConfig(defaultConfig, userConfig);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (error instanceof Error && error.message.includes("File not found")) {
|
|
76
|
+
throw new Error("Could not find 'seyuna.json' in the current directory. Please run 'seyuna ui config --init' to generate it.");
|
|
77
|
+
}
|
|
78
|
+
if (error instanceof SyntaxError) {
|
|
79
|
+
throw new Error(`Failed to parse 'seyuna.json': ${error.message}`);
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A function that applies a color or style to a string.
|
|
3
|
+
*/
|
|
4
|
+
export type ColorFunction = (text: string) => string;
|
|
5
|
+
/**
|
|
6
|
+
* Standard brand colors and styles for the Seyuna CLI.
|
|
7
|
+
* Provides a consistent visual identity across all command outputs.
|
|
8
|
+
*/
|
|
9
|
+
export declare const Brand: {
|
|
10
|
+
/** Vibrant Spring Green - Primary brand color */
|
|
11
|
+
readonly primary: (text: string) => any;
|
|
12
|
+
/** Cyan - Secondary brand color */
|
|
13
|
+
readonly secondary: (text: string) => any;
|
|
14
|
+
/** Magenta - Accent color for highlights */
|
|
15
|
+
readonly accent: (text: string) => any;
|
|
16
|
+
/** Bold Green - Indicates successful operations */
|
|
17
|
+
readonly success: (text: string) => any;
|
|
18
|
+
/** Bold Red - Indicates errors or critical failures */
|
|
19
|
+
readonly error: (text: string) => any;
|
|
20
|
+
/** Bold Yellow - Indicates warnings or non-critical issues */
|
|
21
|
+
readonly warning: (text: string) => any;
|
|
22
|
+
/** Gray - For less important or decorative text */
|
|
23
|
+
readonly muted: (text: string) => any;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Interpolates between two hex colors to create a gradient effect on text.
|
|
27
|
+
*
|
|
28
|
+
* @param text - The string to apply the gradient to
|
|
29
|
+
* @param startRgb - Start color in hex format (e.g., 0x00FF87)
|
|
30
|
+
* @param endRgb - End color in hex format (e.g., 0x60EFFF)
|
|
31
|
+
* @returns The text with ANSI color codes applied per character
|
|
32
|
+
*/
|
|
33
|
+
export declare function gradient(text: string, startRgb: number, endRgb: number): string;
|
|
34
|
+
/**
|
|
35
|
+
* Standard Seyuna gradient (Spring Green to Cyan).
|
|
36
|
+
* Used for logos and primary brand headings.
|
|
37
|
+
*/
|
|
38
|
+
export declare const seyunaGradient: ColorFunction;
|
|
39
|
+
/**
|
|
40
|
+
* Branded Unicode symbols for consistent status reporting.
|
|
41
|
+
*/
|
|
42
|
+
export declare const Symbols: {
|
|
43
|
+
/** Info icon (Cyan) */
|
|
44
|
+
readonly info: any;
|
|
45
|
+
/** Success icon (Green) */
|
|
46
|
+
readonly success: any;
|
|
47
|
+
/** Warning icon (Yellow) */
|
|
48
|
+
readonly warning: any;
|
|
49
|
+
/** Error icon (Red) */
|
|
50
|
+
readonly error: any;
|
|
51
|
+
/** Decorative arrow (Muted) */
|
|
52
|
+
readonly arrow: any;
|
|
53
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as colors from "@std/fmt/colors";
|
|
2
|
+
/**
|
|
3
|
+
* Standard brand colors and styles for the Seyuna CLI.
|
|
4
|
+
* Provides a consistent visual identity across all command outputs.
|
|
5
|
+
*/
|
|
6
|
+
export const Brand = {
|
|
7
|
+
/** Vibrant Spring Green - Primary brand color */
|
|
8
|
+
primary: (text) => colors.rgb24(text, 0x00FF87),
|
|
9
|
+
/** Cyan - Secondary brand color */
|
|
10
|
+
secondary: (text) => colors.rgb24(text, 0x60EFFF),
|
|
11
|
+
/** Magenta - Accent color for highlights */
|
|
12
|
+
accent: (text) => colors.rgb24(text, 0xFF00E5),
|
|
13
|
+
/** Bold Green - Indicates successful operations */
|
|
14
|
+
success: (text) => colors.bold(colors.green(text)),
|
|
15
|
+
/** Bold Red - Indicates errors or critical failures */
|
|
16
|
+
error: (text) => colors.bold(colors.red(text)),
|
|
17
|
+
/** Bold Yellow - Indicates warnings or non-critical issues */
|
|
18
|
+
warning: (text) => colors.bold(colors.yellow(text)),
|
|
19
|
+
/** Gray - For less important or decorative text */
|
|
20
|
+
muted: (text) => colors.gray(text),
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Interpolates between two hex colors to create a gradient effect on text.
|
|
24
|
+
*
|
|
25
|
+
* @param text - The string to apply the gradient to
|
|
26
|
+
* @param startRgb - Start color in hex format (e.g., 0x00FF87)
|
|
27
|
+
* @param endRgb - End color in hex format (e.g., 0x60EFFF)
|
|
28
|
+
* @returns The text with ANSI color codes applied per character
|
|
29
|
+
*/
|
|
30
|
+
export function gradient(text, startRgb, endRgb) {
|
|
31
|
+
const start = {
|
|
32
|
+
r: (startRgb >> 16) & 0xFF,
|
|
33
|
+
g: (startRgb >> 8) & 0xFF,
|
|
34
|
+
b: startRgb & 0xFF,
|
|
35
|
+
};
|
|
36
|
+
const end = {
|
|
37
|
+
r: (endRgb >> 16) & 0xFF,
|
|
38
|
+
g: (endRgb >> 8) & 0xFF,
|
|
39
|
+
b: endRgb & 0xFF,
|
|
40
|
+
};
|
|
41
|
+
const characters = Array.from(text);
|
|
42
|
+
const length = characters.length;
|
|
43
|
+
return characters.map((char, i) => {
|
|
44
|
+
// If text is 1 char long, use start color
|
|
45
|
+
const factor = length > 1 ? i / (length - 1) : 0;
|
|
46
|
+
const r = Math.round(start.r + factor * (end.r - start.r));
|
|
47
|
+
const g = Math.round(start.g + factor * (end.g - start.g));
|
|
48
|
+
const b = Math.round(start.b + factor * (end.b - start.b));
|
|
49
|
+
return colors.rgb24(char, (r << 16) | (g << 8) | b);
|
|
50
|
+
}).join("");
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Standard Seyuna gradient (Spring Green to Cyan).
|
|
54
|
+
* Used for logos and primary brand headings.
|
|
55
|
+
*/
|
|
56
|
+
export const seyunaGradient = (text) => gradient(text, 0x00FF87, 0x60EFFF);
|
|
57
|
+
/**
|
|
58
|
+
* Branded Unicode symbols for consistent status reporting.
|
|
59
|
+
*/
|
|
60
|
+
export const Symbols = {
|
|
61
|
+
/** Info icon (Cyan) */
|
|
62
|
+
info: Brand.secondary("ℹ"),
|
|
63
|
+
/** Success icon (Green) */
|
|
64
|
+
success: Brand.success("✔"),
|
|
65
|
+
/** Warning icon (Yellow) */
|
|
66
|
+
warning: Brand.warning("⚠"),
|
|
67
|
+
/** Error icon (Red) */
|
|
68
|
+
error: Brand.error("✖"),
|
|
69
|
+
/** Decorative arrow (Muted) */
|
|
70
|
+
arrow: Brand.muted("›"),
|
|
71
|
+
};
|
package/esm/src/main.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Seyuna CLI - Entrypoint
|
|
4
|
+
*
|
|
5
|
+
* This is the main orchestrator for the Seyuna Command Line Interface.
|
|
6
|
+
* It defines the command hierarchy and provides the branded "Premium"
|
|
7
|
+
* help screens.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import "../_dnt.polyfills.js";
|
|
12
|
+
import * as dntShim from "../_dnt.shims.js";
|
|
13
|
+
import { Command } from "@cliffy/command";
|
|
14
|
+
import * as colors from "@std/fmt/colors";
|
|
15
|
+
import { initConfig } from "./config/init.js";
|
|
16
|
+
import { compile } from "./ui/compile.js";
|
|
17
|
+
import { Brand, seyunaGradient } from "./helpers/styles.js";
|
|
18
|
+
// Import project name and version from deno.json for single-source-of-truth.
|
|
19
|
+
import denoConfig from "../deno.js";
|
|
20
|
+
const VERSION = denoConfig.version;
|
|
21
|
+
/**
|
|
22
|
+
* High-fidelity ASCII logo for the CLI.
|
|
23
|
+
* Rendered using the primary brand gradient.
|
|
24
|
+
*/
|
|
25
|
+
const seyunaLogo = seyunaGradient(`
|
|
26
|
+
|
|
27
|
+
_____ _____ __ __ _____ _____ _____
|
|
28
|
+
| __| __| | | | | | | _ |
|
|
29
|
+
|__ | __|_ _| | | | | | |
|
|
30
|
+
|_____|_____| |_| |_____|_|___|__|__|
|
|
31
|
+
|
|
32
|
+
`);
|
|
33
|
+
/**
|
|
34
|
+
* 'ui' Command Definition.
|
|
35
|
+
* Handles style compilation, real-time watching, and configuration.
|
|
36
|
+
*/
|
|
37
|
+
const uiCmd = new Command()
|
|
38
|
+
.option("-c, --compile", "Run a one-time compilation of your styles.")
|
|
39
|
+
.option("-w, --watch", "Watch for changes and re-compile automatically.")
|
|
40
|
+
.action(async (options) => {
|
|
41
|
+
if (options.compile || options.watch) {
|
|
42
|
+
await compile({ watch: options.watch });
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
console.log(Brand.warning("Please provide an option or subcommand. See --help for more info."));
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
/**
|
|
49
|
+
* 'ui config' Subcommand.
|
|
50
|
+
* Handles project initialization.
|
|
51
|
+
*/
|
|
52
|
+
.command("config")
|
|
53
|
+
.option("-i, --init", "Initialize a new 'seyuna.json' configuration.")
|
|
54
|
+
.action(async (options) => {
|
|
55
|
+
if (options.init) {
|
|
56
|
+
await initConfig();
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
console.log(Brand.warning("Please provide an option. See --help for more info."));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Root 'seyuna' Command.
|
|
64
|
+
*/
|
|
65
|
+
const cmd = new Command()
|
|
66
|
+
.name("seyuna")
|
|
67
|
+
.version(VERSION)
|
|
68
|
+
.action(() => {
|
|
69
|
+
// Show help by default if no subcommand is provided.
|
|
70
|
+
cmd.showHelp();
|
|
71
|
+
})
|
|
72
|
+
.command("ui", uiCmd)
|
|
73
|
+
.command("generate-json-schema")
|
|
74
|
+
.hidden() // Hidden command for future expansion
|
|
75
|
+
.action(() => {
|
|
76
|
+
console.log(Brand.muted("JSON schema generation is planned for a future release."));
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* Traverses the command tree and prepends the branded logo to every
|
|
80
|
+
* generated help screen. This ensures a consistent premium brand experience.
|
|
81
|
+
*
|
|
82
|
+
* @param command - The Cliffy Command instance to apply the logo to.
|
|
83
|
+
*/
|
|
84
|
+
// deno-lint-ignore no-explicit-any
|
|
85
|
+
const applyBrandedHelp = (command) => {
|
|
86
|
+
const originalGetHelp = command.getHelp;
|
|
87
|
+
command.getHelp = function () {
|
|
88
|
+
return seyunaLogo + originalGetHelp.call(this);
|
|
89
|
+
};
|
|
90
|
+
// Recursively apply to all sub-commands
|
|
91
|
+
command.getCommands().forEach((c) => applyBrandedHelp(c));
|
|
92
|
+
};
|
|
93
|
+
// Initialize branding
|
|
94
|
+
// @ts-ignore: Cliffy Command types are highly generic and conflict after adding subcommands
|
|
95
|
+
applyBrandedHelp(cmd);
|
|
96
|
+
/**
|
|
97
|
+
* CLI Execution Loop.
|
|
98
|
+
* Handles argument parsing and top-level error reporting.
|
|
99
|
+
*/
|
|
100
|
+
if ((import.meta.url === ("file:///" + process.argv[1].replace(/\\/g, "/")).replace(/\/{3,}/, "///"))) {
|
|
101
|
+
try {
|
|
102
|
+
await cmd.parse(dntShim.Deno.args);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (error instanceof Error) {
|
|
106
|
+
console.error(`${colors.bold(colors.red("Error:"))} ${error.message}`);
|
|
107
|
+
}
|
|
108
|
+
dntShim.Deno.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Config } from "../config/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Main entrypoint for the UI compilation process.
|
|
4
|
+
* Orchestrates loading configuration, generating CSS, and optionally
|
|
5
|
+
* starting a file watcher for real-time development.
|
|
6
|
+
*
|
|
7
|
+
* @param options - Compilation options, including 'watch' mode
|
|
8
|
+
* @returns The loaded project configuration
|
|
9
|
+
*/
|
|
10
|
+
export declare function compile(options: {
|
|
11
|
+
watch?: boolean;
|
|
12
|
+
}): Promise<Config>;
|
|
13
|
+
/**
|
|
14
|
+
* The core engine that translates project tokens into functional CSS files.
|
|
15
|
+
* Generates both 'seyuna-global.css' (reset + variables) and
|
|
16
|
+
* 'postcss-variables.css' (design token manifest for PostCSS).
|
|
17
|
+
*
|
|
18
|
+
* @param config - The project configuration object
|
|
19
|
+
*/
|
|
20
|
+
export declare function compileCss(config: Config): Promise<void>;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { GLOBAL_CSS } from "./global.css.js";
|
|
3
|
+
import { createPathFromFileName, loadSeyunaUserConfig, saveFile, } from "../helpers/fs.js";
|
|
4
|
+
import { SeyunaSpinner } from "../helpers/cli.js";
|
|
5
|
+
import { delay } from "@std/async";
|
|
6
|
+
import { Brand } from "../helpers/styles.js";
|
|
7
|
+
/**
|
|
8
|
+
* Main entrypoint for the UI compilation process.
|
|
9
|
+
* Orchestrates loading configuration, generating CSS, and optionally
|
|
10
|
+
* starting a file watcher for real-time development.
|
|
11
|
+
*
|
|
12
|
+
* @param options - Compilation options, including 'watch' mode
|
|
13
|
+
* @returns The loaded project configuration
|
|
14
|
+
*/
|
|
15
|
+
export async function compile(options) {
|
|
16
|
+
try {
|
|
17
|
+
const config = await loadSeyunaUserConfig();
|
|
18
|
+
await compileCss(config);
|
|
19
|
+
if (options.watch) {
|
|
20
|
+
console.log(`\n${Brand.secondary("Watching for changes...")}`);
|
|
21
|
+
await watchConfigAndRecompile("seyuna.json");
|
|
22
|
+
}
|
|
23
|
+
return config;
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Starts an optimized file watcher on the configuration manifest.
|
|
31
|
+
* Provides debounced recompilation to handle rapid file system events.
|
|
32
|
+
*
|
|
33
|
+
* @param file - The path to the configuration file to watch
|
|
34
|
+
*/
|
|
35
|
+
async function watchConfigAndRecompile(file) {
|
|
36
|
+
const watcher = dntShim.Deno.watchFs(file);
|
|
37
|
+
let debounceTimer;
|
|
38
|
+
for await (const event of watcher) {
|
|
39
|
+
// Only trigger on actual content modifications
|
|
40
|
+
if (event.kind === "modify") {
|
|
41
|
+
if (debounceTimer)
|
|
42
|
+
clearTimeout(debounceTimer);
|
|
43
|
+
debounceTimer = setTimeout(async () => {
|
|
44
|
+
// Short delay to ensure the OS has finished writing the file
|
|
45
|
+
await delay(300);
|
|
46
|
+
try {
|
|
47
|
+
const config = await loadSeyunaUserConfig();
|
|
48
|
+
await compileCss(config);
|
|
49
|
+
const time = new Date().toLocaleTimeString();
|
|
50
|
+
console.log(`${Brand.muted(time)} ${Brand.success("Recompiled successfully")}`);
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
console.error(`${Brand.error("Watch Error:")} ${e instanceof Error ? e.message : "Unknown error"}`);
|
|
54
|
+
}
|
|
55
|
+
}, 50);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The core engine that translates project tokens into functional CSS files.
|
|
61
|
+
* Generates both 'seyuna-global.css' (reset + variables) and
|
|
62
|
+
* 'postcss-variables.css' (design token manifest for PostCSS).
|
|
63
|
+
*
|
|
64
|
+
* @param config - The project configuration object
|
|
65
|
+
*/
|
|
66
|
+
export async function compileCss(config) {
|
|
67
|
+
const spinner = new SeyunaSpinner("Compiling your UI styles").start();
|
|
68
|
+
try {
|
|
69
|
+
const ui = getUiConfig(config);
|
|
70
|
+
if (!ui.output_dir) {
|
|
71
|
+
throw new Error("Output directory ('output_dir') must be specified in the UI configuration.");
|
|
72
|
+
}
|
|
73
|
+
spinner.progress("Building token manifest");
|
|
74
|
+
const globalCss = buildGlobalCss(config);
|
|
75
|
+
const postcssVariables = buildPostcssVariables(config);
|
|
76
|
+
const globalPath = createPathFromFileName("seyuna-global.css", ui.output_dir);
|
|
77
|
+
const varsPath = createPathFromFileName("postcss-variables.css", ui.output_dir);
|
|
78
|
+
spinner.progress(`Persisting styles to ${ui.output_dir}`);
|
|
79
|
+
await saveFile(globalPath, globalCss);
|
|
80
|
+
await saveFile(varsPath, postcssVariables);
|
|
81
|
+
spinner.done("Styles compiled successfully!");
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
spinner.error(`Compilation failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Safely extracts the UI block from the root configuration.
|
|
90
|
+
*/
|
|
91
|
+
function getUiConfig(config) {
|
|
92
|
+
if (!config.ui) {
|
|
93
|
+
throw new Error("Missing 'ui' configuration block in 'seyuna.json'.");
|
|
94
|
+
}
|
|
95
|
+
return config.ui;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Combines dynamic token variables with the global reset template.
|
|
99
|
+
*/
|
|
100
|
+
function buildGlobalCss(config) {
|
|
101
|
+
return [buildCssVariables(config), GLOBAL_CSS].join("\n");
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Generates a full CSS variable sheet including base tokens,
|
|
105
|
+
* theme modes, and system scheme adaptivity.
|
|
106
|
+
*/
|
|
107
|
+
function buildCssVariables(config) {
|
|
108
|
+
const ui = getUiConfig(config);
|
|
109
|
+
const out = [":root {"];
|
|
110
|
+
// Hue Tokens (e.g., --alpha-hue)
|
|
111
|
+
for (const [name, val] of Object.entries(ui.theme.hues)) {
|
|
112
|
+
out.push(` --${name}-hue: ${val};`);
|
|
113
|
+
}
|
|
114
|
+
// Global Semantic Colors
|
|
115
|
+
for (const [name, color] of Object.entries(ui.theme.colors)) {
|
|
116
|
+
out.push(` --${name}-lightness: ${color.lightness};`);
|
|
117
|
+
out.push(` --${name}-chroma: ${color.chroma};`);
|
|
118
|
+
out.push(` --${name}-hue: ${color.hue};`);
|
|
119
|
+
}
|
|
120
|
+
out.push("}\n");
|
|
121
|
+
const modes = ["light", "dark"];
|
|
122
|
+
// Explicit Theme Mode Overrides ([data-mode="light"])
|
|
123
|
+
for (const mode of modes) {
|
|
124
|
+
out.push(`[data-mode="${mode}"] {`);
|
|
125
|
+
out.push(buildModeBlock(ui, mode));
|
|
126
|
+
// Mode-specific semantic color overrides
|
|
127
|
+
for (const [name, color] of Object.entries(ui.theme[mode].colors)) {
|
|
128
|
+
out.push(` --${name}-lightness: ${color.lightness};`);
|
|
129
|
+
out.push(` --${name}-chroma: ${color.chroma};`);
|
|
130
|
+
out.push(` --${name}-hue: ${color.hue};`);
|
|
131
|
+
}
|
|
132
|
+
out.push("}\n");
|
|
133
|
+
}
|
|
134
|
+
// System Adatpivity (Auto switching via @media)
|
|
135
|
+
for (const mode of modes) {
|
|
136
|
+
out.push(`@media (prefers-color-scheme: ${mode}) {`);
|
|
137
|
+
out.push(' [data-mode="system"] {');
|
|
138
|
+
out.push(indent(buildModeBlock(ui, mode), 2));
|
|
139
|
+
for (const [name, color] of Object.entries(ui.theme[mode].colors)) {
|
|
140
|
+
out.push(` --${name}-lightness: ${color.lightness};`);
|
|
141
|
+
out.push(` --${name}-chroma: ${color.chroma};`);
|
|
142
|
+
out.push(` --${name}-hue: ${color.hue};`);
|
|
143
|
+
}
|
|
144
|
+
out.push(" }");
|
|
145
|
+
out.push("}\n");
|
|
146
|
+
}
|
|
147
|
+
return out.join("\n");
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Generates a SASS/PostCSS compatible SCSS manifest.
|
|
151
|
+
* Used for deep integration with traditional build tools.
|
|
152
|
+
*/
|
|
153
|
+
function buildPostcssVariables(config) {
|
|
154
|
+
const ui = getUiConfig(config);
|
|
155
|
+
const fixed = Object.keys(ui.theme.colors).map((c) => `'${c}'`).join(", ");
|
|
156
|
+
const hues = Object.keys(ui.theme.hues).map((n) => `'${n}'`).join(", ");
|
|
157
|
+
return [
|
|
158
|
+
`$fixed_colors: (${fixed});`,
|
|
159
|
+
`$standard_colors: (${hues});`,
|
|
160
|
+
"$xs: 20rem;",
|
|
161
|
+
"$sm: 40rem;",
|
|
162
|
+
"$md: 48rem;",
|
|
163
|
+
"$lg: 64rem;",
|
|
164
|
+
"$xl: 80rem;",
|
|
165
|
+
"\n",
|
|
166
|
+
].join("\n");
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Builds a block of core CSS variables (background, text) for a specific mode.
|
|
170
|
+
*/
|
|
171
|
+
function buildModeBlock(ui, mode) {
|
|
172
|
+
const p = ui.theme[mode];
|
|
173
|
+
return [
|
|
174
|
+
` --background: oklch(${p.background.lightness} ${p.background.chroma} ${p.background.hue});`,
|
|
175
|
+
` --text: oklch(${p.text.lightness} ${p.text.chroma} ${p.text.hue});`,
|
|
176
|
+
` --chroma: ${p.chroma};`,
|
|
177
|
+
` --lightness: ${p.lightness};`,
|
|
178
|
+
].join("\n");
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Helper to indent string lines for pretty-printed CSS output.
|
|
182
|
+
*
|
|
183
|
+
* @param s - The string to indent
|
|
184
|
+
* @param level - Number of double-space indents to apply
|
|
185
|
+
* @returns The formatted and indented string
|
|
186
|
+
*/
|
|
187
|
+
function indent(s, level) {
|
|
188
|
+
const pad = " ".repeat(level);
|
|
189
|
+
return s.split(/\r?\n/)
|
|
190
|
+
.filter((line) => line.trim().length > 0)
|
|
191
|
+
.map((line) => `${pad}${line.trim()}\n`)
|
|
192
|
+
.join("");
|
|
193
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { UI } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The default out-of-the-box UI configuration for Seyuna projects.
|
|
4
|
+
* This provides a robust set of design tokens including a full
|
|
5
|
+
* Greek-lettered hue scale and foundational semantic colors.
|
|
6
|
+
*/
|
|
7
|
+
export declare const UI_CONFIGURATION: UI;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default out-of-the-box UI configuration for Seyuna projects.
|
|
3
|
+
* This provides a robust set of design tokens including a full
|
|
4
|
+
* Greek-lettered hue scale and foundational semantic colors.
|
|
5
|
+
*/
|
|
6
|
+
export const UI_CONFIGURATION = {
|
|
7
|
+
theme: {
|
|
8
|
+
/**
|
|
9
|
+
* Global hue scale. Uses Greek letters to provide semantically neutral
|
|
10
|
+
* references for the 360-degree color wheel.
|
|
11
|
+
*/
|
|
12
|
+
hues: {
|
|
13
|
+
"alpha": 0,
|
|
14
|
+
"beta": 15,
|
|
15
|
+
"gamma": 30,
|
|
16
|
+
"delta": 45,
|
|
17
|
+
"epsilon": 60,
|
|
18
|
+
"zeta": 75,
|
|
19
|
+
"eta": 90,
|
|
20
|
+
"theta": 105,
|
|
21
|
+
"iota": 120,
|
|
22
|
+
"kappa": 135,
|
|
23
|
+
"lambda": 150,
|
|
24
|
+
"mu": 165,
|
|
25
|
+
"nu": 180,
|
|
26
|
+
"xi": 195,
|
|
27
|
+
"omicron": 210,
|
|
28
|
+
"pi": 225,
|
|
29
|
+
"rho": 240,
|
|
30
|
+
"sigma": 255,
|
|
31
|
+
"tau": 270,
|
|
32
|
+
"upsilon": 285,
|
|
33
|
+
"phi": 300,
|
|
34
|
+
"chi": 315,
|
|
35
|
+
"psi": 330,
|
|
36
|
+
"omega": 345,
|
|
37
|
+
},
|
|
38
|
+
/** Base semantic colors shared across modes */
|
|
39
|
+
colors: {
|
|
40
|
+
"white": { lightness: 1.0, chroma: 0.0, hue: 0 },
|
|
41
|
+
"black": { lightness: 0.0, chroma: 0.0, hue: 0 },
|
|
42
|
+
"primary": { lightness: 0.66, chroma: 0.26, hue: 240 },
|
|
43
|
+
},
|
|
44
|
+
/** Default light mode palette */
|
|
45
|
+
light: {
|
|
46
|
+
chroma: 0.26,
|
|
47
|
+
lightness: 0.66,
|
|
48
|
+
background: { hue: 0, chroma: 0.0, lightness: 1.0 },
|
|
49
|
+
text: { hue: 0, chroma: 0.0, lightness: 0.0 },
|
|
50
|
+
colors: {},
|
|
51
|
+
},
|
|
52
|
+
/** Default dark mode palette */
|
|
53
|
+
dark: {
|
|
54
|
+
chroma: 0.26,
|
|
55
|
+
lightness: 0.66,
|
|
56
|
+
background: { hue: 0, chroma: 0.0, lightness: 0.0 },
|
|
57
|
+
text: { hue: 0, chroma: 0.0, lightness: 1.0 },
|
|
58
|
+
colors: {},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
/** Default to following the system's preferred color scheme */
|
|
62
|
+
mode: "system",
|
|
63
|
+
/** Standard output directory for generated CSS assets */
|
|
64
|
+
output_dir: "src/styles",
|
|
65
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core global CSS template for the Seyuna framework.
|
|
3
|
+
*
|
|
4
|
+
* Provides a modern baseline including:
|
|
5
|
+
* - CSS Cascade Layers (@layer reset, base, etc.)
|
|
6
|
+
* - Sophisticated CSS Reset with focus on accessibility and modern typography
|
|
7
|
+
* - Responsive typography foundations
|
|
8
|
+
* - Standardized element baselines (links, images, headings)
|
|
9
|
+
*/
|
|
10
|
+
export declare const GLOBAL_CSS = "@layer reset, base, components, utilities;\n\n@layer reset {\n/* \n Reset all elements to a zeroed baseline to ensure consistency in all browsers.\n*/\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n line-height: 2;\n font-family: inherit;\n }\n\n/* \n Prevent automatic font size adjustments on mobile devices to keep text rendering consistent across platforms.\n*/\n html {\n -moz-text-size-adjust: none; /* Firefox */\n -webkit-text-size-adjust: none; /* Safari / older WebKit browsers */\n text-size-adjust: none; /* Modern spec-compliant browsers */\n color: var(--text);\n background-color: var(--background);\n font-size: max(1rem, 0.833vw);\n }\n\n/* \n Remove default list styling from unordered and ordered lists that \n explicitly use role=\"list\".\n*/\n ul[role=\"list\"],\n ol[role=\"list\"] {\n list-style: none;\n }\n\n/* \n Ensure the body element always fills at least the full height of the viewport\n so short pages don't collapse. Improve text rendering across browsers by\n enabling smoother grayscale antialiasing. Set the body as a container\n (queryable by its inline size) for use with CSS container queries, and\n enable smooth scrolling for anchor links and programmatic scroll actions.\n*/\n body {\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n container-type: inline-size;\n scroll-behavior: smooth;\n }\n\n/* \n Improve heading readability by evenly distributing words across lines.\n This prevents awkward line breaks and creates more balanced, aesthetic headings.\n*/\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n text-wrap: balance;\n }\n\n/* \n Reset all link styles to inherit from parent elements.\n This removes default colors, underlines, and visited/hover states.\n*/\n a {\n all: unset;\n color: inherit;\n text-decoration: none;\n cursor: pointer;\n }\n\n/* \n Ensure images and <picture> elements scale within their container,\n preventing overflow and maintaining layout integrity on any screen size.\n*/\n img,\n picture {\n max-width: 100%;\n display: block;\n }\n\n/* \n Add extra scroll margin above and below elements targeted via anchors.\n This prevents the element from hugging the top edge of the viewport when navigated to.\n*/\n :target {\n scroll-margin-block: 1rem;\n }\n}\n";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core global CSS template for the Seyuna framework.
|
|
3
|
+
*
|
|
4
|
+
* Provides a modern baseline including:
|
|
5
|
+
* - CSS Cascade Layers (@layer reset, base, etc.)
|
|
6
|
+
* - Sophisticated CSS Reset with focus on accessibility and modern typography
|
|
7
|
+
* - Responsive typography foundations
|
|
8
|
+
* - Standardized element baselines (links, images, headings)
|
|
9
|
+
*/
|
|
10
|
+
export const GLOBAL_CSS = `@layer reset, base, components, utilities;
|
|
11
|
+
|
|
12
|
+
@layer reset {
|
|
13
|
+
/*
|
|
14
|
+
Reset all elements to a zeroed baseline to ensure consistency in all browsers.
|
|
15
|
+
*/
|
|
16
|
+
*,
|
|
17
|
+
*::before,
|
|
18
|
+
*::after {
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
line-height: 2;
|
|
23
|
+
font-family: inherit;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
Prevent automatic font size adjustments on mobile devices to keep text rendering consistent across platforms.
|
|
28
|
+
*/
|
|
29
|
+
html {
|
|
30
|
+
-moz-text-size-adjust: none; /* Firefox */
|
|
31
|
+
-webkit-text-size-adjust: none; /* Safari / older WebKit browsers */
|
|
32
|
+
text-size-adjust: none; /* Modern spec-compliant browsers */
|
|
33
|
+
color: var(--text);
|
|
34
|
+
background-color: var(--background);
|
|
35
|
+
font-size: max(1rem, 0.833vw);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
Remove default list styling from unordered and ordered lists that
|
|
40
|
+
explicitly use role="list".
|
|
41
|
+
*/
|
|
42
|
+
ul[role="list"],
|
|
43
|
+
ol[role="list"] {
|
|
44
|
+
list-style: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
Ensure the body element always fills at least the full height of the viewport
|
|
49
|
+
so short pages don't collapse. Improve text rendering across browsers by
|
|
50
|
+
enabling smoother grayscale antialiasing. Set the body as a container
|
|
51
|
+
(queryable by its inline size) for use with CSS container queries, and
|
|
52
|
+
enable smooth scrolling for anchor links and programmatic scroll actions.
|
|
53
|
+
*/
|
|
54
|
+
body {
|
|
55
|
+
min-height: 100vh;
|
|
56
|
+
-webkit-font-smoothing: antialiased;
|
|
57
|
+
-moz-osx-font-smoothing: grayscale;
|
|
58
|
+
container-type: inline-size;
|
|
59
|
+
scroll-behavior: smooth;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
Improve heading readability by evenly distributing words across lines.
|
|
64
|
+
This prevents awkward line breaks and creates more balanced, aesthetic headings.
|
|
65
|
+
*/
|
|
66
|
+
h1,
|
|
67
|
+
h2,
|
|
68
|
+
h3,
|
|
69
|
+
h4,
|
|
70
|
+
h5,
|
|
71
|
+
h6 {
|
|
72
|
+
text-wrap: balance;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
Reset all link styles to inherit from parent elements.
|
|
77
|
+
This removes default colors, underlines, and visited/hover states.
|
|
78
|
+
*/
|
|
79
|
+
a {
|
|
80
|
+
all: unset;
|
|
81
|
+
color: inherit;
|
|
82
|
+
text-decoration: none;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/*
|
|
87
|
+
Ensure images and <picture> elements scale within their container,
|
|
88
|
+
preventing overflow and maintaining layout integrity on any screen size.
|
|
89
|
+
*/
|
|
90
|
+
img,
|
|
91
|
+
picture {
|
|
92
|
+
max-width: 100%;
|
|
93
|
+
display: block;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
Add extra scroll margin above and below elements targeted via anchors.
|
|
98
|
+
This prevents the element from hugging the top edge of the viewport when navigated to.
|
|
99
|
+
*/
|
|
100
|
+
:target {
|
|
101
|
+
scroll-margin-block: 1rem;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported color appearance modes.
|
|
3
|
+
* - 'system': Automatically follows OS preference.
|
|
4
|
+
* - 'light': Forces light theme.
|
|
5
|
+
* - 'dark': Forces dark theme.
|
|
6
|
+
*/
|
|
7
|
+
export type Mode = "system" | "light" | "dark";
|
|
8
|
+
/**
|
|
9
|
+
* Represents a color in the Oklch color space.
|
|
10
|
+
* Oklch provides a perceptually uniform way to define colors,
|
|
11
|
+
* making it ideal for accessibility-conscious design systems.
|
|
12
|
+
*/
|
|
13
|
+
export interface Color {
|
|
14
|
+
/** Lightness (0-100%) */
|
|
15
|
+
lightness: number;
|
|
16
|
+
/** Chroma (color intensity, 0-0.4) */
|
|
17
|
+
chroma: number;
|
|
18
|
+
/** Hue (angle on the color wheel, 0-360) */
|
|
19
|
+
hue: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A cohesive set of colors used within a specific theme mode (Light or Dark).
|
|
23
|
+
*/
|
|
24
|
+
export interface Palette {
|
|
25
|
+
/** Base chroma for primary elements */
|
|
26
|
+
chroma: number;
|
|
27
|
+
/** Base lightness for background/text contrast */
|
|
28
|
+
lightness: number;
|
|
29
|
+
/** Prime background color definition */
|
|
30
|
+
background: Color;
|
|
31
|
+
/** Prime text color definition */
|
|
32
|
+
text: Color;
|
|
33
|
+
/** Named semantic colors (e.g., 'primary', 'secondary') */
|
|
34
|
+
colors: Record<string, Color>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The complete theme specification containing both light and dark mode mappings.
|
|
38
|
+
*/
|
|
39
|
+
export interface Theme {
|
|
40
|
+
/** Global hue definitions for consistent branding */
|
|
41
|
+
hues: Record<string, number>;
|
|
42
|
+
/** Global color definitions inherited by palettes */
|
|
43
|
+
colors: Record<string, Color>;
|
|
44
|
+
/** Light mode palette configuration */
|
|
45
|
+
light: Palette;
|
|
46
|
+
/** Dark mode palette configuration */
|
|
47
|
+
dark: Palette;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* UI Engine Configuration.
|
|
51
|
+
* Controls how the CSS is generated and where it's saved.
|
|
52
|
+
*/
|
|
53
|
+
export interface UI {
|
|
54
|
+
/** Theme specification */
|
|
55
|
+
theme: Theme;
|
|
56
|
+
/** Default theme mode */
|
|
57
|
+
mode: Mode;
|
|
58
|
+
/** Relative path to save generated CSS files */
|
|
59
|
+
output_dir?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Merges two color objects. Currently prioritizes the override.
|
|
63
|
+
*/
|
|
64
|
+
export declare function mergeColor(_base: Color, override: Color): Color;
|
|
65
|
+
/**
|
|
66
|
+
* Recursively merges two palettes.
|
|
67
|
+
*/
|
|
68
|
+
export declare function mergePalette(base: Palette, override: Palette): Palette;
|
|
69
|
+
/**
|
|
70
|
+
* Recursively merges two theme specifications.
|
|
71
|
+
*/
|
|
72
|
+
export declare function mergeTheme(base: Theme, override: Theme): Theme;
|
|
73
|
+
/**
|
|
74
|
+
* Recursively merges two UI engine configurations.
|
|
75
|
+
*/
|
|
76
|
+
export declare function mergeUI(base: UI, override: UI): UI;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merges two color objects. Currently prioritizes the override.
|
|
3
|
+
*/
|
|
4
|
+
export function mergeColor(_base, override) {
|
|
5
|
+
return { ...override };
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Recursively merges two palettes.
|
|
9
|
+
*/
|
|
10
|
+
export function mergePalette(base, override) {
|
|
11
|
+
return {
|
|
12
|
+
...base,
|
|
13
|
+
...override,
|
|
14
|
+
background: mergeColor(base.background, override.background),
|
|
15
|
+
text: mergeColor(base.text, override.text),
|
|
16
|
+
colors: { ...base.colors, ...override.colors },
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Recursively merges two theme specifications.
|
|
21
|
+
*/
|
|
22
|
+
export function mergeTheme(base, override) {
|
|
23
|
+
return {
|
|
24
|
+
...base,
|
|
25
|
+
...override,
|
|
26
|
+
hues: { ...base.hues, ...override.hues },
|
|
27
|
+
colors: { ...base.colors, ...override.colors },
|
|
28
|
+
light: mergePalette(base.light, override.light),
|
|
29
|
+
dark: mergePalette(base.dark, override.dark),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Recursively merges two UI engine configurations.
|
|
34
|
+
*/
|
|
35
|
+
export function mergeUI(base, override) {
|
|
36
|
+
return {
|
|
37
|
+
...base,
|
|
38
|
+
...override,
|
|
39
|
+
theme: mergeTheme(base.theme, override.theme),
|
|
40
|
+
};
|
|
41
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seyuna/cli",
|
|
3
|
-
"version": "1.0.0-canary.
|
|
3
|
+
"version": "1.0.0-canary.19",
|
|
4
4
|
"description": "Seyuna CLI",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"node": ">=18"
|
|
21
21
|
},
|
|
22
22
|
"bin": {
|
|
23
|
-
"seyuna": "./esm/main.js"
|
|
23
|
+
"seyuna": "./esm/src/main.js"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@deno/shim-deno": "~0.18.0"
|