alembic 0.1.0 → 0.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -22
  3. data/app/assets/builds/alembic/page_builder.css +1 -1
  4. data/app/assets/builds/alembic/page_builder.js +3 -3
  5. data/app/controllers/alembic/application_controller.rb +0 -16
  6. data/app/controllers/alembic/flows_controller.rb +6 -90
  7. data/app/controllers/alembic/manage/base_controller.rb +1 -0
  8. data/app/controllers/alembic/manage/flows_controller.rb +7 -44
  9. data/app/controllers/alembic/manage/pages_controller.rb +8 -1
  10. data/app/controllers/alembic/manage/previews_controller.rb +4 -19
  11. data/app/controllers/alembic/pages_controller.rb +19 -0
  12. data/app/controllers/concerns/alembic/summarizes.rb +38 -0
  13. data/app/helpers/alembic/pages_helper.rb +39 -0
  14. data/app/models/alembic/flow/definition_summary.rb +9 -0
  15. data/app/models/alembic/flow/run_summary.rb +10 -0
  16. data/app/models/alembic/flow/summaries.rb +36 -5
  17. data/app/models/alembic/flow/summary_version.rb +1 -1
  18. data/app/models/alembic/outputs/grouped.rb +1 -1
  19. data/app/models/alembic/outputs/percentage.rb +1 -1
  20. data/app/models/alembic/outputs/weighted_sum.rb +1 -1
  21. data/app/models/alembic/page/drawing.rb +17 -0
  22. data/app/models/alembic/page/options.rb +46 -0
  23. data/app/models/alembic/page/version.rb +13 -0
  24. data/app/models/alembic/page.rb +32 -0
  25. data/app/views/alembic/flows/complete.html.erb +3 -3
  26. data/app/views/alembic/flows/show.html.erb +6 -6
  27. data/app/views/alembic/manage/flows/edit.html.erb +10 -10
  28. data/app/views/alembic/manage/pages/_block_content.html.erb +1 -1
  29. data/app/views/alembic/manage/pages/index.html.erb +4 -1
  30. data/app/views/alembic/manage/pages/show.html.erb +7 -1
  31. data/app/views/alembic/pages/show.html.erb +5 -0
  32. data/config/routes.rb +8 -19
  33. data/db/migrate/20260825224538_remove_published_version_from_alembic_diagnostics.rb +21 -1
  34. data/db/migrate/20260922120000_add_slug_to_alembic_pages.rb +6 -0
  35. data/db/migrate/20260922130000_create_alembic_page_versions.rb +16 -0
  36. data/db/migrate/20260924220000_move_flows_onto_easy_flow.rb +166 -0
  37. data/lib/alembic/engine.rb +2 -11
  38. data/lib/alembic/version.rb +1 -1
  39. data/lib/alembic.rb +30 -6
  40. metadata +35 -71
  41. data/app/assets/builds/alembic/canvas.js +0 -1
  42. data/app/controllers/alembic/manage/canvas_controller.rb +0 -183
  43. data/app/controllers/alembic/manage/definitions_controller.rb +0 -15
  44. data/app/controllers/alembic/manage/versions_controller.rb +0 -22
  45. data/app/controllers/concerns/alembic/manage/draws_canvas.rb +0 -35
  46. data/app/javascript/alembic/canvas/Canvas.jsx +0 -197
  47. data/app/javascript/alembic/canvas/Connector.jsx +0 -30
  48. data/app/javascript/alembic/canvas/ConnectorLayer.jsx +0 -31
  49. data/app/javascript/alembic/canvas/Control.jsx +0 -42
  50. data/app/javascript/alembic/canvas/Inspector.jsx +0 -44
  51. data/app/javascript/alembic/canvas/Panel.jsx +0 -67
  52. data/app/javascript/alembic/canvas/Placeholder.jsx +0 -24
  53. data/app/javascript/alembic/canvas/Port.jsx +0 -16
  54. data/app/javascript/alembic/canvas/Records.jsx +0 -34
  55. data/app/javascript/alembic/canvas/StepCard.jsx +0 -57
  56. data/app/javascript/alembic/canvas/Toolbar.jsx +0 -19
  57. data/app/javascript/alembic/canvas/TypePicker.jsx +0 -21
  58. data/app/javascript/alembic/canvas/changes.js +0 -5
  59. data/app/javascript/alembic/canvas/choices.js +0 -4
  60. data/app/javascript/alembic/canvas/flow.js +0 -7
  61. data/app/javascript/alembic/canvas/flowSender.js +0 -22
  62. data/app/javascript/alembic/canvas/ids.js +0 -7
  63. data/app/javascript/alembic/canvas/rows.js +0 -2
  64. data/app/javascript/alembic/canvas/styles.js +0 -3
  65. data/app/javascript/alembic/canvas/useConnectors.js +0 -75
  66. data/app/javascript/alembic/canvas/useFlow.js +0 -17
  67. data/app/javascript/alembic/canvas.jsx +0 -13
  68. data/app/models/alembic/asked.rb +0 -3
  69. data/app/models/alembic/choice.rb +0 -7
  70. data/app/models/alembic/flow/admission.rb +0 -19
  71. data/app/models/alembic/flow/canvas.rb +0 -167
  72. data/app/models/alembic/flow/change.rb +0 -18
  73. data/app/models/alembic/flow/condition.rb +0 -24
  74. data/app/models/alembic/flow/definition.rb +0 -132
  75. data/app/models/alembic/flow/digest.rb +0 -116
  76. data/app/models/alembic/flow/document.rb +0 -131
  77. data/app/models/alembic/flow/drawing.rb +0 -15
  78. data/app/models/alembic/flow/edge.rb +0 -9
  79. data/app/models/alembic/flow/edit_history.rb +0 -75
  80. data/app/models/alembic/flow/layout.rb +0 -88
  81. data/app/models/alembic/flow/name.rb +0 -15
  82. data/app/models/alembic/flow/node.rb +0 -5
  83. data/app/models/alembic/flow/output.rb +0 -23
  84. data/app/models/alembic/flow/progress/kept.rb +0 -38
  85. data/app/models/alembic/flow/progress/loose.rb +0 -43
  86. data/app/models/alembic/flow/progress.rb +0 -11
  87. data/app/models/alembic/flow/registry.rb +0 -25
  88. data/app/models/alembic/flow/run.rb +0 -54
  89. data/app/models/alembic/flow/runner.rb +0 -65
  90. data/app/models/alembic/flow/settings.rb +0 -80
  91. data/app/models/alembic/flow/start.rb +0 -13
  92. data/app/models/alembic/flow/step.rb +0 -42
  93. data/app/models/alembic/flow/step_type/declaration.rb +0 -118
  94. data/app/models/alembic/flow/step_type.rb +0 -69
  95. data/app/models/alembic/flow/switch.rb +0 -17
  96. data/app/models/alembic/flow/terminal.rb +0 -13
  97. data/app/models/alembic/flow/validator.rb +0 -171
  98. data/app/models/alembic/flow/version.rb +0 -26
  99. data/app/models/alembic/flow/violation.rb +0 -9
  100. data/app/models/alembic/flow.rb +0 -44
  101. data/app/models/alembic/runner.rb +0 -13
  102. data/app/models/alembic/steps/question.rb +0 -52
  103. data/app/views/alembic/flow/steps/_choosing.html.erb +0 -9
  104. data/app/views/alembic/flows/step.html.erb +0 -27
  105. data/app/views/alembic/manage/definitions/edit.html.erb +0 -13
  106. data/app/views/alembic/manage/flows/index.html.erb +0 -32
  107. data/app/views/alembic/manage/flows/show.html.erb +0 -26
  108. data/app/views/alembic/manage/versions/index.html.erb +0 -41
@@ -11,9 +11,12 @@
11
11
 
12
12
  <%= ui_section(title: "Create a page") do %>
13
13
  <%= ui_form(action: alembic.manage_pages_path, method: :post) do %>
