@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.
@@ -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, DocsCategories, NewsCategories],
47
- admin: [DocsAdmin, NewsAdmin, PagesAdmin, MediaAdmin, DocsCategoriesAdmin, NewsCategoriesAdmin],
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 deep clone of `defaultEditorConfig`,
58
- // so mutating it is safe. Per-field `editorConfig` continues to take
59
- // precedence over whatever is baked in here.
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.settings.options.tablePlugin = false
64
- // c.settings.options.codeHighlightPlugin = false
65
- // c.settings.options.admonitionPlugin = false
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-[30%]',
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-[20%]',
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', 'category', 'featured'],
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
- export function formatNumber(number: number, decimalPlaces: number) {
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-[25%]',
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
- * (same pattern as `aiRichTextAdmin()`). Extension references aren't safe
39
- * to bake into schemas, and floating UIs are now extension-presence
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, DocsCategories, NewsCategories]
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
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@byline/cli",
3
3
  "private": false,
4
4
  "license": "MPL-2.0",
5
- "version": "1.12.0",
5
+ "version": "1.12.1",
6
6
  "engines": {
7
7
  "node": ">=20.9.0"
8
8
  },