okno 1.0.0-alpha.2 → 1.0.0-alpha.21
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/dist/acorn-CZBJD705.js +3143 -0
- package/dist/boot.d.ts +88 -0
- package/dist/boot.d.ts.map +1 -0
- package/dist/boot.js +98 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +22 -0
- package/dist/codegen/generate.d.ts +5 -1
- package/dist/codegen/generate.d.ts.map +1 -1
- package/dist/codegen/scan-imports.d.ts +9 -0
- package/dist/codegen/scan-imports.d.ts.map +1 -0
- package/dist/content-encode-DoojdsVU.js +80 -0
- package/dist/core/content-encode.d.ts +44 -0
- package/dist/core/content-encode.d.ts.map +1 -0
- package/dist/core/order.d.ts +18 -0
- package/dist/core/order.d.ts.map +1 -0
- package/dist/core/parse.d.ts +15 -0
- package/dist/core/parse.d.ts.map +1 -0
- package/dist/editor/index.js +29645 -15293
- package/dist/index-B1PShnsc.js +132 -0
- package/dist/index-B2WzdTzI.js +14263 -0
- package/dist/index-ButdD9Pf.js +1825 -0
- package/dist/index-DKF0siIz.js +5497 -0
- package/dist/index-q-7MEoHz.js +47 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/init.d.ts +39 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +221 -0
- package/dist/mcp.d.ts +4 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +140 -0
- package/dist/okno.css +1 -0
- package/dist/order-bAkdncZC.js +21 -0
- package/dist/plugin-Cy62173B.js +1080 -0
- package/dist/runtime/wrap.d.ts +48 -1
- package/dist/runtime/wrap.d.ts.map +1 -1
- package/dist/runtime/wrap.js +274 -0
- package/dist/types/fields.d.ts +54 -6
- package/dist/types/fields.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/permissions.d.ts +22 -0
- package/dist/types/permissions.d.ts.map +1 -0
- package/dist/types/schema.d.ts +41 -6
- package/dist/types/schema.d.ts.map +1 -1
- package/dist/vite/dev-server.d.ts +8 -0
- package/dist/vite/dev-server.d.ts.map +1 -1
- package/dist/vite/index.js +1 -1
- package/dist/vite/local-engine.d.ts +34 -0
- package/dist/vite/local-engine.d.ts.map +1 -0
- package/dist/vite/plugin.d.ts.map +1 -1
- package/package.json +42 -11
- package/skills/connect-okno/SKILL.md +367 -0
- package/src/runtime/wrap.test.ts +82 -0
- package/src/runtime/wrap.ts +684 -0
- package/dist/plugin-Cwwh6E1P.js +0 -440
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { E as n } from "./index-B2WzdTzI.js";
|
|
2
|
+
var s = n.create({
|
|
3
|
+
name: "textAlign",
|
|
4
|
+
addOptions() {
|
|
5
|
+
return {
|
|
6
|
+
types: [],
|
|
7
|
+
alignments: ["left", "center", "right", "justify"],
|
|
8
|
+
defaultAlignment: null
|
|
9
|
+
};
|
|
10
|
+
},
|
|
11
|
+
addGlobalAttributes() {
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
types: this.options.types,
|
|
15
|
+
attributes: {
|
|
16
|
+
textAlign: {
|
|
17
|
+
default: this.options.defaultAlignment,
|
|
18
|
+
parseHTML: (t) => {
|
|
19
|
+
const e = t.style.textAlign;
|
|
20
|
+
return this.options.alignments.includes(e) ? e : this.options.defaultAlignment;
|
|
21
|
+
},
|
|
22
|
+
renderHTML: (t) => t.textAlign ? { style: `text-align: ${t.textAlign}` } : {}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
},
|
|
28
|
+
addCommands() {
|
|
29
|
+
return {
|
|
30
|
+
setTextAlign: (t) => ({ commands: e }) => this.options.alignments.includes(t) ? this.options.types.map((i) => e.updateAttributes(i, { textAlign: t })).some((i) => i) : !1,
|
|
31
|
+
unsetTextAlign: () => ({ commands: t }) => this.options.types.map((e) => t.resetAttributes(e, "textAlign")).some((e) => e),
|
|
32
|
+
toggleTextAlign: (t) => ({ editor: e, commands: i }) => this.options.alignments.includes(t) ? e.isActive({ textAlign: t }) ? i.unsetTextAlign() : i.setTextAlign(t) : !1
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
addKeyboardShortcuts() {
|
|
36
|
+
return {
|
|
37
|
+
"Mod-Shift-l": () => this.editor.commands.setTextAlign("left"),
|
|
38
|
+
"Mod-Shift-e": () => this.editor.commands.setTextAlign("center"),
|
|
39
|
+
"Mod-Shift-r": () => this.editor.commands.setTextAlign("right"),
|
|
40
|
+
"Mod-Shift-j": () => this.editor.commands.setTextAlign("justify")
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}), l = s;
|
|
44
|
+
export {
|
|
45
|
+
s as TextAlign,
|
|
46
|
+
l as default
|
|
47
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { OknoSchema, OknoConfig, PageDef, CollectionDef, GlobalDef, Fields, AnyField, StringField, NumberField, BooleanField, DateField, EnumField, RichtextField,
|
|
1
|
+
export type { OknoSchema, Schema, OknoConfig, PageDef, CollectionDef, GlobalDef, BlockDef, Fields, AnyField, StringField, NumberField, BooleanField, DateField, EnumField, RichtextField, MediaField, MediaKind, SlugField, ReferenceField, GroupField, BlockField, DividerField, ResourcePermissions, RolePermissions, PermissionsConfig, } from "./types/index.ts";
|
|
2
2
|
export { okno } from "./vite/index.ts";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,UAAU,EACV,UAAU,EACV,OAAO,EACP,aAAa,EACb,SAAS,EACT,MAAM,EACN,QAAQ,EACR,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,SAAS,EACT,aAAa,EACb,SAAS,EACT,SAAS,EACT,cAAc,EACd,UAAU,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,UAAU,EACV,MAAM,EACN,UAAU,EACV,OAAO,EACP,aAAa,EACb,SAAS,EACT,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,SAAS,EACT,aAAa,EACb,UAAU,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,UAAU,EACV,UAAU,EACV,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,iBAAiB,GACjB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.js
CHANGED
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
type Framework = "astro" | "sveltekit" | "vite" | "nuxt" | "unknown";
|
|
2
|
+
interface Detected {
|
|
3
|
+
framework: Framework;
|
|
4
|
+
/** Absolute path to the config file we'd edit, or null if none found. */
|
|
5
|
+
configPath: string | null;
|
|
6
|
+
/** Where the plugin array lives: top-level (vite.config) or nested under `vite`. */
|
|
7
|
+
pluginsAt: "top" | "vite";
|
|
8
|
+
}
|
|
9
|
+
/** Detect the framework from config files + package.json deps. */
|
|
10
|
+
export declare function detect(cwd: string): Detected;
|
|
11
|
+
/** Add `okno()` as the first entry of the config's `plugins: [...]` array and
|
|
12
|
+
* import it, via a format-preserving string edit (works for tabs OR spaces — we
|
|
13
|
+
* never reformat). Returns false (→ manual fallback) if there's no `plugins`
|
|
14
|
+
* array to extend, e.g. a bare `defineConfig({})`. Idempotent. */
|
|
15
|
+
export declare function addPlugin(configPath: string): boolean;
|
|
16
|
+
export type ScaffoldMode = "starter" | "empty";
|
|
17
|
+
/**
|
|
18
|
+
* Scaffold okno/ if there's no okno/ yet. Two modes:
|
|
19
|
+
* - "starter": the site-wide globals every site needs — Meta (SEO/OG), Navbar,
|
|
20
|
+
* Footer — plus a Home page and a `navlink` block the nav/footer reuse. Ships
|
|
21
|
+
* ready to edit so the editor isn't an empty shell on first run.
|
|
22
|
+
* - "empty": just an empty schema — no pages, no globals, nothing. A blank slate
|
|
23
|
+
* to model from scratch (in code or via the editor's structure tools).
|
|
24
|
+
* The `connect-okno` skill explains why those are globals and how to extend the
|
|
25
|
+
* model. Returns false if okno/ already exists (left untouched).
|
|
26
|
+
*/
|
|
27
|
+
export declare function scaffold(cwd: string, mode?: ScaffoldMode): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Install the `connect-okno` skill into the project's `.claude/skills/` so the
|
|
30
|
+
* agent has the okno mental model (when to use collections vs blocks vs globals,
|
|
31
|
+
* the spread-to-edit pattern) on hand. Claude clients only. Returns the relative
|
|
32
|
+
* path written, or null if the bundled skill couldn't be found / already exists.
|
|
33
|
+
*/
|
|
34
|
+
export declare function writeSkill(cwd: string): string | null;
|
|
35
|
+
/** Register the okno MCP server for an AI client. */
|
|
36
|
+
export declare function writeMcpConfig(cwd: string, client: "claude" | "cursor"): string;
|
|
37
|
+
export declare function runInit(): Promise<void>;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAiBA,KAAK,SAAS,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;AAEpE,UAAU,QAAQ;IACjB,SAAS,EAAE,SAAS,CAAA;IACpB,yEAAyE;IACzE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,oFAAoF;IACpF,SAAS,EAAE,KAAK,GAAG,MAAM,CAAA;CACzB;AAiBD,kEAAkE;AAClE,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CA0B5C;AAED;;;mEAGmE;AACnE,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CA8BrD;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,CAAA;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,YAAwB,GAAG,OAAO,CAmB7E;AA2FD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWrD;AAED,qDAAqD;AACrD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAgB/E;AAED,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CA8F7C"}
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { existsSync as f, readFileSync as h, writeFileSync as l, mkdirSync as k } from "node:fs";
|
|
2
|
+
import { join as s, dirname as v } from "node:path";
|
|
3
|
+
import { fileURLToPath as S } from "node:url";
|
|
4
|
+
const u = {
|
|
5
|
+
astro: ["astro.config.mjs", "astro.config.ts", "astro.config.js"],
|
|
6
|
+
sveltekit: ["vite.config.ts", "vite.config.js"],
|
|
7
|
+
nuxt: ["nuxt.config.ts", "nuxt.config.js"],
|
|
8
|
+
vite: ["vite.config.ts", "vite.config.js", "vite.config.mjs"]
|
|
9
|
+
};
|
|
10
|
+
function p(n, e) {
|
|
11
|
+
for (const t of e) {
|
|
12
|
+
const o = s(n, t);
|
|
13
|
+
if (f(o)) return o;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
function b(n) {
|
|
18
|
+
const e = s(n, "package.json"), t = {};
|
|
19
|
+
if (f(e))
|
|
20
|
+
try {
|
|
21
|
+
const i = JSON.parse(h(e, "utf-8"));
|
|
22
|
+
Object.assign(t, i.dependencies, i.devDependencies);
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
const o = (i) => i in t;
|
|
26
|
+
return o("astro") || p(n, u.astro) ? { framework: "astro", configPath: p(n, u.astro), pluginsAt: "vite" } : o("nuxt") ? { framework: "nuxt", configPath: p(n, u.nuxt), pluginsAt: "vite" } : o("@sveltejs/kit") ? { framework: "sveltekit", configPath: p(n, u.vite), pluginsAt: "top" } : o("vite") ? { framework: "vite", configPath: p(n, u.vite), pluginsAt: "top" } : { framework: "unknown", configPath: null, pluginsAt: "top" };
|
|
27
|
+
}
|
|
28
|
+
function x(n) {
|
|
29
|
+
let e = h(n, "utf-8");
|
|
30
|
+
if (/from\s*["']okno\/vite["']/.test(e)) return !0;
|
|
31
|
+
const t = e.match(/\bplugins\s*:\s*\[/);
|
|
32
|
+
if (!t || t.index === void 0) return !1;
|
|
33
|
+
const o = t.index + t[0].length, i = e.slice(o);
|
|
34
|
+
let m;
|
|
35
|
+
const a = i.match(/^[^\S\n]*\n(\s*)/);
|
|
36
|
+
a ? m = `
|
|
37
|
+
${a[1]}okno(),` : i.trimStart().startsWith("]") ? m = "okno()" : m = "okno(), ", e = e.slice(0, o) + m + e.slice(o);
|
|
38
|
+
const d = 'import { okno } from "okno/vite"', r = [...e.matchAll(/^import .*$/gm)];
|
|
39
|
+
if (r.length) {
|
|
40
|
+
const c = r[r.length - 1], g = c.index + c[0].length;
|
|
41
|
+
e = e.slice(0, g) + `
|
|
42
|
+
` + d + e.slice(g);
|
|
43
|
+
} else
|
|
44
|
+
e = d + `
|
|
45
|
+
` + e;
|
|
46
|
+
return l(n, e), !0;
|
|
47
|
+
}
|
|
48
|
+
function A(n, e = "starter") {
|
|
49
|
+
const t = s(n, "okno"), o = s(t, "schema.ts");
|
|
50
|
+
return f(o) ? !1 : (k(t, { recursive: !0 }), e === "empty" ? (l(o, C), !0) : (k(s(t, "pages"), { recursive: !0 }), k(s(t, "globals"), { recursive: !0 }), l(o, j), l(s(t, "pages", "home.ts"), w), l(s(t, "globals", "meta.ts"), E), l(s(t, "globals", "navbar.ts"), R), l(s(t, "globals", "footer.ts"), T), !0));
|
|
51
|
+
}
|
|
52
|
+
const C = `import type { Schema } from "okno"
|
|
53
|
+
|
|
54
|
+
export default {} satisfies Schema
|
|
55
|
+
`, j = `import type { Schema } from "okno"
|
|
56
|
+
|
|
57
|
+
export default {
|
|
58
|
+
pages: {
|
|
59
|
+
home: {
|
|
60
|
+
name: "Home",
|
|
61
|
+
fields: {
|
|
62
|
+
headline: { type: "string" },
|
|
63
|
+
subhead: { type: "string" },
|
|
64
|
+
body: { type: "richtext" },
|
|
65
|
+
image: { type: "media", accept: ["image"] },
|
|
66
|
+
},
|
|
67
|
+
url: "/",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
globals: {
|
|
71
|
+
meta: {
|
|
72
|
+
name: "Meta",
|
|
73
|
+
fields: {
|
|
74
|
+
title: { type: "string" },
|
|
75
|
+
description: { type: "string" },
|
|
76
|
+
shareImage: { type: "media", accept: ["image"], label: "Share image" },
|
|
77
|
+
favicon: { type: "media", accept: ["image"] },
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
navbar: {
|
|
81
|
+
name: "Navbar",
|
|
82
|
+
fields: {
|
|
83
|
+
logo: { type: "string" },
|
|
84
|
+
links: { type: "block", block: "navlink", multiple: true },
|
|
85
|
+
cta: { type: "link", label: "Call to action" },
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
footer: {
|
|
89
|
+
name: "Footer",
|
|
90
|
+
fields: {
|
|
91
|
+
text: { type: "string" },
|
|
92
|
+
links: { type: "block", block: "navlink", multiple: true },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
blocks: {
|
|
97
|
+
navlink: {
|
|
98
|
+
name: "Nav link",
|
|
99
|
+
fields: {
|
|
100
|
+
link: { type: "link" },
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
} satisfies Schema
|
|
105
|
+
`, w = `export default {
|
|
106
|
+
headline: "Hello from okno",
|
|
107
|
+
subhead: "Edit this on the page — press “e” or visit ?okno.",
|
|
108
|
+
body: "<p>This is your home page. Everything here is content in your repo.</p>",
|
|
109
|
+
image: null,
|
|
110
|
+
}
|
|
111
|
+
`, E = `export default {
|
|
112
|
+
title: "My okno site",
|
|
113
|
+
description: "Built with okno — a Git-backed CMS with live editing.",
|
|
114
|
+
shareImage: null,
|
|
115
|
+
favicon: null,
|
|
116
|
+
}
|
|
117
|
+
`, R = `export default {
|
|
118
|
+
logo: "okno",
|
|
119
|
+
links: [
|
|
120
|
+
{ link: { text: "Home", url: "/", external: false } },
|
|
121
|
+
{ link: { text: "About", url: "/about", external: false } },
|
|
122
|
+
],
|
|
123
|
+
cta: { text: "Get started", url: "/", external: false },
|
|
124
|
+
}
|
|
125
|
+
`, T = `export default {
|
|
126
|
+
text: "© My okno site",
|
|
127
|
+
links: [{ link: { text: "Home", url: "/", external: false } }],
|
|
128
|
+
}
|
|
129
|
+
`;
|
|
130
|
+
function P(n) {
|
|
131
|
+
const e = v(S(import.meta.url)), t = s(e, "..", "skills", "connect-okno", "SKILL.md");
|
|
132
|
+
if (!f(t)) return null;
|
|
133
|
+
const o = s(".claude", "skills", "connect-okno", "SKILL.md"), i = s(n, o);
|
|
134
|
+
return f(i) ? null : (k(v(i), { recursive: !0 }), l(i, h(t, "utf-8")), o);
|
|
135
|
+
}
|
|
136
|
+
function M(n, e) {
|
|
137
|
+
const t = e === "claude" ? ".mcp.json" : s(".cursor", "mcp.json"), o = s(n, t);
|
|
138
|
+
let i = { mcpServers: {} };
|
|
139
|
+
if (f(o))
|
|
140
|
+
try {
|
|
141
|
+
i = JSON.parse(h(o, "utf-8"));
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
return i.mcpServers ??= {}, i.mcpServers.okno = { command: "npx", args: ["okno", "mcp"] }, k(s(o, ".."), { recursive: !0 }), l(o, JSON.stringify(i, null, 2) + `
|
|
145
|
+
`), t;
|
|
146
|
+
}
|
|
147
|
+
async function H() {
|
|
148
|
+
const n = process.cwd(), e = await import("@clack/prompts");
|
|
149
|
+
e.intro("okno init");
|
|
150
|
+
let t = b(n);
|
|
151
|
+
if (t.framework === "unknown") {
|
|
152
|
+
const r = await e.select({
|
|
153
|
+
message: "Which framework is this project?",
|
|
154
|
+
options: [
|
|
155
|
+
{ value: "astro", label: "Astro" },
|
|
156
|
+
{ value: "sveltekit", label: "SvelteKit" },
|
|
157
|
+
{ value: "vite", label: "Vite", hint: "React / Vue / Solid" },
|
|
158
|
+
{ value: "nuxt", label: "Nuxt" }
|
|
159
|
+
]
|
|
160
|
+
});
|
|
161
|
+
if (e.isCancel(r)) return e.cancel("Cancelled.");
|
|
162
|
+
const c = r, g = c === "astro" || c === "nuxt" ? "vite" : "top", y = u[c] ?? u.vite;
|
|
163
|
+
t = { framework: c, configPath: p(n, y), pluginsAt: g };
|
|
164
|
+
}
|
|
165
|
+
const o = await e.select({
|
|
166
|
+
message: "Set up the okno MCP server for your AI editor?",
|
|
167
|
+
options: [
|
|
168
|
+
{ value: "claude", label: "Claude Code", hint: ".mcp.json + connect-okno skill" },
|
|
169
|
+
{ value: "cursor", label: "Cursor", hint: ".cursor/mcp.json" },
|
|
170
|
+
{ value: "skip", label: "Skip", hint: "no AI editor" }
|
|
171
|
+
],
|
|
172
|
+
initialValue: "claude"
|
|
173
|
+
});
|
|
174
|
+
if (e.isCancel(o)) return e.cancel("Cancelled.");
|
|
175
|
+
let i = "starter";
|
|
176
|
+
if (!f(s(n, "okno", "schema.ts"))) {
|
|
177
|
+
const r = await e.select({
|
|
178
|
+
message: "Scaffold a Starter, or an empty schema?",
|
|
179
|
+
options: [
|
|
180
|
+
{ value: "starter", label: "Starter", hint: "Home + Meta/Navbar/Footer globals, ready to edit" },
|
|
181
|
+
{ value: "empty", label: "Empty", hint: "nothing scaffolded — model it yourself" }
|
|
182
|
+
],
|
|
183
|
+
initialValue: "starter"
|
|
184
|
+
});
|
|
185
|
+
if (e.isCancel(r)) return e.cancel("Cancelled.");
|
|
186
|
+
i = r;
|
|
187
|
+
}
|
|
188
|
+
const a = [];
|
|
189
|
+
let d = !1;
|
|
190
|
+
if (t.configPath && (d = x(t.configPath)), d)
|
|
191
|
+
a.push(`✓ Added okno() to ${t.configPath.replace(n + "/", "")}`);
|
|
192
|
+
else {
|
|
193
|
+
const r = t.pluginsAt === "vite" ? `import { okno } from "okno/vite"
|
|
194
|
+
// inside defineConfig:
|
|
195
|
+
// vite: { plugins: [okno()] }` : `import { okno } from "okno/vite"
|
|
196
|
+
// inside defineConfig:
|
|
197
|
+
// plugins: [okno()]`;
|
|
198
|
+
a.push(`! Couldn't edit your config automatically — add okno() yourself:
|
|
199
|
+
${r}`);
|
|
200
|
+
}
|
|
201
|
+
if (A(n, i) ? a.push(
|
|
202
|
+
i === "starter" ? "✓ Scaffolded the okno Starter (Home + Meta/Navbar/Footer globals)" : "✓ Scaffolded an empty okno/ (schema only — nothing else)"
|
|
203
|
+
) : a.push("• okno/ already exists — left it alone"), o !== "skip") {
|
|
204
|
+
const r = M(n, o);
|
|
205
|
+
a.push(`✓ Registered the okno MCP server in ${r}`);
|
|
206
|
+
}
|
|
207
|
+
if (o === "claude") {
|
|
208
|
+
const r = P(n);
|
|
209
|
+
r && a.push(`✓ Installed the connect-okno skill in ${r}`);
|
|
210
|
+
}
|
|
211
|
+
e.note(a.join(`
|
|
212
|
+
`), "Done"), e.outro("Run your dev server and press “e” (or visit ?okno) to open the editor.");
|
|
213
|
+
}
|
|
214
|
+
export {
|
|
215
|
+
x as addPlugin,
|
|
216
|
+
b as detect,
|
|
217
|
+
H as runInit,
|
|
218
|
+
A as scaffold,
|
|
219
|
+
M as writeMcpConfig,
|
|
220
|
+
P as writeSkill
|
|
221
|
+
};
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAwCA;kCACkC;AAClC,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAmK5C"}
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { existsSync as l, readdirSync as j, readFileSync as y, mkdirSync as D, writeFileSync as S, unlinkSync as C, realpathSync as N } from "node:fs";
|
|
2
|
+
import { join as i, dirname as O, relative as L, sep as E, resolve as T } from "node:path";
|
|
3
|
+
import { execFileSync as F } from "node:child_process";
|
|
4
|
+
import { p as b, e as M, a as $ } from "./content-encode-DoojdsVU.js";
|
|
5
|
+
function I() {
|
|
6
|
+
const f = process.env.OKNO_DIR;
|
|
7
|
+
if (f) {
|
|
8
|
+
const o = T(process.cwd(), f);
|
|
9
|
+
return l(i(o, "schema.ts")) ? o : l(i(o, "okno", "schema.ts")) ? i(o, "okno") : o;
|
|
10
|
+
}
|
|
11
|
+
let h = process.cwd();
|
|
12
|
+
for (let o = 0; o < 25; o++) {
|
|
13
|
+
if (l(i(h, "okno", "schema.ts"))) return i(h, "okno");
|
|
14
|
+
const s = O(h);
|
|
15
|
+
if (s === h) break;
|
|
16
|
+
h = s;
|
|
17
|
+
}
|
|
18
|
+
console.error("okno mcp: could not find an okno/ directory. Set OKNO_DIR to your project's okno/ path."), process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
async function A() {
|
|
21
|
+
const { McpServer: f } = await import("@modelcontextprotocol/sdk/server/mcp.js"), { StdioServerTransport: h } = await import("@modelcontextprotocol/sdk/server/stdio.js"), { z: o } = await import("zod"), s = I();
|
|
22
|
+
function g(e) {
|
|
23
|
+
if (!e || e.includes("..") || e.includes("\0") || e.startsWith("/")) throw new Error(`Invalid path: ${e}`);
|
|
24
|
+
if (!e.endsWith(".ts")) throw new Error(`Path must be a .ts file: ${e}`);
|
|
25
|
+
const t = i(s, e);
|
|
26
|
+
if (t !== s && !t.startsWith(s + E)) throw new Error(`Path escapes okno dir: ${e}`);
|
|
27
|
+
return t;
|
|
28
|
+
}
|
|
29
|
+
const m = (e, t = s) => F("git", e, { cwd: t, encoding: "utf-8" }).trim();
|
|
30
|
+
let u;
|
|
31
|
+
const _ = () => {
|
|
32
|
+
if (u !== void 0) return u;
|
|
33
|
+
try {
|
|
34
|
+
u = m(["config", "user.name"]) || "local";
|
|
35
|
+
} catch {
|
|
36
|
+
u = "local";
|
|
37
|
+
}
|
|
38
|
+
return u;
|
|
39
|
+
};
|
|
40
|
+
function w() {
|
|
41
|
+
const e = i(s, "schema.ts");
|
|
42
|
+
let t = `import type { Schema } from "okno"
|
|
43
|
+
|
|
44
|
+
`, r = {};
|
|
45
|
+
if (l(e)) {
|
|
46
|
+
const n = y(e, "utf-8"), d = n.indexOf("export default");
|
|
47
|
+
d > 0 && (t = n.slice(0, d)), r = b(n) ?? {};
|
|
48
|
+
}
|
|
49
|
+
return { schema: r, header: t };
|
|
50
|
+
}
|
|
51
|
+
const c = (e) => ({ content: [{ type: "text", text: JSON.stringify(e, null, 2) }] }), a = new f(
|
|
52
|
+
{ name: "okno", version: "0.2.0" },
|
|
53
|
+
{
|
|
54
|
+
instructions: "Okno is a Git-backed CMS: content is .ts files in the project's repo. Call get_schema first to learn the content model (pages, collections, globals, blocks). write_schema replaces the WHOLE schema — read it, merge, write it all back. Writes are drafts on the working branch; a human publishes — you cannot. For how to MODEL content (when to use a collection + reference vs a block vs a global, the spread-to-edit rendering pattern), use the `connect-okno` skill — it's installed at .claude/skills/connect-okno/SKILL.md (run `okno init` if missing)."
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
a.tool(
|
|
58
|
+
"get_schema",
|
|
59
|
+
"Get the Okno project's schema — pages, collections (with field definitions), globals, blocks, locales. Call this first to learn the content model before reading or writing.",
|
|
60
|
+
{},
|
|
61
|
+
async () => c(w().schema)
|
|
62
|
+
), a.tool(
|
|
63
|
+
"list_entries",
|
|
64
|
+
"List the entries in a collection. Returns each entry's slug and file path (relative to the okno dir).",
|
|
65
|
+
{ collection: o.string().describe("Collection slug, e.g. 'articles'") },
|
|
66
|
+
async ({ collection: e }) => {
|
|
67
|
+
const t = i(s, "collections", e);
|
|
68
|
+
if (!l(t)) return c([]);
|
|
69
|
+
const r = j(t, { withFileTypes: !0 }).filter((n) => n.isFile() && n.name.endsWith(".ts") && n.name !== "index.ts").map((n) => ({ slug: n.name.slice(0, -3), path: `collections/${e}/${n.name}` }));
|
|
70
|
+
return c(r);
|
|
71
|
+
}
|
|
72
|
+
), a.tool(
|
|
73
|
+
"read_entry",
|
|
74
|
+
"Read a single content entry's data. `path` is relative to the okno dir, e.g. 'collections/articles/hello.ts' or 'pages/home.ts'.",
|
|
75
|
+
{ path: o.string().describe("Entry path relative to the okno dir") },
|
|
76
|
+
async ({ path: e }) => {
|
|
77
|
+
const t = g(e);
|
|
78
|
+
if (!l(t)) throw new Error(`Not found: ${e}`);
|
|
79
|
+
return c(b(y(t, "utf-8")));
|
|
80
|
+
}
|
|
81
|
+
), a.tool(
|
|
82
|
+
"write_entry",
|
|
83
|
+
"Create or update a content entry by writing its `.ts` file to disk. `path` is relative to the okno dir (e.g. 'collections/articles/hello.ts'). `content` is the entry's fields as an object — match the collection's schema (use get_schema first). created/updated stamps are managed automatically (created is preserved across edits). This is a working-tree write (an uncommitted draft); call `publish` to commit.",
|
|
84
|
+
{
|
|
85
|
+
path: o.string().describe("Entry path relative to the okno dir"),
|
|
86
|
+
content: o.record(o.unknown()).describe("Entry fields as an object, matching the schema")
|
|
87
|
+
},
|
|
88
|
+
async ({ path: e, content: t }) => {
|
|
89
|
+
const r = g(e);
|
|
90
|
+
let n;
|
|
91
|
+
if (l(r)) {
|
|
92
|
+
const p = b(y(r, "utf-8"));
|
|
93
|
+
p.created?.date && (n = p.created);
|
|
94
|
+
}
|
|
95
|
+
const d = M(t, { now: (/* @__PURE__ */ new Date()).toISOString(), author: _(), created: n });
|
|
96
|
+
return D(O(r), { recursive: !0 }), S(r, d), c({ ok: !0, path: e });
|
|
97
|
+
}
|
|
98
|
+
), a.tool(
|
|
99
|
+
"delete_entry",
|
|
100
|
+
"Delete a content entry's `.ts` file by its path (relative to the okno dir). A working-tree change; call `publish` to commit.",
|
|
101
|
+
{ path: o.string().describe("Entry path relative to the okno dir") },
|
|
102
|
+
async ({ path: e }) => {
|
|
103
|
+
const t = g(e);
|
|
104
|
+
return l(t) && C(t), c({ ok: !0, path: e });
|
|
105
|
+
}
|
|
106
|
+
), a.tool(
|
|
107
|
+
"write_schema",
|
|
108
|
+
"Define or change the project's CONTENT MODEL (schema.ts). Pass the COMPLETE schema object: read get_schema first, merge your changes, write the whole thing back. Shape: { pages?, collections?, globals?, blocks?, locales? }. Collections MUST include a slug field `slug:{type:'slug',from:'<fieldKey>'}`. Field `type`s: string, number, boolean, date, enum(options), richtext, media(accept?), slug(from), reference(collection,multiple?), group(fields), block(block,multiple?), color, link, divider. A working-tree write; call `publish` to commit.",
|
|
109
|
+
{ schema: o.record(o.unknown()).describe("The complete schema object: { pages?, collections?, globals?, blocks?, locales? }") },
|
|
110
|
+
async ({ schema: e }) => {
|
|
111
|
+
const { header: t } = w();
|
|
112
|
+
return S(i(s, "schema.ts"), $(e, t)), c({ ok: !0 });
|
|
113
|
+
}
|
|
114
|
+
), a.tool(
|
|
115
|
+
"publish",
|
|
116
|
+
"Commit and push the okno content changes. Stages ONLY the okno/ paths (never the project's code), commits, and pushes the current branch. No-op if there's nothing to commit.",
|
|
117
|
+
{ message: o.string().optional().describe("Optional commit message (default: 'okno: content')") },
|
|
118
|
+
async ({ message: e }) => {
|
|
119
|
+
const t = m(["rev-parse", "--show-toplevel"]), r = L(t, N(s)).split(E).join("/");
|
|
120
|
+
m(["add", "-A", "--", r], t);
|
|
121
|
+
const n = m(["diff", "--cached", "--name-only"], t);
|
|
122
|
+
let d = !1;
|
|
123
|
+
n && (m(["commit", "-m", e?.trim() || "okno: content"], t), d = !0);
|
|
124
|
+
let p = !1, v = null;
|
|
125
|
+
try {
|
|
126
|
+
m(["push"], t), p = !0;
|
|
127
|
+
} catch (k) {
|
|
128
|
+
v = k instanceof Error ? k.message.split(`
|
|
129
|
+
`)[0] : String(k);
|
|
130
|
+
}
|
|
131
|
+
return c({ ok: !0, committed: d, pushed: p, pushError: v, files: n ? n.split(`
|
|
132
|
+
`) : [] });
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
const x = new h();
|
|
136
|
+
await a.connect(x), console.error(`okno mcp connected → ${s} (local filesystem)`);
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
A as runMcp
|
|
140
|
+
};
|
package/dist/okno.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.oknobar.svelte-1krhcxo{-webkit-appearance:none;appearance:none;border-radius:9999px;outline:none;cursor:pointer}.oknobar.svelte-1krhcxo::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;border-radius:9999px;background:#fff;border:1px solid rgba(0,0,0,.3);box-shadow:0 1px 3px #0006}.oknobar.svelte-1krhcxo::-moz-range-thumb{width:14px;height:14px;border-radius:9999px;background:#fff;border:1px solid rgba(0,0,0,.3);box-shadow:0 1px 3px #0006}.oknobar.svelte-1krhcxo::-webkit-slider-runnable-track,.oknobar.svelte-1krhcxo::-moz-range-track{background:transparent}.okno-radial-spin.svelte-17bcvty{animation:svelte-17bcvty-okno-radial-spin 1s linear infinite}@keyframes svelte-17bcvty-okno-radial-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.okno-upload-pulse.svelte-wgxkiz{animation:svelte-wgxkiz-okno-upload-pulse 1.1s ease-in-out infinite}@keyframes svelte-wgxkiz-okno-upload-pulse{0%,to{opacity:1}50%{opacity:.55}}.oknobar.svelte-5hx3s7{-webkit-appearance:none;appearance:none;border-radius:9999px;outline:none;cursor:pointer}.oknobar.svelte-5hx3s7::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;border-radius:9999px;background:#fff;border:1px solid rgba(0,0,0,.3);box-shadow:0 1px 3px #0006}.oknobar.svelte-5hx3s7::-moz-range-thumb{width:14px;height:14px;border-radius:9999px;background:#fff;border:1px solid rgba(0,0,0,.3);box-shadow:0 1px 3px #0006}.oknobar.svelte-5hx3s7::-webkit-slider-runnable-track,.oknobar.svelte-5hx3s7::-moz-range-track{background:transparent}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function a(r, o, n) {
|
|
2
|
+
if (!n || n.length === 0) return r;
|
|
3
|
+
const s = new Map(n.map((t, e) => [t, e])), i = [], u = [];
|
|
4
|
+
for (const t of r) {
|
|
5
|
+
const e = o(t), c = e == null ? void 0 : s.get(e);
|
|
6
|
+
c === void 0 ? u.push(t) : i.push({ item: t, r: c });
|
|
7
|
+
}
|
|
8
|
+
return i.sort((t, e) => t.r - e.r), [...i.map((t) => t.item), ...u];
|
|
9
|
+
}
|
|
10
|
+
function f(r, o) {
|
|
11
|
+
const n = (r ?? []).filter((s) => s !== o);
|
|
12
|
+
return [o, ...n];
|
|
13
|
+
}
|
|
14
|
+
function l(r, o) {
|
|
15
|
+
return (r ?? []).filter((n) => n !== o);
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
f as a,
|
|
19
|
+
l as b,
|
|
20
|
+
a as o
|
|
21
|
+
};
|