14
- <label class="block text-sm font-medium mb-1">Name</label>
14
+ <label class="ks-label mb-1">Name</label>
15
15
  <%= ui_input(name: "page[name]", placeholder: "e.g. Welcome") %>
16
16
 
17
+ <label class="ks-label mb-1">Address</label>
18
+ <%= ui_input(name: "page[slug]", placeholder: "e.g. welcome") %>
19
+
17
20
  <%= ui_button(label: "Create", type: :submit, variant: :secondary) %>
18
21
  <% end %>
19
22
  <% end %>
@@ -4,7 +4,13 @@
4
4
  <%= javascript_include_tag "alembic/page_builder", defer: true %>
5
5
  <% end %>
6
6
 
7
- <%= react_ui("alembic/page-builder", @payload.merge(token: form_authenticity_token)) %>
7
+ <%= ui_form(action: alembic.publish_manage_page_path(@page), method: :post) do %>
8
+ <%= ui_button(label: "Publish", type: :submit, variant: :secondary) %>
9
+ <% end %>
10
+
11
+ <%= ui_panel do %>
12
+ <%= react_ui("alembic/page-builder", @payload.merge(token: form_authenticity_token)) %>
13
+ <% end %>
8
14
 
9
15
  <%= block_contents(@page.blocks) do |block| %>
10
16
  <%= render "alembic/manage/pages/block_content", block: block %>
@@ -0,0 +1,5 @@
1
+ <% content_for :head do %>
2
+ <%= stylesheet_link_tag "ks_blocks" %>
3
+ <% end %>
4
+
5
+ <%= drawn_page(@page) %>
data/config/routes.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  Alembic::Engine.routes.draw do
2
2
  namespace :manage do
3
3
  resources :pages, only: [ :index, :create, :show ] do
4
+ post "publish", on: :member
4
5
  get "layout", to: "page_blocks#layout", as: :layout
5
6
  post "blocks", to: "page_blocks#add_block", as: :blocks
6
7
  patch "blocks", to: "page_blocks#place_blocks"
@@ -8,35 +9,23 @@ Alembic::Engine.routes.draw do
8
9
  delete "blocks/:block_id", to: "page_blocks#remove_block", as: :block
9
10
  end
10
11
 
11
- resources :flows, only: [ :index, :create, :show, :edit, :update, :destroy ] do
12
- resource :definition, only: [ :edit, :update ]
12
+ resources :flows, only: [ :edit, :update ] do
13
13
  resource :preview, only: :show, controller: "previews" do
14
14
  get :step
15
15
  end
16
- resources :versions, only: :index do
17
- post :return, on: :member
18
- end
19
-
20
- resource :canvas, only: :show, controller: "canvas" do
21
- post "steps", action: :add_step
22
- patch "steps/:step", action: :configure_step
23
- delete "steps/:step", action: :remove_step
24
- patch "steps/:step/move", action: :move_step
25
- post "versions", action: :create
26
- post "publish", action: :publish
27
- patch "details", action: :details
28
- post "undo", action: :undo
29
- post "redo", action: :redo
30
- post "edges", action: :connect
31
- delete "edges", action: :disconnect
32
- end
33
16
  end
34
17
  end
35
18
 
19
+ constraints(->(request) { request.path_info.match?(%r{\A/manage/flows(/|\z)}) }) do
20
+ mount EasyFlow::Engine, at: "/", defaults: { easy_flow_host: Alembic::FLOW_HOST.to_s }
21
+ end
22
+
36
23
  post ":slug/runs", to: "flows#start", as: :flow_runs
37
24
  get "runs/:id", to: "flows#step", as: :run
38
25
  patch "runs/:id", to: "flows#update"
39
26
 
27
+ get "pages/:slug", to: "pages#show", as: :page
28
+
40
29
  get ":slug", to: "flows#show", as: :flow
41
30
  get ":slug/step", to: "flows#step", as: :flow_step
42
31
  end
@@ -1,5 +1,25 @@
1
1
  class RemovePublishedVersionFromAlembicDiagnostics < ActiveRecord::Migration[8.1]
2
- def change
2
+ class Diagnostic < ActiveRecord::Base
3
+ self.table_name = "alembic_diagnostics"
4
+ end
5
+
6
+ class DefinitionVersion < ActiveRecord::Base
7
+ self.table_name = "alembic_definition_versions"
8
+ end
9
+
10
+ def up
11
+ [ Diagnostic, DefinitionVersion ].each(&:reset_column_information)
12
+ DefinitionVersion.where(id: Diagnostic.where.not(published_version_id: nil).select(:published_version_id)).update_all(status: "live")
13
+
3
14
  remove_reference :alembic_diagnostics, :published_version, foreign_key: { to_table: :alembic_definition_versions }
4
15
  end
16
+
17
+ def down
18
+ add_reference :alembic_diagnostics, :published_version, foreign_key: { to_table: :alembic_definition_versions }
19
+
20
+ [ Diagnostic, DefinitionVersion ].each(&:reset_column_information)
21
+ DefinitionVersion.where(status: "live").find_each do |version|
22
+ Diagnostic.where(id: version.diagnostic_id).update_all(published_version_id: version.id)
23
+ end
24
+ end
5
25
  end
