rails-markup 1.2.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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +304 -0
  4. data/app/assets/javascripts/rails_markup/toolbar.js +2225 -0
  5. data/app/controllers/rails_markup/annotations_controller.rb +244 -0
  6. data/app/controllers/rails_markup/application_controller.rb +7 -0
  7. data/app/controllers/rails_markup/dashboard_controller.rb +230 -0
  8. data/app/controllers/rails_markup/external/annotations_controller.rb +68 -0
  9. data/app/models/rails_markup/annotation.rb +176 -0
  10. data/app/views/layouts/rails_markup/application.html.erb +158 -0
  11. data/app/views/rails_markup/dashboard/_annotation_card.html.erb +25 -0
  12. data/app/views/rails_markup/dashboard/_annotation_list.html.erb +57 -0
  13. data/app/views/rails_markup/dashboard/_annotation_page.html.erb +13 -0
  14. data/app/views/rails_markup/dashboard/_filters.html.erb +75 -0
  15. data/app/views/rails_markup/dashboard/_stats.html.erb +22 -0
  16. data/app/views/rails_markup/dashboard/_styles.html.erb +115 -0
  17. data/app/views/rails_markup/dashboard/board.html.erb +135 -0
  18. data/app/views/rails_markup/dashboard/index.html.erb +38 -0
  19. data/app/views/rails_markup/dashboard/show.html.erb +129 -0
  20. data/app/views/rails_markup/shared/_toolbar.html.erb +28 -0
  21. data/bin/rails-markup +5 -0
  22. data/config/routes.rb +45 -0
  23. data/db/migrate/20260720000000_add_client_uuid_to_rails_markup_annotations.rb +13 -0
  24. data/db/migrate/20260721000000_backfill_rails_markup_client_uuids.rb +17 -0
  25. data/lib/generators/rails_markup/install/templates/auth_controller.rb.erb +17 -0
  26. data/lib/generators/rails_markup/install/templates/bin_markup.erb +5 -0
  27. data/lib/generators/rails_markup/install/templates/create_rails_markup_annotations.rb.erb +27 -0
  28. data/lib/generators/rails_markup/install/templates/initializer.rb.erb +54 -0
  29. data/lib/generators/rails_markup/install_generator.rb +167 -0
  30. data/lib/generators/rails_markup/uninstall_generator.rb +110 -0
  31. data/lib/rails_markup/cli/base.rb +8 -0
  32. data/lib/rails_markup/cli/initializer_writer.rb +54 -0
  33. data/lib/rails_markup/cli/setup_wizard.rb +229 -0
  34. data/lib/rails_markup/cli.rb +831 -0
  35. data/lib/rails_markup/client_uuid_maintenance.rb +174 -0
  36. data/lib/rails_markup/configuration.rb +160 -0
  37. data/lib/rails_markup/engine.rb +18 -0
  38. data/lib/rails_markup/http_server.rb +226 -0
  39. data/lib/rails_markup/http_store_proxy.rb +122 -0
  40. data/lib/rails_markup/mcp_config.rb +258 -0
  41. data/lib/rails_markup/mcp_server.rb +639 -0
  42. data/lib/rails_markup/server.rb +73 -0
  43. data/lib/rails_markup/store.rb +219 -0
  44. data/lib/rails_markup/version.rb +5 -0
  45. data/lib/rails_markup.rb +11 -0
  46. data/lib/tasks/rails_markup_client_uuids.rake +19 -0
  47. metadata +198 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ba51cbb26a37ce1d218f565367f1f15010b3cbe2b884dc2c19000b3b2607b50d
