keystone_ui-pages 0.1.0

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 (70) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/app/assets/builds/keystone_ui_pages/editor.js +67 -0
  4. data/app/controllers/concerns/keystone_ui/pages/edits_pages.rb +32 -0
  5. data/app/controllers/concerns/keystone_ui/pages/host_checks.rb +25 -0
  6. data/app/controllers/keystone_ui/pages/manage/base_controller.rb +28 -0
  7. data/app/controllers/keystone_ui/pages/manage/columns_controller.rb +15 -0
  8. data/app/controllers/keystone_ui/pages/manage/documents_controller.rb +15 -0
  9. data/app/controllers/keystone_ui/pages/manage/edit_history_controller.rb +21 -0
  10. data/app/controllers/keystone_ui/pages/manage/elements_controller.rb +30 -0
  11. data/app/controllers/keystone_ui/pages/manage/lives_controller.rb +20 -0
  12. data/app/controllers/keystone_ui/pages/manage/nodes_controller.rb +31 -0
  13. data/app/controllers/keystone_ui/pages/manage/pages_controller.rb +41 -0
  14. data/app/controllers/keystone_ui/pages/manage/pdfs_controller.rb +23 -0
  15. data/app/controllers/keystone_ui/pages/manage/rows_controller.rb +15 -0
  16. data/app/controllers/keystone_ui/pages/manage/samples_controller.rb +16 -0
  17. data/app/controllers/keystone_ui/pages/manage/sections_controller.rb +15 -0
  18. data/app/controllers/keystone_ui/pages/manage/versions_controller.rb +25 -0
  19. data/app/controllers/keystone_ui/pages/pages_controller.rb +26 -0
  20. data/app/helpers/keystone_ui/pages/drawing_helper.rb +115 -0
  21. data/app/helpers/keystone_ui/pages/editor_helper.rb +46 -0
  22. data/app/helpers/keystone_ui/pages/host_helper.rb +17 -0
  23. data/app/javascript/keystone_ui_pages/DeviceWidth.jsx +21 -0
  24. data/app/javascript/keystone_ui_pages/ElementFields.jsx +78 -0
  25. data/app/javascript/keystone_ui_pages/ElementPalette.jsx +62 -0
  26. data/app/javascript/keystone_ui_pages/LayoutPanel.jsx +41 -0
  27. data/app/javascript/keystone_ui_pages/ListField.jsx +49 -0
  28. data/app/javascript/keystone_ui_pages/LookChoice.jsx +17 -0
  29. data/app/javascript/keystone_ui_pages/PageEditor.jsx +260 -0
  30. data/app/javascript/keystone_ui_pages/SampleChoice.jsx +15 -0
  31. data/app/javascript/keystone_ui_pages/SelectionPath.jsx +19 -0
  32. data/app/javascript/keystone_ui_pages/SelectionToolbar.jsx +14 -0
  33. data/app/javascript/keystone_ui_pages/ShowWhen.jsx +32 -0
  34. data/app/javascript/keystone_ui_pages/ThemeMode.jsx +23 -0
  35. data/app/javascript/keystone_ui_pages/document.js +10 -0
  36. data/app/javascript/keystone_ui_pages/editor.jsx +8 -0
  37. data/app/javascript/keystone_ui_pages/operations.js +32 -0
  38. data/app/jobs/keystone_ui/pages/pdf_job.rb +11 -0
  39. data/app/models/keystone_ui/pages/page.rb +55 -0
  40. data/app/models/keystone_ui/pages/pdf.rb +12 -0
  41. data/app/models/keystone_ui/pages/version.rb +13 -0
  42. data/app/views/keystone_ui/pages/manage/pages/index.html.erb +35 -0
  43. data/app/views/keystone_ui/pages/manage/pages/new.html.erb +13 -0
  44. data/app/views/keystone_ui/pages/manage/pages/show.html.erb +8 -0
  45. data/app/views/keystone_ui/pages/manage/versions/index.html.erb +28 -0
  46. data/app/views/keystone_ui/pages/manage/versions/show.html.erb +11 -0
  47. data/app/views/keystone_ui/pages/pages/show.html.erb +3 -0
  48. data/app/views/keystone_ui/pages/pdfs/show.html.erb +12 -0
  49. data/config/routes.rb +31 -0
  50. data/db/migrate/20261014120000_create_keystone_ui_pages_pages.rb +17 -0
  51. data/db/migrate/20261014120001_create_keystone_ui_pages_versions.rb +18 -0
  52. data/db/migrate/20261015130000_create_keystone_ui_pages_pdfs.rb +14 -0
  53. data/db/migrate/20261016120000_add_edit_history_to_keystone_ui_pages_pages.rb +8 -0
  54. data/lib/keystone_ui/pages/catalog.rb +29 -0
  55. data/lib/keystone_ui/pages/document.rb +29 -0
  56. data/lib/keystone_ui/pages/engine.rb +25 -0
  57. data/lib/keystone_ui/pages/keystone_elements.rb +109 -0
  58. data/lib/keystone_ui/pages/operations.rb +246 -0
  59. data/lib/keystone_ui/pages/pdf_document.rb +17 -0
  60. data/lib/keystone_ui/pages/pdfs.rb +20 -0
  61. data/lib/keystone_ui/pages/printer.rb +43 -0
  62. data/lib/keystone_ui/pages/values.rb +28 -0
  63. data/lib/keystone_ui/pages/version.rb +7 -0
  64. data/lib/keystone_ui/pages.rb +45 -0
  65. data/lib/keystone_ui-pages.rb +3 -0
  66. data/the_local/agents/keystone_ui-pages-develop.md +277 -0
  67. data/the_local/agents/keystone_ui-pages-info.md +71 -0
  68. data/the_local/agents/keystone_ui-pages-install.md +167 -0
  69. data/the_local/interface.yml +56 -0
  70. metadata +169 -0
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "keystone_ui/pages/version"
4
+ require "keystone_ui/pages/catalog"
5
+ require "keystone_ui/pages/values"
6
+ require "keystone_ui/pages/keystone_elements"
7
+ require "keystone_ui/pages/engine"
8
+
9
+ module KeystoneUi
10
+ module Pages
11
+ class << self
12
+ attr_writer :base_controller, :layout, :editor_layout, :pdf_stylesheet
13
+ attr_accessor :edit_check_method, :view_check_method, :owner_method, :refusal_method, :chrome_path
14
+
15
+ def base_controller
16
+ @base_controller || "ActionController::Base"
17
+ end
18
+
19
+ def layout
20
+ @layout || "application"
21
+ end
22
+
23
+ def editor_layout
24
+ @editor_layout || "application"
25
+ end
26
+
27
+ def pdf_stylesheet
28
+ @pdf_stylesheet || "tailwind.css"
29
+ end
30
+ end
31
+
32
+ def self.pdf(page, values: {})
33
+ require "keystone_ui/pages/pdfs"
34
+ Pdfs.for(page, values: values)
35
+ end
36
+
37
+ def self.prepare_pdf(page, values: {})
38
+ PdfJob.perform_later(page, values.to_h.stringify_keys)
39
+ end
40
+
41
+ def self.catalog
42
+ @catalog ||= Catalog.new.tap { |catalog| KeystoneElements.register(catalog) }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "keystone_ui/pages"
@@ -0,0 +1,277 @@
1
+ ---
2
+ name: keystone_ui-pages-develop
3
+ description: Use PROACTIVELY for working with Keystone UI Pages in a host app — its settings, host values, drawing a page in the host's own screens, making PDFs, linking editors to the builder, and adding or leaving out catalog elements — MUST BE USED instead of hand-building page drawing or PDF printing.
4
+ tools: Read, Write, Edit, Grep
5
+ scope: Pages — building, publishing and printing pages drawn with Keystone UI
6
+ ---
7
+
8
+ This local works with Keystone UI Pages from the host app's side. It always
9
+ works the same way: set what the engine asks the host for, hand it values, and
10
+ draw or print the live version through the gem's own calls rather than reading
11
+ page documents directly.
12
+
13
+ ## What Keystone UI Pages is
14
+
15
+ Keystone UI Pages is a Rails engine that adds a page builder to a host app. A
16
+ page is a JSON document of sections, rows, columns and elements, each element
17
+ drawn by a Keystone UI component. Editors change a page's draft in the builder,
18
+ and Publish saves it as a numbered live version. The host draws the live
19
+ version in its own screens, prints it to PDF, and supplies values such as a
20
+ client's name that bound fields and conditions read.
21
+
22
+ Fire on any request to draw a page in a host screen, make or send a page as a
23
+ PDF, give editors a link to the builder, supply values to pages, add a
24
+ host-drawn element or remove one from the builder, or change who may view or
25
+ edit pages. If the engine is not mounted or its migrations have not run, that
26
+ is the `keystone_ui-pages-install` local's job, not this one's.
27
+
28
+ ## Interface
29
+
30
+ All settings are plain writers, set in `config/initializers/keystone_ui_pages.rb`.
31
+ Every method a setting names is called on the engine's controllers, which
32
+ inherit from the base controller, so define it there.
33
+
34
+ ### Access and layout settings
35
+
36
+ - `KeystoneUi::Pages.base_controller = "ApplicationController"` — a string
37
+ naming the class every engine controller inherits from. Defaults to
38
+ `"ActionController::Base"`. Its filters run on every engine request,
39
+ including published pages.
40
+ - `KeystoneUi::Pages.owner_method = :current_team` — a method with no
41
+ arguments returning the record that owns pages, such as a team or account.
42
+ Every lookup, in the editor's screens and on published pages, is then limited
43
+ to `KeystoneUi::Pages::Page.where(owner: <that record>)`, and a new page is
44
+ created with it as owner. Unset, every page is visible to everyone and new
45
+ pages have no owner.
46
+ - `KeystoneUi::Pages.view_check_method = :may_view_page?` — a method taking the
47
+ page and returning true when the current visitor may see the published page
48
+ at `/pages/:slug`. Unset, every published page is shown.
49
+ - `KeystoneUi::Pages.edit_check_method = :may_edit_pages?` — a method with no
50
+ arguments returning true when the current user may use the editor's screens:
51
+ the page list, the builder, View live, History and the PDF link. It runs
52
+ before every one of those requests. Unset, everyone may.
53
+ - `KeystoneUi::Pages.refusal_method = :refuse_page` — a method with no
54
+ arguments that renders the response when a request is refused: a failed view
55
+ or edit check, an unknown slug, or a page with no live version. Unset, the
56
+ response is an empty 404.
57
+ - `KeystoneUi::Pages.layout = "application"` — the host layout published pages
58
+ and View live are drawn in. Defaults to `"application"`.
59
+ - `KeystoneUi::Pages.editor_layout = "editor"` — the host layout the page
60
+ list, the builder and the version history are drawn in. Defaults to
61
+ `"application"`.
62
+
63
+ ### PDF settings
64
+
65
+ - `KeystoneUi::Pages.chrome_path = "/usr/bin/chromium"` — the Chrome or
66
+ Chromium program that prints PDFs. Unset, the gem uses the `CHROME_BIN`
67
+ environment variable, then the first of `/usr/bin/google-chrome`,
68
+ `/usr/bin/google-chrome-stable`, `/usr/bin/chromium`,
69
+ `/usr/bin/chromium-browser`, and the Google Chrome and Chromium apps in
70
+ `/Applications` that can be run.
71
+ - `KeystoneUi::Pages.pdf_stylesheet = "tailwind.css"` — the name of the host's
72
+ compiled stylesheet in Propshaft, written into every PDF. Defaults to
73
+ `"tailwind.css"`. A name Propshaft cannot find prints the PDF with no styles.
74
+
75
+ ### Host values
76
+
77
+ - `KeystoneUi::Pages::Values.names = { "client_name" => "Client name" }` — a
78
+ hash of the value names the host supplies, each to the label the builder
79
+ shows. When it is not empty, an editor can bind a text, long text, address,
80
+ number, list or data field to one, with fallback text drawn when the value is
81
+ blank, and can show a section, row or element only when a value equals some
82
+ text.
83
+ - `KeystoneUi::Pages::Values.chooser = ->(page) { [["Acme report", "acme"]] }`
84
+ — a callable taking the page and returning `[label, choice]` pairs. The
85
+ builder shows them in a "Preview with" choice, opening on the first pair, and
86
+ remembers the editor's choice per page for the session. Unset, the choice is
87
+ not shown.
88
+ - `KeystoneUi::Pages::Values.provider = ->(page, choice) { { "client_name" => "Acme" } }`
89
+ — a callable taking the page and a choice from the chooser and returning a
90
+ hash of value names to values. The builder's canvas, View live and the page
91
+ list's PDF link draw the page with what it returns for the chosen choice.
92
+ Unset, they draw with no values.
93
+
94
+ ### Drawing and printing
95
+
96
+ - `keystone_ui_page(page, values: {})` — a view helper, available in every host
97
+ view, that draws the page's live version with the values given, inside a
98
+ `<div class="@container">`. It returns nil when the page has never been
99
+ published. `values:` takes value names to values, with string or symbol keys.
100
+ - `KeystoneUi::Pages.pdf(page, values: {})` — returns a
101
+ `KeystoneUi::Pages::Pdf` record for the page's live version drawn with the
102
+ values given. Its `file` is an Active Storage attachment, ready to download,
103
+ link or email. The same live version and values return the record already
104
+ kept, and a newly published version or different values print a new one.
105
+ When nothing is kept it prints in the current request.
106
+ - `KeystoneUi::Pages.prepare_pdf(page, values: {})` — queues
107
+ `KeystoneUi::Pages::PdfJob`, which calls `KeystoneUi::Pages.pdf` with the same
108
+ page and values, so the file is kept before anyone asks for it.
109
+
110
+ ### The builder and the editor's screens
111
+
112
+ - `/manage/pages` under the engine's mount path — the editor's page list, which
113
+ at the `/pages` mount is `/pages/manage/pages`, or
114
+ `keystone_ui_pages.manage_pages_path` from the host.
115
+ - `/pages/manage/pages/:id?return_to=/admin/marketing` — the builder for one
116
+ page, or `keystone_ui_pages.manage_page_path(page, return_to: "/admin/marketing")`.
117
+ `return_to` must be a path on the host's own site, starting with a single
118
+ `/`. The builder's Back link returns there, even after Publish reloads the
119
+ builder. With no `return_to`, or one pointing at another site, Back goes to
120
+ the page list.
121
+
122
+ ### The element catalog
123
+
124
+ - `KeystoneUi::Pages.catalog.register(key:, name:, component:, fields:, body: nil, icon: nil, group: nil, align: nil)`
125
+ — adds an element to the builder's palette, or replaces the one with the same
126
+ key. `key:` is a symbol stored in page documents. `name:` is the label in the
127
+ palette. `component:` is the name of a method the drawing calls with one
128
+ keyword argument per field, such as a `helper_method` on the base controller.
129
+ `body:` names one field whose value is passed as the method's block instead
130
+ of a keyword. `icon:` is one of `:cursor` `:tag` `:copy` `:heading` `:text`
131
+ `:list` `:info` `:code` `:alert` `:card` `:megaphone` `:chart` `:bars`
132
+ `:number` `:progress`, and any other value shows a box. `group:` is the
133
+ palette heading it sits under, `"Other"` when unset. `align:` is the default
134
+ placement in its column: `"stretch"`, `"start"`, `"center"` or `"end"`.
135
+ - `KeystoneUi::Pages.catalog.leave_out(:copy_button)` — removes elements from
136
+ the palette by key. Takes several keys.
137
+
138
+ Each entry in `fields:` is a hash. Its `key:` is the keyword the component
139
+ receives and its `label:` is the label in the builder's field panel. Its
140
+ `kind:` is one of `:text` (the default when no kind is given), `:long_text`,
141
+ `:address`, `:number`, `:one_of`, `:yes_or_no`, `:list` or `:data`. A number
142
+ arrives as an Integer, or a Float when it has a decimal point, a `:one_of`
143
+ arrives as a symbol, and a `:yes_or_no` as true or false. Its `default:` is the
144
+ value used until the editor sets one. A `:one_of` field takes `options:`, the
145
+ list of choices as strings. A `:list` field takes `entry:`, an array of field
146
+ hashes describing each entry's parts, and each entry arrives as a hash with
147
+ symbol keys.
148
+
149
+ A `:data` field cannot be typed in the builder. It holds data the host
150
+ supplies through a bound value.
151
+
152
+ The catalog offers these Keystone UI components by default: `button`,
153
+ `progress`, `alert`, `code`, `heading`, `label`, `paragraph`, `hero`,
154
+ `cta_banner`, `card`, `stat_card`, `figure`, `info`, `badge`, `copy_button`,
155
+ `feature_grid`, `accordion`, `data_table`, `bar_chart`, `line_chart` and
156
+ `value_list`.
157
+
158
+ ## How to use it
159
+
160
+ 1. Confirm the engine is mounted and its migrations have run: `config/routes.rb`
161
+ mounts `KeystoneUi::Pages::Engine` and `db/schema.rb` has
162
+ `keystone_ui_pages_pages`. If not, stop and hand off to
163
+ `keystone_ui-pages-install`.
164
+
165
+ 2. Read `config/initializers/keystone_ui_pages.rb` before changing a setting.
166
+ Every setting from the Interface is set there, and the methods they name are
167
+ on the controller named by `base_controller`.
168
+
169
+ 3. To change who may see or edit pages, set `owner_method`,
170
+ `view_check_method`, `edit_check_method` or `refusal_method`, and write the
171
+ method on the base controller. Who may see and change what is the
172
+ developer's decision. Ask before choosing a rule.
173
+
174
+ 4. To let pages use the host's data, set `Values.names` to the values the host
175
+ can supply. To let editors preview a page with real data, also set
176
+ `Values.chooser` and `Values.provider`. Ask the developer which records the
177
+ chooser lists, since it decides what editors see in the builder.
178
+
179
+ 5. To draw a page in a host screen with one client's values, find the page and
180
+ call the helper:
181
+
182
+ ```erb
183
+ <% page = KeystoneUi::Pages::Page.find_by(slug: "report", owner: current_team) %>
184
+ <%= keystone_ui_page(page, values: { "client_name" => @client.name, "score" => @client.score }) %>
185
+ ```
186
+
187
+ The drawing is kept in `Rails.cache` under the version and the values, so
188
+ the same version and values are drawn once. A page that uses host values
189
+ should be drawn this way, since the published address `/pages/:slug` draws
190
+ it with no values: bound fields show their fallback text there, and
191
+ conditions on a value hide what they guard unless they compare against blank.
192
+
193
+ 6. To make a PDF:
194
+
195
+ ```ruby
196
+ pdf = KeystoneUi::Pages.pdf(page, values: { "client_name" => "Acme" })
197
+ pdf.file
198
+ ```
199
+
200
+ To have it ready before anyone downloads it, such as when an invoice is
201
+ created, call `KeystoneUi::Pages.prepare_pdf(page, values: ...)` instead and
202
+ read `KeystoneUi::Pages.pdf` later with the same values. Publishing a page
203
+ that uses no host values queues its PDF straight away, since it looks the
204
+ same for everyone.
205
+
206
+ The page must be published, since only the live version is printed. The PDF
207
+ is the page drawn in light mode with the stylesheet named by
208
+ `pdf_stylesheet`, printed by Chrome or Chromium with no header or footer.
209
+ Printing keeps chart and cell colours on a white background and does not
210
+ split a section, a table row or a label and value row across pages.
211
+
212
+ When no Chrome or Chromium is found, the page list leaves out its PDF links
213
+ and `KeystoneUi::Pages.pdf` raises `KeystoneUi::Pages::PrintFailed` with a
214
+ message saying to install one or name it with `chrome_path` or `CHROME_BIN`.
215
+ It raises the same error when Chrome cannot be started or prints nothing.
216
+ Called from `prepare_pdf`, the error is raised inside the job, so the job
217
+ backend's retry rules apply. Ask the developer how a failed print should
218
+ reach the user before rescuing it.
219
+
220
+ 7. To give editors a way in, link to the page list, or to one page's builder
221
+ with a `return_to` of the screen they came from:
222
+
223
+ ```erb
224
+ <%= link_to "Edit page", keystone_ui_pages.manage_page_path(page, return_to: request.fullpath) %>
225
+ ```
226
+
227
+ 8. To add an element the host draws, write its component as a method that takes
228
+ the fields as keyword arguments, make it callable from views, and register
229
+ it:
230
+
231
+ ```ruby
232
+ Rails.application.config.after_initialize do
233
+ KeystoneUi::Pages.catalog.register(key: :testimonial, name: "Testimonial", component: :testimonial,
234
+ fields: [ { key: :quote, label: "Quote", kind: :long_text, default: "It works." } ])
235
+ KeystoneUi::Pages.catalog.leave_out(:copy_button)
236
+ end
237
+ ```
238
+
239
+ The method must be reachable in the engine's views and in any host view that
240
+ calls `keystone_ui_page`. A `helper_method` on the base controller covers
241
+ both. Leaving out an element that pages already use makes those pages fail
242
+ to draw, so search the pages' documents for its key before leaving it out.
243
+
244
+ ## Conventions
245
+
246
+ - **Draw through the gem.** Use `keystone_ui_page` and `KeystoneUi::Pages.pdf`.
247
+ Never draw `page.document` or a version's document by hand, and never print a
248
+ PDF another way, because the gem's drawing applies bound values, conditions,
249
+ layout and print rules together.
250
+ - **Only the live version leaves the builder.** The draft is what editors
251
+ change. Visitors, `keystone_ui_page` and PDFs show the live version, so an
252
+ unpublished change shows nowhere else.
253
+ - **Value names are strings.** `Values.names`, the provider's hash and the
254
+ `values:` passed to the helper and to PDFs all use the same names. The gem
255
+ turns symbol keys into strings, so either works, but the names must match.
256
+ - **The editor's page list** at `/pages/manage/pages` shows each page's name,
257
+ whether it is published or a draft, and when it was last published. Each row
258
+ links to Edit, and a published page also links to View live and, when Chrome
259
+ or Chromium is found, to its PDF.
260
+ - **View live** draws the live version in `KeystoneUi::Pages.layout` with the
261
+ values of the sample chosen in the builder, behind the edit check. It is
262
+ linked from the page list and from the builder's top bar once the page is
263
+ published.
264
+ - **History** lists every published version with its number, status and date.
265
+ Opening one draws it, and "Restore this version as the draft" replaces the
266
+ draft with that version's document. Restoring publishes nothing.
267
+ - **Undo and Redo** are the builder's ↶ Undo and ↷ Redo buttons. They step
268
+ through the page's edits one at a time. The history is kept on the page, so
269
+ it survives a reload and a publish, and a new edit after an undo clears what
270
+ can be redone.
271
+ - **The builder's top bar** also has a Mode choice of light, dark and system
272
+ (and custom for an editor in custom mode), a Look choice of the looks the host
273
+ registers with Keystone UI, and a Print choice that previews the printed
274
+ layout. None of them is saved.
275
+ - Out of scope for this local: installing the gem, its migrations, the mount and
276
+ the layouts, which belong to `keystone_ui-pages-install`. Changing the
277
+ builder or the built-in elements belongs in the gem, not the host app.
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: keystone_ui-pages-info
3
+ description: Use to learn what Keystone UI Pages offers — the page builder, published versions, host values, PDFs and the element catalog — and the vocabulary the install and develop locals assume.
4
+ tools: Read
5
+ scope: Pages — building, publishing and printing pages drawn with Keystone UI
6
+ ---
7
+
8
+ This local explains Keystone UI Pages and sends you to the local that does the
9
+ work. It changes nothing and gives no steps.
10
+
11
+ ## What Keystone UI Pages is
12
+
13
+ Keystone UI Pages is a Rails engine gem that adds a page builder to a host app.
14
+ A page is a JSON document of sections, rows, columns and elements, and every
15
+ element is drawn by a Keystone UI component, so a page looks like the rest of an
16
+ app built with Keystone UI. Editors change the draft in a builder in the
17
+ browser, and publishing it saves a numbered version. The live version is what
18
+ visitors, the host's own screens and PDFs show.
19
+
20
+ Reach for it when an app needs pages that people other than developers lay out
21
+ and change: marketing pages, report templates, invoices. A page can hold values
22
+ the host app supplies, such as a client's name, so one page serves as a template
23
+ drawn with each client's values, on screen or as a PDF.
24
+
25
+ ## Interface
26
+
27
+ This local declares no commands. The two working surfaces belong elsewhere:
28
+
29
+ - **Getting the gem into a host app** — adding it, running the engine's and
30
+ Active Storage's migrations, mounting the engine, the layouts and the settings
31
+ file → **`keystone_ui-pages-install`**.
32
+ - **Working with it** — what each setting does, host values, drawing a page in
33
+ the host's own screens, PDFs, linking to the builder and changing the element
34
+ catalog → **`keystone_ui-pages-develop`**.
35
+
36
+ ## How to use it
37
+
38
+ One decision: is Keystone UI Pages already set up in the app?
39
+
40
+ - No, or the setup is out of date → **`keystone_ui-pages-install`**.
41
+ - Yes, and there is a page to draw, a PDF to make, a setting to change or an
42
+ element to add → **`keystone_ui-pages-develop`**.
43
+
44
+ Keystone UI itself must be installed first. Its own install local,
45
+ `keystone_ui-install`, does that.
46
+
47
+ ## Conventions
48
+
49
+ - **Draft and versions.** A page's document is its draft. Publish copies the
50
+ draft into a new version marked live and marks the previous live version
51
+ superseded. Only the live version is ever shown outside the builder. A page
52
+ with no live version shows nothing.
53
+ - **Pages belong to an owner.** A page has a name, a slug that is its address,
54
+ and an optional owner, which is any record. When the host names an owner
55
+ method, every lookup is limited to the current owner's pages, and a slug is
56
+ unique per owner.
57
+ - **Host values.** The host names the values a page can use, such as a client's
58
+ name or a score. An editor binds an element's field to one, with fallback text
59
+ for when it is blank, or shows a section, row or element only when a value
60
+ equals something. The host supplies the values when it draws the page.
61
+ - **The catalog.** The builder offers a fixed list of Keystone UI components as
62
+ elements. A host adds elements drawn by its own components and leaves out ones
63
+ it does not want. Navigation, form inputs, page frames and components whose
64
+ data the catalog cannot describe are not offered.
65
+ - **PDFs are printed by Chrome or Chromium on the server** and kept in Active
66
+ Storage, one per live version and set of values.
67
+ - **The editor's screens.** The page list, the builder, the version history and
68
+ View live all sit under the engine's `manage` routes and are open to whoever
69
+ passes the host's edit check.
70
+ - Ruby >= 3.2. Rails >= 7.1. It depends on keystone_ui >= 0.49.0,
71
+ keystone_ui-styles >= 0.24.0 and keystone_ui-react >= 0.1.3.
@@ -0,0 +1,167 @@
1
+ ---
2
+ name: keystone_ui-pages-install
3
+ description: Use to hook Keystone UI Pages into a project — adding the gem, installing and running the engine's migrations and Active Storage's, mounting the engine, preparing the live-page and editor layouts, and writing the settings file.
4
+ tools: Bash, Read, Edit
5
+ scope: Pages — building, publishing and printing pages drawn with Keystone UI
6
+ ---
7
+
8
+ This local carries the steps for wiring Keystone UI Pages into a host app.
9
+ Follow them in order, exactly as written, and invent none.
10
+
11
+ ## What Keystone UI Pages is
12
+
13
+ A Rails engine gem that adds a page builder whose pages are drawn with Keystone
14
+ UI components, published as versions and printed to PDF; hook it in before
15
+ linking editors to the builder or drawing a page in the host's screens.
16
+
17
+ ## Interface
18
+
19
+ - `bin/rails keystone_ui_pages:install:migrations` — copies the engine's
20
+ migrations into the host's `db/migrate`. They create the pages, versions and
21
+ PDFs tables and add the edit history columns that Undo and Redo use. Safe to
22
+ re-run, since a migration already copied is skipped.
23
+ - `bin/rails active_storage:install` — Rails' own task that copies Active
24
+ Storage's migration. PDFs are kept as Active Storage attachments.
25
+ - `mount KeystoneUi::Pages::Engine => "/pages"` — the line in
26
+ `config/routes.rb` that serves the builder, the editor's screens and the live
27
+ pages under the path given.
28
+
29
+ ## How to use it
30
+
31
+ 1. Confirm the prerequisites:
32
+
33
+ - Ruby >= 3.2 and Rails >= 7.1.
34
+ - Keystone UI installed and working. The gem brings keystone_ui,
35
+ keystone_ui-styles and keystone_ui-react with it, but Keystone UI's own
36
+ setup is the `keystone_ui-install` local's job. Do it first if it is
37
+ missing.
38
+ - Propshaft serving the host's stylesheet, since PDFs read the compiled
39
+ stylesheet from it.
40
+ - An Active Job backend. Publishing a page that uses no host values makes its
41
+ PDF in a job, and so does `KeystoneUi::Pages.prepare_pdf`.
42
+
43
+ 2. Add the gem to the Gemfile and install it:
44
+
45
+ ```ruby
46
+ gem "keystone_ui-pages"
47
+ ```
48
+
49
+ ```bash
50
+ bundle install
51
+ ```
52
+
53
+ If the project sources its own gems from somewhere other than RubyGems (a git
54
+ or path reference), ask the developer which to use rather than choosing.
55
+
56
+ 3. Install the migrations and run them:
57
+
58
+ ```bash
59
+ bin/rails keystone_ui_pages:install:migrations
60
+ bin/rails active_storage:install
61
+ bin/rails db:migrate
62
+ ```
63
+
64
+ Skip `active_storage:install` when the app already has the
65
+ `active_storage_blobs` table. Active Storage also needs a service set in each
66
+ environment's `config.active_storage.service` and defined in
67
+ `config/storage.yml`. If the app has none, ask the developer which service
68
+ production uses before choosing one.
69
+
70
+ The engine's migrations create `keystone_ui_pages_pages`,
71
+ `keystone_ui_pages_versions` and `keystone_ui_pages_pdfs`, and add
72
+ `undo_history` and `undone_changes` to the pages table. Without those two
73
+ columns, every edit in the builder fails, since each edit is recorded for
74
+ Undo and Redo.
75
+
76
+ 4. Mount the engine in `config/routes.rb`:
77
+
78
+ ```ruby
79
+ mount KeystoneUi::Pages::Engine => "/pages"
80
+ ```
81
+
82
+ Ask the developer which path to use; `/pages` is the README's choice and the
83
+ one the other locals assume. With it:
84
+
85
+ - `/pages/manage/pages` is the editor's page list.
86
+ - `/pages/manage/pages/new` creates a page.
87
+ - `/pages/manage/pages/:id` is the builder for one page.
88
+ - `/pages/:slug` is a published page, drawn for visitors.
89
+
90
+ Inside the host app, the engine's route helpers are reached through
91
+ `keystone_ui_pages`, such as `keystone_ui_pages.manage_pages_path`.
92
+
93
+ 5. Prepare the two layouts. Live pages are drawn in the layout named by
94
+ `KeystoneUi::Pages.layout`, and the page list, the builder and the version
95
+ history in the one named by `KeystoneUi::Pages.editor_layout`. Both default
96
+ to `application`. Each layout used must:
97
+
98
+ - add `<%= keystone_theme_attributes %>` inside its `<html` tag, so the page
99
+ follows the viewer's light, dark or custom mode and look;
100
+ - link the host's stylesheet, which carries keystone_ui-styles, the host's
101
+ looks and colours, and its own styles;
102
+ - render `<%= yield :head %>` inside `<head>`, because the builder puts its
103
+ two script tags, `keystone_ui/react` and `keystone_ui_pages/editor`, there.
104
+
105
+ The builder fills the whole window, so the host's own navigation in the
106
+ editor layout sits under it. The gem's test app uses a separate `editor`
107
+ layout holding only the head and the page's content. Ask the developer
108
+ whether to make one or use `application` for both.
109
+
110
+ 6. Write `config/initializers/keystone_ui_pages.rb`. Each setting is described
111
+ in `keystone_ui-pages-develop`. Ask the developer about each rather than
112
+ copying this whole block:
113
+
114
+ ```ruby
115
+ require "keystone_ui/pages"
116
+
117
+ KeystoneUi::Pages.base_controller = "ApplicationController"
118
+ KeystoneUi::Pages.owner_method = :current_team
119
+ KeystoneUi::Pages.view_check_method = :may_view_page?
120
+ KeystoneUi::Pages.edit_check_method = :may_edit_pages?
121
+ KeystoneUi::Pages.refusal_method = :refuse_page
122
+ KeystoneUi::Pages.layout = "application"
123
+ KeystoneUi::Pages.editor_layout = "editor"
124
+ ```
125
+
126
+ - `base_controller` is a string, read when the engine's controllers load, so
127
+ it belongs in this file. Set it to the controller that defines the methods
128
+ the other settings name, normally `ApplicationController`. Its filters,
129
+ such as a sign-in requirement, then run on every engine request, including
130
+ published pages.
131
+ - Without `edit_check_method`, anyone who can reach `/pages/manage` can
132
+ create, edit and publish pages. Without `view_check_method`, every
133
+ published page is public. Without `owner_method`, all pages are shared
134
+ across the whole app. Each of these is a decision about who may see and
135
+ change what, so put each to the developer.
136
+ - Host values, catalog changes, `chrome_path` and `pdf_stylesheet` go in the
137
+ same file once the developer wants them. The develop local carries them.
138
+
139
+ 7. Install Chrome or Chromium wherever the app makes PDFs, including the
140
+ production server or image. The gem looks for `KeystoneUi::Pages.chrome_path`,
141
+ then the `CHROME_BIN` environment variable, then these places:
142
+ `/usr/bin/google-chrome`, `/usr/bin/google-chrome-stable`,
143
+ `/usr/bin/chromium`, `/usr/bin/chromium-browser`, and the Google Chrome and
144
+ Chromium apps in `/Applications` on macOS. Ask the developer how the
145
+ deployment installs it. Without one, everything except PDFs still works.
146
+
147
+ 8. Check the install. Boot the app, open `/pages/manage/pages`, create a page,
148
+ add an element in the builder, publish it, and open `/pages/<slug>`. The page
149
+ list shows a PDF link only when Chrome or Chromium was found.
150
+
151
+ ## Conventions
152
+
153
+ - **Re-run the migration install after upgrading the gem.** A later version can
154
+ add migrations, as the edit history one was added, and they reach the host
155
+ only through `bin/rails keystone_ui_pages:install:migrations` followed by
156
+ `bin/rails db:migrate`.
157
+ - **The engine registers itself with Keystone UI's Tailwind build.** On boot it
158
+ appends its views, helpers, JavaScript and Ruby files to Keystone UI's
159
+ Tailwind sources, so the host adds nothing to its Tailwind setup for the
160
+ engine.
161
+ - **The engine adds `keystone_ui_page` to every host view.** No helper include
162
+ is needed in the host.
163
+ - **The methods the settings name live on the base controller.** The engine
164
+ calls them on its own controllers, which inherit from it, so a method defined
165
+ anywhere else is not found.
166
+ - Drawing pages, making PDFs, host values and the catalog are out of scope here
167
+ and belong to `keystone_ui-pages-develop`.
@@ -0,0 +1,56 @@
1
+ scope: Pages — building, publishing and printing pages drawn with Keystone UI
2
+
3
+ install:
4
+ - bin/rails keystone_ui_pages:install:migrations
5
+ - bin/rails active_storage:install
6
+ - mount KeystoneUi::Pages::Engine
7
+
8
+ develop:
9
+ - KeystoneUi::Pages.base_controller
10
+ - KeystoneUi::Pages.owner_method
11
+ - KeystoneUi::Pages.view_check_method
12
+ - KeystoneUi::Pages.edit_check_method
13
+ - KeystoneUi::Pages.refusal_method
14
+ - KeystoneUi::Pages.layout
15
+ - KeystoneUi::Pages.editor_layout
16
+ - KeystoneUi::Pages.chrome_path
17
+ - KeystoneUi::Pages.pdf_stylesheet
18
+ - KeystoneUi::Pages::Values.names
19
+ - KeystoneUi::Pages::Values.chooser
20
+ - KeystoneUi::Pages::Values.provider
21
+ - keystone_ui_page
22
+ - KeystoneUi::Pages.pdf
23
+ - KeystoneUi::Pages.prepare_pdf
24
+ - KeystoneUi::Pages.catalog.register
25
+ - KeystoneUi::Pages.catalog.leave_out
26
+ - /manage/pages
27
+
28
+ sources:
29
+ - README.md
30
+ - config/routes.rb
31
+ - lib/keystone_ui/pages.rb
32
+ - lib/keystone_ui/pages/engine.rb
33
+ - lib/keystone_ui/pages/values.rb
34
+ - lib/keystone_ui/pages/catalog.rb
35
+ - lib/keystone_ui/pages/keystone_elements.rb
36
+ - lib/keystone_ui/pages/pdfs.rb
37
+ - lib/keystone_ui/pages/pdf_document.rb
38
+ - lib/keystone_ui/pages/printer.rb
39
+ - app/helpers/keystone_ui/pages/host_helper.rb
40
+ - app/helpers/keystone_ui/pages/drawing_helper.rb
41
+ - app/helpers/keystone_ui/pages/editor_helper.rb
42
+ - app/controllers/concerns/keystone_ui/pages/host_checks.rb
43
+ - app/controllers/keystone_ui/pages/pages_controller.rb
44
+ - app/controllers/keystone_ui/pages/manage/base_controller.rb
45
+ - app/controllers/keystone_ui/pages/manage/pages_controller.rb
46
+ - app/controllers/keystone_ui/pages/manage/lives_controller.rb
47
+ - app/controllers/keystone_ui/pages/manage/pdfs_controller.rb
48
+ - app/controllers/keystone_ui/pages/manage/versions_controller.rb
49
+ - app/controllers/keystone_ui/pages/manage/edit_history_controller.rb
50
+ - app/models/keystone_ui/pages/page.rb
51
+ - app/models/keystone_ui/pages/pdf.rb
52
+ - app/jobs/keystone_ui/pages/pdf_job.rb
53
+ - db/migrate/20261014120000_create_keystone_ui_pages_pages.rb
54
+ - db/migrate/20261014120001_create_keystone_ui_pages_versions.rb
55
+ - db/migrate/20261015130000_create_keystone_ui_pages_pdfs.rb
56
+ - db/migrate/20261016120000_add_edit_history_to_keystone_ui_pages_pages.rb