@byline/cli 0.1.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.
Files changed (251) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +23 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +72 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/doctor.d.ts +2 -0
  8. package/dist/commands/doctor.d.ts.map +1 -0
  9. package/dist/commands/doctor.js +36 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/init.d.ts +16 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +76 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/context.d.ts +38 -0
  16. package/dist/context.d.ts.map +1 -0
  17. package/dist/context.js +37 -0
  18. package/dist/context.js.map +1 -0
  19. package/dist/index.d.ts +5 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +4 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/lib/pg-url.d.ts +11 -0
  24. package/dist/lib/pg-url.d.ts.map +1 -0
  25. package/dist/lib/pg-url.js +22 -0
  26. package/dist/lib/pg-url.js.map +1 -0
  27. package/dist/manifest/deps.d.ts +29 -0
  28. package/dist/manifest/deps.d.ts.map +1 -0
  29. package/dist/manifest/deps.js +97 -0
  30. package/dist/manifest/deps.js.map +1 -0
  31. package/dist/manifest/env.d.ts +18 -0
  32. package/dist/manifest/env.d.ts.map +1 -0
  33. package/dist/manifest/env.js +38 -0
  34. package/dist/manifest/env.js.map +1 -0
  35. package/dist/phases/db-init.d.ts +3 -0
  36. package/dist/phases/db-init.d.ts.map +1 -0
  37. package/dist/phases/db-init.js +163 -0
  38. package/dist/phases/db-init.js.map +1 -0
  39. package/dist/phases/db.d.ts +11 -0
  40. package/dist/phases/db.d.ts.map +1 -0
  41. package/dist/phases/db.js +93 -0
  42. package/dist/phases/db.js.map +1 -0
  43. package/dist/phases/deps.d.ts +3 -0
  44. package/dist/phases/deps.d.ts.map +1 -0
  45. package/dist/phases/deps.js +115 -0
  46. package/dist/phases/deps.js.map +1 -0
  47. package/dist/phases/env.d.ts +3 -0
  48. package/dist/phases/env.d.ts.map +1 -0
  49. package/dist/phases/env.js +172 -0
  50. package/dist/phases/env.js.map +1 -0
  51. package/dist/phases/host.d.ts +3 -0
  52. package/dist/phases/host.d.ts.map +1 -0
  53. package/dist/phases/host.js +99 -0
  54. package/dist/phases/host.js.map +1 -0
  55. package/dist/phases/index.d.ts +7 -0
  56. package/dist/phases/index.d.ts.map +1 -0
  57. package/dist/phases/index.js +40 -0
  58. package/dist/phases/index.js.map +1 -0
  59. package/dist/phases/preflight.d.ts +4 -0
  60. package/dist/phases/preflight.d.ts.map +1 -0
  61. package/dist/phases/preflight.js +81 -0
  62. package/dist/phases/preflight.js.map +1 -0
  63. package/dist/phases/routes.d.ts +3 -0
  64. package/dist/phases/routes.d.ts.map +1 -0
  65. package/dist/phases/routes.js +145 -0
  66. package/dist/phases/routes.js.map +1 -0
  67. package/dist/phases/scaffold.d.ts +3 -0
  68. package/dist/phases/scaffold.d.ts.map +1 -0
  69. package/dist/phases/scaffold.js +113 -0
  70. package/dist/phases/scaffold.js.map +1 -0
  71. package/dist/phases/stub.d.ts +3 -0
  72. package/dist/phases/stub.d.ts.map +1 -0
  73. package/dist/phases/stub.js +25 -0
  74. package/dist/phases/stub.js.map +1 -0
  75. package/dist/phases/ui.d.ts +3 -0
  76. package/dist/phases/ui.d.ts.map +1 -0
  77. package/dist/phases/ui.js +93 -0
  78. package/dist/phases/ui.js.map +1 -0
  79. package/dist/phases/wire/index.d.ts +3 -0
  80. package/dist/phases/wire/index.d.ts.map +1 -0
  81. package/dist/phases/wire/index.js +67 -0
  82. package/dist/phases/wire/index.js.map +1 -0
  83. package/dist/phases/wire/root-tsx.d.ts +3 -0
  84. package/dist/phases/wire/root-tsx.d.ts.map +1 -0
  85. package/dist/phases/wire/root-tsx.js +57 -0
  86. package/dist/phases/wire/root-tsx.js.map +1 -0
  87. package/dist/phases/wire/server-ts.d.ts +3 -0
  88. package/dist/phases/wire/server-ts.d.ts.map +1 -0
  89. package/dist/phases/wire/server-ts.js +54 -0
  90. package/dist/phases/wire/server-ts.js.map +1 -0
  91. package/dist/phases/wire/shared.d.ts +34 -0
  92. package/dist/phases/wire/shared.d.ts.map +1 -0
  93. package/dist/phases/wire/shared.js +2 -0
  94. package/dist/phases/wire/shared.js.map +1 -0
  95. package/dist/phases/wire/start-ts.d.ts +3 -0
  96. package/dist/phases/wire/start-ts.d.ts.map +1 -0
  97. package/dist/phases/wire/start-ts.js +149 -0
  98. package/dist/phases/wire/start-ts.js.map +1 -0
  99. package/dist/phases/wire/tsconfig.d.ts +3 -0
  100. package/dist/phases/wire/tsconfig.d.ts.map +1 -0
  101. package/dist/phases/wire/tsconfig.js +105 -0
  102. package/dist/phases/wire/tsconfig.js.map +1 -0
  103. package/dist/phases/wire/vite-config.d.ts +3 -0
  104. package/dist/phases/wire/vite-config.d.ts.map +1 -0
  105. package/dist/phases/wire/vite-config.js +46 -0
  106. package/dist/phases/wire/vite-config.js.map +1 -0
  107. package/dist/prompts.d.ts +34 -0
  108. package/dist/prompts.d.ts.map +1 -0
  109. package/dist/prompts.js +49 -0
  110. package/dist/prompts.js.map +1 -0
  111. package/dist/runner.d.ts +5 -0
  112. package/dist/runner.d.ts.map +1 -0
  113. package/dist/runner.js +91 -0
  114. package/dist/runner.js.map +1 -0
  115. package/dist/state.d.ts +18 -0
  116. package/dist/state.d.ts.map +1 -0
  117. package/dist/state.js +68 -0
  118. package/dist/state.js.map +1 -0
  119. package/dist/templates/byline/admin.config.ts +41 -0
  120. package/dist/templates/byline/i18n.ts +47 -0
  121. package/dist/templates/byline/routes.ts +28 -0
  122. package/dist/templates/byline/seed.ts +19 -0
  123. package/dist/templates/byline/seeds/admin.ts +62 -0
  124. package/dist/templates/byline/server.config.ts +92 -0
  125. package/dist/templates/byline-examples/admin.config.ts +74 -0
  126. package/dist/templates/byline-examples/blocks/photo-block.ts +59 -0
  127. package/dist/templates/byline-examples/blocks/richtext-block.ts +35 -0
  128. package/dist/templates/byline-examples/collections/doc-example-flat-locale-all.ts +373 -0
  129. package/dist/templates/byline-examples/collections/doc-example-flat-locale-en.ts +283 -0
  130. package/dist/templates/byline-examples/collections/doc-example-tree-locale-all.ts +278 -0
  131. package/dist/templates/byline-examples/collections/doc-example-tree-locale-en.ts +205 -0
  132. package/dist/templates/byline-examples/collections/docs/admin.tsx +204 -0
  133. package/dist/templates/byline-examples/collections/docs/components/.gitkeep +0 -0
  134. package/dist/templates/byline-examples/collections/docs/components/feature-formatter.tsx +10 -0
  135. package/dist/templates/byline-examples/collections/docs/hooks/.gitkeep +0 -0
  136. package/dist/templates/byline-examples/collections/docs/index.ts +10 -0
  137. package/dist/templates/byline-examples/collections/docs/schema.ts +209 -0
  138. package/dist/templates/byline-examples/collections/docs-categories/admin.tsx +78 -0
  139. package/dist/templates/byline-examples/collections/docs-categories/components/.gitkeep +0 -0
  140. package/dist/templates/byline-examples/collections/docs-categories/hooks/.gitkeep +0 -0
  141. package/dist/templates/byline-examples/collections/docs-categories/index.ts +10 -0
  142. package/dist/templates/byline-examples/collections/docs-categories/schema.ts +33 -0
  143. package/dist/templates/byline-examples/collections/media/admin.tsx +188 -0
  144. package/dist/templates/byline-examples/collections/media/components/media-list-view.tsx +330 -0
  145. package/dist/templates/byline-examples/collections/media/components/media-thumbnail.tsx +63 -0
  146. package/dist/templates/byline-examples/collections/media/hooks/.gitkeep +0 -0
  147. package/dist/templates/byline-examples/collections/media/index.ts +10 -0
  148. package/dist/templates/byline-examples/collections/media/schema.ts +157 -0
  149. package/dist/templates/byline-examples/collections/news/admin.tsx +192 -0
  150. package/dist/templates/byline-examples/collections/news/components/.gitkeep +0 -0
  151. package/dist/templates/byline-examples/collections/news/hooks/.gitkeep +0 -0
  152. package/dist/templates/byline-examples/collections/news/index.ts +10 -0
  153. package/dist/templates/byline-examples/collections/news/schema.ts +91 -0
  154. package/dist/templates/byline-examples/collections/news-categories/admin.tsx +78 -0
  155. package/dist/templates/byline-examples/collections/news-categories/components/.gitkeep +0 -0
  156. package/dist/templates/byline-examples/collections/news-categories/hooks/.gitkeep +0 -0
  157. package/dist/templates/byline-examples/collections/news-categories/index.ts +10 -0
  158. package/dist/templates/byline-examples/collections/news-categories/schema.ts +33 -0
  159. package/dist/templates/byline-examples/collections/pages/admin.tsx +183 -0
  160. package/dist/templates/byline-examples/collections/pages/components/.gitkeep +0 -0
  161. package/dist/templates/byline-examples/collections/pages/hooks/.gitkeep +0 -0
  162. package/dist/templates/byline-examples/collections/pages/index.ts +10 -0
  163. package/dist/templates/byline-examples/collections/pages/schema.ts +96 -0
  164. package/dist/templates/byline-examples/components/length-indicator.tsx +138 -0
  165. package/dist/templates/byline-examples/components/pill.tsx +38 -0
  166. package/dist/templates/byline-examples/components/summary-length.tsx +39 -0
  167. package/dist/templates/byline-examples/fields/available-languages-field.ts +90 -0
  168. package/dist/templates/byline-examples/fields/lexical-richtext-compact.ts +88 -0
  169. package/dist/templates/byline-examples/i18n.ts +47 -0
  170. package/dist/templates/byline-examples/routes.ts +28 -0
  171. package/dist/templates/byline-examples/scripts/regenerate-media.ts +275 -0
  172. package/dist/templates/byline-examples/seed.ts +25 -0
  173. package/dist/templates/byline-examples/seeds/admin.ts +62 -0
  174. package/dist/templates/byline-examples/seeds/doc-categories.ts +71 -0
  175. package/dist/templates/byline-examples/seeds/docs.ts +293 -0
  176. package/dist/templates/byline-examples/seeds/news-categories.ts +71 -0
  177. package/dist/templates/byline-examples/server.config.ts +179 -0
  178. package/dist/templates/host/vite.config.ts +41 -0
  179. package/dist/templates/migrations/0000_condemned_kronos.sql +324 -0
  180. package/dist/templates/migrations/0001_sudden_phantom_reporter.sql +1 -0
  181. package/dist/templates/migrations/meta/0000_snapshot.json +2793 -0
  182. package/dist/templates/migrations/meta/0001_snapshot.json +2799 -0
  183. package/dist/templates/migrations/meta/_journal.json +20 -0
  184. package/dist/templates/routes/(byline)/admin/account/index.tsx +11 -0
  185. package/dist/templates/routes/(byline)/admin/collections/$collection/$id/api.tsx +16 -0
  186. package/dist/templates/routes/(byline)/admin/collections/$collection/$id/history.tsx +19 -0
  187. package/dist/templates/routes/(byline)/admin/collections/$collection/$id/index.tsx +16 -0
  188. package/dist/templates/routes/(byline)/admin/collections/$collection/create.tsx +11 -0
  189. package/dist/templates/routes/(byline)/admin/collections/$collection/index.tsx +11 -0
  190. package/dist/templates/routes/(byline)/admin/index.tsx +11 -0
  191. package/dist/templates/routes/(byline)/admin/permissions/index.tsx +11 -0
  192. package/dist/templates/routes/(byline)/admin/roles/$id/index.tsx +11 -0
  193. package/dist/templates/routes/(byline)/admin/roles/index.tsx +11 -0
  194. package/dist/templates/routes/(byline)/admin/route.tsx +11 -0
  195. package/dist/templates/routes/(byline)/admin/users/$id/index.tsx +11 -0
  196. package/dist/templates/routes/(byline)/admin/users/index.tsx +11 -0
  197. package/dist/templates/routes/(byline)/sign-in.tsx +11 -0
  198. package/dist/templates/ui-byline/blocks/photo-block/index.tsx +80 -0
  199. package/dist/templates/ui-byline/blocks/richtext-block/index.tsx +46 -0
  200. package/dist/templates/ui-byline/components/admonition/index.tsx +40 -0
  201. package/dist/templates/ui-byline/components/code/code-serializer.tsx +20 -0
  202. package/dist/templates/ui-byline/components/code/code.tsx +50 -0
  203. package/dist/templates/ui-byline/components/code/index.module.scss +137 -0
  204. package/dist/templates/ui-byline/components/code/index.ts +2 -0
  205. package/dist/templates/ui-byline/components/code/types.ts +5 -0
  206. package/dist/templates/ui-byline/components/code/utils.ts +20 -0
  207. package/dist/templates/ui-byline/components/heading-anchor/heading-anchor.tsx +69 -0
  208. package/dist/templates/ui-byline/components/heading-anchor/index.ts +1 -0
  209. package/dist/templates/ui-byline/components/heading-anchor/utils.ts +15 -0
  210. package/dist/templates/ui-byline/components/inline-image/index.tsx +109 -0
  211. package/dist/templates/ui-byline/components/layout/index.tsx +63 -0
  212. package/dist/templates/ui-byline/components/link/lang-link.tsx +70 -0
  213. package/dist/templates/ui-byline/components/link/link-field.tsx +298 -0
  214. package/dist/templates/ui-byline/components/link/link-lexical.tsx +191 -0
  215. package/dist/templates/ui-byline/components/list/index.ts +2 -0
  216. package/dist/templates/ui-byline/components/list/list-item.tsx +32 -0
  217. package/dist/templates/ui-byline/components/list/list.tsx +17 -0
  218. package/dist/templates/ui-byline/components/responsive-image/index.tsx +205 -0
  219. package/dist/templates/ui-byline/components/richtext-lexical/index.tsx +31 -0
  220. package/dist/templates/ui-byline/components/richtext-lexical/serialize/index.tsx +249 -0
  221. package/dist/templates/ui-byline/components/richtext-lexical/serialize/richtext-node-formats.ts +66 -0
  222. package/dist/templates/ui-byline/components/richtext-lexical/serialize/types.ts +48 -0
  223. package/dist/templates/ui-byline/components/richtext-lexical/serialize/utils.ts +15 -0
  224. package/dist/templates/ui-byline/components/table-cell/index.tsx +36 -0
  225. package/dist/templates/ui-byline/components/vimeo/index.tsx +21 -0
  226. package/dist/templates/ui-byline/components/youtube/index.tsx +22 -0
  227. package/dist/templates/ui-byline/render-blocks.tsx +71 -0
  228. package/dist/templates/ui-byline/types/i18n.ts +14 -0
  229. package/dist/templates/ui-byline/utils/image-sources.ts +102 -0
  230. package/dist/templates/ui-byline/utils/to-kebab-case.ts +5 -0
  231. package/dist/types.d.ts +54 -0
  232. package/dist/types.d.ts.map +1 -0
  233. package/dist/types.js +2 -0
  234. package/dist/types.js.map +1 -0
  235. package/dist/ui/diff.d.ts +4 -0
  236. package/dist/ui/diff.d.ts.map +1 -0
  237. package/dist/ui/diff.js +23 -0
  238. package/dist/ui/diff.js.map +1 -0
  239. package/dist/ui/grid.d.ts +7 -0
  240. package/dist/ui/grid.d.ts.map +1 -0
  241. package/dist/ui/grid.js +24 -0
  242. package/dist/ui/grid.js.map +1 -0
  243. package/dist/ui/logger.d.ts +14 -0
  244. package/dist/ui/logger.d.ts.map +1 -0
  245. package/dist/ui/logger.js +30 -0
  246. package/dist/ui/logger.js.map +1 -0
  247. package/dist/ui/snippet.d.ts +2 -0
  248. package/dist/ui/snippet.d.ts.map +1 -0
  249. package/dist/ui/snippet.js +7 -0
  250. package/dist/ui/snippet.js.map +1 -0
  251. package/package.json +69 -0
