okno 1.0.0-alpha.1 → 1.0.0-beta5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +695 -0
  2. package/dist/adapters/astro.d.ts +3 -0
  3. package/dist/adapters/astro.d.ts.map +1 -0
  4. package/dist/adapters/astro.js +32 -0
  5. package/dist/adapters/index.d.ts +3 -0
  6. package/dist/adapters/index.d.ts.map +1 -0
  7. package/dist/adapters/vite.d.ts +4 -0
  8. package/dist/adapters/vite.d.ts.map +1 -0
  9. package/dist/adapters/vite.js +21 -0
  10. package/dist/client-CeppRtrT.js +55 -0
  11. package/dist/core/__tests__/builders.test.d.ts +2 -0
  12. package/dist/core/__tests__/builders.test.d.ts.map +1 -0
  13. package/dist/core/__tests__/fields.test.d.ts +2 -0
  14. package/dist/core/__tests__/fields.test.d.ts.map +1 -0
  15. package/dist/core/__tests__/runtime.test.d.ts +2 -0
  16. package/dist/core/__tests__/runtime.test.d.ts.map +1 -0
  17. package/dist/core/builders/collection.d.ts +9 -0
  18. package/dist/core/builders/collection.d.ts.map +1 -0
  19. package/dist/core/builders/component.d.ts +26 -0
  20. package/dist/core/builders/component.d.ts.map +1 -0
  21. package/dist/core/builders/global.d.ts +8 -0
  22. package/dist/core/builders/global.d.ts.map +1 -0
  23. package/dist/core/builders/group.d.ts +18 -0
  24. package/dist/core/builders/group.d.ts.map +1 -0
  25. package/dist/core/builders/index.d.ts +8 -0
  26. package/dist/core/builders/index.d.ts.map +1 -0
  27. package/dist/core/builders/page.d.ts +9 -0
  28. package/dist/core/builders/page.d.ts.map +1 -0
  29. package/dist/core/builders/utils.d.ts +8 -0
  30. package/dist/core/builders/utils.d.ts.map +1 -0
  31. package/dist/core/components/index.d.ts +3 -0
  32. package/dist/core/components/index.d.ts.map +1 -0
  33. package/dist/core/components/link.d.ts +12 -0
  34. package/dist/core/components/link.d.ts.map +1 -0
  35. package/dist/core/components/meta.d.ts +12 -0
  36. package/dist/core/components/meta.d.ts.map +1 -0
  37. package/dist/core/field-result.d.ts +26 -0
  38. package/dist/core/field-result.d.ts.map +1 -0
  39. package/dist/core/fields/boolean.d.ts +12 -0
  40. package/dist/core/fields/boolean.d.ts.map +1 -0
  41. package/dist/core/fields/date.d.ts +14 -0
  42. package/dist/core/fields/date.d.ts.map +1 -0
  43. package/dist/core/fields/enum.d.ts +14 -0
  44. package/dist/core/fields/enum.d.ts.map +1 -0
  45. package/dist/core/fields/index.d.ts +12 -0
  46. package/dist/core/fields/index.d.ts.map +1 -0
  47. package/dist/core/fields/media.d.ts +33 -0
  48. package/dist/core/fields/media.d.ts.map +1 -0
  49. package/dist/core/fields/number.d.ts +16 -0
  50. package/dist/core/fields/number.d.ts.map +1 -0
  51. package/dist/core/fields/reference.d.ts +12 -0
  52. package/dist/core/fields/reference.d.ts.map +1 -0
  53. package/dist/core/fields/richtext.d.ts +13 -0
  54. package/dist/core/fields/richtext.d.ts.map +1 -0
  55. package/dist/core/fields/slug.d.ts +10 -0
  56. package/dist/core/fields/slug.d.ts.map +1 -0
  57. package/dist/core/fields/string.d.ts +21 -0
  58. package/dist/core/fields/string.d.ts.map +1 -0
  59. package/dist/core/fields/types.d.ts +101 -0
  60. package/dist/core/fields/types.d.ts.map +1 -0
  61. package/dist/core/fields/user.d.ts +13 -0
  62. package/dist/core/fields/user.d.ts.map +1 -0
  63. package/dist/core/index.d.ts +6 -0
  64. package/dist/core/index.d.ts.map +1 -0
  65. package/dist/core/index.js +23 -0
  66. package/dist/core/okno.d.ts +21 -0
  67. package/dist/core/okno.d.ts.map +1 -0
  68. package/dist/core/runtime.d.ts +9 -0
  69. package/dist/core/runtime.d.ts.map +1 -0
  70. package/dist/core/schema.d.ts +2 -0
  71. package/dist/core/schema.d.ts.map +1 -0
  72. package/dist/editor/index.js +1527 -19113
  73. package/dist/index.d.ts +7 -2
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +25 -2
  76. package/dist/meta-D-_nuC5Y.js +548 -0
  77. package/dist/server/client.d.ts +29 -0
  78. package/dist/server/client.d.ts.map +1 -0
  79. package/dist/server/index.d.ts +3 -0
  80. package/dist/server/index.d.ts.map +1 -0
  81. package/dist/server/index.js +6 -0
  82. package/dist/server/types.d.ts +48 -0
  83. package/dist/server/types.d.ts.map +1 -0
  84. package/dist/types/global.d.ts +54 -0
  85. package/dist/types/global.d.ts.map +1 -0
  86. package/dist/types/index.d.ts +3 -2
  87. package/dist/types/index.d.ts.map +1 -1
  88. package/dist/types/path.d.ts +35 -0
  89. package/dist/types/path.d.ts.map +1 -0
  90. package/dist/types/query.d.ts +31 -0
  91. package/dist/types/query.d.ts.map +1 -0
  92. package/dist/vite.config.d.ts +3 -0
  93. package/dist/vite.config.d.ts.map +1 -0
  94. package/dist/vite.dev.config.d.ts +3 -0
  95. package/dist/vite.dev.config.d.ts.map +1 -0
  96. package/package.json +44 -35
  97. package/dist/codegen/generate.d.ts +0 -6
  98. package/dist/codegen/generate.d.ts.map +0 -1
  99. package/dist/plugin-C4ZJr14i.js +0 -440
  100. package/dist/runtime/wrap.d.ts +0 -11
  101. package/dist/runtime/wrap.d.ts.map +0 -1
  102. package/dist/types/fields.d.ts +0 -73
  103. package/dist/types/fields.d.ts.map +0 -1
  104. package/dist/types/schema.d.ts +0 -47
  105. package/dist/types/schema.d.ts.map +0 -1
  106. package/dist/vite/dev-server.d.ts +0 -8
  107. package/dist/vite/dev-server.d.ts.map +0 -1
  108. package/dist/vite/index.d.ts +0 -2
  109. package/dist/vite/index.d.ts.map +0 -1
  110. package/dist/vite/index.js +0 -4
  111. package/dist/vite/plugin.d.ts +0 -4
  112. package/dist/vite/plugin.d.ts.map +0 -1