@@ -0,0 +1,6 @@
1
+ class AddSlugToAlembicPages < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :alembic_pages, :slug, :string
4
+ add_index :alembic_pages, :slug, unique: true
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ class CreateAlembicPageVersions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_page_versions do |t|
4
+ t.references :page, null: false, foreign_key: { to_table: :alembic_pages }
5
+ t.integer :number, null: false
6
+ t.json :blocks, default: [], null: false
7
+ t.string :status, null: false, default: "draft"
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :alembic_page_versions, [ :page_id, :number ], unique: true
13
+ add_index :alembic_page_versions, :page_id, unique: true, where: "status = 'live'",
14
+ name: "index_alembic_page_versions_on_one_live_per_page"
15
+ end
16
+ end
@@ -0,0 +1,166 @@
1
+ class MoveFlowsOntoEasyFlow < ActiveRecord::Migration[8.1]
2
+ HOST = "alembic"
3
+ FLOW_COLUMNS = %w[slug title start_label kind status persists document definition_cursor
4
+ changes_since_version undo_history undone_changes created_at updated_at].freeze
5
+
6
+ class OldFlow < ActiveRecord::Base
7
+ self.table_name = "alembic_flows"
8
+ end
9
+
10
+ class Definition < ActiveRecord::Base
11
+ self.table_name = "easy_flow_definitions"
12
+ end
13
+
14
+ class OldVersion < ActiveRecord::Base
15
+ self.table_name = "alembic_flow_versions"
16
+ end
17
+
18
+ class Version < ActiveRecord::Base
19
+ self.table_name = "easy_flow_versions"
20
+ end
21
+
22
+ class OldRun < ActiveRecord::Base
23
+ self.table_name = "alembic_flow_runs"
24
+ end
25
+
26
+ class Run < ActiveRecord::Base
27
+ self.table_name = "easy_flow_runs"
28
+ end
29
+
30
+ class SummaryVersion < ActiveRecord::Base
31
+ self.table_name = "alembic_flow_summaries"
32
+ end
33
+
34
+ class DefinitionSummary < ActiveRecord::Base
35
+ self.table_name = "alembic_flow_definition_summaries"
36
+ end
37
+
38
+ class RunSummary < ActiveRecord::Base
39
+ self.table_name = "alembic_flow_run_summaries"
40
+ end
41
+
42
+ def up
43
+ [ OldFlow, Definition, OldVersion, Version, OldRun, Run, SummaryVersion ].each(&:reset_column_information)
44
+ flows = copy_flows
45
+ versions = copy_versions(flows)
46
+ runs = copy_runs(flows, versions)
47
+ summaries = OldFlow.pluck(:id, :summary, :summary_cursor)
48
+ pins = OldRun.where.not(summary_version_id: nil).pluck(:id, :summary_version_id)
49
+
50
+ drop_table :alembic_flow_runs
51
+ remove_foreign_key :alembic_flow_summaries, column: :flow_id
52
+ repoint_summary_versions(flows)
53
+ drop_table :alembic_flow_versions
54
+ drop_table :alembic_flows
55
+ add_foreign_key :alembic_flow_summaries, :easy_flow_definitions, column: :flow_id, on_delete: :cascade
56
+
57
+ create_table :alembic_flow_definition_summaries do |t|
58
+ t.references :flow, null: false, index: { unique: true },
59
+ foreign_key: { to_table: :easy_flow_definitions, on_delete: :cascade }
60
+ t.text :summary
61
+ t.integer :summary_cursor
62
+ t.timestamps
63
+ end
64
+ restore_summaries(summaries, flows)
65
+
66
+ create_table :alembic_flow_run_summaries do |t|
67
+ t.references :run, null: false, index: { unique: true },
68
+ foreign_key: { to_table: :easy_flow_runs, on_delete: :cascade }
69
+ t.references :summary_version, null: false,
70
+ foreign_key: { to_table: :alembic_flow_summaries, on_delete: :cascade }
71
+ t.timestamps
72
+ end
73
+ restore_pins(pins, runs)
74
+ end
75
+
76
+ def down
77
+ drop_table :alembic_flow_run_summaries
78
+ drop_table :alembic_flow_definition_summaries
79
+ remove_foreign_key :alembic_flow_summaries, column: :flow_id
80
+ execute "DELETE FROM alembic_flow_summaries"
81
+
82
+ create_table :alembic_flows do |t|
83
+ t.string :slug
84
+ t.string :title
85
+ t.text :summary
86
+ t.string :start_label
87
+ t.string :kind
88
+ t.string :status, null: false, default: "active"
89
+ t.string :persists, null: false, default: "unsaved"
90
+ t.json :document
91
+ t.integer :definition_cursor
92
+ t.integer :summary_cursor
93
+ t.json :changes_since_version
94
+ t.json :undo_history
95
+ t.json :undone_changes
96
+ t.timestamps
97
+ end
98
+ add_index :alembic_flows, :slug, unique: true
99
+
100
+ create_table :alembic_flow_versions do |t|
101
+ t.references :flow, null: false, foreign_key: { to_table: :alembic_flows }
102
+ t.integer :number, null: false
103
+ t.json :definition
104
+ t.json :changes_captured
105
+ t.string :status, null: false, default: "draft"
106
+ t.datetime :created_at, null: false
107
+ end
108
+ add_index :alembic_flow_versions, [ :flow_id, :number ], unique: true
109
+ add_index :alembic_flow_versions, :flow_id, unique: true, where: "status = 'live'",
110
+ name: "index_alembic_flow_versions_on_one_live_per_flow"
111
+
112
+ add_foreign_key :alembic_flow_summaries, :alembic_flows, column: :flow_id
113
+
114
+ create_table :alembic_flow_runs do |t|
115
+ t.references :flow, null: false, foreign_key: { to_table: :alembic_flows }
116
+ t.references :definition_version, null: false, foreign_key: { to_table: :alembic_flow_versions }
117
+ t.references :summary_version, foreign_key: { to_table: :alembic_flow_summaries }
118
+ t.references :owner, polymorphic: true
119
+ t.json :recorded
120
+ t.string :label
121
+ t.string :status
122
+ t.timestamps
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def copy_flows
129
+ OldFlow.order(:id).to_h do |flow|
130
+ [ flow.id, Definition.create!(flow.attributes.slice(*FLOW_COLUMNS).merge("host" => HOST)).id ]
131
+ end
132
+ end
133
+
134
+ def copy_versions(flows)
135
+ OldVersion.order(:id).to_h do |version|
136
+ copied = version.attributes.slice("number", "definition", "changes_captured", "status", "created_at")
137
+ [ version.id, Version.create!(copied.merge("flow_id" => flows.fetch(version.flow_id))).id ]
138
+ end
139
+ end
140
+
141
+ def copy_runs(flows, versions)
142
+ OldRun.order(:id).to_h do |run|
143
+ copied = run.attributes.slice("owner_type", "owner_id", "recorded", "label", "status", "created_at", "updated_at")
144
+ moved = copied.merge("flow_id" => flows.fetch(run.flow_id), "definition_version_id" => versions.fetch(run.definition_version_id))
145
+ [ run.id, Run.create!(moved).id ]
146
+ end
147
+ end
148
+
149
+ def restore_summaries(summaries, flows)
150
+ DefinitionSummary.reset_column_information
151
+ summaries.each do |flow_id, summary, cursor|
152
+ next if summary.nil? && cursor.nil?
153
+
154
+ DefinitionSummary.create!(flow_id: flows.fetch(flow_id), summary: summary, summary_cursor: cursor)
155
+ end
156
+ end
157
+
158
+ def restore_pins(pins, runs)
159
+ RunSummary.reset_column_information
160
+ pins.each { |run_id, summary_version_id| RunSummary.create!(run_id: runs.fetch(run_id), summary_version_id: summary_version_id) }
161
+ end
162
+
163
+ def repoint_summary_versions(flows)
164
+ SummaryVersion.find_each { |version| version.update_columns(flow_id: flows.fetch(version.flow_id)) }
165
+ end
166
+ end
@@ -1,6 +1,7 @@
1
1
  require "alembic/leftover_stylesheet"
2
2
  require "keystone_ui-blocks"
3
3
  require "keystone_ui-react"
4
+ require "easy_flow"
4
5
 
5
6
  module Alembic
6
7
  class Engine < ::Rails::Engine
@@ -19,18 +20,8 @@ module Alembic
19
20
  KeystoneUi.configuration.tailwind_sources << root.join("app/assets/builds/alembic/*.js").to_s
20
21
  end
21
22
 
22
- initializer "alembic.step_types" do |app|
23
+ initializer "alembic.output_types" do |app|
23
24
  app.config.to_prepare do
24
- Alembic::Flow::Start.register
25
- Alembic::Flow::Terminal.register
26
-
27
- Alembic::Steps::Question.register
28
- Alembic::Flow::Condition.register
29
- Alembic::Flow::Switch.register
30
-
31
- Alembic::Flow.check(:unrouted_value)
32
- Alembic::Flow.check(:unfollowed_path)
33
- Alembic::Flow.check(:dead_end)
34
25
  Alembic::Outputs::WeightedSum.register
35
26
  Alembic::Outputs::Percentage.register
36
27
  Alembic::Outputs::Grouped.register
@@ -1,3 +1,3 @@
1
1
  module Alembic
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/alembic.rb CHANGED
@@ -3,14 +3,36 @@ require "alembic/version"
3
3
  require "alembic/engine"
4
4
 
5
5
  module Alembic
6
- class NotPublished < StandardError; end
7
- class NotPermitted < StandardError; end
8
- class OutOfService < StandardError; end
9
- class Withdrawn < StandardError; end
6
+ NotPublished = EasyFlow::NotPublished
7
+ NotPermitted = EasyFlow::NotPermitted
8
+ OutOfService = EasyFlow::OutOfService
9
+ Withdrawn = EasyFlow::Withdrawn
10
+
11
+ FLOW_HOST = :alembic
12
+ FLOW_HOST_SETTINGS = %i[layout admin_layout admin_authentication_method
13
+ visitor_authorization_method refusal_method].freeze
10
14
 
11
15
  class << self
12
- attr_writer :layout, :base_controller, :admin_layout
13
- attr_accessor :lead_partial, :admin_authentication_method, :visitor_authorization_method, :refusal_method
16
+ attr_accessor :lead_partial
17
+ attr_reader :admin_authentication_method, :visitor_authorization_method, :refusal_method
18
+
19
+ FLOW_HOST_SETTINGS.each do |name|
20
+ define_method(:"#{name}=") do |value|
21
+ instance_variable_set(:"@#{name}", value)
22
+ set_up_flow_host
23
+ end
24
+ end
25
+
26
+ def set_up_flow_host
27
+ EasyFlow.host(FLOW_HOST) do |host|
28
+ FLOW_HOST_SETTINGS.each { |name| host.public_send(:"#{name}=", public_send(name)) }
29
+ end
30
+ end
31
+
32
+ def base_controller=(value)
33
+ @base_controller = value
34
+ EasyFlow.base_controller = value
35
+ end
14
36
 
