active_canvas 0.0.4 → 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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +23 -1
  3. data/app/assets/javascripts/active_canvas/admin/data_panel.js +357 -0
  4. data/app/assets/javascripts/active_canvas/admin/field_builder.js +84 -0
  5. data/app/assets/javascripts/active_canvas/admin/media_select_preview.js +11 -0
  6. data/app/assets/javascripts/active_canvas/editor/ac_bindings.js +81 -0
  7. data/app/assets/javascripts/active_canvas/editor/ac_chips.js +192 -0
  8. data/app/assets/javascripts/active_canvas/editor/code_panel.js +10 -7
  9. data/app/assets/javascripts/active_canvas/editor/directive_traits.js +95 -0
  10. data/app/assets/javascripts/active_canvas/editor/live_data.js +178 -0
  11. data/app/assets/javascripts/active_canvas/editor/panels.js +23 -7
  12. data/app/assets/javascripts/active_canvas/editor/preview_modal.js +112 -0
  13. data/app/assets/javascripts/active_canvas/editor/template_validator.js +95 -0
  14. data/app/assets/javascripts/active_canvas/editor.js +21 -3
  15. data/app/assets/stylesheets/active_canvas/editor.css +626 -0
  16. data/app/controllers/active_canvas/admin/application_controller.rb +8 -0
  17. data/app/controllers/active_canvas/admin/collection_items_controller.rb +93 -0
  18. data/app/controllers/active_canvas/admin/collections_controller.rb +51 -0
  19. data/app/controllers/active_canvas/admin/form_submissions_controller.rb +51 -0
  20. data/app/controllers/active_canvas/admin/pages_controller.rb +127 -5
  21. data/app/controllers/active_canvas/form_submissions_controller.rb +90 -0
  22. data/app/controllers/active_canvas/pages_controller.rb +8 -0
  23. data/app/controllers/active_canvas/redirects_controller.rb +10 -0
  24. data/app/helpers/active_canvas/admin/collection_items_helper.rb +33 -0
  25. data/app/models/active_canvas/auto_drop.rb +69 -0
  26. data/app/models/active_canvas/collection.rb +98 -0
  27. data/app/models/active_canvas/collection_item.rb +66 -0
  28. data/app/models/active_canvas/collection_item_version.rb +18 -0
  29. data/app/models/active_canvas/current.rb +2 -0
  30. data/app/models/active_canvas/form_submission.rb +9 -0
  31. data/app/models/active_canvas/page.rb +76 -15
  32. data/app/models/active_canvas/page_redirect.rb +7 -0
  33. data/app/services/active_canvas/collection_schema.rb +110 -0
  34. data/app/services/active_canvas/collection_source.rb +81 -0
  35. data/app/services/active_canvas/content_sanitizer.rb +33 -21
  36. data/app/services/active_canvas/form_schema.rb +48 -0
  37. data/app/services/active_canvas/form_stamper.rb +86 -0
  38. data/app/services/active_canvas/template_renderer/binding_resolver.rb +138 -0
  39. data/app/services/active_canvas/template_renderer/directive_expander.rb +54 -0
  40. data/app/services/active_canvas/template_renderer.rb +160 -0
  41. data/app/views/active_canvas/admin/collection_items/_field_input.html.erb +40 -0
  42. data/app/views/active_canvas/admin/collection_items/_form.html.erb +28 -0
  43. data/app/views/active_canvas/admin/collection_items/edit.html.erb +24 -0
  44. data/app/views/active_canvas/admin/collection_items/history.html.erb +34 -0
  45. data/app/views/active_canvas/admin/collection_items/index.html.erb +63 -0
  46. data/app/views/active_canvas/admin/collection_items/new.html.erb +9 -0
  47. data/app/views/active_canvas/admin/collections/_field_builder.html.erb +35 -0
  48. data/app/views/active_canvas/admin/collections/_form.html.erb +31 -0
  49. data/app/views/active_canvas/admin/collections/edit.html.erb +10 -0
  50. data/app/views/active_canvas/admin/collections/index.html.erb +45 -0
  51. data/app/views/active_canvas/admin/collections/new.html.erb +9 -0
  52. data/app/views/active_canvas/admin/form_submissions/index.html.erb +80 -0
  53. data/app/views/active_canvas/admin/form_submissions/show.html.erb +99 -0
  54. data/app/views/active_canvas/admin/pages/_data_panel.html.erb +81 -0
  55. data/app/views/active_canvas/admin/pages/_form.html.erb +10 -0
  56. data/app/views/active_canvas/admin/pages/editor.html.erb +62 -1
  57. data/app/views/active_canvas/pages/show.html.erb +4 -1
  58. data/app/views/layouts/active_canvas/admin/application.html.erb +15 -0
  59. data/config/routes.rb +20 -0
  60. data/db/migrate/20260517100001_add_template_enabled_and_bindings_to_active_canvas_pages.rb +6 -0
  61. data/db/migrate/20260517100002_add_bindings_to_active_canvas_page_versions.rb +6 -0
  62. data/db/migrate/20260702000001_create_active_canvas_page_redirects.rb +11 -0
  63. data/db/migrate/20260702000002_create_active_canvas_form_submissions.rb +14 -0
  64. data/db/migrate/20260702000003_create_active_canvas_collections.rb +12 -0
  65. data/db/migrate/20260702000004_create_active_canvas_collection_items.rb +16 -0
  66. data/db/migrate/20260702000005_create_active_canvas_collection_item_versions.rb +17 -0
  67. data/lib/active_canvas/configuration.rb +32 -1
  68. data/lib/active_canvas/data_sources/errors.rb +51 -0
  69. data/lib/active_canvas/data_sources/param_spec.rb +64 -0
  70. data/lib/active_canvas/data_sources/registration.rb +60 -0
  71. data/lib/active_canvas/data_sources/source.rb +47 -0
  72. data/lib/active_canvas/data_sources.rb +54 -0
  73. data/lib/active_canvas/engine.rb +15 -0
  74. data/lib/active_canvas/version.rb +1 -1
  75. data/lib/active_canvas.rb +6 -0
  76. metadata +85 -3
