trek 0.1.20 โ†’ 0.1.22

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.npmignore +3 -0
  3. data/Gemfile.lock +86 -82
  4. data/README.md +8 -0
  5. data/app/assets/stylesheets/trek/_colors.css +181 -0
  6. data/app/components/trek/button_component.rb +9 -3
  7. data/app/components/trek/dashboard/tile_component/tile_component.html.slim +1 -1
  8. data/app/components/trek/dashboard/tile_component.rb +2 -0
  9. data/app/components/trek/form/actions_component/actions_component.css +6 -0
  10. data/app/components/trek/form/image_field_component/image_field_component.css +8 -0
  11. data/app/components/trek/form/image_field_component/image_field_component.html.slim +20 -8
  12. data/app/components/trek/form/image_field_component/image_field_component.js +23 -1
  13. data/app/components/trek/form/image_field_component.rb +13 -0
  14. data/app/components/trek/form/switch_box_component.rb +1 -0
  15. data/app/components/trek/tabs_component/tabs_component.css +1 -0
  16. data/app/controllers/concerns/trek/model.rb +4 -1
  17. data/config/locales/trek.en.yml +3 -0
  18. data/config/locales/trek.fr.yml +1 -0
  19. data/docs/.vitepress/config.mjs +133 -0
  20. data/docs/.vitepress/theme/index.js +17 -0
  21. data/docs/.vitepress/theme/style.css +139 -0
  22. data/docs/customize.md +63 -7
  23. data/docs/getting-started.md +160 -0
  24. data/docs/index.md +39 -0
  25. data/docs/public/logo.svg +1 -0
  26. data/docs/reference/components.md +40 -0
  27. data/docs/reference/concerns/contentable.md +28 -0
  28. data/docs/reference/concerns/formattable.md +24 -0
  29. data/docs/reference/concerns/fragmentable.md +33 -0
  30. data/docs/reference/concerns/keyable.md +18 -0
  31. data/docs/reference/concerns/orderable.md +26 -0
  32. data/docs/reference/concerns/pageable.md +35 -0
  33. data/docs/reference/concerns/pathable.md +28 -0
  34. data/docs/reference/concerns/phonable.md +28 -0
  35. data/docs/reference/concerns/search-engine-optimizable.md +19 -0
  36. data/docs/reference/concerns/searchable.md +22 -0
  37. data/docs/reference/concerns/sectionable.md +24 -0
  38. data/docs/reference/concerns/sluggable.md +21 -0
  39. data/docs/reference/concerns/taggable.md +20 -0
  40. data/docs/reference/concerns/translatable.md +35 -0
  41. data/docs/reference/concerns/versionable.md +27 -0
  42. data/docs/reference/controllers.md +23 -0
  43. data/docs/reference/environment-variables.md +9 -0
  44. data/docs/reference/formatters.md +30 -0
  45. data/docs/reference/forms.md +21 -0
  46. data/docs/reference/generators/admin-user.md +17 -0
  47. data/docs/reference/generators/install.md +42 -0
  48. data/docs/reference/generators/scaffold.md +37 -0
  49. data/docs/reference/generators/taxonomies.md +21 -0
  50. data/docs/reference/icons.md +11 -0
  51. data/docs/reference/index.md +47 -0
  52. data/docs/reference/models/current.md +11 -0
  53. data/docs/reference/models/external-link.md +16 -0
  54. data/docs/reference/models/fragment.md +28 -0
  55. data/docs/reference/models/menu-node.md +30 -0
  56. data/docs/reference/models/page-path.md +19 -0
  57. data/docs/reference/models/page-version.md +30 -0
  58. data/docs/reference/models/page.md +48 -0
  59. data/docs/reference/models/tag.md +41 -0
  60. data/docs/reference/models/user.md +65 -0
  61. data/docs/reference/policies.md +30 -0
  62. data/docs/reference/uploaders.md +19 -0
  63. data/docs/what-is-trek.md +37 -0
  64. data/esbuild.config.js +10 -7
  65. data/lib/generators/trek/templates/initializers/shrine.rb +1 -0
  66. data/lib/trek/version.rb +1 -1
  67. data/netlify.toml +4 -0
  68. data/package.json +10 -5
  69. data/renovate.json +4 -0
  70. data/yarn.lock +2015 -235
  71. metadata +48 -2