15
37
  # The host app sets this to render the engine inside its own layout
16
38
  # (e.g. "marketing"). Defaults to the engine's own layout.
@@ -31,4 +53,6 @@ module Alembic
31
53
  @admin_layout || "application"
32
54
  end
33
55
  end
56
+
57
+ set_up_flow_host
34
58
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alembic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - tylercschneider
@@ -43,14 +43,34 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 0.2.0
46
+ version: 0.7.0
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: 0.2.0
53
+ version: 0.7.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: easy_flow
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.4'
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.4.1
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '0.4'
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.4.1
54
74
  description: Alembic builds and runs data-driven interactive diagnostics — scored
55
75
  assessments and branching guides — that place a visitor on an outcome and capture
56
76
  the result. A runtime engine plus a UI builder.
@@ -63,85 +83,30 @@ files:
63
83
  - MIT-LICENSE
64
84
  - README.md
65
85
  - Rakefile
66
- - app/assets/builds/alembic/canvas.js
67
86
  - app/assets/builds/alembic/page_builder.css
68
87
  - app/assets/builds/alembic/page_builder.js
69
88
  - app/assets/stylesheets/alembic/application.css
70
89
  - app/controllers/alembic/application_controller.rb
71
90
  - app/controllers/alembic/flows_controller.rb
72
91
  - app/controllers/alembic/manage/base_controller.rb
73
- - app/controllers/alembic/manage/canvas_controller.rb
74
- - app/controllers/alembic/manage/definitions_controller.rb
75
92
  - app/controllers/alembic/manage/flows_controller.rb
76
93
  - app/controllers/alembic/manage/page_blocks_controller.rb
77
94
  - app/controllers/alembic/manage/pages_controller.rb
78
95
  - app/controllers/alembic/manage/previews_controller.rb
79
- - app/controllers/alembic/manage/versions_controller.rb
96
+ - app/controllers/alembic/pages_controller.rb
80
97
  - app/controllers/concerns/alembic/manage/authenticates_admin.rb
81
- - app/controllers/concerns/alembic/manage/draws_canvas.rb
98
+ - app/controllers/concerns/alembic/summarizes.rb
82
99
  - app/helpers/alembic/application_helper.rb
83
- - app/javascript/alembic/canvas.jsx
84
- - app/javascript/alembic/canvas/Canvas.jsx
85
- - app/javascript/alembic/canvas/Connector.jsx
86
- - app/javascript/alembic/canvas/ConnectorLayer.jsx
87
- - app/javascript/alembic/canvas/Control.jsx
88
- - app/javascript/alembic/canvas/Inspector.jsx
89
- - app/javascript/alembic/canvas/Panel.jsx
90
- - app/javascript/alembic/canvas/Placeholder.jsx
91
- - app/javascript/alembic/canvas/Port.jsx
92
- - app/javascript/alembic/canvas/Records.jsx
93
- - app/javascript/alembic/canvas/StepCard.jsx
94
- - app/javascript/alembic/canvas/Toolbar.jsx
95
- - app/javascript/alembic/canvas/TypePicker.jsx
96
- - app/javascript/alembic/canvas/changes.js
97
- - app/javascript/alembic/canvas/choices.js
98
- - app/javascript/alembic/canvas/flow.js
99
- - app/javascript/alembic/canvas/flowSender.js
100
- - app/javascript/alembic/canvas/ids.js
101
- - app/javascript/alembic/canvas/rows.js
102
- - app/javascript/alembic/canvas/styles.js
103
- - app/javascript/alembic/canvas/useConnectors.js
104
- - app/javascript/alembic/canvas/useFlow.js
100
+ - app/helpers/alembic/pages_helper.rb
105
101
  - app/javascript/alembic/page_builder.jsx
106
102
  - app/javascript/alembic/page_builder/PageBuilder.jsx
107
103
  - app/jobs/alembic/application_job.rb
108
104
  - app/mailers/alembic/application_mailer.rb
109
105
  - app/models/alembic/application_record.rb
110
- - app/models/alembic/asked.rb
111
- - app/models/alembic/choice.rb
112
- - app/models/alembic/flow.rb
113
- - app/models/alembic/flow/admission.rb
114
- - app/models/alembic/flow/canvas.rb
115
- - app/models/alembic/flow/change.rb
116
- - app/models/alembic/flow/condition.rb
117
- - app/models/alembic/flow/definition.rb
118
- - app/models/alembic/flow/digest.rb
119
- - app/models/alembic/flow/document.rb
120
- - app/models/alembic/flow/drawing.rb
121
- - app/models/alembic/flow/edge.rb
122
- - app/models/alembic/flow/edit_history.rb
123
- - app/models/alembic/flow/layout.rb
124
- - app/models/alembic/flow/name.rb
125
- - app/models/alembic/flow/node.rb
126
- - app/models/alembic/flow/output.rb
127
- - app/models/alembic/flow/progress.rb
128
- - app/models/alembic/flow/progress/kept.rb
129
- - app/models/alembic/flow/progress/loose.rb
130
- - app/models/alembic/flow/registry.rb
131
- - app/models/alembic/flow/run.rb
132
- - app/models/alembic/flow/runner.rb
133
- - app/models/alembic/flow/settings.rb
134
- - app/models/alembic/flow/start.rb
135
- - app/models/alembic/flow/step.rb
136
- - app/models/alembic/flow/step_type.rb
137
- - app/models/alembic/flow/step_type/declaration.rb
106
+ - app/models/alembic/flow/definition_summary.rb
107
+ - app/models/alembic/flow/run_summary.rb
138
108
  - app/models/alembic/flow/summaries.rb
139
109
  - app/models/alembic/flow/summary_version.rb
140
- - app/models/alembic/flow/switch.rb
141
- - app/models/alembic/flow/terminal.rb
142
- - app/models/alembic/flow/validator.rb
143
- - app/models/alembic/flow/version.rb
144
- - app/models/alembic/flow/violation.rb
145
110
  - app/models/alembic/outputs/band.rb
146
111
  - app/models/alembic/outputs/grouped.rb
147
112
  - app/models/alembic/outputs/lowest.rb
@@ -149,26 +114,22 @@ files:
149
114
  - app/models/alembic/outputs/tally.rb
150
115
  - app/models/alembic/outputs/weighted_sum.rb
151
116
  - app/models/alembic/page.rb
152
- - app/models/alembic/runner.rb
153
- - app/models/alembic/steps/question.rb
117
+ - app/models/alembic/page/drawing.rb
118
+ - app/models/alembic/page/options.rb
119
+ - app/models/alembic/page/version.rb
154
120
  - app/models/alembic/summary.rb
155
121
  - app/models/alembic/summary/output_type.rb
156
122
  - app/models/alembic/summary/registry.rb
157
123
  - app/models/alembic/summary/report.rb
158
124
  - app/models/alembic/summary/result.rb
159
125
  - app/models/alembic/summary/run.rb
160
- - app/views/alembic/flow/steps/_choosing.html.erb
161
126
  - app/views/alembic/flows/complete.html.erb
162
127
  - app/views/alembic/flows/show.html.erb
163
- - app/views/alembic/flows/step.html.erb
164
- - app/views/alembic/manage/definitions/edit.html.erb
165
128
  - app/views/alembic/manage/flows/edit.html.erb
166
- - app/views/alembic/manage/flows/index.html.erb
167
- - app/views/alembic/manage/flows/show.html.erb
168
129
  - app/views/alembic/manage/pages/_block_content.html.erb
169
130
  - app/views/alembic/manage/pages/index.html.erb