package/README.md ADDED
@@ -0,0 +1,695 @@
1
+ # okno Documentation
2
+
3
+ ## Introduction
4
+
5
+ okno is a developer-friendly headless CMS with live editing capabilities for static and dynamic sites.
6
+
7
+ ### Installation
8
+
9
+ ```bash
10
+ npm install okno
11
+ npm install okno/astro # Framework adapter
12
+
13
+ ```
14
+
15
+ ### Quick Start
16
+
17
+ ```tsx
18
+ // /okno.ts
19
+ import { schema, collection, page, string, richtext } from "okno"
20
+
21
+ export const okno= schema({
22
+ articles: collection({
23
+ title: string(),
24
+ content: richtext()
25
+ }),
26
+
27
+ home: page({
28
+ heroTitle: string()
29
+ })
30
+ })
31
+
32
+ // Use in your components
33
+ import { okno } from "@/okno"
34
+
35
+ const articles = okno("articles")
36
+ const homeTitle = okno("home.heroTitle")
37
+
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Schema
43
+
44
+ ### Types
45
+
46
+ okno has three content types:
47
+
48
+ **Collections** - Multiple items (e.g., blog posts, products)
49
+
50
+ ```tsx
51
+ articles: collection({
52
+ title: string(),
53
+ slug: slug("title"),
54
+ content: richtext()
55
+ }).draftable()
56
+
57
+ ```
58
+
59
+ **Pages** - Single items with optional routes (e.g., Home, About)
60
+
61
+ ```tsx
62
+ home: page({
63
+ heroTitle: string(),
64
+ heroImage: media().image()
65
+ }).route('/')
66
+
67
+ ```
68
+
69
+ **Globals** - Site-wide singletons (e.g., Navbar, Footer, Settings)
70
+
71
+ ```tsx
72
+ navbar: global({
73
+ logo: media().image(),
74
+ links: string().array()
75
+ })
76
+
77
+ ```
78
+
79
+ ### Fields
80
+
81
+ ### Basic Fields
82
+
83
+ ```tsx
84
+ // String types
85
+ string() // Basic text field, required by default
86
+ string().optional() // Optional text field
87
+ string().max(200) // Length limit
88
+ string().min(10) // Minimum length
89
+ string().email() // Email validation
90
+ string().url() // URL validation
91
+ string().phone() // Phone validation
92
+ string().pattern(/^[a-z0-9-]+$/) // Regex pattern
93
+ string().array() // Array of strings
94
+ string().default('hello') // Default value
95
+ string().help("Enter your full name") // Help text in editor
96
+ string().protected() // Not editable in UI
97
+
98
+ // Numbers
99
+ number() // Numeric field
100
+ number().min(0).max(100) // With range
101
+ number().default(0) // Default value
102
+
103
+ // Boolean
104
+ boolean() // True/false
105
+ boolean().default(false)
106
+
107
+ // Enums
108
+ enum(['draft', 'published', 'archived'])
109
+ enum(['javascript', 'svelte', 'typescript']).multiple() // Multi-select
110
+
111
+ // Dates
112
+ date() // Date field
113
+ date().default(() => new Date()) // Dynamic default
114
+
115
+ // Rich content
116
+ richtext() // HTML content field
117
+ richtext().slots(['price', 'date']) // With dynamic slots
118
+ markdown() // TODO: Markdown content field (future)
119
+
120
+ // Media
121
+ media() // Accept any media type
122
+ media().image() // Only images
123
+ media().video() // Only videos
124
+ media().audio() // Only audio
125
+ media().document() // Only documents (PDF, Word, etc)
126
+
127
+ // Media with multiple types and rules
128
+ media()
129
+ .image({ maxWidth: 1920, maxHeight: 1080, format: ['jpg', 'png', 'webp'] })
130
+ .video({ maxSize: '100MB', maxDuration: 300 })
131
+ .document({ maxSize: '10MB', accept: ['pdf', 'docx'] })
132
+
133
+ ```
134
+
135
+ ### System Fields
136
+
137
+ ```tsx
138
+ slug("fieldName") // Auto-generated from field
139
+
140
+ ```
141
+
142
+ ### Built-in Fields (Collections Only)
143
+
144
+ All **collection items** automatically include:
145
+
146
+ - `id` - unique identifier
147
+ - `slug` - URL-friendly key (defined with `slug("fieldName")`)
148
+ - `created` - timestamp
149
+ - `updated` - timestamp
150
+ - `published` - timestamp or null (for draftable collections)
151
+ - `createdBy` - user reference
152
+ - `updatedBy` - user reference
153
+ - `publishedBy` - user reference or null
154
+
155
+ Pages and globals do not have these fields - they are singletons identified by their key name.
156
+
157
+ ### Groups
158
+
159
+ Organize related fields in the editor UI:
160
+
161
+ ```tsx
162
+ article: collection({
163
+ title: string(),
164
+ content: richtext(),
165
+
166
+ seo: group({
167
+ metaTitle: string().max(60).help("Appears in search results"),
168
+ metaDescription: string().max(160),
169
+ ogImage: media().image()
170
+ }),
171
+
172
+ advanced: group({
173
+ customCSS: string().optional(),
174
+ trackingCode: string().optional()
175
+ })
176
+ })
177
+
178
+ // Access with dot notation
179
+ okno("articles.my-post.seo.metaTitle")
180
+
181
+ ```
182
+
183
+ ### Conditional Fields
184
+
185
+ Show/hide fields based on other field values:
186
+
187
+ ```tsx
188
+ post: collection({
189
+ type: enum(['article', 'video', 'podcast']),
190
+
191
+ // Only show when type is 'article'
192
+ content: richtext().when('type', 'article'),
193
+
194
+ // Only show when type is 'video'
195
+ videoUrl: string().url().when('type', 'video'),
196
+ videoThumbnail: media().image().when('type', 'video'),
197
+
198
+ // Show when type is either 'video' or 'podcast'
199
+ duration: number().min(0).when('type', ['video', 'podcast'])
200
+ })
201
+
202
+ ```
203
+
204
+ Groups can also be conditional:
205
+
206
+ ```tsx
207
+ article: collection({
208
+ type: enum(['article', 'video', 'podcast']),
209
+ premium: boolean().default(false),
210
+
211
+ // Show entire video group only when type is 'video'
212
+ video: group({
213
+ url: string().url().required(),
214
+ thumbnail: media().image(),
215
+ duration: number().min(0),
216
+ subtitles: string().optional()
217
+ }).when('type', 'video'),
218
+
219
+ // Show entire SEO group only when premium is true
220
+ seo: group({
221
+ metaTitle: string().max(60),
222
+ metaDescription: string().max(160),
223
+ ogImage: media().image()
224
+ }).when('premium', true)
225
+ })
226
+
227
+ ```
228
+
229
+ ### Default Values
230
+
231
+ ```tsx
232
+ article: collection({
233
+ status: enum(['draft', 'published']).default('draft'),
234
+ views: number().default(0),
235
+ featured: boolean().default(false),
236
+ publishedAt: date().default(() => new Date()),
237
+ tags: enum(['js', 'css', 'html']).multiple().default(['js'])
238
+ })
239
+
240
+ ```
241
+
242
+ ### References
243
+
244
+ Reference other collections:
245
+
246
+ ```tsx
247
+ reference("collection") // Single reference
248
+ reference("collection").multiple() // Multiple references
249
+
250
+ // Example
251
+ articles: collection({
252
+ title: string(),
253
+ category: reference("categories"),
254
+ relatedPosts: reference("articles").multiple()
255
+ })
256
+
257
+ ```
258
+
259
+ ### Users
260
+
261
+ The `users` collection is special - it links to PocketBase authentication.
262
+
263
+ ### Defining Users
264
+
265
+ ```tsx
266
+ users: collection({
267
+ bio: string().optional(),
268
+ avatar: media().image().optional(),
269
+ website: string().url().optional()
270
+ })
271
+
272
+ ```
273
+
274
+ When users authenticate, okno auto-creates their `users` record linked to their PocketBase auth user.
275
+
276
+ ### User References
277
+
278
+ ```tsx
279
+ user() // Reference to users collection
280
+ user().multiple() // Multiple user references
281
+ user().default("current") // Default to logged-in user
282
+
283
+ // Example
284
+ articles: collection({
285
+ title: string(),
286
+ author: user().default("current"),
287
+ contributors: user().multiple()
288
+ })
289
+
290
+ ```
291
+
292
+ ### Localization
293
+
294
+ Enable multiple languages:
295
+
296
+ ```tsx
297
+ export const okno = schema({
298
+ articles: collection({
299
+ title: string().localized(),
300
+ slug: slug("title"), // not localized
301
+ content: richtext().localized(),
302
+ featuredImage: media().image() // same for all locales
303
+ }).localized()
304
+ })
305
+ .locales({
306
+ en: locale({ name: "English", default: true }),
307
+ es: locale({ name: "Español" }),
308
+ "cs-CZ": locale({ name: "Čeština" })
309
+ })
310
+
311
+ ```
312
+
313
+ Content structure with locales:
314
+
315
+ ```tsx
316
+ {
317
+ id: "123",
318
+ slug: "my-article",
319
+ title: {
320
+ en: "My Article",
321
+ es: "Mi Artículo",
322
+ "cs-CZ": "Můj článek"
323
+ },
324
+ content: {
325
+ en: "English content",
326
+ es: "Contenido en español"
327
+ }
328
+ }
329
+
330
+ ```
331
+
332
+ Query with locale:
333
+
334
+ ```tsx
335
+ // Specific locale
336
+ okno("articles", { locale: "es" })
337
+
338
+ // All locales
339
+ okno("articles", { locale: "*" }) // default
340
+
341
+ // With fallback
342
+ okno("articles", { locale: "es", fallback: ["en"] })
343
+
344
+ ```
345
+
346
+ ### Config
347
+
348
+ Configuration is chained after schema and/or locales:
349
+
350
+ ```tsx
351
+ export const okno = schema({
352
+ users: collection({...}),
353
+ articles: collection({...})
354
+ })
355
+ .locales({
356
+ en: locale({ name: "English", default: true })
357
+ })
358
+ .config({
359
+ siteUrl: 'https://mysite.com' // Required for user invitations
360
+ })
361
+
362
+ ```
363
+
364
+ ### User Invitations
365
+
366
+ The `siteUrl` enables user invitations in the editor:
367
+
368
+ 1. Owner clicks "Invite user" in editor
369
+ 2. Enters email and role (owner/editor/viewer)
370
+ 3. Invitation email sent: `https://mysite.com/invite?token=xyz`
371
+ 4. Recipient registers/logs in via PocketBase
372
+ 5. okno auto-creates their `users` record
373
+ 6. User has access to the space
374
+
375
+ Without `siteUrl`, invite button is disabled with a setup warning.
376
+
377
+ ### Forms
378
+
379
+ _Forms are not the current priority._
380
+
381
+ Define forms in your schema:
382
+
383
+ ```tsx
384
+ contact: form({
385
+ name: string().required(),
386
+ email: string().email().required(),
387
+ password: string().required(),
388
+ message: textarea().required()
389
+ })
390
+
391
+ ```
392
+
393
+ Spread forms and fields for automatic handling:
394
+
395
+ ```tsx
396
+ <form {...okno("forms.contact")}>
397
+ {/* Fields auto-detect type from schema */}
398
+ <input {...okno("forms.contact.name")} />
399
+ {/* → type="text", name="name", required, etc. */}
400
+
401
+ <input {...okno("forms.contact.email")} />
402
+ {/* → type="email", name="email", required, etc. */}
403
+
404
+ {/* Override type when needed */}
405
+ <input {...okno("forms.contact.password")} type="password" />
406
+ {/* → type="password" (overridden), name="password", required, etc. */}
407
+
408
+ <textarea {...okno("forms.contact.message")}></textarea>
409
+
410
+ <button type="submit">Send</button>
411
+ </form>
412
+
413
+ {/* Error handling per field */}
414
+ {#each okno("forms.contact.email").errors() as error}
415
+ <span class="error">{error.message}</span>
416
+ {/each}
417
+
418
+ ```
419
+
420
+ The form spread provides `onSubmit` that prevents default and POSTs to custom PocketBase endpoint.
421
+
422
+ ---
423
+
424
+ ## Query
425
+
426
+ ### Basic
427
+
428
+ ```tsx
429
+ import { okno } from "@/okno"
430
+
431
+ // Get single item by key
432
+ okno("home.heroTitle")
433
+
434
+ // Get collection
435
+ okno("articles")
436
+
437
+ // Get by slug
438
+ okno("articles", { slug: "getting-started" })
439
+
440
+ // Get by ID
441
+ okno("articles", { id: "rec_123" })
442
+
443
+ // Get specific fields
444
+ okno("articles", { select: ["title", "slug", "excerpt"] })
445
+
446
+ ```
447
+
448
+ ### Filters
449
+
450
+ ```tsx
451
+ // Simple filters
452
+ okno("articles", {
453
+ where: {
454
+ featured: true,
455
+ category: "tutorial"
456
+ }
457
+ })
458
+
459
+ // Sorting
460
+ okno("articles", {
461
+ sort: "-publishedAt" // Descending
462
+ })
463
+
464
+ okno("articles", {
465
+ sort: "title" // Ascending
466
+ })
467
+
468
+ // Limit
469
+ okno("articles", {
470
+ limit: 10
471
+ })
472
+
473
+ // Combine filters, sorting, and limit
474
+ okno("articles", {
475
+ where: { featured: true, category: "tutorial" },
476
+ sort: "-publishedAt",
477
+ limit: 10,
478
+ select: ["title", "slug", "excerpt"]
479
+ })
480
+
481
+ //TODO: pagination?
482
+ ```
483
+
484
+ ### Drafts
485
+
486
+ For draftable collections:
487
+
488
+ ```tsx
489
+ articles: collection({...}).draftable()
490
+ ```
491
+
492
+ Content can be in three states:
493
+
494
+ 1. **Draft** - Created but not published
495
+ 2. **Published** - Live content
496
+ 3. **Published with edits** - Has unpublished changes
497
+
498
+ Query drafts:
499
+
500
+ ```tsx
501
+ okno("articles") // Published only (default)
502
+ okno("articles", { drafts: true }) // Include drafts
503
+ okno("articles", { drafts: 'only' }) // Only drafts
504
+
505
+ ```
506
+
507
+ ### Slots
508
+
509
+ Define dynamic content placeholders:
510
+
511
+ ```tsx
512
+ articles: collection({
513
+ content: richtext().slots(['price', 'count', 'date'])
514
+ })
515
+
516
+ // TODO: non defined slots, just .slots() and create them on the go.
517
+ ```
518
+
519
+ Content with placeholders:
520
+
521
+ ```tsx
522
+ {
523
+ content: "We have {{count}} items starting at {{price}}. Sale ends {{date}}!"
524
+ }
525
+
526
+ ```
527
+
528
+ Populate slots dynamically:
529
+
530
+ ```tsx
531
+ okno("sale.content", {
532
+ slots: {
533
+ count: inventory.length,
534
+ price: "$9.99",
535
+ date: "March 31st"
536
+ }
537
+ })
538
+ // Returns: "We have 42 items starting at $9.99. Sale ends March 31st!"
539
+
540
+ ```
541
+
542
+ ---
543
+
544
+ ## Editor
545
+
546
+ ### Framework Support
547
+
548
+ ```bash
549
+ npm install okno
550
+ npm install okno/astro # or okno/vite, okno/next, etc.
551
+ ```
552
+
553
+ Framework adapters:
554
+
555
+ ```tsx
556
+ // astro.config.mjs
557
+ import { okno } from "okno/astro"
558
+
559
+ // vite.config.js
560
+ import { okno } from "okno/vite"
561
+ ```
562
+
563
+ The editor is imported separately and handles live editing in the browser.
564
+
565
+ ### Content Rendering
566
+
567
+ ### Editable Content
568
+
569
+ ```
570
+ <!-- Text content needs set:html for edit spans -->
571
+ <h1 set:html={okno("home.title")} />
572
+
573
+ <!-- Images don't need special handling -->
574
+ <img {...okno("home.heroImage")} />
575
+
576
+ ```
577
+
578
+ ### Non-editable Context
579
+
580
+ ```
581
+ <!-- Get plain text without HTML -->
582
+ <meta name="description" content={okno("home.title", { plain: true })} />
583
+
584
+ <!-- Get raw HTML without edit spans -->
585
+ <div set:html={okno("home.content", { raw: true })} />
586
+
587
+ ```
588
+
589
+ ### Data Attributes
590
+
591
+ All non-text content automatically includes `data-okno` attributes for editor targeting:
592
+
593
+ ```tsx
594
+ // Images include data-okno
595
+ okno("home.heroImage")
596
+ // Returns:
597
+ {
598
+ src: "/hero.jpg",
599
+ alt: "Hero image",
600
+ width: 1920,
601
+ height: 1080,
602
+ "data-okno": "home.heroImage"
603
+ }
604
+
605
+ // Just spread and it works
606
+ <img {...okno("home.heroImage")} />
607
+ // Renders: <img src="..." data-okno="home.heroImage" />
608
+
609
+ ```
610
+
611
+ ### Collection Items
612
+
613
+ For collections, each item's fields include the specific item ID:
614
+
615
+ ```tsx
616
+ const articles = okno("articles")
617
+
618
+ articles.map(article => (
619
+ <article>
620
+ {/* Text fields are wrapped automatically */}
621
+ <h2 set:html={article.title} />
622
+
623
+ {/* Images include the specific article ID */}
624
+ <img {...article.featuredImage} />
625
+ {/* Renders: <img src="..." data-okno="articles.abc-123.featuredImage" /> */}
626
+ </article>
627
+ ))
628
+
629
+ ```
630
+
631
+ ### Data Attribute Format
632
+
633
+ - **Direct queries**: `data-okno="home.heroImage"`
634
+ - **Collection items**: `data-okno="articles.{id}.featuredImage"`
635
+ - **Nested fields**: `data-okno="home.hero.backgroundImage"`
636
+
637
+ This allows the editor to precisely target any piece of content for editing.
638
+
639
+ ---
640
+
641
+ ## TODO: Future Features
642
+
643
+ The following features are planned but not yet implemented:
644
+
645
+ ### Rich Text Editor Configuration
646
+
647
+ - Toolbar customization
648
+ - Custom blocks/components
649
+ - Image upload handling within richtext
650
+ - Embed support (YouTube, Twitter, etc.)
651
+
652
+ ### Media Upload Configuration
653
+
654
+ - Global upload limits (size, dimensions)
655
+ - Storage provider configuration
656
+ - Image optimization settings
657
+ - CDN integration
658
+
659
+ ### Collection Ordering
660
+
661
+ ```tsx
662
+ articles: collection({...}).sortable() // Manual drag-and-drop ordering
663
+ ```
664
+
665
+ ### Custom Validation Messages
666
+
667
+ ```tsx
668
+ email: string().email("Please enter a valid email address")
669
+ age: number().min(18, "Must be 18 or older")
670
+ ```
671
+
672
+ ### Full-Text Search
673
+
674
+ ```tsx
675
+ okno("articles", { search: "keyword" }) // Search across all text fields
676
+ okno("articles", { search: "keyword", fields: ["title", "content"] })
677
+
678
+ ```
679
+
680
+ ### Webhooks
681
+
682
+ ```tsx
683
+ articles: collection({...})
684
+ .webhook({
685
+ onPublish: 'https://api.example.com/notify',
686
+ onUpdate: 'https://api.example.com/sync'
687
+ })
688
+ ```
689
+
690
+ ### Draft Preview URLs
691
+
692
+ ```tsx
693
+ articles: collection({...})
694
+ .preview((item) => `/preview/${item.slug}?token=${item.previewToken}`)
695
+ ```
@@ -0,0 +1,3 @@
1
+ import type { AstroIntegration } from "astro";
2
+ export declare function okno(): AstroIntegration;
3
+ //# sourceMappingURL=astro.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astro.d.ts","sourceRoot":"","sources":["../../adapters/astro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAK9C,wBAAgB,IAAI,IAAI,gBAAgB,CA4DvC"}