4
+ data.tar.gz: efca7d5c37141a1a45b01d6b47e84388f6acc50c514626d53adaea6bb78b71a9
5
+ SHA512:
6
+ metadata.gz: e47f3a1181804194ceaa5c1787b720df7df1a5eb9e3bdecf3f9cf3d3e622d79fb11f7aa4d1d5b0c75b971f70e02f2e8910bb75ae4711e1bf096ad1cf49241474
7
+ data.tar.gz: e65d97dd4e0730aaa0023aaf8cc8405b770af6e85f566a47aeab5c39ef0dd38a24d143586ef2064bfc05dd330a4df504c73901d7fdc2cb93ce6cab5ba585cbbd
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 InventList
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,304 @@
1
+ # Rails Markup
2
+
3
+ Point-and-click annotation tool for Rails apps. Click any element, describe what needs to change, and your AI agent reads and acts on your feedback via MCP.
4
+
5
+ **v1.0** — Author attribution, export, search, screenshots with drawing tools, kanban board, notification hooks.
6
+
7
+ ## How it works
8
+
9
+ 1. **Install the gem** — adds migration, toolbar, auth controller, and routes
10
+ 2. **Open your app** — the annotation toolbar appears as a floating button
11
+ 3. **Click any element** — hover to highlight, click to annotate with screenshots
12
+ 4. **Draw on screenshots** — arrows, rectangles, highlights on captured elements
13
+ 5. **Agent reads it** — AI calls `rails_markup_read` via MCP
14
+ 6. **Agent fixes it** — resolves the annotation and moves on
15
+
16
+ ## Install
17
+
18
+ ```ruby
19
+ # Gemfile
20
+ gem "rails-markup", github: "inventlist/rails-markup", require: "rails_markup"
21
+ ```
22
+
23
+ ```bash
24
+ bundle install
25
+ rails generate rails_markup:install
26
+ rails db:migrate
27
+ ```
28
+
29
+ When upgrading an existing installation, copy and run new engine migrations:
30
+
31
+ ```bash
32
+ bin/rails railties:install:migrations FROM=rails_markup
33
+ bin/rails db:migrate
34
+ ```
35
+
36
+ ### Rails Markup 1.2 rolling UUID upgrade
37
+
38
+ The 1.2 upgrade migration adds, repairs, and uniquely indexes `client_uuid`, but
39
+ deliberately leaves the column nullable while old application instances may
40
+ still be serving traffic. Fresh installs create it as `NOT NULL` immediately.
41
+
42
+ For a rolling upgrade:
43
+
44
+ 1. Run the copied migrations and deploy 1.2 to every application instance.
45
+ 2. Wait until all old pre-1.2 instances are drained and have stopped.
46
+ 3. Repair any rows written during the mixed-version window, then verify:
47
+
48
+ ```bash
49
+ bin/rails rails_markup:client_uuids:repair
50
+ bin/rails rails_markup:client_uuids:verify
51
+ ```
52
+
53
+ Both commands are safe to repeat. A pull containing any lingering blank or
54
+ noncanonical identity returns `503` without mutating data. Do not add a
55
+ `NOT NULL` constraint until verification stays green after old instances are
56
+ drained; that explicit contract migration belongs in a later release.
57
+
58
+ The generator creates:
59
+
60
+ | File | Purpose |
61
+ |------|---------|
62
+ | `db/migrate/*_create_rails_markup_annotations.rb` | Annotations table |
63
+ | `config/initializers/rails_markup.rb` | Configuration |
64
+ | `app/controllers/rails_markup_auth_controller.rb` | Auth controller |
65
+ | `bin/markup` | CLI wrapper |
66
+ | Route mount | Engine at `/admin/annotations` |
67
+ | Toolbar injection | `<%= render "rails_markup/shared/toolbar" %>` in your layout |
68
+
69
+ ### Generator options
70
+
71
+ ```bash
72
+ rails generate rails_markup:install \
73
+ --mount-path=/admin/annotations \
74
+ --base-controller=Admin::BaseController \
75
+ --layout=application
76
+ ```
77
+
78
+ ### Authentication and browser synchronization
79
+
80
+ The generated `RailsMarkupAuthController` protects both the mounted dashboard
81
+ and the toolbar API. It denies access unless `current_user.admin?` is true;
82
+ customize `authorize_rails_markup!` if your host uses another authorization
83
+ policy. The configured base controller must actually enforce authorization.
84
+ Choosing a public controller makes both interfaces public.
85
+
86
+ The toolbar uses the same host authentication boundary as the dashboard and
87
+ sends normal Rails CSRF tokens on every mutation. Keep `csrf_meta_tags` in the
88
+ host layout. Rails is authoritative; localStorage is an offline cache and a
89
+ durable desired-state outbox, keyed by a client UUID.
90
+
91
+ During reconciliation, browser-owned content, intent, severity, selection,
92
+ target, permitted metadata, and an explicitly dirty status remain local until a
93
+ successful UUID PUT. Server-owned thread, author/user identity, and timestamps
94
+ always win. A complete exact-page pull may remove a synced local record absent
95
+ from Rails, but never removes pending upserts or delete tombstones.
96
+
97
+ ### Uninstall
98
+
99
+ ```bash
100
+ rails generate rails_markup:uninstall
101
+ # Add --remove-migration to also delete the migration file
102
+ ```
103
+
104
+ ## Configuration
105
+
106
+ ```ruby
107
+ # config/initializers/rails_markup.rb
108
+ RailsMarkup.configure do |config|
109
+ # Auth controller for dashboard and toolbar API; it must authorize access.
110
+ config.base_controller_class = "RailsMarkupAuthController"
111
+
112
+ # External API token (for MCP production tools)
113
+ config.api_token = Rails.application.credentials.dig(:rails_markup, :api_token)
114
+
115
+ # Author display name from current_user
116
+ config.author_name_method = :name
117
+ # Or use a Proc:
118
+ # config.author_name_method = ->(user) { "#{user.first_name} #{user.last_name}" }
119
+
120
+ # Callback fired after each annotation is created
121
+ config.on_create_callback = ->(annotation) {
122
+ SlackNotifier.notify("Feedback on #{annotation.page_url}: #{annotation.content}")
123
+ }
124
+
125
+ # Toolbar accent color: indigo, amber, blue, emerald, rose
126
+ config.toolbar_accent = "indigo"
127
+
128
+ # Show or hide the annotation toolbar and FAB (default: true)
129
+ config.toolbar_enabled = true
130
+
131
+ # FAB corner: bl, br, tl, tr; size: slim, compact, default
132
+ config.toolbar_position = "bl"
133
+ config.toolbar_size = "default"
134
+
135
+ # Element screenshots (default: true)
136
+ config.enable_screenshots = true
137
+
138
+ # Dashboard pagination (default: 25)
139
+ config.per_page = 25
140
+
141
+ # "Back to app" link
142
+ config.return_url = "/admin"
143
+ end
144
+ ```
145
+
146
+ ## Dashboard
147
+
148
+ Visit `/admin/annotations` for the full dashboard:
149
+
150
+ - **List view** — status filters, search, author filter, load-more pagination
151
+ - **Board view** — kanban with drag-and-drop status transitions
152
+ - **Export** — CSV and JSON downloads (respects current filters)
153
+ - **Detail panel** — full content, metadata, screenshots, thread
154
+ - **Bulk actions** — dismiss all pending/acknowledged
155
+
156
+ ### Search & Filters
157
+
158
+ - **Text search** — searches annotation content and selected text
159
+ - **Author filter** — filter by who created the annotation
160
+ - **Page URL filter** — filter by annotated page
161
+ - **Status pills** — pending, acknowledged, resolved, dismissed
162
+
163
+ ## Screenshots & Drawing
164
+
165
+ When you click an element, the toolbar captures a screenshot using SVG foreignObject. Before submitting, you can draw on it:
166
+
167
+ - **Arrow** — click and drag to draw red arrows
168
+ - **Rectangle** — click and drag for red outline boxes
169
+ - **Highlight** — freehand semi-transparent yellow strokes
170
+ - **Undo** — remove the last drawing operation
171
+
172
+ Screenshots are stored as base64 in annotation metadata and displayed in the dashboard detail view.
173
+
174
+ ## CLI
175
+
176
+ ```bash
177
+ # Setup
178
+ bin/markup init # Interactive setup wizard (TUI)
179
+ bin/markup configure # Set .mcp.json env vars
180
+ bin/markup status # Show config (tokens masked)
181
+ bin/markup setup-production --url=URL # Generate token + configure
182
+
183
+ # Server
184
+ bin/markup server # HTTP + MCP server
185
+ bin/markup mcp # MCP-only server (stdio)
186
+
187
+ # Annotations (match MCP tool verbs)
188
+ bin/markup pending # Fetch pending from dev
189
+ bin/markup pending --production # Fetch from production
190
+ bin/markup resolve 42 --summary "..." # Resolve an annotation
191
+ bin/markup dismiss 42 --reason "..." # Dismiss an annotation
192
+ bin/markup reply 42 "message" # Reply to an annotation
193
+ bin/markup acknowledge 42 # Mark as acknowledged
194
+ bin/markup watch # Poll for new annotations
195
+ bin/markup watch --production # Watch production
196
+
197
+ # Info
198
+ bin/markup sessions # Session info (MCP only)
199
+ bin/markup session ID # Session detail (MCP only)
200
+ ```
201
+
202
+ ### Setup Wizard
203
+
204
+ Run `bin/markup init` for an interactive setup that walks you through:
205
+
206
+ 1. Toolbar accent color (indigo, amber, blue, emerald, rose)
207
+ 2. Toolbar position (bottom-left, bottom-right, top-left, top-right)
208
+ 3. Toolbar size (slim, compact, default)
209
+ 4. Screenshot capture (on/off)
210
+ 5. Production URL (optional)
211
+
212
+ Writes `config/initializers/rails_markup.rb` and updates `.mcp.json` automatically.
213
+
214
+ ## MCP Setup
215
+
216
+ Add to `.mcp.json`:
217
+
218
+ ```json
219
+ {
220
+ "mcpServers": {
221
+ "rails-markup": {
222
+ "type": "stdio",
223
+ "command": "ruby",
224
+ "args": ["bin/markup", "mcp"],
225
+ "env": {
226
+ "RAILS_MARKUP_DEV_URL": "http://localhost:3000",
227
+ "RAILS_MARKUP_PROD_URL": "https://yourapp.com",
228
+ "RAILS_MARKUP_PROD_TOKEN": "your-prod-api-token"
229
+ }
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ ### MCP tools (five canonical thin-enum tools)
236
+
237
+ The MCP server advertises exactly five tools. Their schemas reject unknown
238
+ arguments and use small enums instead of multiplying environment-specific tool
239
+ names:
240
+
241
+ | Tool | Contract |
242
+ |------|----------|
243
+ | `rails_markup_read` | Read `pending`, `sessions`, `session`, or `annotation`; never acknowledges |
244
+ | `rails_markup_watch` | Watch development annotations with bounded timeout/window |
245
+ | `rails_markup_transition` | `acknowledge` or `resolve` an annotation |
246
+ | `rails_markup_reply` | Add an agent reply |
247
+ | `rails_markup_dismiss` | Destructively dismiss with a reason |
248
+
249
+ `environment` is `development` or `production` where supported. Production URL
250
+ and bearer credentials come only from trusted process configuration.
251
+ Caller-supplied URL and token overrides are rejected and are never forwarded.
252
+
253
+ ### Legacy MCP compatibility
254
+
255
+ Legacy names remain hidden but callable as explicit, warning-emitting adapters
256
+ during the 1.2/1.3 compatibility window. They never restore caller-controlled
257
+ credentials or mutating reads. They will be removed after 1.3.0; migrate clients
258
+ to the five canonical tools above.
259
+
260
+ ## Annotation Data
261
+
262
+ Each annotation includes:
263
+
264
+ - **Content** — what needs to change (max 5000 chars)
265
+ - **CSS selector** — `div#hero.max-w-xl`
266
+ - **CSS path** — `body > main > div:nth-of-type(2) > div#hero`
267
+ - **Bounding box** — `{ top, left, width, height }`
268
+ - **Nearby text** — first 80 chars of element text
269
+ - **Selected text** — highlighted text before clicking
270
+ - **Screenshot** — element capture with optional drawings
271
+ - **Author** — who created it (via `author_name_method`)
272
+ - **Intent** — `fix`, `change`, `question`, or `approve`
273
+ - **Severity** — `suggestion`, `important`, or `blocking`
274
+
275
+ ## Architecture
276
+
277
+ ```
278
+ Browser Toolbar AI Agent (Claude Code, Cursor)
279
+ | |
280
+ | Authenticated same-origin API | MCP (stdio JSON-RPC)
281
+ | (pull + UUID PUT/DELETE, CSRF) | or CLI (bin/markup pending)
282
+ v v
283
+ +----------------------------------------------------------+
284
+ | Rails Engine |
285
+ | |
286
+ | Toolbar → Annotations → Dashboard |
287
+ | (vanilla JS) (ActiveRecord) (list + board views) |
288
+ | ↑ |
289
+ | External API (/external/*) |
290
+ | (token-authenticated, used by MCP) |
291
+ +----------------------------------------------------------+
292
+ ```
293
+
294
+ ## Development
295
+
296
+ ```bash
297
+ git clone https://github.com/inventlist/rails-markup
298
+ cd rails-markup && bundle install
299
+ bundle exec ruby -Ilib:test test/**/*_test.rb
300
+ ```
301
+
302
+ ## License
303
+
304
+ MIT. See [LICENSE](LICENSE).