170
131
  - app/views/alembic/manage/pages/show.html.erb
171
- - app/views/alembic/manage/versions/index.html.erb
132
+ - app/views/alembic/pages/show.html.erb
172
133
  - app/views/layouts/alembic/application.html.erb
173
134
  - config/routes.rb
174
135
  - db/migrate/20260604215031_create_alembic_diagnostics.rb
@@ -221,6 +182,9 @@ files:
221
182
  - db/migrate/20260831000000_add_persists_to_alembic_flows.rb
222
183
  - db/migrate/20260914120000_create_alembic_pages.rb
223
184
  - db/migrate/20260915120000_add_blocks_to_alembic_pages.rb
185
+ - db/migrate/20260922120000_add_slug_to_alembic_pages.rb
186
+ - db/migrate/20260922130000_create_alembic_page_versions.rb
187
+ - db/migrate/20260924220000_move_flows_onto_easy_flow.rb
224
188
  - lib/alembic.rb
225
189
  - lib/alembic/engine.rb
226
190
  - lib/alembic/leftover_stylesheet.rb
@@ -1 +0,0 @@
1
- (()=>{var Dt=globalThis.ReactDOM;var{createPortal:ze,createRoot:K,flushSync:Be,hydrateRoot:Ee,preconnect:Te,prefetchDNS:$e,preinit:Ae,preinitModule:De,preload:Pe,preloadModule:Le,requestFormReset:Ie,unstable_batchedUpdates:Me,useFormState:_e,useFormStatus:Oe,version:He}=Dt;var Z=globalThis.React,e=Z,{Children:Fe,Component:Ve,Fragment:We,PureComponent:Ye,StrictMode:Ue,Suspense:Xe,cloneElement:Ge,createContext:qe,createElement:Je,createRef:Ke,forwardRef:Ze,isValidElement:Qe,lazy:Re,memo:to,startTransition:eo,useCallback:oo,useContext:ro,useDebugValue:no,useDeferredValue:so,useEffect:E,useId:ao,useImperativeHandle:lo,useLayoutEffect:Q,useMemo:P,useReducer:io,useRef:j,useState:b,useSyncExternalStore:co,useTransition:mo}=Z;var R=new Map,L=new Map,tt=(t,o)=>R.set(t,o),et=()=>{L.forEach(t=>t.unmount()),L.clear()},ot=(t,o)=>t.querySelectorAll("[data-react-ui]").forEach(s=>{if(L.has(s))return;let r=R.get(s.dataset.reactUi),a=o(s);L.set(s,a),a.render(e.createElement(r,JSON.parse(s.dataset.props)))});var rt=(t,o)=>{let s=()=>ot(t,o);t.addEventListener("turbo:load",s),t.addEventListener("turbo:frame-load",s),t.addEventListener("turbo:before-cache",et),t.readyState==="loading"?t.addEventListener("DOMContentLoaded",s):s()};var Pt="border border-amber-500 bg-white text-amber-700 dark:bg-zinc-900 dark:text-amber-400",Lt="border border-gray-300 bg-white text-gray-500 dark:border-zinc-600 dark:bg-zinc-900 dark:text-gray-400",It="border border-accent-600 bg-accent-600 text-white",Mt=({name:t,connected:o,armed:s,onArm:r,connecting:a})=>e.createElement("button",{onClick:l=>{a||(l.stopPropagation(),r(l))},title:s?"Now choose a step to connect to":"Connect this branch",className:s?It:o?Lt:Pt,style:{padding:"1px 8px",marginRight:4,borderRadius:999,fontSize:11,cursor:"pointer"}},t||"next"),nt=Mt;var _t={width:240,boxSizing:"border-box",borderRadius:8},Ot={padding:"8px 14px",textAlign:"center",fontWeight:600},Ht={fontWeight:600,lineHeight:1.3},jt=(t,o,s)=>{let r=s?"ring-4 ring-accent-600/15":"shadow-sm";return t||s&&!o?`border-accent-600 ${r}`:o?`border-red-600 ${r}`:"border-gray-300 shadow-sm dark:border-zinc-700"},Ft=({node:t,selected:o,armed:s,connecting:r,onSelect:a,onArm:l,onDragEnd:i,onDragStart:f})=>{let m=t.begins_here||t.ends_here,d=t.begins_here,p=t.ends_here?[]:t.ports,u=r&&!d;return e.createElement("div",{ref:t.ref,"data-step":t.id,draggable:!d,onDragStart:c=>{c.dataTransfer.effectAllowed="move",c.dataTransfer.setData("text/plain",t.id),f()},onDragEnd:i,onClick:a,className:`bg-white border-2 dark:bg-zinc-900 ${jt(u,t.violations.length>0,o)}`,style:{..._t,padding:m?0:"12px 14px",cursor:d?"default":r?"crosshair":"grab"}},m&&e.createElement("div",{className:"text-gray-500 dark:text-gray-400",style:Ot},t.label),!m&&e.createElement("div",{style:Ht},t.label),!m&&e.createElement("div",{className:"text-gray-500 dark:text-gray-400",style:{fontSize:11,marginTop:2}},t.type),t.violations.map(c=>e.createElement("div",{key:c.problem+c.detail,className:"text-red-600 dark:text-red-400",style:{fontSize:11,padding:"0 14px 6px"}},c.problem.replace(/_/g," "),c.detail?`: ${c.detail}`:"")),p.length>0&&e.createElement("div",{style:{padding:t.begins_here?"0 14px 8px":"10px 0 0"}},p.map(c=>e.createElement(nt,{key:c,name:c,connected:t.connected.includes(c),connecting:r,armed:s===c,onArm:x=>l(c,x)}))))},F=Ft;var st=(t,o)=>t.includes(o)?t.filter(s=>s!==o):[...t,o],at=(t,o)=>({...o?.choices||{},...t?.choices||{}});var k=(...t)=>t.filter(Boolean).join(" ");var Vt=({as:t="input",disabled:o,className:s,...r})=>e.createElement(t,{...r,disabled:o,className:k("ks-input",o&&"ks-input-disabled",s)}),w=Vt;var Wt=t=>e.createElement(w,{...t,as:"select"}),lt=Wt;var Yt=({className:t,...o})=>e.createElement("input",{type:"checkbox",...o,className:k("ks-checkbox",t)}),V=Yt;var Ut=t=>(t||[]).map(o=>typeof o=="object"?o:{value:o,label:o}),Xt=({type:t,value:o,choices:s,onChange:r,onSettle:a})=>{if(t==="boolean")return e.createElement(V,{checked:!!o,onChange:l=>a(l.target.checked)});if(t==="select"||t==="previous_step"||t==="from_step")return e.createElement(lt,{className:"mb-3",value:o??"",onChange:l=>a(l.target.value)},e.createElement("option",{value:""}),Ut(s).map(l=>e.createElement("option",{key:l.value,value:l.value},l.label)));if(t==="multi_select"){let l=Array.isArray(o)?o:[],i=f=>a(st(l,f));return e.createElement("div",{style:{display:"flex",flexWrap:"wrap",gap:8}},(s||[]).map(f=>e.createElement("label",{key:f,style:{display:"inline-flex",alignItems:"center",gap:4}},e.createElement(V,{checked:l.includes(f),onChange:()=>i(f)}),e.createElement("span",null,f))))}return t==="integer"||t==="float"?e.createElement(w,{className:"mb-3",type:"number",step:t==="integer"?"1":"any",value:o??"",onChange:l=>r(l.target.value),onBlur:l=>a(l.target.value)}):e.createElement(w,{className:"mb-3",type:"text",value:o??"",onChange:l=>r(l.target.value),onBlur:l=>a(l.target.value)})},I=Xt;var it=(t,o,s,r)=>t.map((a,l)=>l===o?{...a,[s]:r}:a);var Gt={primary:"ks-button-primary",secondary:"ks-button-secondary",danger:"ks-button-danger"},qt={sm:"ks-button-sm",md:"ks-button-md",lg:"ks-button-lg"},Jt=({variant:t="primary",size:o="md",className:s,children:r,...a})=>{let l=a.href?"a":"button";return e.createElement(l,{...a,className:k("ks-button",Gt[t],qt[o],s)},r)},y=Jt;var Kt=({holds:t,labels:o,rows:s,onChange:r,onSettle:a})=>{let l=Array.isArray(s)?s:[],i=(f,m,d,p)=>{let u=it(l,f,m,d);p?a(u):r(u)};return e.createElement("div",{style:{marginBottom:12}},l.map((f,m)=>e.createElement("div",{key:m,className:"rounded-md border border-gray-200 dark:border-zinc-700",style:{padding:"8px 8px 2px",marginBottom:6}},Object.entries(t).map(([d,p])=>e.createElement("label",{key:d,style:{display:"block"}},e.createElement("span",{className:"text-gray-500 dark:text-gray-400",style:{display:"block",marginBottom:2,fontSize:11}},(o||{})[d]||d),e.createElement(I,{type:p,value:f[d],onChange:u=>i(m,d,u,!1),onSettle:u=>i(m,d,u,!0)}))),e.createElement(y,{variant:"secondary",size:"sm",className:"mb-1.5",onClick:()=>a(l.filter((d,p)=>p!==m))},"Remove"))),e.createElement(y,{variant:"secondary",size:"sm",className:"w-full",onClick:()=>a([...l,{}])},"Add"))},dt=Kt;var Zt=({as:t="div",className:o,children:s,...r})=>e.createElement(t,{...r,className:k("ks-panel",o)},s),$=Zt;var M=(t,o)=>({className:s,children:r,...a})=>e.createElement(t,{...a,className:k(o,s)},r),A=M("label","ks-label"),er=M("p","ks-hint"),or=M("p","ks-error"),rr=M("span","ks-required");var Qt={width:280,padding:20,overflowY:"auto"},Rt=({node:t,fields:o,holds:s,labels:r,recordLabels:a,choices:l,onSave:i,onDelete:f,onClose:m})=>{let[d,p]=b(t.config);E(()=>p(t.config),[t.id,t.config]);let u=c=>{p(c),JSON.stringify(c)!==JSON.stringify(t.config)&&i(c)};return e.createElement($,{as:"aside",style:Qt,"data-inspector":!0},e.createElement("div",{style:{display:"flex",alignItems:"start",justifyContent:"space-between",gap:8}},e.createElement("h2",{style:{fontWeight:600,marginBottom:2}},t.label),e.createElement("button",{title:"Close",onClick:m,className:"text-gray-500 dark:text-gray-400",style:{border:"none",background:"none",cursor:"pointer",fontSize:18,lineHeight:1}},"\xD7")),e.createElement("p",{className:"text-gray-500 dark:text-gray-400",style:{fontSize:11,marginBottom:16}},t.id," \xB7 ",t.type),Object.entries(o).map(([c,x])=>e.createElement(A,{key:c},e.createElement("span",{style:{display:"block",marginBottom:3}},r[c]||c),x==="list"?e.createElement(dt,{holds:s[c]||{},labels:a[c]||{},rows:d[c],onChange:h=>p({...d,[c]:h}),onSettle:h=>u({...d,[c]:h})}):e.createElement(I,{type:x,value:d[c],choices:l[c],onChange:h=>p({...d,[c]:h}),onSettle:h=>u({...d,[c]:h})}))),e.createElement(y,{variant:"danger",size:"sm",className:"w-full",onClick:f},"Delete step"))},ct=Rt;var te={width:240,boxSizing:"border-box",padding:"10px 14px",borderRadius:8,textAlign:"center",fontSize:12,cursor:"pointer"},ee="border-2 border-dashed border-amber-500 bg-amber-50 text-amber-700 dark:bg-amber-950 dark:text-amber-300",oe="border-2 border-dashed border-accent-600 bg-accent-50 text-accent-800 dark:bg-accent-950 dark:text-accent-200",re=({node:t,dragging:o,onFill:s,onDrop:r})=>e.createElement("div",{ref:t.ref,"data-placeholder":t.id,title:`Choose the step \u201C${t.label}\u201D should lead to`,onClick:s,onDragOver:a=>{o&&(a.preventDefault(),a.dataTransfer.dropEffect="move")},onDrop:a=>{a.preventDefault(),r()},className:o?oe:ee,style:te},e.createElement("div",{style:{fontWeight:600}},t.label),e.createElement("div",{style:{fontSize:11}},o?"drop a step here":"leads nowhere yet \u2014 click to choose")),mt=re;var ne=({entries:t,at:o,onPick:s,onConnect:r,onDismiss:a})=>e.createElement($,{className:"shadow-lg",style:{position:"absolute",zIndex:9,top:o.y,left:o.x,width:210,padding:8,borderRadius:8}},e.createElement("p",{className:"text-gray-500 dark:text-gray-400",style:{margin:"0 0 6px",fontSize:11}},"Add a step"),t.map(l=>e.createElement(y,{key:l.type,variant:"secondary",size:"sm",className:"w-full mb-1.5 flex-col",onClick:()=>s(l)},l.label,e.createElement("span",{className:"opacity-75",style:{display:"block",fontSize:11}},l.type))),r&&e.createElement(y,{variant:"secondary",size:"sm",className:"w-full mb-1.5",onClick:r},"Connect to a step already here"),e.createElement(y,{variant:"secondary",size:"sm",className:"w-full",onClick:a},"Cancel")),pt=ne;var se={width:24,height:24,padding:0,fontSize:15,lineHeight:"15px"},ut=({label:t,title:o,idle:s,enabled:r,onUse:a})=>e.createElement(y,{variant:"secondary",size:"sm",title:r?o:s,disabled:!r,onClick:a,style:{opacity:r?1:.4,cursor:r?"pointer":"default"}},t),ae=({undoable:t,redoable:o,onAdd:s,onUndo:r,onRedo:a})=>e.createElement("div",{style:{position:"absolute",top:16,left:16,zIndex:5,display:"flex",gap:8}},e.createElement(y,{variant:"secondary",className:"rounded-full",style:se,title:"Add a step",onClick:s},"+"),e.createElement(ut,{label:"\u21B6 Undo",title:"Undo the last change",idle:"Nothing to undo",enabled:t,onUse:r}),e.createElement(ut,{label:"\u21B7 Redo",title:"Redo the change you undid",idle:"Nothing to redo",enabled:o,onUse:a})),ft=ae;var le=t=>e.createElement(w,{...t,as:"textarea"}),gt=le;var yt=t=>{let o=t.problem.replace(/_/g," ");return t.node?`\u201C${t.node}\u201D ${o}`:o};var ht=({version:t,published:o})=>t?o?o===t?`Version ${t} \xB7 live`:`Version ${t} \xB7 visitors still on ${o}`:`Version ${t} \xB7 never published`:"No version yet";var ie={width:280,padding:20,overflowY:"auto"},W={fontWeight:600,marginBottom:6,marginTop:14},_={fontSize:12,marginBottom:4,lineHeight:1.4},Y={display:"block",marginBottom:3},U=(t,o,s)=>r=>{r.target.value!==(t[o]??"")&&s({[o]:r.target.value})},de=({flow:t,changes:o,problems:s,refusal:r,notice:a,onCreate:l,onPublish:i,onSaveDetails:f,onClose:m})=>e.createElement($,{as:"aside",style:ie,"data-builder-panel":!0},e.createElement("div",{style:{display:"flex",alignItems:"start",justifyContent:"space-between",gap:8}},e.createElement("h2",{style:{fontWeight:600},"data-flow-name":!0},t.title||t.slug),e.createElement("button",{title:"Close",onClick:m,className:"text-gray-500 dark:text-gray-400",style:{border:"none",background:"none",cursor:"pointer",fontSize:18,lineHeight:1}},"\xD7")),e.createElement("a",{className:"text-accent-600 dark:text-accent-400",style:{fontSize:12,display:"block"},href:t.history_url,"data-history":!0},ht(t)),e.createElement("h2",{style:W},"Problems"),r&&e.createElement("p",{className:"text-red-800 dark:text-red-400",style:{..._,fontWeight:600},"data-refusal":!0},r),a&&!r&&e.createElement("p",{className:"text-emerald-800 dark:text-emerald-400",style:{..._,fontWeight:600},"data-notice":!0},a),s.length===0&&!r?e.createElement("p",{className:"text-gray-500 dark:text-gray-400",style:{fontSize:12}},"Nothing wrong with this flow."):s.map(d=>e.createElement("p",{key:`${d.node}-${d.problem}`,className:"text-amber-700 dark:text-amber-400",style:_,"data-problem":!0},"\u26A0 ",yt(d))),e.createElement("h2",{style:W},"Changes since the last version"),o.length===0?e.createElement("p",{className:"text-gray-500 dark:text-gray-400",style:{fontSize:12}},"Nothing has changed."):o.map((d,p)=>e.createElement("p",{key:p,style:_,"data-change":!0},d)),e.createElement("h2",{style:W},"Details"),e.createElement(A,null,e.createElement("span",{style:Y},"Title"),e.createElement(w,{className:"mb-3",defaultValue:t.title??"",onBlur:U(t,"title",f),"data-flow-title":!0})),e.createElement(A,null,e.createElement("span",{style:Y},"Summary"),e.createElement(gt,{className:"mb-3",rows:4,defaultValue:t.summary??"",onBlur:U(t,"summary",f),"data-flow-summary":!0})),e.createElement(A,null,e.createElement("span",{style:Y},"Start label"),e.createElement(w,{className:"mb-3",defaultValue:t.start_label??"",onBlur:U(t,"start_label",f),"data-flow-start-label":!0})),e.createElement("div",{style:{marginTop:16}},e.createElement(y,{variant:"secondary",size:"sm",className:"w-full mb-1.5",onClick:l,"data-create-version":!0},"Create version"),e.createElement(y,{size:"sm",className:"w-full mb-1.5",onClick:i,"data-publish":!0},"Publish"),e.createElement(y,{variant:"secondary",size:"sm",className:"w-full mb-1.5",href:t.definition_url,"data-definition":!0},"Definition"),e.createElement(y,{variant:"secondary",size:"sm",className:"w-full mb-1.5",href:t.details_url,"data-details":!0},"Edit details"))),bt=de;var xt={width:24,height:24,padding:0,fontSize:15,lineHeight:"15px"},ce=({link:t,onInsert:o,onRemove:s,onDrop:r,dragging:a})=>{let[l,i]=b(!1),f=l||a;return e.createElement("div",{"data-connector":`${t.source}-${t.target}`,style:{position:"absolute",left:t.midX-34,top:t.midY-20,width:68,height:40,zIndex:4},onMouseEnter:()=>i(!0),onMouseLeave:()=>i(!1),onDragEnter:()=>i(!0),onDragLeave:()=>i(!1),onDragOver:m=>{m.preventDefault(),m.dataTransfer.dropEffect="move"},onDrop:m=>{m.preventDefault(),i(!1),r()}},e.createElement("div",{style:{display:"flex",gap:4,justifyContent:"center",alignItems:"center",height:"100%",opacity:f?1:0,transition:"opacity .12s"}},e.createElement(y,{variant:"secondary",title:a?"Move the step here":"Insert a step here",onClick:o,className:"rounded-full data-[over=true]:ring-2 data-[over=true]:ring-accent-600","data-over":!!(a&&l),style:xt},"+"),!a&&e.createElement(y,{variant:"danger",title:"Remove this connection",onClick:s,className:"rounded-full",style:xt},"\xD7")))},vt=ce;var me=({links:t,extent:o,dragging:s,onInsert:r,onRemove:a,onDrop:l})=>e.createElement(e.Fragment,null,e.createElement("svg",{width:o.width,height:o.height,style:{position:"absolute",top:0,left:0,pointerEvents:"none",zIndex:1}},e.createElement("defs",null,e.createElement("marker",{id:"alembic-arrow",viewBox:"0 0 10 10",refX:"8",refY:"5",markerWidth:"5",markerHeight:"5",orient:"auto"},e.createElement("path",{d:"M 0 0 L 10 5 L 0 10 z",className:"fill-gray-400 dark:fill-zinc-500"}))),t.map(i=>e.createElement("path",{key:i.id,"data-link":i.id,className:"stroke-gray-400 dark:stroke-zinc-500",fill:"none",strokeWidth:"1.5",markerEnd:"url(#alembic-arrow)",d:i.path}))),t.filter(i=>i.label).map(i=>e.createElement("div",{key:`${i.id}-label`,"data-link-label":i.id,className:"bg-surface-50 text-gray-500 dark:bg-surface-950 dark:text-gray-400",style:{position:"absolute",left:i.midX-10,top:i.midY-18,zIndex:3,fontSize:11,padding:"0 3px"}},i.label)),t.filter(i=>!i.placeholder).map(i=>e.createElement(vt,{key:i.id,link:i,dragging:!!s,onInsert:()=>r(i),onRemove:()=>a(i),onDrop:()=>l(i)}))),kt=me;var pe="Your change was not saved because the server could not be reached.",wt=({base:t,token:o,fetch:s,onFlow:r,onError:a,onNotice:l})=>async(i,f,m)=>{let d=await s(t+i,{method:f,headers:{"Content-Type":"application/json","X-CSRF-Token":o},body:m&&JSON.stringify(m)}).catch(()=>null);if(!d)return l(null),a(pe);let p=await d.json().catch(()=>({}));if(!d.ok)return l(null),a(p.error||"That change was refused");a(null),l(p.notice||null),r(await(await s(t+".json",{headers:{Accept:"application/json"}})).json())};var ue=(t,o,s)=>{let[r,a]=b(s),[l,i]=b(null),[f,m]=b(null),d=P(()=>wt({base:t,token:o,fetch:(...p)=>window.fetch(...p),onFlow:a,onError:i,onNotice:m}),[t,o]);return{flow:r,error:l,notice:f,send:d}},Ct=ue;var O=(t,o,s,r)=>{let a=t.left-s.left+r.scrollLeft,l=t.top-s.top+r.scrollTop;return o==="left"?{x:a,y:l+t.height/2}:o==="right"?{x:a+t.width,y:l+t.height/2}:o==="top"?{x:a+t.width/2,y:l}:{x:a+t.width/2,y:l+t.height}},fe=(t,o)=>o==="top"?{x:t.x,y:t.y-3}:o==="left"?{x:t.x-3,y:t.y}:o==="right"?{x:t.x+3,y:t.y}:{x:t.x,y:t.y+3},ge=(t,o,s,r,a)=>{let l={straight:`M ${o.x} ${o.y} L ${s.x} ${s.y}`,turn:`M ${o.x} ${o.y} H ${s.x} V ${s.y}`,lane:`M ${o.x} ${o.y} V ${r} H ${s.x} V ${s.y}`,detour:`M ${o.x} ${o.y} H ${a} V ${s.y} H ${s.x}`}[t.route];return{...t,path:l,midX:t.route==="detour"?a:(o.x+s.x)/2,midY:t.route==="lane"?r:(o.y+s.y)/2}},ye=(t,o,s,r)=>{let[a,l]=b([]),[i,f]=b({width:"100%",height:"100%"});return Q(()=>{let m=()=>{let p=o.current?.getBoundingClientRect();p&&(f({width:o.current.scrollWidth,height:o.current.scrollHeight}),l(t.edges.flatMap(u=>{let c=s.current[u.source]?.getBoundingClientRect(),x=s.current[u.target]?.getBoundingClientRect();if(!c||!x)return[];let h=O(c,u.leaves,p,o.current),C=fe(O(x,u.enters,p,o.current),u.enters),N=(O(c,"bottom",p,o.current).y+O(x,"top",p,o.current).y)/2,z=u.leaves==="left"?Math.min(h.x,C.x)-28:Math.max(h.x,C.x)+28;return[ge(u,h,C,N,z)]})))};m(),window.addEventListener("resize",m);let d=new ResizeObserver(m);return o.current&&d.observe(o.current),()=>{window.removeEventListener("resize",m),d.disconnect()}},r),{links:a,extent:i}},St=ye;var Nt=(t,o)=>{let s=new Set(o),r=t,a=2;for(;s.has(r);)r=`${t}_${a++}`;return r};var xe={display:"flex",height:"100%",minHeight:0,fontSize:13},ve={flex:1,overflow:"auto",position:"relative"},ke={position:"absolute",top:56,left:16,zIndex:5,width:264,padding:12,borderRadius:8},we={display:"grid",rowGap:64,columnGap:0,padding:40,justifyContent:"center",position:"relative"},Ce={position:"sticky",zIndex:8,top:8,margin:"8px auto 0",width:"fit-content",padding:"6px 12px",borderRadius:6,fontSize:12},Se=({port:t,onCancel:o})=>e.createElement("div",{className:"bg-accent-100 text-accent-800 dark:bg-accent-900 dark:text-accent-100",style:Ce},"Choose the step \u201C",t||"next","\u201D should lead to \u2014 ",e.createElement("button",{onClick:o,className:"text-accent-800 underline dark:text-accent-100",style:{border:"none",background:"none",cursor:"pointer",fontSize:12,padding:0}},"cancel")),Ne=({base:t,token:o,initial:s})=>{let{flow:r,error:a,notice:l,send:i}=Ct(t,o,s),[f,m]=b(null),[d,p]=b(null),[u,c]=b(null),[x,h]=b(null),[C,N]=b(!1),z=j({}),B=j(null);E(()=>{let n=()=>{m(null),N(!0)};return document.addEventListener("alembic:open-flow",n),()=>document.removeEventListener("alembic:open-flow",n)},[]),E(()=>{if(!C)return;let n=g=>{g.target.closest("[data-builder-panel], [data-open-panel]")||N(!1)};return document.addEventListener("click",n),()=>document.removeEventListener("click",n)},[C]),E(()=>{document.dispatchEvent(new CustomEvent("alembic:flow-named",{detail:r.flow?.title||r.flow?.slug}))},[r.flow?.title,r.flow?.slug]);let Bt=P(()=>Object.fromEntries(r.nodes.map(n=>[n.id,n])),[r.nodes]),{links:Et,extent:Tt}=St(r,B,z,[r,f,Bt]),X=P(()=>{let n={};return r.violations.forEach(g=>{var v;return(n[v=g.node]||(n[v]=[])).push(g)}),n},[r.violations]),H=r.nodes.filter(n=>!n.loose),G=r.nodes.filter(n=>n.loose),$t=Math.max(0,...H.map(n=>n.row))+1,At=Math.max(0,...H.map(n=>n.column))+1,q=n=>{if(!u)return;let[g,v]=u;c(null),g!==n&&i("/edges","POST",{from:g,to:n,on:v||null})},S=r.nodes.find(n=>n.id===f),D=r.palette.find(n=>n.type===S?.type);return e.createElement("div",{className:"text-gray-900 dark:text-gray-100",style:xe,onMouseUp:()=>h(null),onKeyDown:n=>{n.key==="Escape"&&(m(null),c(null),p(null))},tabIndex:-1},e.createElement("div",{ref:B,className:"bg-surface-50 dark:bg-surface-950",style:ve,onClick:n=>{n.target===B.current&&(m(null),c(null),N(!1))}},u&&e.createElement(Se,{port:u[1],onCancel:()=>c(null)}),e.createElement(ft,{undoable:r.undoable,redoable:r.redoable,onAdd:()=>p({at:{x:16,y:52}}),onUndo:()=>{m(null),i("/undo","POST")},onRedo:()=>{m(null),i("/redo","POST")}}),e.createElement(kt,{links:Et,extent:Tt,dragging:x,onInsert:n=>p({from:n.source,to:n.target,at:{x:n.midX+30,y:n.midY}}),onRemove:n=>{m(null),i("/edges","DELETE",{from:n.source,to:n.target})},onDrop:n=>{let g=x;h(null),i("/steps/"+g+"/move","PATCH",{from:n.source,to:n.target})}}),G.length>0&&e.createElement("div",{"data-loose":!0,className:"border border-dashed border-gray-300 bg-gray-50 dark:border-zinc-700 dark:bg-zinc-900",style:ke},e.createElement("div",{className:"text-gray-500 dark:text-gray-400",style:{fontSize:11,marginBottom:6}},"Not in the flow yet \u2014 drag one onto a connection"),G.map(n=>e.createElement("div",{key:n.id,style:{marginBottom:8}},e.createElement(F,{node:{...n,ref:g=>{z.current[n.id]=g},violations:X[n.id]||[],connected:[]},selected:f===n.id,armed:u&&u[0]===n.id?u[1]:null,connecting:!!u,onSelect:()=>u?q(n.id):m(n.id),onArm:(g,v)=>{v.stopPropagation(),c([n.id,g])},onDragStart:()=>h(n.id),onDragEnd:()=>h(null)})))),e.createElement("div",{style:{...we,gridTemplateColumns:`repeat(${At+1}, ${280/2}px)`,gridTemplateRows:`repeat(${$t}, auto)`}},H.map(n=>e.createElement("div",{key:n.id,style:{gridRow:n.row+1,gridColumn:`${n.column+1} / span 2`,zIndex:2}},n.placeholder&&e.createElement(mt,{node:{...n,ref:g=>{z.current[n.id]=g}},dragging:!!x,onFill:()=>p({from:n.from,on:n.on,at:{x:16,y:52}}),onDrop:()=>{let g=x;h(null),g&&i("/edges","POST",{from:n.from,to:g,on:n.on})}}),!n.placeholder&&e.createElement(F,{node:{...n,ref:g=>{z.current[n.id]=g},violations:X[n.id]||[],connected:r.edges.filter(g=>g.source===n.id).map(g=>g.label||null)},selected:n.id===f,armed:u&&u[0]===n.id?u[1]:null,connecting:!!u&&u[0]!==n.id,onSelect:()=>u?q(n.id):m(n.id),onArm:(g,v)=>{let J=B.current.getBoundingClientRect();p({from:n.id,on:g,at:{x:v.clientX-J.left+B.current.scrollLeft+8,y:v.clientY-J.top+B.current.scrollTop+8}})},onDragEnd:()=>h(null),onDragStart:()=>h(n.id)})))),d&&e.createElement(pt,{entries:r.palette,at:d.at,onDismiss:()=>p(null),onConnect:d.on!==void 0?()=>{c([d.from,d.on]),p(null)}:null,onPick:n=>{let g=d;p(null),i("/steps","POST",{id:Nt(n.type,r.nodes.map(v=>v.id)),type:n.type,from:g.from,to:g.to,on:g.on})}})),C&&!S&&e.createElement(bt,{flow:r.flow||{},changes:r.changes||[],problems:r.violations,refusal:a,notice:l,onClose:()=>N(!1),onCreate:()=>i("/versions","POST"),onPublish:()=>i("/publish","POST"),onSaveDetails:n=>i("/details","PATCH",{flow:n})}),S&&e.createElement(ct,{node:S,fields:D?.fields||{},holds:D?.records||{},labels:D?.labels||{},recordLabels:D?.record_labels||{},choices:at(S,D),onClose:()=>m(null),onSave:n=>i(`/steps/${S.id}`,"PATCH",{config:n}),onDelete:()=>{m(null),i(`/steps/${S.id}`,"DELETE")}}))},zt=Ne;tt("alembic/flow-editor",zt);document.addEventListener("alembic:flow-named",t=>{let o=document.querySelector("[data-flow-heading]");o&&(o.textContent=t.detail)});rt(document,K);})();