@@ -2927,3 +2927,629 @@
2927
2927
  color: var(--editor-danger);
2928
2928
  font-size: 0.8125rem;
2929
2929
  }
2930
+
2931
+ /* ==================== Data Panel ==================== */
2932
+
2933
+ .data-panel {
2934
+ display: flex;
2935
+ flex-direction: column;
2936
+ gap: 0.75rem;
2937
+ padding: 0.75rem;
2938
+ }
2939
+
2940
+ .data-panel-header {
2941
+ display: flex;
2942
+ align-items: center;
2943
+ justify-content: space-between;
2944
+ flex-shrink: 0;
2945
+ }
2946
+
2947
+ .data-panel-title {
2948
+ font-size: 0.6875rem;
2949
+ font-weight: 600;
2950
+ color: var(--editor-text-muted);
2951
+ text-transform: uppercase;
2952
+ letter-spacing: 0.04em;
2953
+ }
2954
+
2955
+ .data-panel-add-btn {
2956
+ display: inline-flex;
2957
+ align-items: center;
2958
+ gap: 0.375rem;
2959
+ padding: 0.375rem 0.625rem;
2960
+ font-size: 0.75rem;
2961
+ font-weight: 500;
2962
+ color: white;
2963
+ background: var(--editor-primary);
2964
+ border: none;
2965
+ border-radius: 6px;
2966
+ cursor: pointer;
2967
+ transition: background 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
2968
+ }
2969
+
2970
+ .data-panel-add-btn:hover {
2971
+ background: var(--editor-primary-hover);
2972
+ transform: translateY(-1px);
2973
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
2974
+ }
2975
+
2976
+ .data-panel-list {
2977
+ list-style: none;
2978
+ margin: 0;
2979
+ padding: 0;
2980
+ display: flex;
2981
+ flex-direction: column;
2982
+ gap: 0.5rem;
2983
+ }
2984
+
2985
+ .data-panel-list[data-empty="true"] {
2986
+ display: none;
2987
+ }
2988
+
2989
+ /* Empty state ------------------------------------------------ */
2990
+
2991
+ .data-panel-empty {
2992
+ display: none;
2993
+ flex-direction: column;
2994
+ align-items: center;
2995
+ text-align: center;
2996
+ padding: 1.5rem 1rem;
2997
+ }
2998
+
2999
+ .data-panel-list[data-empty="true"] + .data-panel-empty {
3000
+ display: flex;
3001
+ }
3002
+
3003
+ .data-panel-empty-icon {
3004
+ width: 56px;
3005
+ height: 56px;
3006
+ display: flex;
3007
+ align-items: center;
3008
+ justify-content: center;
3009
+ background: var(--editor-bg);
3010
+ border: 1px solid var(--editor-border);
3011
+ border-radius: 14px;
3012
+ color: var(--editor-text-muted);
3013
+ margin-bottom: 0.875rem;
3014
+ }
3015
+
3016
+ .data-panel-empty h3 {
3017
+ font-size: 0.9375rem;
3018
+ font-weight: 600;
3019
+ color: var(--editor-text);
3020
+ margin: 0 0 0.375rem 0;
3021
+ }
3022
+
3023
+ .data-panel-empty p {
3024
+ font-size: 0.8125rem;
3025
+ color: var(--editor-text-muted);
3026
+ margin: 0 0 1rem 0;
3027
+ max-width: 240px;
3028
+ line-height: 1.5;
3029
+ }
3030
+
3031
+ .data-panel-empty p code {
3032
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
3033
+ font-size: 0.75rem;
3034
+ color: var(--editor-primary);
3035
+ background: var(--editor-primary-light);
3036
+ padding: 0.0625rem 0.3125rem;
3037
+ border-radius: 4px;
3038
+ }
3039
+
3040
+ .data-panel-empty-btn {
3041
+ display: inline-flex;
3042
+ align-items: center;
3043
+ gap: 0.5rem;
3044
+ padding: 0.5rem 0.875rem;
3045
+ font-size: 0.8125rem;
3046
+ font-weight: 500;
3047
+ color: white;
3048
+ background: var(--editor-primary);
3049
+ border: none;
3050
+ border-radius: 8px;
3051
+ cursor: pointer;
3052
+ transition: background 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
3053
+ }
3054
+
3055
+ .data-panel-empty-btn:hover {
3056
+ background: var(--editor-primary-hover);
3057
+ transform: translateY(-1px);
3058
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
3059
+ }
3060
+
3061
+ /* Binding row ------------------------------------------------ */
3062
+
3063
+ .data-panel-row {
3064
+ background: var(--editor-surface);
3065
+ border: 1px solid var(--editor-border);
3066
+ border-radius: 8px;
3067
+ padding: 0.625rem 0.75rem;
3068
+ display: flex;
3069
+ flex-direction: column;
3070
+ gap: 0.5rem;
3071
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
3072
+ }
3073
+
3074
+ .data-panel-row:hover {
3075
+ border-color: var(--editor-primary);
3076
+ box-shadow: 0 0 0 1px var(--editor-primary);
3077
+ }
3078
+
3079
+ .data-panel-row-header {
3080
+ display: flex;
3081
+ align-items: center;
3082
+ gap: 0.5rem;
3083
+ }
3084
+
3085
+ .data-panel-row-name {
3086
+ font-size: 0.8125rem;
3087
+ font-weight: 600;
3088
+ color: var(--editor-text);
3089
+ }
3090
+
3091
+ .data-panel-row-source {
3092
+ font-size: 0.6875rem;
3093
+ font-weight: 500;
3094
+ color: var(--editor-text-muted);
3095
+ text-transform: uppercase;
3096
+ letter-spacing: 0.03em;
3097
+ margin-left: auto;
3098
+ margin-right: 0.25rem;
3099
+ }
3100
+
3101
+ .data-panel-row-action {
3102
+ width: 22px;
3103
+ height: 22px;
3104
+ display: flex;
3105
+ align-items: center;
3106
+ justify-content: center;
3107
+ background: transparent;
3108
+ border: none;
3109
+ border-radius: 4px;
3110
+ color: var(--editor-text-muted);
3111
+ cursor: pointer;
3112
+ opacity: 0;
3113
+ transition: opacity 0.15s ease, color 0.15s ease, background 0.15s ease;
3114
+ }
3115
+
3116
+ .data-panel-row:hover .data-panel-row-action,
3117
+ .data-panel-row-action:focus-visible {
3118
+ opacity: 1;
3119
+ }
3120
+
3121
+ .data-panel-row-action:hover {
3122
+ color: var(--editor-text);
3123
+ background: var(--editor-surface-hover);
3124
+ }
3125
+
3126
+ .data-panel-row-literal {
3127
+ width: 100%;
3128
+ padding: 0.3125rem 0.5rem;
3129
+ font-size: 0.8125rem;
3130
+ color: var(--editor-text);
3131
+ background: var(--editor-bg);
3132
+ border: 1px solid var(--editor-border);
3133
+ border-radius: 6px;
3134
+ box-sizing: border-box;
3135
+ }
3136
+
3137
+ .data-panel-row-literal:focus {
3138
+ outline: none;
3139
+ border-color: var(--editor-primary);
3140
+ box-shadow: 0 0 0 2px var(--editor-primary-light);
3141
+ }
3142
+
3143
+ .data-panel-row-params {
3144
+ font-size: 0.75rem;
3145
+ color: var(--editor-text-muted);
3146
+ overflow-wrap: anywhere;
3147
+ }
3148
+
3149
+ .data-panel-row-remove:hover {
3150
+ color: var(--editor-danger, #ef4444);
3151
+ background: rgba(239, 68, 68, 0.1);
3152
+ }
3153
+
3154
+ .data-panel-row-chips {
3155
+ display: flex;
3156
+ flex-wrap: wrap;
3157
+ gap: 0.375rem;
3158
+ }
3159
+
3160
+ .data-panel-chip {
3161
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
3162
+ font-size: 0.75rem;
3163
+ color: var(--editor-primary);
3164
+ background: var(--editor-primary-light);
3165
+ border: none;
3166
+ border-radius: 4px;
3167
+ padding: 0.1875rem 0.5rem;
3168
+ cursor: pointer;
3169
+ transition: background 0.15s ease, color 0.15s ease;
3170
+ }
3171
+
3172
+ .data-panel-chip:hover {
3173
+ background: var(--editor-primary);
3174
+ color: white;
3175
+ }
3176
+
3177
+ .data-panel-chip.is-copied {
3178
+ background: var(--editor-success, #10b981);
3179
+ color: white;
3180
+ }
3181
+
3182
+ /* Add card --------------------------------------------------- */
3183
+
3184
+ .data-panel-add-card {
3185
+ background: var(--editor-surface);
3186
+ border: 1px solid var(--editor-primary);
3187
+ box-shadow: 0 0 0 1px var(--editor-primary-light);
3188
+ border-radius: 8px;
3189
+ padding: 0.75rem;
3190
+ display: flex;
3191
+ flex-direction: column;
3192
+ gap: 0.625rem;
3193
+ }
3194
+
3195
+ .data-panel-add-card-header {
3196
+ display: flex;
3197
+ align-items: center;
3198
+ justify-content: space-between;
3199
+ }
3200
+
3201
+ .data-panel-add-card-title {
3202
+ font-size: 0.6875rem;
3203
+ font-weight: 600;
3204
+ color: var(--editor-text-muted);
3205
+ text-transform: uppercase;
3206
+ letter-spacing: 0.04em;
3207
+ }
3208
+
3209
+ .data-panel-add-card-close {
3210
+ width: 22px;
3211
+ height: 22px;
3212
+ display: flex;
3213
+ align-items: center;
3214
+ justify-content: center;
3215
+ background: transparent;
3216
+ border: none;
3217
+ border-radius: 4px;
3218
+ color: var(--editor-text-muted);
3219
+ cursor: pointer;
3220
+ transition: color 0.15s ease, background 0.15s ease;
3221
+ }
3222
+
3223
+ .data-panel-add-card-close:hover {
3224
+ color: var(--editor-text);
3225
+ background: var(--editor-surface-hover);
3226
+ }
3227
+
3228
+ .data-panel-add-card-params {
3229
+ display: flex;
3230
+ flex-direction: column;
3231
+ gap: 0.5rem;
3232
+ }
3233
+
3234
+ .data-panel-add-card-actions {
3235
+ display: flex;
3236
+ justify-content: flex-end;
3237
+ gap: 0.5rem;
3238
+ margin-top: 0.25rem;
3239
+ }
3240
+
3241
+ /* Form fields ------------------------------------------------ */
3242
+
3243
+ .data-panel-field {
3244
+ display: flex;
3245
+ flex-direction: column;
3246
+ gap: 0.25rem;
3247
+ }
3248
+
3249
+ .data-panel-field-inline {
3250
+ flex-direction: row;
3251
+ align-items: center;
3252
+ gap: 0.5rem;
3253
+ }
3254
+
3255
+ .data-panel-field-label {
3256
+ font-size: 0.6875rem;
3257
+ font-weight: 600;
3258
+ color: var(--editor-text-muted);
3259
+ text-transform: uppercase;
3260
+ letter-spacing: 0.04em;
3261
+ }
3262
+
3263
+ .data-panel-field input[type="text"],
3264
+ .data-panel-field input[type="number"],
3265
+ .data-panel-field select {
3266
+ width: 100%;
3267
+ padding: 0.4375rem 0.625rem;
3268
+ font-size: 0.8125rem;
3269
+ color: var(--editor-text);
3270
+ background: var(--editor-bg);
3271
+ border: 1px solid var(--editor-border);
3272
+ border-radius: 6px;
3273
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
3274
+ box-sizing: border-box;
3275
+ }
3276
+
3277
+ .data-panel-field input[type="text"]:focus,
3278
+ .data-panel-field input[type="number"]:focus,
3279
+ .data-panel-field select:focus {
3280
+ outline: none;
3281
+ border-color: var(--editor-primary);
3282
+ box-shadow: 0 0 0 2px var(--editor-primary-light);
3283
+ }
3284
+
3285
+ .data-panel-field input[type="checkbox"] {
3286
+ accent-color: var(--editor-primary);
3287
+ }
3288
+
3289
+ /* Buttons ---------------------------------------------------- */
3290
+
3291
+ .data-panel-btn {
3292
+ display: inline-flex;
3293
+ align-items: center;
3294
+ justify-content: center;
3295
+ padding: 0.4375rem 0.875rem;
3296
+ font-size: 0.8125rem;
3297
+ font-weight: 500;
3298
+ border: 1px solid transparent;
3299
+ border-radius: 6px;
3300
+ cursor: pointer;
3301
+ transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
3302
+ }
3303
+
3304
+ .data-panel-btn-primary {
3305
+ color: white;
3306
+ background: var(--editor-primary);
3307
+ }
3308
+
3309
+ .data-panel-btn-primary:hover {
3310
+ background: var(--editor-primary-hover);
3311
+ }
3312
+
3313
+ .data-panel-btn-ghost {
3314
+ color: var(--editor-text-muted);
3315
+ background: transparent;
3316
+ border-color: var(--editor-border);
3317
+ }
3318
+
3319
+ .data-panel-btn-ghost:hover {
3320
+ color: var(--editor-text);
3321
+ background: var(--editor-surface-hover);
3322
+ }
3323
+
3324
+ /* Help footer ------------------------------------------------ */
3325
+
3326
+ .data-panel-help {
3327
+ font-size: 0.75rem;
3328
+ color: var(--editor-text-muted);
3329
+ margin: 0;
3330
+ padding: 0 0.125rem;
3331
+ }
3332
+
3333
+ .data-panel-list[data-empty="true"] ~ .data-panel-help {
3334
+ display: none;
3335
+ }
3336
+
3337
+ /* ==================== Preview Modal ==================== */
3338
+
3339
+ .preview-modal {
3340
+ position: fixed;
3341
+ inset: 0;
3342
+ z-index: 9999;
3343
+ display: flex;
3344
+ flex-direction: column;
3345
+ background: rgba(0, 0, 0, 0.75);
3346
+ backdrop-filter: blur(4px);
3347
+ animation: previewModalFadeIn 0.15s ease-out;
3348
+ }
3349
+
3350
+ .preview-modal[hidden] {
3351
+ display: none;
3352
+ }
3353
+
3354
+ @keyframes previewModalFadeIn {
3355
+ from { opacity: 0; }
3356
+ to { opacity: 1; }
3357
+ }
3358
+
3359
+ .preview-modal-toolbar {
3360
+ display: flex;
3361
+ align-items: center;
3362
+ justify-content: space-between;
3363
+ padding: 0.625rem 1rem;
3364
+ background: var(--editor-surface);
3365
+ border-bottom: 1px solid var(--editor-border);
3366
+ flex-shrink: 0;
3367
+ }
3368
+
3369
+ .preview-modal-title {
3370
+ display: flex;
3371
+ align-items: center;
3372
+ gap: 0.5rem;
3373
+ font-size: 0.8125rem;
3374
+ color: var(--editor-text-muted);
3375
+ }
3376
+
3377
+ .preview-modal-title strong {
3378
+ color: var(--editor-text);
3379
+ font-weight: 600;
3380
+ }
3381
+
3382
+ .preview-modal-title svg {
3383
+ color: var(--editor-primary);
3384
+ }
3385
+
3386
+ .preview-modal-close {
3387
+ width: 32px;
3388
+ height: 32px;
3389
+ display: flex;
3390
+ align-items: center;
3391
+ justify-content: center;
3392
+ background: transparent;
3393
+ border: 1px solid var(--editor-border);
3394
+ border-radius: 8px;
3395
+ color: var(--editor-text-muted);
3396
+ cursor: pointer;
3397
+ transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
3398
+ }
3399
+
3400
+ .preview-modal-close:hover {
3401
+ color: var(--editor-text);
3402
+ background: var(--editor-surface-hover);
3403
+ border-color: var(--editor-primary);
3404
+ }
3405
+
3406
+ .preview-modal-body {
3407
+ position: relative;
3408
+ flex: 1;
3409
+ background: #ffffff;
3410
+ overflow: hidden;
3411
+ }
3412
+
3413
+ .preview-modal-iframe {
3414
+ width: 100%;
3415
+ height: 100%;
3416
+ border: 0;
3417
+ display: block;
3418
+ background: #ffffff;
3419
+ }
3420
+
3421
+ .preview-modal-loading,
3422
+ .preview-modal-error {
3423
+ position: absolute;
3424
+ inset: 0;
3425
+ display: flex;
3426
+ flex-direction: column;
3427
+ align-items: center;
3428
+ justify-content: center;
3429
+ gap: 0.75rem;
3430
+ background: var(--editor-bg);
3431
+ color: var(--editor-text-muted);
3432
+ font-size: 0.875rem;
3433
+ }
3434
+
3435
+ .preview-modal-loading[hidden],
3436
+ .preview-modal-error[hidden] {
3437
+ display: none;
3438
+ }
3439
+
3440
+ .preview-modal-spinner {
3441
+ width: 32px;
3442
+ height: 32px;
3443
+ border: 3px solid var(--editor-border);
3444
+ border-top-color: var(--editor-primary);
3445
+ border-radius: 50%;
3446
+ animation: previewModalSpin 0.8s linear infinite;
3447
+ }
3448
+
3449
+ @keyframes previewModalSpin {
3450
+ to { transform: rotate(360deg); }
3451
+ }
3452
+
3453
+ .preview-modal-error svg {
3454
+ color: var(--editor-danger, #ef4444);
3455
+ }
3456
+
3457
+ .preview-modal-error h3 {
3458
+ margin: 0;
3459
+ font-size: 1rem;
3460
+ font-weight: 600;
3461
+ color: var(--editor-text);
3462
+ }
3463
+
3464
+ .preview-modal-error p {
3465
+ margin: 0;
3466
+ max-width: 480px;
3467
+ padding: 0 1rem;
3468
+ text-align: center;
3469
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
3470
+ font-size: 0.8125rem;
3471
+ color: var(--editor-text-muted);
3472
+ }
3473
+
3474
+ /* ==================== Template error banner ==================== */
3475
+ .ac-error-banner {
3476
+ display: flex;
3477
+ align-items: center;
3478
+ justify-content: space-between;
3479
+ gap: 8px;
3480
+ background: #dc2626;
3481
+ color: #fff;
3482
+ padding: 8px 12px;
3483
+ font: 13px ui-monospace, SFMono-Regular, Menlo, monospace;
3484
+ }
3485
+ .ac-error-banner-close {
3486
+ background: transparent;
3487
+ border: 0;
3488
+ color: #fff;
3489
+ font-size: 18px;
3490
+ line-height: 1;
3491
+ cursor: pointer;
3492
+ padding: 0 4px;
3493
+ }
3494
+
3495
+ /* ==================== Data panel: sample data ==================== */
3496
+ .data-panel-sample { margin-top: 6px; font-size: 12px; }
3497
+ .data-panel-sample summary { cursor: pointer; opacity: 0.8; }
3498
+ .data-panel-sample-body {
3499
+ margin: 6px 0 0;
3500
+ max-height: 180px;
3501
+ overflow: auto;
3502
+ padding: 6px 8px;
3503
+ border-radius: 4px;
3504
+ background: rgba(0, 0, 0, 0.25);
3505
+ font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
3506
+ white-space: pre-wrap;
3507
+ word-break: break-word;
3508
+ }
3509
+
3510
+ /* ==================== Data panel: messages ==================== */
3511
+ .data-panel-message {
3512
+ margin: 0;
3513
+ padding: 0.5rem 0.625rem;
3514
+ border-radius: 6px;
3515
+ font-size: 0.75rem;
3516
+ color: #fecaca;
3517
+ background: rgba(220, 38, 38, 0.15);
3518
+ border: 1px solid rgba(220, 38, 38, 0.4);
3519
+ }
3520
+ .data-panel-field-error {
3521
+ display: block;
3522
+ margin-top: 0.25rem;
3523
+ font-size: 0.6875rem;
3524
+ color: #fca5a5;
3525
+ }
3526
+
3527
+ /* ==================== Data panel: dynamic toggle ==================== */
3528
+ .data-panel-toggle {
3529
+ display: flex;
3530
+ align-items: center;
3531
+ gap: 0.5rem;
3532
+ font-size: 0.8125rem;
3533
+ color: var(--editor-text);
3534
+ cursor: pointer;
3535
+ }
3536
+ .data-panel-notice {
3537
+ margin: 0;
3538
+ padding: 0.5rem 0.625rem;
3539
+ border-radius: 6px;
3540
+ font-size: 0.75rem;
3541
+ line-height: 1.45;
3542
+ color: #fde68a;
3543
+ background: rgba(245, 158, 11, 0.12);
3544
+ border: 1px solid rgba(245, 158, 11, 0.4);
3545
+ }
3546
+ .data-panel-notice code {
3547
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
3548
+ }
3549
+
3550
+ /* ==================== Live data switch ==================== */
3551
+ #btn-live-data.active {
3552
+ background: rgba(34, 197, 94, 0.15);
3553
+ color: #4ade80;
3554
+ border-color: rgba(34, 197, 94, 0.5);
3555
+ }
@@ -8,9 +8,17 @@ module ActiveCanvas
8
8
 
9
9
  before_action :enforce_authentication_configured
10
10
  before_action :active_canvas_authenticate_admin
11
+ before_action :set_current_editor
11
12
 
12
13
  private
13
14
 
15
+ # A readable label for version history: a name or email when the host's
16
+ # user object has one, otherwise its to_s.
17
+ def set_current_editor
18
+ user = active_canvas_current_user
19
+ ActiveCanvas::Current.editor = user && [ user.try(:name), user.try(:email), user.to_s ].map(&:presence).compact.first
20
+ end
21
+
14
22
  def enforce_authentication_configured
15
23
  # Skip if inheriting from a custom parent controller (assumes parent handles auth)
16
24
  return if ActiveCanvas.config.admin_parent_controller != "ActionController::Base"