data/docs/index.md ADDED
@@ -0,0 +1,39 @@
1
+ ---
2
+ # https://vitepress.dev/reference/default-theme-home-page
3
+ layout: home
4
+
5
+ hero:
6
+ name: "Trek"
7
+ text: "Take the easy path"
8
+ tagline: "A modern content management & back-office system for Ruby on Rails"
9
+ image:
10
+ src: /logo.svg
11
+ alt: Trek
12
+ actions:
13
+ - theme: brand
14
+ text: What's Trek?
15
+ link: /what-is-trek
16
+ - theme: alt
17
+ text: Quick start
18
+ link: /getting-started
19
+
20
+ features:
21
+ - icon: ๐Ÿš€
22
+ title: Ready out of the box
23
+ details: One installer sets up authentication, authorization, file uploads, emails, i18n and a polished admin UI โ€” with sensible conventions you can override later.
24
+ - icon: ๐Ÿ“
25
+ title: Modern content editing
26
+ details: A Tiptap-based rich text editor, hierarchical pages with versioning and SEO-friendly paths, plus reusable content fragments.
27
+ - icon: ๐Ÿ› 
28
+ title: Scaffold-driven development
29
+ details: One command generates the model, admin panel, views, policy, routes and locales for any resource โ€” just like Rails scaffolding, but for your back-office.
30
+ - icon: ๐ŸŒ
31
+ title: Multilingual by default
32
+ details: Content and admin UI translations powered by Mobility and i18n-tasks, with optional automatic translation via DeepL.
33
+ - icon: ๐Ÿ”
34
+ title: Roles & policies built in
35
+ details: Sorcery authentication and ActionPolicy authorization with admin, editor, user, reader and guest roles, enforced across every panel.
36
+ - icon: ๐Ÿงฉ
37
+ title: Hotwire-native UI
38
+ details: ViewComponents, Stimulus and Turbo under the hood, with light & dark themes, Radix colors and a customizable design system.
39
+ ---
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" x="0" y="0" style="enable-background:new 0 0 512 512" version="1.1" viewBox="0 0 512 512"><path d="M0 0h512v512H0z" style="fill:#6ae5ac"/><path d="M201.9 93.5v91h-91c0 50.2 40.7 91 91 91v52c0 50.2 40.7 91 91 91v-325h-91zM292.9 184.5c0 50.2 40.7 91 91 91v-91h-91z" style="fill:#2d2d2d"/><path d="M292.9 418.5v-91h91c-.1 50.2-40.8 91-91 91z" style="fill:#fff"/></svg>
@@ -0,0 +1,40 @@
1
+ # Components
2
+
3
+ Trek's admin UI is built with [ViewComponent](https://viewcomponent.org), [Stimulus](https://stimulus.hotwired.dev) and [Turbo](https://turbo.hotwired.dev), styled with PostCSS and [Radix colors](https://www.radix-ui.com/colors), with light & dark themes.
4
+
5
+ ## UI components
6
+
7
+ The main building blocks, all in the `Trek::` namespace:
8
+
9
+ | Component | Purpose |
10
+ | --- | --- |
11
+ | `LayoutComponent` | admin layout, with header, menu and panel slots |
12
+ | `HeaderComponent` | top navigation bar |
13
+ | `MenuComponent` | sidebar navigation, with polymorphic slots |
14
+ | `BrandComponent` | logo, title & subtitle area |
15
+ | `DialogComponent` | modal dialogs, with configurable title and buttons |
16
+ | `TabsComponent` | tabbed interfaces |
17
+ | `ButtonComponent` | buttons & button-styled links |
18
+ | `ListComponent` | record lists |
19
+ | `PropertiesComponent` | key-value property display |
20
+ | `PaginationComponent` | Kaminari-backed pagination |
21
+ | `ToasterComponent` / `ToastComponent` | flash notifications |
22
+ | `HeadingComponent` | headings |
23
+ | `GateComponent` | authorization-based visibility gate |
24
+ | `IconComponent` | SVG [icon](/reference/icons) renderer |
25
+
26
+ ## Form components
27
+
28
+ Rendered by the [form builder](/reference/forms) helpers, under `Trek::Form::`:
29
+
30
+ `GroupComponent`, `FieldsetComponent`, `ActionsComponent` (configurable aspect & classnames), `ErrorsComponent`, `TextFieldComponent`, `ImageFieldComponent`, `SoundFieldComponent`, `SwitchBoxComponent`, `CollectionSelectComponent`, `GroupedCollectionSelectComponent`, `LinkSelectComponent` and `ContentEditorComponent`.
31
+
32
+ ## Content editor
33
+
34
+ `Trek::Form::ContentEditorComponent` wraps the [Tiptap](https://tiptap.dev) editor and accepts three options โ€” `nodes`, `blocks` and `floating` (all enabled by default) โ€” to toggle the node toolbar, block formatting and floating menu:
35
+
36
+ ```ruby
37
+ f.content_editor :intro, nodes: false, floating: false
38
+ ```
39
+
40
+ Content is stored as ProseMirror JSON and rendered with [`formatted_content`](/reference/concerns/contentable). Image uploads (Uppy), link insertion and prompts are served by the [panel controllers](/reference/controllers).
@@ -0,0 +1,28 @@
1
+ # Trek::Contentable
2
+
3
+ Utilities for rich text stored as ProseMirror JSON in a `content` column โ€” the format produced by Trek's [content editor](/reference/components#content-editor).
4
+
5
+ ## Methods
6
+
7
+ | Method | Returns |
8
+ | --- | --- |
9
+ | `parsed_content` | the content as a hash (parsing JSON strings if needed) |
10
+ | `formatted_content` | safe HTML, rendered through the [ProseMirror pipeline](/reference/formatters) |
11
+ | `content_text` | plain text extracted from all text nodes |
12
+ | `prosemirror_content?` | whether `content` is a valid ProseMirror document |
13
+
14
+ ## Usage
15
+
16
+ ```ruby
17
+ class Page < ApplicationRecord
18
+ include Trek::Contentable
19
+ end
20
+
21
+ page.formatted_content # => "<p>Hello <strong>world</strong></p>"
22
+ page.content_text # => "Hello world"
23
+ ```
24
+
25
+ ## Requirements
26
+
27
+ - A `content` jsonb column
28
+ - Used by [Page](/reference/models/page), [PageVersion](/reference/models/page-version) and [Fragment](/reference/models/fragment)
@@ -0,0 +1,24 @@
1
+ # Trek::Formattable
2
+
3
+ Applies formatters to attributes before validation โ€” out of the box, [`Trek::TypographyFormatter`](/reference/formatters#typographyformatter) and its French typography rules.
4
+
5
+ ## API
6
+
7
+ ```ruby
8
+ class Page < ApplicationRecord
9
+ include Trek::Formattable
10
+ format_attributes Trek::TypographyFormatter, :title, :description
11
+ end
12
+ ```
13
+
14
+ `format_attributes(formatter_class, *attr_names)` registers the attributes; a `before_validation` callback runs the formatter on each of them.
15
+
16
+ ## Usage
17
+
18
+ ```ruby
19
+ page = Page.new(title: "Un titre: voici...")
20
+ page.valid?
21
+ page.title # => "Un titre : voiciโ€ฆ"
22
+ ```
23
+
24
+ Works on plain strings **and** on ProseMirror documents (only `text` nodes are touched; the structure is preserved).
@@ -0,0 +1,33 @@
1
+ # Trek::Fragmentable
2
+
3
+ Composes a record from reusable [fragments](/reference/models/fragment), based on a configuration matrix mapping record keys to fragment ids.
4
+
5
+ ## Configuration
6
+
7
+ ```yaml
8
+ # page_fragments configuration
9
+ page_fragments:
10
+ matrix:
11
+ home: [1, 2, 3]
12
+ about: [4, 5]
13
+ ```
14
+
15
+ ## Methods
16
+
17
+ | Method | Purpose |
18
+ | --- | --- |
19
+ | `fragments` | the Fragment records configured for this record's `key` |
20
+ | `fragments_attributes=` | bulk-updates fragments from nested form attributes |
21
+
22
+ ## Usage
23
+
24
+ ```ruby
25
+ class Page < ApplicationRecord
26
+ include Trek::Fragmentable
27
+ end
28
+
29
+ page = Page.find_by(key: "home")
30
+ page.fragments # => [Fragment(1), Fragment(2), Fragment(3)]
31
+ ```
32
+
33
+ This is what powers the fragment editing UI on page forms in the admin.
@@ -0,0 +1,18 @@
1
+ # Trek::Keyable
2
+
3
+ Batch lookup of records by their `key` attribute, preserving the requested order.
4
+
5
+ ## Methods
6
+
7
+ | Method | Returns |
8
+ | --- | --- |
9
+ | `Model.take_by_keys(keys)` | the records matching `keys`, **in the same order**, with `nil` for missing keys |
10
+
11
+ ## Usage
12
+
13
+ ```ruby
14
+ Page.take_by_keys(%w[services about missing])
15
+ # => [#<Page key="services">, #<Page key="about">, nil]
16
+ ```
17
+
18
+ Handy for rendering a fixed set of pages or fragments in a specific order in your views.
@@ -0,0 +1,26 @@
1
+ # Trek::Orderable
2
+
3
+ Position tracking with [acts_as_list](https://github.com/brendon/acts_as_list), scoped by parent.
4
+
5
+ ## What it adds
6
+
7
+ - `acts_as_list scope: :parent, top_of_list: 0`
8
+ - An `ordered` scope (position order)
9
+ - All the `acts_as_list` instance methods: `insert_at`, `move_to_top`, `move_to_bottom`, `move_higher`, `move_lower`โ€ฆ
10
+
11
+ ## Requirements
12
+
13
+ - A `position` integer column (and a `parent_id` column for the scope)
14
+
15
+ ## Usage
16
+
17
+ ```ruby
18
+ class MenuNode < ApplicationRecord
19
+ include Trek::Orderable
20
+ end
21
+
22
+ MenuNode.where(parent: menu).ordered
23
+ node.move_to_top
24
+ ```
25
+
26
+ This is what powers drag & drop reordering (SortableJS) in the admin lists.
@@ -0,0 +1,35 @@
1
+ # Trek::Pageable
2
+
3
+ The bridge between your content models (articles, productsโ€ฆ) and their [Page](/reference/models/page) representation. The model owns the data; the page owns the title, slug, SEO and position.
4
+
5
+ ## What it adds
6
+
7
+ - `has_one :page, as: :pageable, dependent: :destroy, autosave: true` with `accepts_nested_attributes_for :page`
8
+ - Delegates `title`, `image`, `image_url`, `current_image`, `current_image_url`, `to_s` to the page
9
+ - `before_create` guarantees the page exists
10
+ - `to_param` uses the page slug on the public site (and the id in the admin)
11
+
12
+ ## Class macros
13
+
14
+ | Macro | Purpose |
15
+ | --- | --- |
16
+ | `has_position_from_page` | delegates `position` to the page and adds a `by_position` scope |
17
+ | `has_root_scope` | adds a `root` scope finding resources under their index page |
18
+ | `find_with_page_slug(slug)` | class method โ€” resource lookup by page slug |
19
+
20
+ ## Navigation helpers
21
+
22
+ `parent?`, `parent`, `children?`, `children` โ€” traverse the page hierarchy from the resource side.
23
+
24
+ ## Usage
25
+
26
+ ```ruby
27
+ class Article < ApplicationRecord
28
+ include Trek::Pageable
29
+ has_position_from_page
30
+ end
31
+
32
+ article = Article.create(page_attributes: { title: "My Article" })
33
+ article.title # => "My Article" (delegated)
34
+ article.to_param # => "my-article"
35
+ ```
@@ -0,0 +1,28 @@
1
+ # Trek::Pages::Pathable
2
+
3
+ Manages a page's URL paths across locales: the current path, the full history (for redirects), and automatic regeneration when pages move in the tree.
4
+
5
+ ## What it adds
6
+
7
+ - `belongs_to :current_path` and `has_many :paths` โ†’ [PagePath](/reference/models/page-path)
8
+ - `after_save_commit`, paths are (re)created for the page **and all its descendants**
9
+
10
+ ## Methods
11
+
12
+ | Method | Returns |
13
+ | --- | --- |
14
+ | `Page.find_at_full_path(path)` | the page at this path (raises `ActiveRecord::RecordNotFound`) |
15
+ | `full_path` | the current path string |
16
+ | `full_path_from_tree` | the path computed from ancestor slugs |
17
+
18
+ ## Usage
19
+
20
+ ```ruby
21
+ parent = Page.create(title: "Services")
22
+ child = Page.create(title: "Design", parent: parent)
23
+
24
+ child.full_path # => "services/design"
25
+ Page.find_at_full_path("services/design") # => child
26
+ ```
27
+
28
+ Old paths remain in `page.paths`, which lets your front-end controllers redirect outdated URLs to `full_path`.
@@ -0,0 +1,28 @@
1
+ # Trek::Phonable
2
+
3
+ Phone number validation, normalization and formatting, backed by [Phonelib](https://github.com/daddyz/phonelib).
4
+
5
+ ## API
6
+
7
+ ```ruby
8
+ class Organization < ApplicationRecord
9
+ include Trek::Phonable
10
+ phonable_attributes :phone, :fax
11
+ end
12
+ ```
13
+
14
+ For each declared attribute:
15
+
16
+ - a validation (`phone: { possible: true, allow_blank: true }`)
17
+ - normalization to E.164 international format on save
18
+ - `formatted_{attr}` โ€” national format (`"01 23 45 67 89"`)
19
+ - `{attr}_with_country_code` โ€” E.164 (`"+33123456789"`)
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ org = Organization.create(phone: "0123456789")
25
+ org.phone # => "+33123456789"
26
+ org.formatted_phone # => "01 23 45 67 89"
27
+ org.phone_with_country_code # => "+33123456789"
28
+ ```
@@ -0,0 +1,19 @@
1
+ # Trek::Pages::SearchEngineOptimizable
2
+
3
+ SEO metadata with sensible fallbacks: explicit title/description/image when set, model defaults otherwise (including the parent page's image).
4
+
5
+ ## Methods
6
+
7
+ | Method | Returns |
8
+ | --- | --- |
9
+ | `seo_values` | `{ title:, description:, image:, slug: }` with fallbacks applied |
10
+ | `meta_tags` | `seo_values` without the slug โ€” ready for your `<head>` |
11
+ | `default_title` / `default_description` / `default_image` | the fallback values |
12
+
13
+ ## Usage
14
+
15
+ ```slim
16
+ / app/views/layouts/application.html.slim
17
+ - @page.meta_tags.each do |name, content|
18
+ meta name=name content=content
19
+ ```
@@ -0,0 +1,22 @@
1
+ # Trek::Searchable
2
+
3
+ Accent-insensitive full-text search on a record's page title, using PostgreSQL's `unaccent` extension.
4
+
5
+ ## What it adds
6
+
7
+ - A `search(query)` scope โ€” joins to the associated page and matches the title with an unaccented `ILIKE`; returns everything when the query is blank
8
+
9
+ ## Usage
10
+
11
+ ```ruby
12
+ class Article < ApplicationRecord
13
+ include Trek::Pageable
14
+ include Trek::Searchable
15
+ end
16
+
17
+ Article.search("รฉlรจve") # matches "eleve", "ร‰lรจve"โ€ฆ
18
+ ```
19
+
20
+ ## Controller side
21
+
22
+ The `Trek::Search` controller concern wires this into index actions: it filters `@objects` by `params[:search]` whenever the model responds to `search` โ€” that's what powers the search field in admin lists.
@@ -0,0 +1,24 @@
1
+ # Trek::Sectionable
2
+
3
+ Organizes a record's content into ordered, typed sections (hero, features, testimonialsโ€ฆ).
4
+
5
+ ## What it adds
6
+
7
+ - `has_many :sections, -> { ordered }, as: :sectionable, dependent: :destroy`
8
+ - `sections_instances` โ€” the unwrapped section objects, in order
9
+
10
+ ## Usage
11
+
12
+ ```ruby
13
+ class Page < ApplicationRecord
14
+ include Trek::Sectionable
15
+
16
+ SECTIONS = [HeroSection, FeaturesSection].freeze
17
+ end
18
+
19
+ page.sections_instances.each do |section|
20
+ render section_component_for(section)
21
+ end
22
+ ```
23
+
24
+ Sections are opt-in per page via the `sectioned` boolean column. The including class defines the allowed section types through the `SECTIONS` constant.
@@ -0,0 +1,21 @@
1
+ # Trek::Sluggable
2
+
3
+ Auto-generates locale-aware URL slugs before validation, for every available locale.
4
+
5
+ ## Behavior
6
+
7
+ - `before_validation`, the slug is regenerated per locale: from a route translation of the page `key` when one exists, otherwise by parameterizing the SEO title
8
+ - Validates slug presence (except for the root page) and uniqueness among siblings (`scope: :parent_id`)
9
+
10
+ ## Requirements
11
+
12
+ - [Translatable](/reference/concerns/translatable) with a translated `slug`
13
+ - A `key` attribute, a `root?` predicate and `seo_values` (provided by [SearchEngineOptimizable](/reference/concerns/search-engine-optimizable))
14
+
15
+ ## Usage
16
+
17
+ ```ruby
18
+ page = Page.new(key: "services", title: "Our Services")
19
+ page.save
20
+ page.slug # => "our-services"
21
+ ```
@@ -0,0 +1,20 @@
1
+ # Trek::Taggable
2
+
3
+ Tag associations for any model, via the [taxonomy models](/reference/models/tag) (installed by the [taxonomies generator](/reference/generators/taxonomies)).
4
+
5
+ ## What it adds
6
+
7
+ - `has_many :taggings, as: :taggable, dependent: :destroy`
8
+ - `has_many :tags, through: :taggings`
9
+ - `tag_names` โ€” the tag names as an array of strings
10
+
11
+ ## Usage
12
+
13
+ ```ruby
14
+ class Article < ApplicationRecord
15
+ include Trek::Taggable
16
+ end
17
+
18
+ article.tags << Tag.find_or_create_by(name: "Ruby", key: "ruby")
19
+ article.tag_names # => ["Ruby"]
20
+ ```
@@ -0,0 +1,35 @@
1
+ # Trek::Translatable
2
+
3
+ Multi-language attributes, backed by [Mobility](https://github.com/shioyama/mobility). Translated values are stored in `jsonb` columns keyed by locale.
4
+
5
+ ## API
6
+
7
+ ```ruby
8
+ class Page < ApplicationRecord
9
+ include Trek::Translatable
10
+ translate_attributes :title, :description
11
+ end
12
+ ```
13
+
14
+ `translate_attributes(*attr_names)` declares the translatable attributes (it delegates to Mobility's `translates`).
15
+
16
+ ## Usage
17
+
18
+ ```ruby
19
+ page = Page.create(title: "Home")
20
+ I18n.with_locale(:fr) { page.update(title: "Accueil") }
21
+
22
+ page.title # => "Home" (current locale)
23
+ I18n.with_locale(:fr) { page.title } # => "Accueil"
24
+ ```
25
+
26
+ Stored as:
27
+
28
+ ```json
29
+ { "en": "Home", "fr": "Accueil" }
30
+ ```
31
+
32
+ ## Requirements
33
+
34
+ - A `jsonb` column per translated attribute (default `{}`)
35
+ - Used by [Page](/reference/models/page), [PagePath](/reference/models/page-path), [Fragment](/reference/models/fragment) and [MenuNode](/reference/models/menu-node)
@@ -0,0 +1,27 @@
1
+ # Trek::Pages::Versionable
2
+
3
+ Content versioning for [pages](/reference/models/page): snapshot the current content and image as a [PageVersion](/reference/models/page-version), then keep editing while the snapshot stays live.
4
+
5
+ ## What it adds
6
+
7
+ - `has_many :versions` (most recent first) with a `current` sub-scope, plus `accepts_nested_attributes_for :versions`
8
+
9
+ ## Methods
10
+
11
+ | Method | Purpose |
12
+ | --- | --- |
13
+ | `create_current_version_from_page!` | snapshots the page's `content` and `image` as the new current version |
14
+ | `current_version` | the live version (or `nil` when not versioned) |
15
+ | `current_content` / `current_sections` | content from the current version, falling back to the page itself |
16
+ | `current_image` / `current_image_url` | image from the current version, falling back to the page, then its parent |
17
+
18
+ ## Usage
19
+
20
+ Versioning is opt-in per page via the `versioned` boolean column:
21
+
22
+ ```ruby
23
+ page.update(versioned: true)
24
+ page.create_current_version_from_page! # freeze what's live
25
+ page.update(content: new_draft) # keep editing safely
26
+ page.current_content # => still the frozen snapshot
27
+ ```
@@ -0,0 +1,23 @@
1
+ # Controllers
2
+
3
+ - **`Trek::BaseController`** โ€” the base for all admin controllers: authentication, authorization, localization, the `admin` layout and Trek's [form builder](/reference/forms).
4
+ - **`Trek::ResourceController`** โ€” extends `BaseController` with the full CRUD (`index`, `show`, `new`, `create`, `edit`, `update`, `destroy`), wired to ActionPolicy (`authorized_scope`, `authorize!`) and ordering. Scaffolded controllers inherit from it and only need to declare their `model`:
5
+
6
+ ```ruby
7
+ module Admin
8
+ class BooksController < Trek::ResourceController
9
+ private
10
+
11
+ def model
12
+ Book
13
+ end
14
+ end
15
+ end
16
+ ```
17
+
18
+ - **Panel controllers** (`Trek::Panels::*`) โ€” support endpoints used by the admin UI: image uploads (Uppy), link insertion and content editor prompts.
19
+
20
+ ## Mixins
21
+
22
+ - `include Trek::Pagination` โ€” paginates the index with Kaminari
23
+ - `include Trek::Search` โ€” filters the index by `params[:search]` when the model is [Searchable](/reference/concerns/searchable)
@@ -0,0 +1,9 @@
1
+ # Environment variables
2
+
3
+ | Variable | Purpose |
4
+ | --- | --- |
5
+ | `ADMIN_EMAIL` / `ADMIN_PASSWORD` | credentials used by [`trek:admin:user`](/reference/generators/admin-user) |
6
+ | `POSTMARK_API_TOKEN` | Postmark email delivery (auto-injected into credentials by the [installer](/reference/generators/install)) |
7
+ | `DEEPL_AUTH_KEY` | automatic locale translation in the [scaffold generator](/reference/generators/scaffold) |
8
+ | `FEATURE_STORAGE` | `local` (development) or `s3` (production) |
9
+ | `S3_BUCKET`, `S3_REGION`, `S3_ENDPOINT`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, `S3_BASE_URL` | S3 storage configuration for [uploads](/reference/uploaders) |
@@ -0,0 +1,30 @@
1
+ # Formatters
2
+
3
+ ## TypographyFormatter
4
+
5
+ `Trek::TypographyFormatter` applies French typography rules to strings **and** ProseMirror documents (only `text` nodes are modified):
6
+
7
+ - non-breaking spaces before `: ; ! ? % โ‚ฌ $ ยป` and after `ยซ`, dashes, `nยฐ`
8
+ - non-breaking spaces before units (km, kg, h, min, ยฐCโ€ฆ) and between digit groups
9
+ - `...` โ†’ `โ€ฆ`, ASCII apostrophes โ†’ typographic `'`
10
+ - removes double spaces and spaces before periods/commas
11
+
12
+ Wire it to attributes with [Formattable](/reference/concerns/formattable):
13
+
14
+ ```ruby
15
+ include Trek::Formattable
16
+ format_attributes Trek::TypographyFormatter, :title
17
+ ```
18
+
19
+ ## ProseMirror โ†’ HTML pipeline
20
+
21
+ `formatted_content` (from [Contentable](/reference/concerns/contentable)) renders ProseMirror JSON to safe HTML through the `prosemirror_to_html` gem, extended with:
22
+
23
+ | Piece | Role |
24
+ | --- | --- |
25
+ | `Nodes::ImageBlock` | renders `image_block` nodes as figures (Shrine image, alt, caption, size) |
26
+ | `Nodes::PromptsBlock` | renders `prompts_block` nodes as call-to-action buttons |
27
+ | `RemoveEmptyParagraphsFormatter` | strips empty `<p></p>` tags |
28
+ | `GlobalIdToLinksFormatter` | resolves `gid://` references in links to real record URLs |
29
+
30
+ The pipeline is orchestrated by the `Prosemirror` model โ€” extend it with your own node renderers when you add custom blocks to the editor.
@@ -0,0 +1,21 @@
1
+ # Forms
2
+
3
+ Trek ships `Trek::FormBuilder`, an extension of [`ViewComponent::Form`](https://github.com/pantographe/view_component-form)'s builder, automatically used in admin views. On top of the standard helpers, it provides:
4
+
5
+ | Helper | Renders |
6
+ | --- | --- |
7
+ | `f.group` | a label + input wrapper with errors |
8
+ | `f.content_editor` | the Tiptap rich text editor |
9
+ | `f.image_field` | an image uploader with thumbnail preview (Uppy + Shrine) |
10
+ | `f.sound_field` | an audio file uploader |
11
+ | `f.switch_box` | a toggle switch |
12
+ | `f.link_select` | a link picker (internal pages & external links) |
13
+
14
+ Each helper renders the matching component from the [`Trek::Form` namespace](/reference/components#form-components).
15
+
16
+ ```slim
17
+ = form_with model: [:admin, @book] do |f|
18
+ = f.group :title
19
+ = f.content_editor :intro, floating: false
20
+ = f.switch_box :published
21
+ ```
@@ -0,0 +1,17 @@
1
+ # Admin user
2
+
3
+ ```sh
4
+ rails g trek:admin:user
5
+ # or (soon)
6
+ trek admin:user
7
+ ```
8
+
9
+ Creates an admin user with the credentials defined by the `ADMIN_EMAIL` and `ADMIN_PASSWORD` ENV variables, typically set in `.env`:
10
+
11
+ ```sh
12
+ # .env
13
+ ADMIN_EMAIL=admin@example.com
14
+ ADMIN_PASSWORD=a-strong-password
15
+ ```
16
+
17
+ The created user gets the `admin` [role](/reference/models/user#roles), which makes it `privileged?` and grants it full access in the default [policies](/reference/policies).
@@ -0,0 +1,42 @@
1
+ # Installer
2
+
3
+ ```sh
4
+ rails g trek:install
5
+ # or (soon)
6
+ trek install
7
+ ```
8
+
9
+ Trek provides an install generator, that will:
10
+
11
+ - install all dependencies
12
+ - create the `User` model and include Trek's concerns
13
+ - create the `Page`, `PagePath` and `PageVersion` models and include Trek's concerns
14
+ - create the `Fragment` model and include Trek's concerns
15
+ - create the relevant policies
16
+ - generate and run the relevant migrations
17
+
18
+ ## Sub-generators
19
+
20
+ The installer orchestrates dedicated sub-generators, all under the `trek:install:*` namespace. Each one can also be run individually, e.g. `rails g trek:install:healthcheck`.
21
+
22
+ | Area | What gets set up |
23
+ | --- | --- |
24
+ | Authentication | Sorcery, the `UserSession` form model, login/logout & password reset panels |
25
+ | Authorization | ActionPolicy and the base policies |
26
+ | Models | `User`, `Page`, `PagePath`, `PageVersion`, `Fragment`, `MenuNode`, `ExternalLink`, `Current` โ€” with migrations |
27
+ | Attachments | Shrine configuration and the uploaders |
28
+ | Emails | Postmark and the user mailer |
29
+ | I18n | rails-i18n, Mobility, i18n-tasks and the locale files |
30
+ | Admin panels | Pages, fragments, users, sessions, password resets, routes, dashboard |
31
+ | Pagination | Kaminari |
32
+ | Tooling | esbuild, PostCSS, EditorConfig, Procfiles, Makefile, health check endpoint |
33
+ | Linting | Standard, ESLint, Stylelint, Prettier, Slim Lint, Lefthook git hooks |
34
+ | Security | Brakeman, bundler-audit, ruby_audit |
35
+ | CI | GitLab CI configuration |
36
+
37
+ ## ENV variables read by the installer
38
+
39
+ Define these **before** running the installer to have them injected automatically:
40
+
41
+ - `POSTMARK_API_TOKEN` โ€” injected into the Rails credentials for email delivery
42
+ - `DEEPL_AUTH_KEY` โ€” enables automatic locale translation in the [scaffold generator](/reference/generators/scaffold)