@byline/cli 1.12.0 → 1.12.1
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/templates/byline-examples/admin.config.ts +18 -10
- package/dist/templates/byline-examples/collections/docs/admin.tsx +3 -16
- package/dist/templates/byline-examples/collections/docs/schema.ts +0 -43
- package/dist/templates/byline-examples/collections/media/components/media-list-view.tsx +1 -1
- package/dist/templates/byline-examples/collections/media/schema.ts +1 -0
- package/dist/templates/byline-examples/collections/news/admin.tsx +10 -2
- package/dist/templates/byline-examples/collections/news/components/feature-formatter.tsx +10 -0
- package/dist/templates/byline-examples/collections/news/schema.ts +7 -0
- package/dist/templates/byline-examples/fields/available-languages-field.ts +10 -1
- package/dist/templates/byline-examples/fields/lexical-richtext-compact.ts +20 -4
- package/dist/templates/byline-examples/fields/published-on-field.ts +9 -0
- package/dist/templates/byline-examples/seed-docs.ts +0 -2
- package/dist/templates/byline-examples/seeds/docs.ts +0 -76
- package/dist/templates/byline-examples/server.config.ts +1 -2
- package/dist/templates/host/vite.config.ts +1 -0
- package/package.json +1 -1
|
@@ -29,7 +29,6 @@ import { RichTextField as LexicalRichTextField } from '@byline/richtext-lexical'
|
|
|
29
29
|
// import { lexicalEditor } from '@byline/richtext-lexical'
|
|
30
30
|
|
|
31
31
|
import { Docs, DocsAdmin } from './collections/docs/index.js'
|
|
32
|
-
import { DocsCategories, DocsCategoriesAdmin } from './collections/docs-categories/index.js'
|
|
33
32
|
import { Media, MediaAdmin } from './collections/media/index.js'
|
|
34
33
|
import { News, NewsAdmin } from './collections/news/index.js'
|
|
35
34
|
import { NewsCategories, NewsCategoriesAdmin } from './collections/news-categories/index.js'
|
|
@@ -43,8 +42,8 @@ export const config: ClientConfig = {
|
|
|
43
42
|
serverURL,
|
|
44
43
|
i18n,
|
|
45
44
|
routes,
|
|
46
|
-
collections: [Docs, News, Pages, Media,
|
|
47
|
-
admin: [DocsAdmin, NewsAdmin, PagesAdmin, MediaAdmin,
|
|
45
|
+
collections: [Docs, News, Pages, Media, NewsCategories],
|
|
46
|
+
admin: [DocsAdmin, NewsAdmin, PagesAdmin, MediaAdmin, NewsCategoriesAdmin],
|
|
48
47
|
fields: {
|
|
49
48
|
// Default registration — every `type: 'richText'` field gets the full
|
|
50
49
|
// Lexical feature set unless overridden per-field via
|
|
@@ -53,16 +52,25 @@ export const config: ClientConfig = {
|
|
|
53
52
|
richText: { editor: LexicalRichTextField },
|
|
54
53
|
|
|
55
54
|
// ---------------------------------------------------------------------
|
|
56
|
-
// Alternatively — register the editor with site-wide custom settings
|
|
57
|
-
// The `configure` callback receives a
|
|
58
|
-
//
|
|
59
|
-
//
|
|
55
|
+
// Alternatively — register the editor with site-wide custom settings
|
|
56
|
+
// and an edited extensions list. The `configure` callback receives a
|
|
57
|
+
// fresh seed (default settings + the canonical extensions list), and
|
|
58
|
+
// mutations are local to this call. Per-field `editorConfig`
|
|
59
|
+
// continues to take precedence over whatever is baked in here.
|
|
60
|
+
//
|
|
61
|
+
// import {
|
|
62
|
+
// lexicalEditor,
|
|
63
|
+
// AdmonitionExtension,
|
|
64
|
+
// CodeHighlightExtension,
|
|
65
|
+
// TableExtension,
|
|
66
|
+
// } from '@byline/richtext-lexical'
|
|
60
67
|
//
|
|
61
68
|
// richText: {
|
|
62
69
|
// editor: lexicalEditor((c) => {
|
|
63
|
-
// c.
|
|
64
|
-
//
|
|
65
|
-
//
|
|
70
|
+
// c.extensions
|
|
71
|
+
// .remove(TableExtension)
|
|
72
|
+
// .remove(CodeHighlightExtension)
|
|
73
|
+
// .remove(AdmonitionExtension)
|
|
66
74
|
// c.settings.placeholderText = 'Start writing...'
|
|
67
75
|
// return c
|
|
68
76
|
// }),
|
|
@@ -11,7 +11,6 @@ import { DateTimeFormatter } from '@byline/ui/react'
|
|
|
11
11
|
|
|
12
12
|
import { SummaryLength } from '~/components/summary-length.js'
|
|
13
13
|
|
|
14
|
-
import { FeaturedFormatter } from './components/feature-formatter.js'
|
|
15
14
|
import { Docs } from './schema.js'
|
|
16
15
|
|
|
17
16
|
/**
|
|
@@ -34,14 +33,7 @@ const listViewColumns: ColumnDefinition[] = [
|
|
|
34
33
|
label: 'Title',
|
|
35
34
|
sortable: true,
|
|
36
35
|
align: 'left',
|
|
37
|
-
className: 'w-[
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
fieldName: 'featured',
|
|
41
|
-
label: 'Featured',
|
|
42
|
-
align: 'center',
|
|
43
|
-
className: 'w-[10%]',
|
|
44
|
-
formatter: { component: FeaturedFormatter },
|
|
36
|
+
className: 'w-[50%]',
|
|
45
37
|
},
|
|
46
38
|
{
|
|
47
39
|
fieldName: 'status',
|
|
@@ -54,7 +46,7 @@ const listViewColumns: ColumnDefinition[] = [
|
|
|
54
46
|
label: 'Last Updated',
|
|
55
47
|
sortable: true,
|
|
56
48
|
align: 'right',
|
|
57
|
-
className: 'w-[
|
|
49
|
+
className: 'w-[35%]',
|
|
58
50
|
formatter: { component: DateTimeFormatter },
|
|
59
51
|
},
|
|
60
52
|
]
|
|
@@ -166,18 +158,13 @@ export const DocsAdmin: CollectionAdminConfig = defineAdmin(Docs, {
|
|
|
166
158
|
{
|
|
167
159
|
name: 'details',
|
|
168
160
|
label: 'Details',
|
|
169
|
-
fields: ['title', 'summary', 'featureImage'
|
|
161
|
+
fields: ['title', 'summary', 'featureImage'],
|
|
170
162
|
},
|
|
171
163
|
{
|
|
172
164
|
name: 'content',
|
|
173
165
|
label: 'Content',
|
|
174
166
|
fields: ['content'],
|
|
175
167
|
},
|
|
176
|
-
{
|
|
177
|
-
name: 'reviews',
|
|
178
|
-
label: 'Reviews & Links',
|
|
179
|
-
fields: ['reviews', 'links'],
|
|
180
|
-
},
|
|
181
168
|
],
|
|
182
169
|
},
|
|
183
170
|
],
|
|
@@ -148,20 +148,6 @@ export const Docs = defineCollection({
|
|
|
148
148
|
optional: true,
|
|
149
149
|
},
|
|
150
150
|
publishedOnField,
|
|
151
|
-
{
|
|
152
|
-
name: 'category',
|
|
153
|
-
label: 'Category',
|
|
154
|
-
type: 'relation',
|
|
155
|
-
targetCollection: 'docs-categories',
|
|
156
|
-
displayField: 'name',
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
name: 'featured',
|
|
160
|
-
label: 'Featured',
|
|
161
|
-
type: 'checkbox',
|
|
162
|
-
optional: true,
|
|
163
|
-
helpText: 'Feature this document.',
|
|
164
|
-
},
|
|
165
151
|
{
|
|
166
152
|
name: 'content',
|
|
167
153
|
label: 'Content',
|
|
@@ -169,35 +155,6 @@ export const Docs = defineCollection({
|
|
|
169
155
|
optional: true,
|
|
170
156
|
blocks: [RichTextBlock, PhotoBlock],
|
|
171
157
|
},
|
|
172
|
-
{
|
|
173
|
-
name: 'reviews',
|
|
174
|
-
label: 'Reviews',
|
|
175
|
-
type: 'array',
|
|
176
|
-
optional: true,
|
|
177
|
-
fields: [
|
|
178
|
-
{
|
|
179
|
-
name: 'reviewItem',
|
|
180
|
-
label: 'Review Item',
|
|
181
|
-
type: 'group',
|
|
182
|
-
fields: [
|
|
183
|
-
{ name: 'rating', label: 'Rating', type: 'integer' },
|
|
184
|
-
{
|
|
185
|
-
name: 'comment',
|
|
186
|
-
label: 'Comments',
|
|
187
|
-
type: 'richText',
|
|
188
|
-
localized: false,
|
|
189
|
-
},
|
|
190
|
-
],
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
name: 'links',
|
|
196
|
-
label: 'Links',
|
|
197
|
-
type: 'array',
|
|
198
|
-
optional: true,
|
|
199
|
-
fields: [{ name: 'link', label: 'Link', type: 'text' }],
|
|
200
|
-
},
|
|
201
158
|
availableLanguagesField(),
|
|
202
159
|
],
|
|
203
160
|
})
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
import styles from './media-list-view.module.css'
|
|
39
39
|
import { FormatBadge } from './media-thumbnail'
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
function formatNumber(number: number, decimalPlaces: number) {
|
|
42
42
|
if (typeof number !== 'number' || Number.isNaN(number)) {
|
|
43
43
|
throw new TypeError('Input must be a valid number')
|
|
44
44
|
}
|
|
@@ -56,6 +56,7 @@ export const Media = defineCollection({
|
|
|
56
56
|
name: 'image',
|
|
57
57
|
label: 'Image',
|
|
58
58
|
type: 'image',
|
|
59
|
+
helpText: 'Select an image for this media item.',
|
|
59
60
|
upload: {
|
|
60
61
|
// Allow common image types. Extend with 'video/*', 'application/pdf'
|
|
61
62
|
// etc. for a more general media field.
|
|
@@ -11,6 +11,7 @@ import { DateTimeFormatter } from '@byline/ui/react'
|
|
|
11
11
|
|
|
12
12
|
import { SummaryLength } from '~/components/summary-length.js'
|
|
13
13
|
|
|
14
|
+
import { FeaturedFormatter } from './components/feature-formatter.js'
|
|
14
15
|
import { News } from './schema.js'
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -33,7 +34,14 @@ const listViewColumns: ColumnDefinition[] = [
|
|
|
33
34
|
label: 'Title',
|
|
34
35
|
sortable: true,
|
|
35
36
|
align: 'left',
|
|
36
|
-
className: 'w-[
|
|
37
|
+
className: 'w-[30%]',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
fieldName: 'featured',
|
|
41
|
+
label: 'Featured',
|
|
42
|
+
align: 'center',
|
|
43
|
+
className: 'w-[10%]',
|
|
44
|
+
formatter: { component: FeaturedFormatter },
|
|
37
45
|
},
|
|
38
46
|
{
|
|
39
47
|
fieldName: 'status',
|
|
@@ -201,6 +209,6 @@ export const NewsAdmin: CollectionAdminConfig = defineAdmin(News, {
|
|
|
201
209
|
*/
|
|
202
210
|
layout: {
|
|
203
211
|
main: ['main'],
|
|
204
|
-
sidebar: ['availableLanguages', 'publishedOn'],
|
|
212
|
+
sidebar: ['featured', 'availableLanguages', 'publishedOn'],
|
|
205
213
|
},
|
|
206
214
|
})
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FormatterProps } from '@byline/core'
|
|
2
|
+
|
|
3
|
+
export function FeaturedFormatter({ value, record }: FormatterProps) {
|
|
4
|
+
if (value == null || value === false) return null
|
|
5
|
+
return (
|
|
6
|
+
<span title={`"${record.title}" is featured`} className="text-amber-500">
|
|
7
|
+
★
|
|
8
|
+
</span>
|
|
9
|
+
)
|
|
10
|
+
}
|
|
@@ -66,6 +66,13 @@ export const News = defineCollection({
|
|
|
66
66
|
displayField: 'title',
|
|
67
67
|
optional: true,
|
|
68
68
|
},
|
|
69
|
+
{
|
|
70
|
+
name: 'featured',
|
|
71
|
+
label: 'Featured',
|
|
72
|
+
type: 'checkbox',
|
|
73
|
+
optional: true,
|
|
74
|
+
helpText: 'Feature this document.',
|
|
75
|
+
},
|
|
69
76
|
{
|
|
70
77
|
name: 'content',
|
|
71
78
|
label: 'Content',
|
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* **Schema-side helper.** Returns a `GroupField` schema that emits one
|
|
11
|
+
* `checkbox` field per configured content locale. Drop the result into
|
|
12
|
+
* a collection's `fields` array in `<collection>/schema.ts`. Pure data;
|
|
13
|
+
* the only imports are `@byline/core` types and the i18n locale list.
|
|
14
|
+
*
|
|
15
|
+
* See `docs/FIELDS.md` for the schema-vs-admin model.
|
|
16
|
+
*/
|
|
17
|
+
|
|
9
18
|
import type { GroupField } from '@byline/core'
|
|
10
19
|
|
|
11
20
|
import { contentLocales, type LocaleDefinition } from '../i18n.js'
|
|
@@ -39,7 +48,7 @@ const builtInValidate = (value: Record<string, boolean> | undefined): string | u
|
|
|
39
48
|
!Array.isArray(value) &&
|
|
40
49
|
Object.values(value).some(Boolean)
|
|
41
50
|
if (!hasSelection) {
|
|
42
|
-
return 'At least one language must be selected
|
|
51
|
+
return 'At least one language must be selected'
|
|
43
52
|
}
|
|
44
53
|
return undefined
|
|
45
54
|
}
|
|
@@ -6,6 +6,23 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* **Schema-side helper.** Returns a `RichTextField` schema — drop into a
|
|
11
|
+
* collection's `fields` array in `<collection>/schema.ts`. Bakes a
|
|
12
|
+
* compact Lexical `editorConfig` (settings only) into the schema. Pure
|
|
13
|
+
* data: no React, no CSS — schema files must stay tsx-loadable for
|
|
14
|
+
* seeds (see `byline/server.config.ts`).
|
|
15
|
+
*
|
|
16
|
+
* **Constraint** — `editorConfig` baked into a schema can only override
|
|
17
|
+
* **settings** (placeholder, toolbar UI flags, `embedRelationsOnSave`).
|
|
18
|
+
* Extension references (TableExtension, AdmonitionExtension, etc.) are
|
|
19
|
+
* not JSON-safe and would break tsx-loaded seeds; per-field extension
|
|
20
|
+
* removal goes through a client-side wrapper component registered via
|
|
21
|
+
* `FieldAdminConfig.editor`.
|
|
22
|
+
*
|
|
23
|
+
* See `docs/FIELDS.md` for the full schema-vs-admin model.
|
|
24
|
+
*/
|
|
25
|
+
|
|
9
26
|
import type { RichTextField } from '@byline/core'
|
|
10
27
|
// Import from `/server` (data-only) rather than the package root so this
|
|
11
28
|
// schema helper stays tsx-loadable. The root barrel evaluates `RichTextField`
|
|
@@ -34,10 +51,9 @@ type Options = Partial<Omit<RichTextField, 'type' | 'editorConfig'>> & {
|
|
|
34
51
|
*
|
|
35
52
|
* To narrow the *extension* set per-field — drop tables, lists, embeds,
|
|
36
53
|
* the floating format toolbar, the table action menu — register a
|
|
37
|
-
* `LexicalRichTextCompact` wrapper component via `FieldAdminConfig.editor
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* controlled rather than settings-controlled.
|
|
54
|
+
* `LexicalRichTextCompact` wrapper component via `FieldAdminConfig.editor`.
|
|
55
|
+
* Extension references aren't safe to bake into schemas, and floating
|
|
56
|
+
* UIs are now extension-presence controlled rather than settings-controlled.
|
|
41
57
|
*/
|
|
42
58
|
function applyCompactPreset(config: EditorConfig): EditorConfig {
|
|
43
59
|
const o = config.settings.options
|
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* **Schema-side field.** A pre-built `datetime` field definition. Drop
|
|
11
|
+
* the exported `publishedOnField` (the value, not a call) into a
|
|
12
|
+
* collection's `fields` array in `<collection>/schema.ts`. Pure data;
|
|
13
|
+
* safe to import from any schema file.
|
|
14
|
+
*
|
|
15
|
+
* See `docs/FIELDS.md` for the schema-vs-admin model.
|
|
16
|
+
*/
|
|
17
|
+
|
|
9
18
|
import { defineField, type FieldData } from '@byline/core'
|
|
10
19
|
|
|
11
20
|
/**
|
|
@@ -10,12 +10,10 @@
|
|
|
10
10
|
import 'dotenv/config'
|
|
11
11
|
import './server.config.js'
|
|
12
12
|
|
|
13
|
-
import { seedDocsCategories } from './seeds/doc-categories.js'
|
|
14
13
|
import { seedDocs } from './seeds/docs.js'
|
|
15
14
|
import { seedNewsCategories } from './seeds/news-categories.js'
|
|
16
15
|
|
|
17
16
|
async function run() {
|
|
18
|
-
await seedDocsCategories()
|
|
19
17
|
await seedNewsCategories()
|
|
20
18
|
await seedDocs()
|
|
21
19
|
}
|
|
@@ -24,7 +24,6 @@ const sampleDocument = {
|
|
|
24
24
|
// targetCollectionId: "cat-123",
|
|
25
25
|
// targetDocumentId: "electronics-audio"
|
|
26
26
|
// },
|
|
27
|
-
featured: false,
|
|
28
27
|
publishedOn: new Date('2024-01-15T10:00:00'),
|
|
29
28
|
content: [
|
|
30
29
|
{
|
|
@@ -165,81 +164,6 @@ const sampleDocument = {
|
|
|
165
164
|
},
|
|
166
165
|
},
|
|
167
166
|
],
|
|
168
|
-
reviews: [
|
|
169
|
-
{
|
|
170
|
-
reviewItem: {
|
|
171
|
-
rating: 5,
|
|
172
|
-
comment: {
|
|
173
|
-
root: {
|
|
174
|
-
children: [
|
|
175
|
-
{
|
|
176
|
-
children: [
|
|
177
|
-
{
|
|
178
|
-
detail: 0,
|
|
179
|
-
format: 0,
|
|
180
|
-
mode: 'normal',
|
|
181
|
-
style: '',
|
|
182
|
-
text: 'Some review text here...',
|
|
183
|
-
type: 'text',
|
|
184
|
-
version: 1,
|
|
185
|
-
},
|
|
186
|
-
],
|
|
187
|
-
direction: 'ltr',
|
|
188
|
-
format: '',
|
|
189
|
-
indent: 0,
|
|
190
|
-
type: 'paragraph',
|
|
191
|
-
version: 1,
|
|
192
|
-
textFormat: 0,
|
|
193
|
-
textStyle: '',
|
|
194
|
-
},
|
|
195
|
-
],
|
|
196
|
-
direction: 'ltr',
|
|
197
|
-
format: '',
|
|
198
|
-
indent: 0,
|
|
199
|
-
type: 'root',
|
|
200
|
-
version: 1,
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
reviewItem: {
|
|
207
|
-
rating: 3,
|
|
208
|
-
comment: {
|
|
209
|
-
root: {
|
|
210
|
-
children: [
|
|
211
|
-
{
|
|
212
|
-
children: [
|
|
213
|
-
{
|
|
214
|
-
detail: 0,
|
|
215
|
-
format: 0,
|
|
216
|
-
mode: 'normal',
|
|
217
|
-
style: '',
|
|
218
|
-
text: 'Some review text here...',
|
|
219
|
-
type: 'text',
|
|
220
|
-
version: 1,
|
|
221
|
-
},
|
|
222
|
-
],
|
|
223
|
-
direction: 'ltr',
|
|
224
|
-
format: '',
|
|
225
|
-
indent: 0,
|
|
226
|
-
type: 'paragraph',
|
|
227
|
-
version: 1,
|
|
228
|
-
textFormat: 0,
|
|
229
|
-
textStyle: '',
|
|
230
|
-
},
|
|
231
|
-
],
|
|
232
|
-
direction: 'ltr',
|
|
233
|
-
format: '',
|
|
234
|
-
indent: 0,
|
|
235
|
-
type: 'root',
|
|
236
|
-
version: 1,
|
|
237
|
-
},
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
],
|
|
242
|
-
links: [{ link: 'https://example.com' }, { link: 'https://another-example.com' }],
|
|
243
167
|
}
|
|
244
168
|
|
|
245
169
|
export async function seedDocs(count = 15) {
|
|
@@ -28,7 +28,6 @@ import { localStorageProvider } from '@byline/storage-local'
|
|
|
28
28
|
// admin UI configs (React components, CSS modules) that are not loadable
|
|
29
29
|
// outside Vite (e.g. when running seeds via tsx).
|
|
30
30
|
import { Docs } from './collections/docs/schema.js'
|
|
31
|
-
import { DocsCategories } from './collections/docs-categories/schema.js'
|
|
32
31
|
import { Media } from './collections/media/schema.js'
|
|
33
32
|
import { News } from './collections/news/schema.js'
|
|
34
33
|
import { NewsCategories } from './collections/news-categories/schema.js'
|
|
@@ -38,7 +37,7 @@ import { DEFAULT_SERVER_URL, routes } from './routes.js'
|
|
|
38
37
|
|
|
39
38
|
const serverURL = process.env.VITE_SERVER_URL || DEFAULT_SERVER_URL
|
|
40
39
|
|
|
41
|
-
const collections = [Docs, News, Pages, Media,
|
|
40
|
+
const collections = [Docs, News, Pages, Media, NewsCategories]
|
|
42
41
|
|
|
43
42
|
// HMR-safe singleton. Vite's program reload re-evaluates this module
|
|
44
43
|
// without disposing the previous module's resources — every reload
|
|
@@ -132,6 +132,7 @@ const config = defineConfig({
|
|
|
132
132
|
browserAsyncHooksAlias(),
|
|
133
133
|
devtools(),
|
|
134
134
|
nitro({
|
|
135
|
+
preset: 'node',
|
|
135
136
|
// @byline/ui ships compiled JS that does `import './foo_module.css'`.
|
|
136
137
|
// @byline/host-tanstack-start re-exports route factories from
|
|
137
138
|
// @byline/ui at runtime in the SSR graph. Nitro externalizes
|