prompt_navigator 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 206bf5dde601862fee9a65dc6fd8b8af2324a4e3ae8a952ef30be31b1c650028
4
+ data.tar.gz: 32724a12d078a3f9bba48c6ce120010c2f7d1b2d41158d373d8bdc03c17cc754
5
+ SHA512:
6
+ metadata.gz: 51872a8c58c3dee2aa6ed61c4622b4eee4bfe0fa81f4a593f7ab4be01d2bcb5beebb94aeba3a5a959ed8b000610bf3cd213b8ab668870866a2ed0af4e83dc3c1
7
+ data.tar.gz: a36897e24af8beb7718bfe5693ca69ce746f57905bf823dd3a0ecbf13142984339f5e81b4b7852d0fed83d77515ed6ba1aa839dabd15fdd3df1da05b5cfa46d3
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright dhq_boiler
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # PromptNavigator
2
+
3
+ Rails engine for managing prompt execution history with visual interface.
4
+
5
+ ## Features
6
+
7
+ - Visual history stack with parent-child relationships
8
+ - Modern CSS with nested syntax
9
+ - Stimulus-powered interactive arrows
10
+ - Automatic asset pipeline integration
11
+ - Active state highlighting
12
+ - Responsive design with hover effects
13
+ - Built-in model for tracking prompt executions
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem "prompt_navigator"
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ ```bash
26
+ $ bundle install
27
+ ```
28
+
29
+ Generate the migration for prompt executions:
30
+
31
+ ```bash
32
+ $ rails generate prompt_navigator:modeling
33
+ $ rails db:migrate
34
+ ```
35
+
36
+ This will create the `prompt_navigator_prompt_executions` table with the following fields:
37
+
38
+ - `execution_id` - Unique identifier (UUID) for each prompt execution
39
+ - `prompt` - The prompt text
40
+ - `llm_platform` - The LLM platform used (e.g., "openai", "anthropic")
41
+ - `model` - The model name (e.g., "gpt-4", "claude-3")
42
+ - `configuration` - Model configuration settings
43
+ - `response` - The LLM response
44
+ - `previous_id` - Reference to the parent execution (for history tree)
45
+
46
+ ## Usage
47
+
48
+ ### Basic Setup
49
+
50
+ In your application's layout file (`app/views/layouts/application.html.erb`), make sure you have `<%= yield :head %>` in the `<head>` section:
51
+
52
+ ```erb
53
+ <head>
54
+ <title>Your App</title>
55
+ <%= csrf_meta_tags %>
56
+ <%= csp_meta_tag %>
57
+
58
+ <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
59
+ <%= javascript_importmap_tags %>
60
+
61
+ <%= yield :head %>
62
+ </head>
63
+ ```
64
+
65
+ Include the history partial in your view:
66
+
67
+ ```erb
68
+ <%= render 'prompt_navigator/history', locals: { active_uuid: @current_execution_id, card_path: ->(execution_id) { my_path(execution_id) } } %>
69
+ ```
70
+
71
+ ### Asset Pipeline Configuration
72
+
73
+ The engine automatically configures the asset pipeline to include:
74
+ - `prompt_navigator/history.css` - Styles for the history component
75
+ - `controllers/history_controller.js` - Stimulus controller for arrow drawing
76
+
77
+ The assets are automatically precompiled and made available to your application. For Rails 7+ with importmap, the CSS will be loaded via `stylesheet_link_tag` when you render the history partial, and the Stimulus controller will be automatically registered.
78
+
79
+ ### Controller Setup
80
+
81
+ Include `HistoryManageable` concern in your controller:
82
+
83
+ ```ruby
84
+ class MyController < ApplicationController
85
+ include HistoryManageable
86
+
87
+ def index
88
+ # Initialize history with prompt executions
89
+ initialize_history(PromptNavigator::PromptExecution.all)
90
+
91
+ # Set the active execution ID (optional)
92
+ set_active_message_uuid(params[:execution_id])
93
+ end
94
+
95
+ def create
96
+ # Add new execution to history
97
+ new_execution = PromptNavigator::PromptExecution.create(
98
+ prompt: params[:prompt],
99
+ llm_platform: "openai",
100
+ model: "gpt-4",
101
+ configuration: params[:config].to_json,
102
+ response: llm_response,
103
+ previous: @current_execution
104
+ )
105
+ push_to_history(new_execution)
106
+ end
107
+ end
108
+ ```
109
+
110
+ ### Using PromptExecution Model
111
+
112
+ The `PromptNavigator::PromptExecution` model has the following attributes and methods:
113
+
114
+ ```ruby
115
+ execution = PromptNavigator::PromptExecution.create(
116
+ prompt: "Your prompt text",
117
+ llm_platform: "openai",
118
+ model: "gpt-4",
119
+ configuration: "{\"temperature\": 0.7}",
120
+ response: "LLM response text",
121
+ previous: parent_execution # Optional: for building history tree
122
+ )
123
+
124
+ # Access fields
125
+ execution.execution_id # UUID, automatically generated
126
+ execution.prompt # The prompt text
127
+ execution.llm_platform # LLM platform used
128
+ execution.model # Model name
129
+ execution.configuration # Configuration JSON
130
+ execution.response # LLM response
131
+ execution.previous # Parent execution (belongs_to association)
132
+ ```
133
+
134
+ ### Using the Helper Method
135
+
136
+ The helper methods are automatically included in your views. You can use the `history_list` helper:
137
+
138
+ ```erb
139
+ <%= history_list(->(execution_id) { my_item_path(execution_id) }, active_uuid: @current_execution_id) %>
140
+ ```
141
+
142
+ ### Customizing the Card Path
143
+
144
+ The `card_path` parameter should be a callable (Proc or lambda) that takes an execution_id and returns a path:
145
+
146
+ ```erb
147
+ <%= render 'prompt_navigator/history',
148
+ locals: {
149
+ active_uuid: @current_execution_id,
150
+ card_path: ->(execution_id) { my_item_path(execution_id) }
151
+ }
152
+ %>
153
+ ```
154
+
155
+ Or using the helper:
156
+
157
+ ```erb
158
+ <%= history_list(->(execution_id) { my_item_path(execution_id) }, active_uuid: @current_execution_id) %>
159
+ ```
160
+
161
+ ## Troubleshooting
162
+
163
+ ### Styles not loading
164
+
165
+ Make sure you have `<%= yield :head %>` in your application layout's `<head>` section.
166
+
167
+ ### Arrows not appearing
168
+
169
+ The arrow visualization requires:
170
+ 1. Stimulus to be properly configured in your application
171
+ 2. The `history_controller.js` to be loaded (automatic with importmap)
172
+ 3. Parent-child relationships to be properly set using the `previous` association in your PromptExecution records
173
+
174
+ ### History not displaying
175
+
176
+ Ensure that:
177
+ 1. `@history` instance variable is set in your controller using `initialize_history`
178
+ 2. Your PromptExecution records have `execution_id` values (automatically generated on create)
179
+ 3. The `card_path` callable is correctly defined and returns valid paths
180
+
181
+ ## Requirements
182
+
183
+ - Rails >= 8.1.2
184
+ - Stimulus (Hotwired)
185
+
186
+ ## Installation
187
+
188
+ Add this line to your application's Gemfile:
189
+
190
+ ```ruby
191
+ gem "prompt_navigator"
192
+ ```
193
+
194
+ And then execute:
195
+
196
+ ```bash
197
+ $ bundle
198
+ ```
199
+
200
+ Or install it yourself as:
201
+
202
+ ```bash
203
+ $ gem install prompt_navigator
204
+ ```
205
+
206
+ ## Contributing
207
+ Contribution directions go here.
208
+
209
+ ## License
210
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ require "bundler/gem_tasks"
@@ -0,0 +1,2 @@
1
+ //= link_tree ../stylesheets/prompt_navigator .css
2
+ //= link_tree ../../javascript/controllers .js
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,76 @@
1
+ .history-stack {
2
+ position: relative;
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 8px;
6
+ padding-left: 32px; /* space for arrows */
7
+ }
8
+
9
+ .history-card {
10
+ background: #fff;
11
+ border: 1px solid #ddd;
12
+ border-radius: 8px;
13
+ padding: 8px 10px;
14
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
15
+ position: relative;
16
+ z-index: 1;
17
+ transition:
18
+ box-shadow 0.15s ease,
19
+ transform 0.15s ease;
20
+
21
+ &.is-active {
22
+ border-color: #007bff;
23
+ box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
24
+ }
25
+
26
+ &:hover {
27
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
28
+ transform: translateY(-2px);
29
+ }
30
+ }
31
+
32
+ .history-card-link {
33
+ display: grid;
34
+ grid-template-columns: auto 1fr auto;
35
+ grid-gap: 8px;
36
+ text-decoration: none;
37
+ color: #333;
38
+ align-items: center;
39
+ }
40
+
41
+ .history-card-number {
42
+ font-size: 12px;
43
+ font-weight: 600;
44
+ color: #555;
45
+ }
46
+
47
+ .history-card-prompt {
48
+ font-size: 12px;
49
+ line-height: 1.3;
50
+ overflow: hidden;
51
+ display: -webkit-box;
52
+ -webkit-line-clamp: 2;
53
+ -webkit-box-orient: vertical;
54
+ }
55
+
56
+ .history-arrows {
57
+ position: absolute;
58
+ top: 0;
59
+ left: 0;
60
+ width: 100%;
61
+ height: 100%;
62
+ pointer-events: none;
63
+ overflow: visible;
64
+ z-index: 0;
65
+ }
66
+
67
+ .history-straight-arrow {
68
+ display: flex;
69
+ justify-content: center;
70
+ align-items: center;
71
+ height: 8px;
72
+ margin: 0;
73
+ font-size: 10px;
74
+ color: #555;
75
+ line-height: 1;
76
+ }
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptNavigator
4
+ module HistoryManageable
5
+ extend ActiveSupport::Concern
6
+
7
+ def initialize_history(history)
8
+ @history = history.to_a
9
+ end
10
+
11
+ def set_active_message_uuid(uuid)
12
+ @active_message_uuid = uuid
13
+ end
14
+
15
+ def push_to_history(new_state)
16
+ @history << new_state
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ module PromptNavigator
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module PromptNavigator
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,123 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ // Draw arrows connecting child cards to their parent card vertically.
4
+ export default class extends Controller {
5
+ static targets = ["svg", "cards"]
6
+
7
+ // Private fields
8
+ #drawArrowsBound
9
+ #markerId = "history-arrow-head"
10
+ #startX = 32
11
+ #curveOffset = 40
12
+
13
+ connect() {
14
+ this.#drawArrows()
15
+ this.#drawArrowsBound = this.#drawArrows.bind(this)
16
+ window.addEventListener("resize", this.#drawArrowsBound)
17
+ }
18
+
19
+ disconnect() {
20
+ window.removeEventListener("resize", this.#drawArrowsBound)
21
+ }
22
+
23
+ // ================= Private methods =================
24
+ #drawArrows() {
25
+ // Skip if svg target is missing (template variant without arrow canvas)
26
+ if (!this.hasSvgTarget) return
27
+ const svg = this.svgTarget
28
+
29
+ this.#clearSvg(svg)
30
+ const cardMap = this.#buildCardMap()
31
+ const bbox = this.#setupSvgDimensions(svg)
32
+
33
+ this.#ensureArrowMarker(svg)
34
+
35
+ // Iterate over Stimulus targets instead of querySelectorAll
36
+ for (const card of this.cardsTargets) {
37
+ this.#drawArrowForCard(card, cardMap, bbox, svg)
38
+ }
39
+ }
40
+
41
+ #clearSvg(svg) {
42
+ svg.replaceChildren()
43
+ }
44
+
45
+ #buildCardMap() {
46
+ const cardMap = new Map()
47
+ for (const c of this.cardsTargets) {
48
+ cardMap.set(c.dataset.uuid, c)
49
+ }
50
+ return cardMap
51
+ }
52
+
53
+ #setupSvgDimensions(svg) {
54
+ const bbox = this.element.getBoundingClientRect()
55
+ svg.setAttribute("width", bbox.width)
56
+ svg.setAttribute("height", bbox.height)
57
+ svg.setAttribute("viewBox", `0 0 ${bbox.width} ${bbox.height}`)
58
+ return bbox
59
+ }
60
+
61
+ #ensureArrowMarker(svg) {
62
+ if (svg.querySelector(`#${this.#markerId}`)) return
63
+
64
+ const marker = document.createElementNS(
65
+ "http://www.w3.org/2000/svg",
66
+ "marker"
67
+ )
68
+ marker.setAttribute("id", this.#markerId)
69
+ marker.setAttribute("markerWidth", "6")
70
+ marker.setAttribute("markerHeight", "6")
71
+ marker.setAttribute("refX", "5")
72
+ marker.setAttribute("refY", "3")
73
+ marker.setAttribute("orient", "auto")
74
+
75
+ const arrowPath = document.createElementNS(
76
+ "http://www.w3.org/2000/svg",
77
+ "path"
78
+ )
79
+ arrowPath.setAttribute("d", "M0,0 L6,3 L0,6 Z")
80
+ arrowPath.setAttribute("fill", "#555")
81
+ marker.appendChild(arrowPath)
82
+
83
+ const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs")
84
+ defs.appendChild(marker)
85
+ svg.appendChild(defs)
86
+ }
87
+
88
+ #drawArrowForCard(card, cardMap, bbox, svg) {
89
+ const parentUuid = card.dataset.parentUuid
90
+ if (!parentUuid) return
91
+
92
+ const parentCard = cardMap.get(parentUuid)
93
+ if (!parentCard) return
94
+
95
+ const childRect = card.getBoundingClientRect()
96
+ const parentRect = parentCard.getBoundingClientRect()
97
+
98
+ const startY = parentRect.top + parentRect.height / 2 - bbox.top
99
+ const endY = childRect.top + childRect.height / 2 - bbox.top
100
+ const verticalGap = Math.abs(endY - startY)
101
+
102
+ // Skip if cards are adjacent - straight arrow is rendered by helper
103
+ if (verticalGap < 80) return
104
+
105
+ const path = this.#createCurvedArrowPath(startY, endY)
106
+ svg.appendChild(path)
107
+ }
108
+
109
+ #createCurvedArrowPath(startY, endY) {
110
+ const startX = this.#startX // left edge margin
111
+ const curveX = startX - this.#curveOffset // curve outward to the left
112
+ const pathData = `M ${startX} ${startY} C ${curveX} ${startY}, ${curveX} ${endY}, ${startX} ${endY}`
113
+
114
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path")
115
+ path.setAttribute("d", pathData)
116
+ path.setAttribute("fill", "none")
117
+ path.setAttribute("stroke", "#555")
118
+ path.setAttribute("stroke-width", "1.2")
119
+ path.setAttribute("marker-end", `url(#${this.#markerId})`)
120
+
121
+ return path
122
+ }
123
+ }
@@ -0,0 +1,4 @@
1
+ module PromptNavigator
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module PromptNavigator
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module PromptNavigator
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ module PromptNavigator
2
+ class PromptExecution < ApplicationRecord
3
+ belongs_to :previous, class_name: "PromptNavigator::PromptExecution", optional: true
4
+
5
+ before_create :set_execution_id
6
+
7
+ private
8
+
9
+ def set_execution_id
10
+ self.execution_id ||= SecureRandom.uuid
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Prompt navigator</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= yield :head %>
9
+
10
+ <%= stylesheet_link_tag "prompt_navigator/application", media: "all" %>
11
+ </head>
12
+ <body>
13
+
14
+ <%= yield %>
15
+
16
+ </body>
17
+ </html>
@@ -0,0 +1,16 @@
1
+ <%
2
+ active_uuid = locals[:active_uuid]
3
+ card_path = locals[:card_path]
4
+ %>
5
+
6
+ <h2>History</h2>
7
+ <% if @history.present? %>
8
+ <div class="history-stack" data-controller="history">
9
+ <% @history.each_with_index do |ann, idx| %>
10
+ <%= render 'prompt_navigator/history_card', locals: { ann: ann, next_ann: @history[idx + 1], is_active: ann.execution_id == active_uuid, card_path: card_path } %>
11
+ <% end %>
12
+ <svg class="history-arrows" data-history-target="svg"></svg>
13
+ </div>
14
+ <% else %>
15
+ <p class="history-empty">No history yet</p>
16
+ <% end %>
@@ -0,0 +1,18 @@
1
+ <%
2
+ ann = locals[:ann]
3
+ next_ann = locals[:next_ann]
4
+ is_active = locals[:is_active]
5
+ parent_uuid = ann.previous&.execution_id
6
+ card_path = locals[:card_path]
7
+ %>
8
+
9
+ <div class="history-card<%= ' is-active' if is_active %>" data-history-target="cards" data-uuid="<%= ann.execution_id %>" data-parent-uuid="<%= parent_uuid %>">
10
+ <%= link_to card_path.call(ann.execution_id), class: "history-card-link" do %>
11
+ <div class="history-card-prompt"><%= truncate(ann.prompt.to_s, length: 30) %></div>
12
+ <% end %>
13
+ </div>
14
+
15
+ <%# Check !next_ann.nil? to prevent the "↑" arrow from appearing below the bottom history entry, as parent_uuid would be nil in that case. %>
16
+ <% if !next_ann.nil? && parent_uuid == next_ann.execution_id %>
17
+ <div class="history-straight-arrow">↑</div>
18
+ <% end %>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ PromptNavigator::Engine.routes.draw do
2
+ end
@@ -0,0 +1,19 @@
1
+
2
+ module PromptNavigator
3
+ module Generators
4
+ class ModelingGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+
7
+ source_root File.expand_path("templates", __dir__)
8
+
9
+ def self.next_migration_number(dirname)
10
+ next_migration_number = current_migration_number(dirname) + 1
11
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
12
+ end
13
+
14
+ def add_migrations
15
+ migration_template "db/migrate/20260129073026_create_prompt_navigator_prompt_executions.rb", "db/migrate/create_prompt_navigator_prompt_executions.rb"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ class CreatePromptNavigatorPromptExecutions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :prompt_navigator_prompt_executions do |t|
4
+ t.references :previous, foreign_key: { to_table: :prompt_navigator_prompt_executions }, index: true, null: true
5
+ # Unique identifier for prompt execution
6
+ # Used to highlight active entries in History list and display details on click
7
+ t.string :execution_id
8
+ t.text :prompt
9
+ t.string :llm_platform
10
+ t.string :model
11
+ t.string :configuration
12
+ t.text :response
13
+
14
+ t.timestamps
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptNavigator
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace PromptNavigator
6
+
7
+ # Initialize helper methods to be available in ActionView
8
+ # This makes PromptNavigator::Helpers methods accessible in Rails views
9
+ initializer "prompt_navigator.helpers" do
10
+ ActiveSupport.on_load(:action_view) do
11
+ include PromptNavigator::Helpers
12
+ end
13
+ end
14
+
15
+ initializer "prompt_navigator.controllers" do
16
+ ActiveSupport.on_load(:action_controller) do
17
+ include PromptNavigator::HistoryManageable
18
+ end
19
+ end
20
+
21
+ # Configure asset paths for the engine
22
+ # This ensures that JavaScript and CSS files are properly loaded
23
+ initializer "prompt_navigator.assets", before: "sprockets.environment" do |app|
24
+ # Add asset paths
25
+ app.config.assets.paths << root.join("app/assets/stylesheets").to_s
26
+ app.config.assets.paths << root.join("app/javascript").to_s
27
+
28
+ # Precompile assets
29
+ if app.config.respond_to?(:assets)
30
+ app.config.assets.precompile += %w[
31
+ prompt_navigator/history.css
32
+ prompt_navigator/application.css
33
+ controllers/history_controller.js
34
+ ]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,7 @@
1
+ module PromptNavigator
2
+ module Helpers
3
+ def history_list(card_path, active_uuid: nil)
4
+ render "prompt_navigator/history", locals: { card_path: card_path, active_uuid: active_uuid }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module PromptNavigator
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prompt_navigator/version"
4
+ require "prompt_navigator/engine"
5
+ require "prompt_navigator/helpers"
6
+ require_relative "../app/controllers/concerns/prompt_navigator/history_manageable"
7
+
8
+ module PromptNavigator
9
+ # Your code goes here...
10
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :prompt_navigator do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prompt_navigator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - dhq_boiler
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '8.1'
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 8.1.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '8.1'
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 8.1.2
32
+ description: PromptNavigator is a Rails engine that provides a visual history stack
33
+ UI for tracking LLM prompt executions. It includes a self-referencing PromptExecution
34
+ model for building conversation trees, Stimulus-powered SVG arrow visualization
35
+ between parent-child history cards, and automatic asset pipeline integration.
36
+ email:
37
+ - dhq_boiler@live.jp
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - MIT-LICENSE
43
+ - README.md
44
+ - Rakefile
45
+ - app/assets/config/prompt_navigator_manifest.js
46
+ - app/assets/stylesheets/prompt_navigator/application.css
47
+ - app/assets/stylesheets/prompt_navigator/history.css
48
+ - app/controllers/concerns/prompt_navigator/history_manageable.rb
49
+ - app/controllers/prompt_navigator/application_controller.rb
50
+ - app/helpers/prompt_navigator/application_helper.rb
51
+ - app/javascript/controllers/history_controller.js
52
+ - app/jobs/prompt_navigator/application_job.rb
53
+ - app/mailers/prompt_navigator/application_mailer.rb
54
+ - app/models/prompt_navigator/application_record.rb
55
+ - app/models/prompt_navigator/prompt_execution.rb
56
+ - app/views/layouts/prompt_navigator/application.html.erb
57
+ - app/views/prompt_navigator/_history.html.erb
58
+ - app/views/prompt_navigator/_history_card.html.erb
59
+ - config/routes.rb
60
+ - lib/generators/prompt_navigator/modeling/modeling_generator.rb
61
+ - lib/generators/prompt_navigator/modeling/templates/db/migrate/20260129073026_create_prompt_navigator_prompt_executions.rb
62
+ - lib/prompt_navigator.rb
63
+ - lib/prompt_navigator/engine.rb
64
+ - lib/prompt_navigator/helpers.rb
65
+ - lib/prompt_navigator/version.rb
66
+ - lib/tasks/prompt_navigator_tasks.rake
67
+ homepage: https://github.com/jdkim/prompt_manager
68
+ licenses:
69
+ - MIT
70
+ metadata:
71
+ allowed_push_host: https://rubygems.org
72
+ homepage_uri: https://github.com/jdkim/prompt_manager
73
+ source_code_uri: https://github.com/jdkim/prompt_manager
74
+ changelog_uri: https://github.com/jdkim/prompt_manager/blob/main/CHANGELOG.md
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 3.2.0
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubygems_version: 3.6.9
90
+ specification_version: 4
91
+ summary: A Rails engine for managing and visualizing LLM prompt execution history.
92
+ test_files: []