@tooee/config 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 ADDED
@@ -0,0 +1,5 @@
1
+ # @tooee/config
2
+
3
+ Layered configuration system for Tooee apps.
4
+
5
+ Part of the [Tooee](https://github.com/gingerhendrix/tooee) monorepo. See the main repo for documentation.
@@ -0,0 +1,10 @@
1
+ import { type ReactNode } from "react";
2
+ import type { TooeeConfig } from "./types.js";
3
+ export declare function ConfigProvider({ overrides, children, }: {
4
+ overrides?: Partial<TooeeConfig>;
5
+ children: ReactNode;
6
+ }): ReactNode;
7
+ export declare function useConfig(): TooeeConfig;
8
+ export declare function useThemeConfig(): TooeeConfig["theme"];
9
+ export declare function useKeymapConfig(): Record<string, string>;
10
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAY7C,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,QAAQ,GACT,EAAE;IACD,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAChC,QAAQ,EAAE,SAAS,CAAA;CACpB,aAGA;AAED,wBAAgB,SAAS,IAAI,WAAW,CAEvC;AAED,wBAAgB,cAAc,IAAI,WAAW,CAAC,OAAO,CAAC,CAErD;AAED,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExD"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "@opentui/react/jsx-runtime";
2
+ import { createContext, useContext, useMemo } from "react";
3
+ import { loadConfig } from "./load.js";
4
+ const DEFAULTS = {
5
+ theme: {
6
+ name: "tokyonight",
7
+ mode: "dark",
8
+ },
9
+ };
10
+ const ConfigContext = createContext(DEFAULTS);
11
+ export function ConfigProvider({ overrides, children, }) {
12
+ const config = useMemo(() => loadConfig(overrides), [overrides]);
13
+ return _jsx(ConfigContext.Provider, { value: config, children: children });
14
+ }
15
+ export function useConfig() {
16
+ return useContext(ConfigContext);
17
+ }
18
+ export function useThemeConfig() {
19
+ return useConfig().theme;
20
+ }
21
+ export function useKeymapConfig() {
22
+ return useConfig().keys ?? {};
23
+ }
24
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAA;AAE1E,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEtC,MAAM,QAAQ,GAAgB;IAC5B,KAAK,EAAE;QACL,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,MAAM;KACb;CACF,CAAA;AAED,MAAM,aAAa,GAAG,aAAa,CAAc,QAAQ,CAAC,CAAA;AAE1D,MAAM,UAAU,cAAc,CAAC,EAC7B,SAAS,EACT,QAAQ,GAIT;IACC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAA;IAChE,OAAO,KAAC,aAAa,CAAC,QAAQ,IAAC,KAAK,EAAE,MAAM,YAAG,QAAQ,GAA0B,CAAA;AACnF,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,UAAU,CAAC,aAAa,CAAC,CAAA;AAClC,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,SAAS,EAAE,CAAC,KAAK,CAAA;AAC1B,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,SAAS,EAAE,CAAC,IAAI,IAAI,EAAE,CAAA;AAC/B,CAAC"}
@@ -0,0 +1,4 @@
1
+ export type { TooeeConfig, Mode } from "./types.js";
2
+ export { loadConfig, writeGlobalConfig } from "./load.js";
3
+ export { ConfigProvider, useConfig, useThemeConfig, useKeymapConfig } from "./context.jsx";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { loadConfig, writeGlobalConfig } from "./load.js";
2
+ export { ConfigProvider, useConfig, useThemeConfig, useKeymapConfig } from "./context.jsx";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA"}
package/dist/load.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { TooeeConfig } from "./types.js";
2
+ export declare function loadConfig(overrides?: Partial<TooeeConfig>): TooeeConfig;
3
+ export declare function writeGlobalConfig(partial: Partial<TooeeConfig>): void;
4
+ //# sourceMappingURL=load.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAmD7C,wBAAgB,UAAU,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAQxE;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAWrE"}
package/dist/load.js ADDED
@@ -0,0 +1,73 @@
1
+ import { readFileSync, mkdirSync, writeFileSync, existsSync } from "fs";
2
+ import { join, dirname } from "path";
3
+ const DEFAULTS = {
4
+ theme: {
5
+ name: "tokyonight",
6
+ mode: "dark",
7
+ },
8
+ };
9
+ function deepMerge(target, source) {
10
+ const result = { ...target };
11
+ for (const key of Object.keys(source)) {
12
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
13
+ result[key] = deepMerge(result[key] ?? {}, source[key]);
14
+ }
15
+ else if (source[key] !== undefined) {
16
+ result[key] = source[key];
17
+ }
18
+ }
19
+ return result;
20
+ }
21
+ function readJsonFile(path) {
22
+ try {
23
+ if (!existsSync(path))
24
+ return {};
25
+ return JSON.parse(readFileSync(path, "utf-8"));
26
+ }
27
+ catch {
28
+ return {};
29
+ }
30
+ }
31
+ function getGlobalConfigPath() {
32
+ const xdg = process.env.XDG_CONFIG_HOME ?? join(process.env.HOME ?? "", ".config");
33
+ return join(xdg, "tooee", "config.json");
34
+ }
35
+ function findProjectConfig() {
36
+ let dir = process.cwd();
37
+ const seen = new Set();
38
+ while (dir && !seen.has(dir)) {
39
+ seen.add(dir);
40
+ const configPath = join(dir, ".tooee", "config.json");
41
+ if (existsSync(configPath)) {
42
+ return readJsonFile(configPath);
43
+ }
44
+ const parent = dirname(dir);
45
+ if (parent === dir)
46
+ break;
47
+ dir = parent;
48
+ }
49
+ return {};
50
+ }
51
+ export function loadConfig(overrides) {
52
+ let config = { ...DEFAULTS };
53
+ config = deepMerge(config, readJsonFile(getGlobalConfigPath()));
54
+ config = deepMerge(config, findProjectConfig());
55
+ if (overrides) {
56
+ config = deepMerge(config, overrides);
57
+ }
58
+ return config;
59
+ }
60
+ export function writeGlobalConfig(partial) {
61
+ const path = getGlobalConfigPath();
62
+ const dir = dirname(path);
63
+ try {
64
+ const existing = readJsonFile(path);
65
+ const merged = deepMerge(existing, partial);
66
+ mkdirSync(dir, { recursive: true });
67
+ writeFileSync(path, JSON.stringify(merged, null, 2) + "\n");
68
+ }
69
+ catch {
70
+ // ignore write errors
71
+ }
72
+ }
73
+ //# sourceMappingURL=load.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export type Mode = "dark" | "light";
2
+ export interface TooeeConfig {
3
+ theme?: {
4
+ name?: string;
5
+ mode?: Mode;
6
+ };
7
+ keys?: Record<string, string>;
8
+ view?: {
9
+ wrap?: boolean;
10
+ gutter?: boolean;
11
+ };
12
+ }
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,OAAO,CAAA;AAEnC,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,IAAI,CAAC,EAAE,IAAI,CAAA;KACZ,CAAA;IACD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,IAAI,CAAC,EAAE;QACL,IAAI,CAAC,EAAE,OAAO,CAAA;QACd,MAAM,CAAC,EAAE,OAAO,CAAA;KACjB,CAAA;CACF"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@tooee/config",
3
+ "version": "0.1.0",
4
+ "description": "Layered configuration system for Tooee apps",
5
+ "license": "MIT",
6
+ "author": "Gareth Andrew",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/gingerhendrix/tooee.git",
10
+ "directory": "packages/config"
11
+ },
12
+ "homepage": "https://github.com/gingerhendrix/tooee",
13
+ "bugs": "https://github.com/gingerhendrix/tooee/issues",
14
+ "keywords": ["tui", "terminal", "cli", "opentui", "configuration"],
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "import": {
19
+ "@tooee/source": "./src/index.ts",
20
+ "default": "./dist/index.js"
21
+ }
22
+ }
23
+ },
24
+ "files": ["dist", "src"],
25
+ "scripts": {
26
+ "typecheck": "tsc --noEmit"
27
+ },
28
+ "devDependencies": {
29
+ "@opentui/react": "^0.1.67",
30
+ "@types/bun": "^1.3.5",
31
+ "@types/react": "^19.1.10",
32
+ "typescript": "^5.8.3"
33
+ },
34
+ "peerDependencies": {
35
+ "react": "^18.0.0 || ^19.0.0"
36
+ }
37
+ }
@@ -0,0 +1,35 @@
1
+ import { createContext, useContext, useMemo, type ReactNode } from "react"
2
+ import type { TooeeConfig } from "./types.js"
3
+ import { loadConfig } from "./load.js"
4
+
5
+ const DEFAULTS: TooeeConfig = {
6
+ theme: {
7
+ name: "tokyonight",
8
+ mode: "dark",
9
+ },
10
+ }
11
+
12
+ const ConfigContext = createContext<TooeeConfig>(DEFAULTS)
13
+
14
+ export function ConfigProvider({
15
+ overrides,
16
+ children,
17
+ }: {
18
+ overrides?: Partial<TooeeConfig>
19
+ children: ReactNode
20
+ }) {
21
+ const config = useMemo(() => loadConfig(overrides), [overrides])
22
+ return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>
23
+ }
24
+
25
+ export function useConfig(): TooeeConfig {
26
+ return useContext(ConfigContext)
27
+ }
28
+
29
+ export function useThemeConfig(): TooeeConfig["theme"] {
30
+ return useConfig().theme
31
+ }
32
+
33
+ export function useKeymapConfig(): Record<string, string> {
34
+ return useConfig().keys ?? {}
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type { TooeeConfig, Mode } from "./types.js"
2
+ export { loadConfig, writeGlobalConfig } from "./load.js"
3
+ export { ConfigProvider, useConfig, useThemeConfig, useKeymapConfig } from "./context.jsx"
package/src/load.ts ADDED
@@ -0,0 +1,75 @@
1
+ import { readFileSync, mkdirSync, writeFileSync, existsSync } from "fs"
2
+ import { join, dirname } from "path"
3
+ import type { TooeeConfig } from "./types.js"
4
+
5
+ const DEFAULTS: TooeeConfig = {
6
+ theme: {
7
+ name: "tokyonight",
8
+ mode: "dark",
9
+ },
10
+ }
11
+
12
+ function deepMerge(target: any, source: any): any {
13
+ const result = { ...target }
14
+ for (const key of Object.keys(source)) {
15
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
16
+ result[key] = deepMerge(result[key] ?? {}, source[key])
17
+ } else if (source[key] !== undefined) {
18
+ result[key] = source[key]
19
+ }
20
+ }
21
+ return result
22
+ }
23
+
24
+ function readJsonFile(path: string): Partial<TooeeConfig> {
25
+ try {
26
+ if (!existsSync(path)) return {}
27
+ return JSON.parse(readFileSync(path, "utf-8")) as Partial<TooeeConfig>
28
+ } catch {
29
+ return {}
30
+ }
31
+ }
32
+
33
+ function getGlobalConfigPath(): string {
34
+ const xdg = process.env.XDG_CONFIG_HOME ?? join(process.env.HOME ?? "", ".config")
35
+ return join(xdg, "tooee", "config.json")
36
+ }
37
+
38
+ function findProjectConfig(): Partial<TooeeConfig> {
39
+ let dir = process.cwd()
40
+ const seen = new Set<string>()
41
+ while (dir && !seen.has(dir)) {
42
+ seen.add(dir)
43
+ const configPath = join(dir, ".tooee", "config.json")
44
+ if (existsSync(configPath)) {
45
+ return readJsonFile(configPath)
46
+ }
47
+ const parent = dirname(dir)
48
+ if (parent === dir) break
49
+ dir = parent
50
+ }
51
+ return {}
52
+ }
53
+
54
+ export function loadConfig(overrides?: Partial<TooeeConfig>): TooeeConfig {
55
+ let config: TooeeConfig = { ...DEFAULTS }
56
+ config = deepMerge(config, readJsonFile(getGlobalConfigPath()))
57
+ config = deepMerge(config, findProjectConfig())
58
+ if (overrides) {
59
+ config = deepMerge(config, overrides)
60
+ }
61
+ return config
62
+ }
63
+
64
+ export function writeGlobalConfig(partial: Partial<TooeeConfig>): void {
65
+ const path = getGlobalConfigPath()
66
+ const dir = dirname(path)
67
+ try {
68
+ const existing = readJsonFile(path)
69
+ const merged = deepMerge(existing, partial)
70
+ mkdirSync(dir, { recursive: true })
71
+ writeFileSync(path, JSON.stringify(merged, null, 2) + "\n")
72
+ } catch {
73
+ // ignore write errors
74
+ }
75
+ }
package/src/types.ts ADDED
@@ -0,0 +1,13 @@
1
+ export type Mode = "dark" | "light"
2
+
3
+ export interface TooeeConfig {
4
+ theme?: {
5
+ name?: string
6
+ mode?: Mode
7
+ }
8
+ keys?: Record<string, string>
9
+ view?: {
10
+ wrap?: boolean
11
+ gutter?: boolean
12
+ }
13
+ }