@@ -0,0 +1,96 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import type { CollectionFieldData } from '@byline/core'
10
+ import { defineCollection, defineWorkflow } from '@byline/core'
11
+
12
+ import { availableLanguagesField } from '~/fields/available-languages-field.js'
13
+
14
+ // ---- Schema (server-safe, no UI concerns) ----
15
+
16
+ export const Pages = defineCollection({
17
+ path: 'pages',
18
+ labels: {
19
+ singular: 'Page',
20
+ plural: 'Pages',
21
+ },
22
+ // Workflow: defineWorkflow() guarantees draft, published, and archived are
23
+ // always present and correctly ordered. No custom statuses here — the
24
+ // standard three-step lifecycle is used.
25
+ //
26
+ // Resulting order: [draft, published, archived]
27
+ workflow: defineWorkflow({
28
+ draft: { label: 'Draft', verb: 'Revert to Draft' },
29
+ published: { label: 'Published', verb: 'Publish' },
30
+ archived: { label: 'Archived', verb: 'Archive' },
31
+ }),
32
+ showStats: true,
33
+ search: { fields: ['title'] },
34
+ useAsTitle: 'title',
35
+ useAsPath: 'title',
36
+ fields: [
37
+ { name: 'title', label: 'Title', type: 'text', localized: true },
38
+ {
39
+ name: 'summary',
40
+ label: 'Summary',
41
+ type: 'textArea',
42
+ localized: true,
43
+ helpText:
44
+ 'Enter a short summary. The first 150 characters are used for social media meta descriptions. Aim for 100–300 characters.',
45
+ },
46
+ // Relation field demo. Points at the Media upload collection
47
+ // so editors can choose a feature image via the relation picker widget.
48
+ // Set `displayField: 'title'` so the picker's row label reads from the
49
+ // uploaded item's `title` field rather than falling back to its path.
50
+ // Will display the picker defined columns if present in the admin.tsx
51
+ // configuration.
52
+ {
53
+ name: 'featureImage',
54
+ label: 'Feature Image',
55
+ type: 'relation',
56
+ targetCollection: 'media',
57
+ displayField: 'title',
58
+ optional: true,
59
+ },
60
+ {
61
+ name: 'category',
62
+ label: 'Category',
63
+ type: 'select',
64
+ optional: true,
65
+ helpText: 'Select a category for this page',
66
+ options: [
67
+ { label: 'Foo', value: 'foo' },
68
+ { label: 'Bar', value: 'bar' },
69
+ { label: 'Baz', value: 'baz' },
70
+ ],
71
+ },
72
+ {
73
+ name: 'content',
74
+ label: 'Content',
75
+ type: 'richText',
76
+ helpText: 'Enter the main content for this page.',
77
+ localized: true,
78
+ },
79
+ {
80
+ name: 'publishedOn',
81
+ label: 'Published On',
82
+ type: 'datetime',
83
+ mode: 'datetime',
84
+ },
85
+ {
86
+ name: 'featured',
87
+ label: 'Featured',
88
+ type: 'checkbox',
89
+ optional: true,
90
+ helpText: 'Feature this page.',
91
+ },
92
+ availableLanguagesField(),
93
+ ],
94
+ })
95
+
96
+ export type PageFields = CollectionFieldData<typeof Pages>
@@ -0,0 +1,138 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Portions Copyright (c) Payload CMS, LLC info@payloadcms.com
5
+ * Copyright notices appear at the top of source files where applicable
6
+ * and are licensed under the MIT license found in the
7
+ * LICENSE file in the root directory of this source tree
8
+ *
9
+ * https://github.com/payloadcms/payload/
10
+ * From the Payload SEO plugin
11
+ * https://github.com/payloadcms/payload/tree/main/packages/plugin-seo
12
+ */
13
+
14
+ import type React from 'react'
15
+ import { Fragment, useEffect, useState } from 'react'
16
+
17
+ import { Pill } from './pill'
18
+
19
+ export const LengthIndicator: React.FC<{
20
+ maxLength?: number
21
+ minLength?: number
22
+ text?: string
23
+ }> = (props) => {
24
+ const { maxLength = 0, minLength = 0, text } = props
25
+
26
+ const [labelStyle, setLabelStyle] = useState({
27
+ backgroundColor: '',
28
+ color: '',
29
+ })
30
+
31
+ const [label, setLabel] = useState('')
32
+ const [barWidth, setBarWidth] = useState<number>(0)
33
+
34
+ useEffect(() => {
35
+ const textLength = text?.length ?? 0
36
+
37
+ if (textLength === 0) {
38
+ setLabel('Missing')
39
+ setLabelStyle({
40
+ backgroundColor: 'red',
41
+ color: 'white',
42
+ })
43
+ setBarWidth(0)
44
+ } else {
45
+ const progress = (textLength - minLength) / (maxLength - minLength)
46
+
47
+ if (progress < 0) {
48
+ const ratioUntilMin = textLength / minLength
49
+
50
+ if (ratioUntilMin > 0.9) {
51
+ setLabel('Almost there')
52
+ setLabelStyle({
53
+ backgroundColor: 'orange',
54
+ color: 'white',
55
+ })
56
+ } else {
57
+ setLabel('Too short')
58
+ setLabelStyle({
59
+ backgroundColor: 'orangered',
60
+ color: 'white',
61
+ })
62
+ }
63
+
64
+ setBarWidth(ratioUntilMin)
65
+ }
66
+
67
+ if (progress >= 0 && progress <= 1) {
68
+ setLabel('Good')
69
+ setLabelStyle({
70
+ backgroundColor: 'green',
71
+ color: 'white',
72
+ })
73
+ setBarWidth(progress)
74
+ }
75
+
76
+ if (progress > 1) {
77
+ setLabel('Too long')
78
+ setLabelStyle({
79
+ backgroundColor: 'red',
80
+ color: 'white',
81
+ })
82
+ setBarWidth(1)
83
+ }
84
+ }
85
+ }, [minLength, maxLength, text])
86
+
87
+ const textLength = text?.length ?? 0
88
+
89
+ const charsUntilMax = maxLength - textLength
90
+ const charsUntilMin = minLength - textLength
91
+
92
+ return (
93
+ <div
94
+ style={{
95
+ alignItems: 'center',
96
+ display: 'flex',
97
+ width: '100%',
98
+ }}
99
+ >
100
+ <Pill backgroundColor={labelStyle.backgroundColor} color={labelStyle.color} label={label} />
101
+ <div
102
+ style={{
103
+ flexShrink: 0,
104
+ lineHeight: 1,
105
+ marginRight: '10px',
106
+ whiteSpace: 'nowrap',
107
+ fontVariantNumeric: 'tabular-nums',
108
+ }}
109
+ >
110
+ <small>
111
+ {`${text?.length ?? 0}/${minLength}-${maxLength} chars, `}
112
+ {(textLength === 0 || charsUntilMin > 0) && `${charsUntilMin} to go`}
113
+ {charsUntilMin <= 0 && charsUntilMax >= 0 && `${charsUntilMax} left over`}
114
+ {charsUntilMax < 0 && <Fragment>{`${charsUntilMax * -1} too many`}</Fragment>}
115
+ </small>
116
+ </div>
117
+ <div
118
+ style={{
119
+ backgroundColor: '#F3F3F3',
120
+ height: '2px',
121
+ position: 'relative',
122
+ width: '100%',
123
+ }}
124
+ >
125
+ <div
126
+ style={{
127
+ backgroundColor: labelStyle.backgroundColor,
128
+ height: '100%',
129
+ left: 0,
130
+ position: 'absolute',
131
+ top: 0,
132
+ width: `${barWidth * 100}%`,
133
+ }}
134
+ />
135
+ </div>
136
+ </div>
137
+ )
138
+ }
@@ -0,0 +1,38 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Portions Copyright (c) Payload CMS, LLC info@payloadcms.com
5
+ * Copyright notices appear at the top of source files where applicable
6
+ * and are licensed under the MIT license found in the
7
+ * LICENSE file in the root directory of this source tree
8
+ *
9
+ * https://github.com/payloadcms/payload/
10
+ * From the Payload SEO plugin
11
+ * https://github.com/payloadcms/payload/tree/main/packages/plugin-seo
12
+ */
13
+ import type React from 'react'
14
+
15
+ export const Pill: React.FC<{
16
+ backgroundColor: string
17
+ color: string
18
+ label: string
19
+ }> = (props) => {
20
+ const { backgroundColor, color, label } = props
21
+
22
+ return (
23
+ <div
24
+ style={{
25
+ backgroundColor,
26
+ borderRadius: '2px',
27
+ color,
28
+ flexShrink: 0,
29
+ lineHeight: 1,
30
+ marginRight: '10px',
31
+ padding: '4px 6px',
32
+ whiteSpace: 'nowrap',
33
+ }}
34
+ >
35
+ <small>{label}</small>
36
+ </div>
37
+ )
38
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import type { FieldHelpTextSlotProps, SlotComponent } from '@byline/core'
10
+ import { useFieldValue } from '@byline/ui/react/forms'
11
+
12
+ import { LengthIndicator } from './length-indicator'
13
+
14
+ /**
15
+ * Custom HelpText slot component for summary-style textArea fields.
16
+ *
17
+ * Renders the field's `helpText` description alongside a live character-count
18
+ * indicator that updates reactively as the user types. Wire it up via the
19
+ * `components.HelpText` slot in the collection's `CollectionAdminConfig.fields`
20
+ * or directly on the field definition's `components` property.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * // In CollectionAdminConfig
25
+ * fields: { summary: { components: { HelpText: SummaryLength } } }
26
+ * ```
27
+ */
28
+ export const SummaryLength: SlotComponent<FieldHelpTextSlotProps> = ({ path, helpText }) => {
29
+ const value = useFieldValue<string>(path)
30
+
31
+ return (
32
+ <div className="mt-2 flex flex-col gap-1">
33
+ <LengthIndicator minLength={100} maxLength={300} text={value} />
34
+ {helpText && (
35
+ <p className="text-sm text-gray-500 dark:text-gray-400 leading-tight mb-1">{helpText}</p>
36
+ )}
37
+ </div>
38
+ )
39
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import type { GroupField } from '@byline/core'
10
+
11
+ import { contentLocales, type LocaleDefinition } from '../i18n.js'
12
+
13
+ type Options = Partial<Omit<GroupField, 'type' | 'fields'>> & {
14
+ locales?: readonly LocaleDefinition[]
15
+ }
16
+
17
+ type LocaleFields<T extends readonly LocaleDefinition[]> = {
18
+ [K in keyof T]: {
19
+ name: T[K]['code']
20
+ label: T[K]['label']
21
+ type: 'checkbox'
22
+ optional: true
23
+ }
24
+ }
25
+
26
+ type WithOverride<O, K extends string, V, D> = O extends { [P in K]: V } ? O[K] : D
27
+
28
+ type AvailableLanguagesField<Opts extends Options> = Omit<GroupField, 'name' | 'fields'> & {
29
+ name: WithOverride<Opts, 'name', string, 'availableLanguages'>
30
+ fields: LocaleFields<
31
+ WithOverride<Opts, 'locales', readonly LocaleDefinition[], typeof contentLocales>
32
+ >
33
+ }
34
+
35
+ const builtInValidate = (value: Record<string, boolean> | undefined): string | undefined => {
36
+ const hasSelection =
37
+ value != null &&
38
+ typeof value === 'object' &&
39
+ !Array.isArray(value) &&
40
+ Object.values(value).some(Boolean)
41
+ if (!hasSelection) {
42
+ return 'At least one language must be selected.'
43
+ }
44
+ return undefined
45
+ }
46
+
47
+ /**
48
+ * Returns a `GroupField` that renders one checkbox per locale entry.
49
+ * Validation requires at least one language to be selected; if the caller
50
+ * supplies its own `validate`, the built-in rule runs first and the caller's
51
+ * validator only runs when the built-in passes.
52
+ *
53
+ * @description This field is intended for use in a document's "Edit" view
54
+ * to allow editors to specify which languages a document is available in.
55
+ * It is orthogonal to the defined workflow system and is here as a 'signal'
56
+ * to frontend websites / consumers - allowing them to implement their own
57
+ * logic around content availability per language.
58
+ *
59
+ * @param options - Optional overrides. Accepts any `GroupField` property
60
+ * except `type` and `fields` (which are computed), plus a `locales` array
61
+ * that drives the generated checkbox set.
62
+ */
63
+ export function availableLanguagesField<const Opts extends Options>(
64
+ options: Opts = {} as Opts
65
+ ): AvailableLanguagesField<Opts> {
66
+ const { name, label, helpText, locales, validate: userValidate, ...rest } = options
67
+
68
+ const validate = userValidate
69
+ ? (value: any, data: Record<string, any>) => {
70
+ const builtInError = builtInValidate(value)
71
+ if (builtInError) return builtInError
72
+ return userValidate(value, data)
73
+ }
74
+ : builtInValidate
75
+
76
+ return {
77
+ ...rest,
78
+ name: (name ?? 'availableLanguages') as any,
79
+ label: label ?? 'Published Languages',
80
+ helpText: helpText ?? 'Select the languages this document is available in.',
81
+ type: 'group',
82
+ fields: (locales ?? contentLocales).map(({ code, label }) => ({
83
+ name: code,
84
+ label,
85
+ type: 'checkbox' as const,
86
+ optional: true,
87
+ })) as any,
88
+ validate,
89
+ }
90
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import type { RichTextField } from '@byline/core'
10
+ import { defaultEditorConfig, type EditorConfig } from '@byline/richtext-lexical'
11
+ import { cloneDeep } from 'lodash-es'
12
+
13
+ type Options = Partial<Omit<RichTextField, 'type' | 'editorConfig'>> & {
14
+ /**
15
+ * Optional callback to further customise the compact defaults. Receives a
16
+ * mutable copy of the compact config; mutate and return, or return a new
17
+ * object. Runs after the compact preset is applied, so callers can re-enable
18
+ * specific options for a particular field without re-listing the full set.
19
+ */
20
+ configure?: (config: EditorConfig) => EditorConfig
21
+ }
22
+
23
+ /**
24
+ * Compact preset — disables block-level features (tables, layouts,
25
+ * admonitions, code highlight, lists, embeds, inline images, alignment) and
26
+ * keeps a slim toolbar suitable for inline body copy like image captions,
27
+ * byline strap-lines, or compact form fields.
28
+ */
29
+ function applyCompactPreset(config: EditorConfig): EditorConfig {
30
+ const o = config.settings.options
31
+ o.textAlignment = false
32
+ o.tablePlugin = false
33
+ o.tableActionMenuPlugin = false
34
+ o.tableCellBackgroundColor = false
35
+ o.tableCellMerge = false
36
+ o.layoutPlugin = false
37
+ o.admonitionPlugin = false
38
+ o.codeHighlightPlugin = false
39
+ o.horizontalRulePlugin = false
40
+ o.listPlugin = false
41
+ o.checkListPlugin = false
42
+ o.inlineImagePlugin = false
43
+ o.autoEmbedPlugin = false
44
+ o.floatingTextFormatToolbarPlugin = false
45
+ o.textStyle = false
46
+ o.inlineCode = false
47
+ o.undoRedo = false
48
+ return config
49
+ }
50
+
51
+ /**
52
+ * Returns a `RichTextField` with a reduced Lexical feature set baked into
53
+ * `editorConfig`. Use this for caption-style or otherwise constrained rich-text
54
+ * fields where the full editor surface would be inappropriate.
55
+ *
56
+ * The compact preset disables tables, layouts, lists, code highlight, inline
57
+ * images, embeds, and most secondary toolbar features while keeping bold /
58
+ * italic / link editing and the floating link editor.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * fields: [
63
+ * lexicalRichTextCompact({ name: 'caption', label: 'Caption' }),
64
+ * // Compact + re-enable lists for a specific field:
65
+ * lexicalRichTextCompact({
66
+ * name: 'summary',
67
+ * label: 'Summary',
68
+ * configure: (c) => {
69
+ * c.settings.options.listPlugin = true
70
+ * return c
71
+ * },
72
+ * }),
73
+ * ]
74
+ * ```
75
+ */
76
+ export function lexicalRichTextCompact(options: Options = {}): RichTextField {
77
+ const { configure, ...rest } = options
78
+ const base = applyCompactPreset(cloneDeep(defaultEditorConfig))
79
+ const editorConfig = configure ? configure(base) : base
80
+
81
+ return {
82
+ name: 'richText',
83
+ label: 'RichText',
84
+ ...rest,
85
+ type: 'richText',
86
+ editorConfig,
87
+ }
88
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ /**
10
+ * Shared i18n locale configuration — no upstream dependencies, safe to import
11
+ * from both config entry-points and collection schema files without circular
12
+ * references.
13
+ *
14
+ * `interface` locales govern the CMS admin UI language.
15
+ * `content` locales govern the languages a document can be published in.
16
+ */
17
+
18
+ export interface LocaleDefinition {
19
+ code: string
20
+ label: string
21
+ }
22
+
23
+ /** Locales available in the CMS admin interface. */
24
+ export const interfaceLocales: LocaleDefinition[] = [
25
+ { code: 'en', label: 'English' },
26
+ { code: 'fr', label: 'Français' },
27
+ ]
28
+
29
+ /** Locales a document can be published in. */
30
+ export const contentLocales = [
31
+ { code: 'en', label: 'English' },
32
+ { code: 'fr', label: 'Français' },
33
+ { code: 'es', label: 'Español' },
34
+ { code: 'de', label: 'Deutsch' },
35
+ ] as const
36
+
37
+ /** Derived config object — passed directly to defineServerConfig / defineClientConfig. */
38
+ export const i18n = {
39
+ interface: {
40
+ defaultLocale: 'en',
41
+ locales: interfaceLocales.map((l) => l.code),
42
+ },
43
+ content: {
44
+ defaultLocale: 'en',
45
+ locales: contentLocales.map((l) => l.code),
46
+ },
47
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ /**
10
+ * URL segments for admin and (future) public API routes. Defaults of
11
+ * `/admin` and `/api` are applied automatically by `resolveRoutes()` —
12
+ * keys only need to be set here when overriding either default.
13
+ */
14
+
15
+ import type { RoutesConfig } from '@byline/core'
16
+
17
+ export const routes: Partial<RoutesConfig> = {
18
+ admin: '/admin',
19
+ api: '/api',
20
+ }
21
+
22
+ /**
23
+ * Fallback used by both server and admin entry points when no
24
+ * `VITE_SERVER_URL` env var is set. Each entry resolves the env var
25
+ * itself (Vite's `import.meta.env` on the client, Node's `process.env`
26
+ * on the server) and falls back to this literal.
27
+ */
28
+ export const DEFAULT_SERVER_URL = 'http://localhost:5173/'