glancer 1.0.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 (142) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +96 -0
  3. data/.rubocop.yml +54 -0
  4. data/CHANGELOG.md +88 -0
  5. data/CLAUDE.md +115 -0
  6. data/CODE_OF_CONDUCT.md +132 -0
  7. data/README.md +354 -0
  8. data/app/assets/config/glancer_manifest.js +1 -0
  9. data/app/assets/javascripts/glancer/application.js +15 -0
  10. data/app/assets/javascripts/glancer/controllers/chat_controller.js +101 -0
  11. data/app/assets/javascripts/glancer/controllers/message_controller.js +1052 -0
  12. data/app/assets/javascripts/glancer/controllers/toast_controller.js +63 -0
  13. data/app/assets/stylesheets/glancer/application.css +350 -0
  14. data/app/assets/stylesheets/glancer/code-blocks.css +6 -0
  15. data/app/assets/stylesheets/glancer/list.css +31 -0
  16. data/app/assets/stylesheets/glancer/scrollbar.css +16 -0
  17. data/app/assets/stylesheets/glancer/table.css +97 -0
  18. data/app/controllers/glancer/application_controller.rb +33 -0
  19. data/app/controllers/glancer/chats_controller.rb +49 -0
  20. data/app/controllers/glancer/messages_controller.rb +144 -0
  21. data/app/controllers/glancer/schema_controller.rb +29 -0
  22. data/app/controllers/glancer/settings_controller.rb +23 -0
  23. data/app/helpers/glancer/application_helper.rb +17 -0
  24. data/app/jobs/glancer/application_job.rb +6 -0
  25. data/app/jobs/glancer/process_message_job.rb +38 -0
  26. data/app/models/glancer/audit.rb +12 -0
  27. data/app/models/glancer/chat.rb +8 -0
  28. data/app/models/glancer/code_version.rb +12 -0
  29. data/app/models/glancer/embedding.rb +6 -0
  30. data/app/models/glancer/message.rb +25 -0
  31. data/app/models/glancer/setting.rb +23 -0
  32. data/app/models/glancer/sql_version.rb +6 -0
  33. data/app/views/glancer/_data/_importmap.json.erb +7 -0
  34. data/app/views/glancer/chats/_chat_sidebar.html.erb +2 -0
  35. data/app/views/glancer/chats/_show.html.erb +52 -0
  36. data/app/views/glancer/chats/_sidebar_chat_list.html.erb +30 -0
  37. data/app/views/glancer/chats/index.html.erb +10 -0
  38. data/app/views/glancer/chats/show.html.erb +1 -0
  39. data/app/views/glancer/messages/_data_table.html.erb +268 -0
  40. data/app/views/glancer/messages/_execution_error.html.erb +26 -0
  41. data/app/views/glancer/messages/_form.html.erb +93 -0
  42. data/app/views/glancer/messages/_message.html.erb +206 -0
  43. data/app/views/glancer/messages/_message_info.html.erb +176 -0
  44. data/app/views/glancer/messages/_temp_form.html.erb +100 -0
  45. data/app/views/glancer/messages/create.turbo_stream.erb +25 -0
  46. data/app/views/glancer/schema/show.html.erb +123 -0
  47. data/app/views/glancer/settings/show.html.erb +306 -0
  48. data/app/views/glancer/shared/_icons.html.erb +126 -0
  49. data/app/views/layouts/glancer/application.html.erb +234 -0
  50. data/config/locales/glancer.en.yml +90 -0
  51. data/config/locales/glancer.es.yml +90 -0
  52. data/config/locales/glancer.pt-BR.yml +90 -0
  53. data/config/routes.rb +20 -0
  54. data/db/migrate/20250629212642_create_glancer_audits.rb +19 -0
  55. data/db/migrate/20250629212643_create_glancer_chats.rb +10 -0
  56. data/db/migrate/20250629212645_create_glancer_embeddings.rb +17 -0
  57. data/db/migrate/20250629212647_create_glancer_messages.rb +29 -0
  58. data/db/migrate/20260513204129_add_user_edited_sql_to_glancer_messages.rb +11 -0
  59. data/db/migrate/20260513210647_create_glancer_sql_versions.rb +18 -0
  60. data/db/migrate/20260513210648_add_message_id_to_glancer_audits.rb +8 -0
  61. data/db/migrate/20260513220000_create_glancer_settings.rb +12 -0
  62. data/db/migrate/20260514083509_add_llm_model_to_glancer_messages.rb +9 -0
  63. data/db/migrate/20260523120000_rename_code_columns_in_glancer_messages.rb +8 -0
  64. data/db/migrate/20260523120001_rename_code_column_in_glancer_audits.rb +7 -0
  65. data/db/migrate/20260523120002_add_code_type_to_glancer_tables.rb +10 -0
  66. data/db/migrate/20260523120003_rename_glancer_sql_versions_to_code_versions.rb +8 -0
  67. data/db/migrate/20260523130000_add_enriched_question_to_glancer_messages.rb +7 -0
  68. data/db/migrate/20260524100000_add_status_to_glancer_messages.rb +9 -0
  69. data/lib/generators/glancer/install/install_generator.rb +74 -0
  70. data/lib/generators/glancer/install/templates/glancer.rb +227 -0
  71. data/lib/generators/glancer/install/templates/llm_context.glancer.md +51 -0
  72. data/lib/glancer/async_runner.rb +50 -0
  73. data/lib/glancer/chart_analyzer.rb +230 -0
  74. data/lib/glancer/configuration.rb +372 -0
  75. data/lib/glancer/engine.rb +90 -0
  76. data/lib/glancer/indexer/context_indexer.rb +58 -0
  77. data/lib/glancer/indexer/model_indexer.rb +64 -0
  78. data/lib/glancer/indexer/schema_indexer.rb +171 -0
  79. data/lib/glancer/indexer.rb +50 -0
  80. data/lib/glancer/retriever.rb +114 -0
  81. data/lib/glancer/utils/logger.rb +83 -0
  82. data/lib/glancer/utils/markdown_helper.rb +56 -0
  83. data/lib/glancer/utils/result_formatter.rb +25 -0
  84. data/lib/glancer/utils/table_stats.rb +18 -0
  85. data/lib/glancer/utils/transaction.rb +59 -0
  86. data/lib/glancer/version.rb +5 -0
  87. data/lib/glancer/workflow/ar_executor.rb +104 -0
  88. data/lib/glancer/workflow/ar_extractor.rb +25 -0
  89. data/lib/glancer/workflow/ar_prompt_builder.rb +64 -0
  90. data/lib/glancer/workflow/ar_sanitizer.rb +88 -0
  91. data/lib/glancer/workflow/builder.rb +129 -0
  92. data/lib/glancer/workflow/cache.rb +55 -0
  93. data/lib/glancer/workflow/executor.rb +72 -0
  94. data/lib/glancer/workflow/llm.rb +123 -0
  95. data/lib/glancer/workflow/prompt_builder.rb +143 -0
  96. data/lib/glancer/workflow/query_enricher.rb +117 -0
  97. data/lib/glancer/workflow/sql_extractor.rb +42 -0
  98. data/lib/glancer/workflow/sql_sanitizer.rb +42 -0
  99. data/lib/glancer/workflow/sql_validator.rb +67 -0
  100. data/lib/glancer/workflow.rb +158 -0
  101. data/lib/glancer.rb +50 -0
  102. data/lib/tasks/glancer/tailwind.rake +8 -0
  103. data/lib/tasks/glancer.rake +99 -0
  104. data/spec/glancer_spec.rb +62 -0
  105. data/spec/lib/glancer/async_runner_spec.rb +133 -0
  106. data/spec/lib/glancer/chart_analyzer_spec.rb +296 -0
  107. data/spec/lib/glancer/configuration_spec.rb +858 -0
  108. data/spec/lib/glancer/engine_spec.rb +209 -0
  109. data/spec/lib/glancer/indexer/context_indexer_spec.rb +96 -0
  110. data/spec/lib/glancer/indexer/model_indexer_spec.rb +103 -0
  111. data/spec/lib/glancer/indexer/schema_indexer_spec.rb +382 -0
  112. data/spec/lib/glancer/indexer_spec.rb +95 -0
  113. data/spec/lib/glancer/retriever_spec.rb +179 -0
  114. data/spec/lib/glancer/utils/logger_spec.rb +85 -0
  115. data/spec/lib/glancer/utils/markdown_helper_spec.rb +92 -0
  116. data/spec/lib/glancer/utils/result_formatter_spec.rb +73 -0
  117. data/spec/lib/glancer/utils/table_stats_spec.rb +34 -0
  118. data/spec/lib/glancer/utils/transaction_spec.rb +73 -0
  119. data/spec/lib/glancer/workflow/ar_executor_spec.rb +155 -0
  120. data/spec/lib/glancer/workflow/ar_extractor_spec.rb +50 -0
  121. data/spec/lib/glancer/workflow/ar_prompt_builder_spec.rb +79 -0
  122. data/spec/lib/glancer/workflow/ar_sanitizer_spec.rb +175 -0
  123. data/spec/lib/glancer/workflow/builder_spec.rb +204 -0
  124. data/spec/lib/glancer/workflow/cache_spec.rb +142 -0
  125. data/spec/lib/glancer/workflow/executor_spec.rb +149 -0
  126. data/spec/lib/glancer/workflow/llm_spec.rb +124 -0
  127. data/spec/lib/glancer/workflow/prompt_builder_spec.rb +196 -0
  128. data/spec/lib/glancer/workflow/query_enricher_spec.rb +184 -0
  129. data/spec/lib/glancer/workflow/sql_extractor_spec.rb +82 -0
  130. data/spec/lib/glancer/workflow/sql_sanitizer_spec.rb +98 -0
  131. data/spec/lib/glancer/workflow/sql_validator_spec.rb +166 -0
  132. data/spec/lib/glancer/workflow_spec.rb +308 -0
  133. data/spec/models/glancer/audit_spec.rb +82 -0
  134. data/spec/models/glancer/chat_spec.rb +60 -0
  135. data/spec/models/glancer/code_version_spec.rb +71 -0
  136. data/spec/models/glancer/embedding_spec.rb +73 -0
  137. data/spec/models/glancer/message_spec.rb +144 -0
  138. data/spec/models/glancer/setting_spec.rb +88 -0
  139. data/spec/models/glancer/sql_version_spec.rb +4 -0
  140. data/spec/spec_helper.rb +128 -0
  141. data/spec/support/schema.rb +55 -0
  142. metadata +255 -0
@@ -0,0 +1,306 @@
1
+ <% content_for :main_content do %>
2
+ <div class="flex flex-col h-full overflow-hidden">
3
+
4
+ <%# Header %>
5
+ <div class="flex items-center gap-3 px-6 py-4 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 flex-shrink-0">
6
+ <%= link_to glancer.root_path,
7
+ class: "p-1.5 rounded-md text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors",
8
+ aria: { label: t("glancer.nav.back_to_chat") },
9
+ data: { turbo: false } do %>
10
+ <svg class="w-4 h-4" aria-hidden="true"><use href="#icon-arrow-left"/></svg>
11
+ <% end %>
12
+ <h1 class="text-sm font-semibold text-gray-900 dark:text-white"><%= t("glancer.settings.title") %></h1>
13
+ </div>
14
+
15
+ <%# Body %>
16
+ <div class="flex-1 overflow-y-auto px-6 py-6">
17
+ <div class="max-w-xl mx-auto space-y-8">
18
+
19
+ <% if flash[:notice] %>
20
+ <div class="flex items-center gap-2 px-4 py-3 rounded-xl bg-green-50 dark:bg-green-950 border border-green-200 dark:border-green-800 text-sm text-green-700 dark:text-green-400">
21
+ <svg class="w-4 h-4 flex-shrink-0" aria-hidden="true"><use href="#icon-check"/></svg>
22
+ <%= flash[:notice] %>
23
+ </div>
24
+ <% end %>
25
+
26
+ <%# ── Editable preferences ─────────────────────────────────────────────── %>
27
+ <section>
28
+ <h2 class="text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500 mb-4">Preferences</h2>
29
+
30
+ <%= form_with url: glancer.settings_path, method: :patch, data: { turbo: false } do |f| %>
31
+ <div class="space-y-5">
32
+
33
+ <%# Interface language %>
34
+ <div>
35
+ <label class="block text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500 mb-2">
36
+ <%= t("glancer.settings.language.label") %>
37
+ </label>
38
+ <select name="settings[ui_language]"
39
+ class="w-full px-3 py-2 text-sm bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-primary-500">
40
+ <option value="en" <%= "selected" if @settings[:ui_language] == "en" %>><%= t("glancer.settings.language.en") %></option>
41
+ <option value="pt-BR" <%= "selected" if @settings[:ui_language] == "pt-BR" %>><%= t("glancer.settings.language.pt_br") %></option>
42
+ <option value="es" <%= "selected" if @settings[:ui_language] == "es" %>><%= t("glancer.settings.language.es") %></option>
43
+ </select>
44
+ </div>
45
+
46
+ <%# Speech recognition language %>
47
+ <div>
48
+ <label class="block text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500 mb-2">
49
+ <%= t("glancer.settings.speech.label") %>
50
+ </label>
51
+ <select name="settings[speech_language]"
52
+ class="w-full px-3 py-2 text-sm bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-primary-500">
53
+ <option value="auto" <%= "selected" if @settings[:speech_language] == "auto" %>><%= t("glancer.settings.speech.auto") %></option>
54
+ <option value="en-US" <%= "selected" if @settings[:speech_language] == "en-US" %>><%= t("glancer.settings.speech.en_us") %></option>
55
+ <option value="pt-BR" <%= "selected" if @settings[:speech_language] == "pt-BR" %>><%= t("glancer.settings.speech.pt_br") %></option>
56
+ <option value="es-ES" <%= "selected" if @settings[:speech_language] == "es-ES" %>><%= t("glancer.settings.speech.es_es") %></option>
57
+ </select>
58
+ </div>
59
+
60
+ <%# Custom LLM instructions %>
61
+ <div>
62
+ <label class="block text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500 mb-1">
63
+ <%= t("glancer.settings.instructions.label") %>
64
+ </label>
65
+ <p class="text-[11px] text-gray-400 dark:text-gray-500 mb-2">
66
+ <%= t("glancer.settings.instructions.help") %>
67
+ </p>
68
+ <textarea name="settings[custom_instructions]"
69
+ rows="6"
70
+ placeholder="<%= t("glancer.settings.instructions.placeholder") %>"
71
+ class="w-full px-3 py-2 text-sm font-mono bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 resize-y min-h-[120px]"><%= @settings[:custom_instructions] %></textarea>
72
+ </div>
73
+
74
+ <%# Save button %>
75
+ <div class="pt-1">
76
+ <button type="submit"
77
+ class="px-5 py-2 text-sm font-medium rounded-lg bg-primary-600 hover:bg-primary-700 active:bg-primary-800 text-white transition-colors">
78
+ <%= t("glancer.settings.save") %>
79
+ </button>
80
+ </div>
81
+
82
+ </div>
83
+ <% end %>
84
+ </section>
85
+
86
+ <%# ── Read-only configuration ──────────────────────────────────────────── %>
87
+ <section>
88
+ <div class="flex items-center gap-2 mb-4">
89
+ <h2 class="text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">Configuration</h2>
90
+ <span class="text-[10px] font-medium px-1.5 py-0.5 rounded bg-gray-100 dark:bg-gray-800 text-gray-400 dark:text-gray-500">config/initializers/glancer.rb</span>
91
+ </div>
92
+
93
+ <div class="rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden divide-y divide-gray-100 dark:divide-gray-800 text-sm">
94
+
95
+ <%# ─ LLM ─ %>
96
+ <div class="px-4 py-2 bg-gray-50 dark:bg-gray-800/60">
97
+ <span class="text-[10px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">LLM — defaults</span>
98
+ </div>
99
+
100
+ <% cfg = @glancer_config %>
101
+
102
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
103
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">llm_provider</span>
104
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.llm_provider %></span>
105
+ </div>
106
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
107
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">llm_model</span>
108
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.llm_model %></span>
109
+ </div>
110
+
111
+ <%# SQL role %>
112
+ <div class="px-4 py-2 bg-gray-50 dark:bg-gray-800/60">
113
+ <span class="text-[10px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">LLM — code generation</span>
114
+ </div>
115
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
116
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">code_provider</span>
117
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200">
118
+ <%= cfg.code_provider || %(<span class="text-gray-400 dark:text-gray-600 italic">#{cfg.llm_provider} (default)</span>).html_safe %>
119
+ </span>
120
+ </div>
121
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
122
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">code_model</span>
123
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200">
124
+ <%= cfg.code_model || %(<span class="text-gray-400 dark:text-gray-600 italic">#{cfg.llm_model} (default)</span>).html_safe %>
125
+ </span>
126
+ </div>
127
+
128
+ <%# Chat role %>
129
+ <div class="px-4 py-2 bg-gray-50 dark:bg-gray-800/60">
130
+ <span class="text-[10px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">LLM — chat responses</span>
131
+ </div>
132
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
133
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">chat_provider</span>
134
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200">
135
+ <%= cfg.chat_provider || %(<span class="text-gray-400 dark:text-gray-600 italic">#{cfg.llm_provider} (default)</span>).html_safe %>
136
+ </span>
137
+ </div>
138
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
139
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">chat_model</span>
140
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200">
141
+ <%= cfg.chat_model || %(<span class="text-gray-400 dark:text-gray-600 italic">#{cfg.llm_model} (default)</span>).html_safe %>
142
+ </span>
143
+ </div>
144
+
145
+ <%# Embeddings %>
146
+ <div class="px-4 py-2 bg-gray-50 dark:bg-gray-800/60">
147
+ <span class="text-[10px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">LLM — embeddings</span>
148
+ </div>
149
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
150
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">embedding_provider</span>
151
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200">
152
+ <%= cfg.embedding_provider || %(<span class="text-gray-400 dark:text-gray-600 italic">#{cfg.llm_provider} (default)</span>).html_safe %>
153
+ </span>
154
+ </div>
155
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
156
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">embedding_model</span>
157
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200">
158
+ <%= cfg.embedding_model || %(<span class="text-gray-400 dark:text-gray-600 italic">provider default</span>).html_safe %>
159
+ </span>
160
+ </div>
161
+
162
+ <%# Query Enrichment %>
163
+ <div class="px-4 py-2 bg-gray-50 dark:bg-gray-800/60">
164
+ <span class="text-[10px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">LLM — query enrichment</span>
165
+ </div>
166
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
167
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">query_enrichment_enabled</span>
168
+ <span class="<%= cfg.query_enrichment_enabled ? 'text-green-600 dark:text-green-400' : 'text-gray-400' %> text-xs font-medium">
169
+ <%= cfg.query_enrichment_enabled ? "enabled" : "disabled" %>
170
+ </span>
171
+ </div>
172
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
173
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">enrichment_provider</span>
174
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200">
175
+ <%= cfg.enrichment_provider || %(<span class="text-gray-400 dark:text-gray-600 italic">#{cfg.llm_provider} (default)</span>).html_safe %>
176
+ </span>
177
+ </div>
178
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
179
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">enrichment_model</span>
180
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200">
181
+ <%= cfg.enrichment_model || %(<span class="text-gray-400 dark:text-gray-600 italic">#{cfg.llm_model} (default)</span>).html_safe %>
182
+ </span>
183
+ </div>
184
+
185
+ <%# Database %>
186
+ <div class="px-4 py-2 bg-gray-50 dark:bg-gray-800/60">
187
+ <span class="text-[10px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">Database</span>
188
+ </div>
189
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
190
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">adapter</span>
191
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.resolved_adapter %></span>
192
+ </div>
193
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
194
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">read_only_db</span>
195
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200">
196
+ <%= cfg.read_only_db ? "configured" : %(<span class="text-gray-400 dark:text-gray-600 italic">not set</span>).html_safe %>
197
+ </span>
198
+ </div>
199
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
200
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">statement_timeout</span>
201
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.statement_timeout.to_i %>s</span>
202
+ </div>
203
+
204
+ <%# Retrieval %>
205
+ <div class="px-4 py-2 bg-gray-50 dark:bg-gray-800/60">
206
+ <span class="text-[10px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">Retrieval</span>
207
+ </div>
208
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
209
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">k</span>
210
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.k %></span>
211
+ </div>
212
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
213
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">min_score</span>
214
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.min_score %></span>
215
+ </div>
216
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
217
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">history_limit</span>
218
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.history_limit %></span>
219
+ </div>
220
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
221
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">workflow_cache_ttl</span>
222
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.workflow_cache_ttl.to_i %>s</span>
223
+ </div>
224
+
225
+ <%# Indexing %>
226
+ <div class="px-4 py-2 bg-gray-50 dark:bg-gray-800/60">
227
+ <span class="text-[10px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">Indexing</span>
228
+ </div>
229
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
230
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">schema_permission</span>
231
+ <span class="<%= cfg.schema_permission ? 'text-green-600 dark:text-green-400' : 'text-gray-400' %> text-xs font-medium">
232
+ <%= cfg.schema_permission ? "enabled" : "disabled" %>
233
+ </span>
234
+ </div>
235
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
236
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">models_permission</span>
237
+ <span class="<%= cfg.models_permission ? 'text-green-600 dark:text-green-400' : 'text-gray-400' %> text-xs font-medium">
238
+ <%= cfg.models_permission ? "enabled" : "disabled" %>
239
+ </span>
240
+ </div>
241
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
242
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">chunk_size</span>
243
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.chunk_size %></span>
244
+ </div>
245
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
246
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">chunk_overlap</span>
247
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.chunk_overlap %></span>
248
+ </div>
249
+
250
+ <%# Logging %>
251
+ <div class="px-4 py-2 bg-gray-50 dark:bg-gray-800/60">
252
+ <span class="text-[10px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">Logging</span>
253
+ </div>
254
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
255
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">log_verbosity</span>
256
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= cfg.log_verbosity %></span>
257
+ </div>
258
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
259
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">log_output_path</span>
260
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200">
261
+ <%= cfg.log_output_path || %(<span class="text-gray-400 dark:text-gray-600 italic">Rails.logger</span>).html_safe %>
262
+ </span>
263
+ </div>
264
+
265
+ <% if cfg.resolved_blazer_path.present? %>
266
+ <%# Integrations %>
267
+ <div class="px-4 py-2 bg-gray-50 dark:bg-gray-800/60">
268
+ <span class="text-[10px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">Integrations</span>
269
+ </div>
270
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
271
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono">blazer_path</span>
272
+ <span class="text-xs font-medium text-green-600 dark:text-green-400"><%= cfg.resolved_blazer_path %></span>
273
+ </div>
274
+ <% end %>
275
+
276
+ </div>
277
+
278
+ <p class="mt-3 text-[11px] text-gray-400 dark:text-gray-600">
279
+ These values are set in your initializer and require a server restart to take effect.
280
+ </p>
281
+ </section>
282
+
283
+ <%# ── Embeddings stats ─────────────────────────────────────────────────── %>
284
+ <% embedding_count = Glancer::Embedding.count rescue 0 %>
285
+ <% if embedding_count > 0 %>
286
+ <section>
287
+ <h2 class="text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500 mb-4">Index</h2>
288
+ <div class="rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden divide-y divide-gray-100 dark:divide-gray-800 text-sm">
289
+ <% Glancer::Embedding.group(:source_type).count.each do |type, count| %>
290
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center">
291
+ <span class="text-xs text-gray-500 dark:text-gray-400 font-mono"><%= type %></span>
292
+ <span class="text-xs font-medium text-gray-800 dark:text-gray-200"><%= count %> chunk<%= count == 1 ? "" : "s" %></span>
293
+ </div>
294
+ <% end %>
295
+ <div class="grid grid-cols-2 px-4 py-2.5 items-center bg-gray-50 dark:bg-gray-800/60">
296
+ <span class="text-xs font-semibold text-gray-500 dark:text-gray-400">Total</span>
297
+ <span class="text-xs font-semibold text-gray-800 dark:text-gray-200"><%= embedding_count %> chunks</span>
298
+ </div>
299
+ </div>
300
+ </section>
301
+ <% end %>
302
+
303
+ </div>
304
+ </div>
305
+ </div>
306
+ <% end %>
@@ -0,0 +1,126 @@
1
+ <div hidden aria-hidden="true">
2
+ <svg xmlns="http://www.w3.org/2000/svg">
3
+ <symbol id="icon-database" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
4
+ <ellipse cx="12" cy="5" rx="9" ry="3"/>
5
+ <path d="M3 5v4c0 1.657 4.03 3 9 3s9-1.343 9-3V5"/>
6
+ <path d="M3 9v4c0 1.657 4.03 3 9 3s9-1.343 9-3V9"/>
7
+ <path d="M3 13v4c0 1.657 4.03 3 9 3s9-1.343 9-3v-4"/>
8
+ </symbol>
9
+
10
+ <symbol id="icon-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
11
+ <path d="M12 5v14M5 12h14"/>
12
+ </symbol>
13
+
14
+ <symbol id="icon-trash" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
15
+ <path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6"/>
16
+ <path d="M10 11v6M14 11v6"/>
17
+ </symbol>
18
+
19
+ <symbol id="icon-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
20
+ <rect x="9" y="9" width="13" height="13" rx="2"/>
21
+ <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/>
22
+ </symbol>
23
+
24
+ <symbol id="icon-send" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
25
+ <path d="M22 2L11 13M22 2L15 22l-4-9-9-4 20-7z"/>
26
+ </symbol>
27
+
28
+ <symbol id="icon-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
29
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/>
30
+ </symbol>
31
+
32
+ <symbol id="icon-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
33
+ <circle cx="12" cy="12" r="10"/>
34
+ <path d="M12 16v-4M12 8h.01"/>
35
+ </symbol>
36
+
37
+ <symbol id="icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
38
+ <path d="M20 6L9 17l-5-5"/>
39
+ </symbol>
40
+
41
+ <symbol id="icon-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
42
+ <path d="M18 6L6 18M6 6l12 12"/>
43
+ </symbol>
44
+
45
+ <symbol id="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
46
+ <circle cx="12" cy="12" r="4"/>
47
+ <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
48
+ </symbol>
49
+
50
+ <symbol id="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
51
+ <path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
52
+ </symbol>
53
+
54
+ <symbol id="icon-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
55
+ <path d="M3 12h18M3 6h18M3 18h18"/>
56
+ </symbol>
57
+
58
+ <symbol id="icon-play" viewBox="0 0 24 24" fill="currentColor" viewBox="0 0 24 24">
59
+ <path d="M5 3l14 9-14 9V3z"/>
60
+ </symbol>
61
+
62
+ <symbol id="icon-chat" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
63
+ <path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/>
64
+ </symbol>
65
+
66
+ <symbol id="icon-chevron-left" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
67
+ <path d="M15 18l-6-6 6-6"/>
68
+ </symbol>
69
+
70
+ <symbol id="icon-chevron-right" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
71
+ <path d="M9 18l6-6-6-6"/>
72
+ </symbol>
73
+
74
+ <symbol id="icon-warning" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
75
+ <path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/>
76
+ <path d="M12 9v4M12 17h.01"/>
77
+ </symbol>
78
+
79
+ <symbol id="icon-mic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
80
+ <path d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z"/>
81
+ <path d="M19 10v2a7 7 0 01-14 0v-2M12 19v4M8 23h8"/>
82
+ </symbol>
83
+
84
+ <symbol id="icon-mic-off" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
85
+ <path d="M1 1l22 22M9 9v3a3 3 0 005.12 2.12M15 9.34V4a3 3 0 00-5.94-.6"/>
86
+ <path d="M17 16.95A7 7 0 015 12v-2m14 0v2a7 7 0 01-.11 1.23M12 19v4M8 23h8"/>
87
+ </symbol>
88
+
89
+ <symbol id="icon-external" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
90
+ <path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3"/>
91
+ </symbol>
92
+
93
+ <symbol id="icon-edit" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
94
+ <path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/>
95
+ <path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/>
96
+ </symbol>
97
+
98
+ <symbol id="icon-table" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
99
+ <rect x="3" y="3" width="18" height="18" rx="2"/>
100
+ <path d="M3 9h18M3 15h18M9 3v18"/>
101
+ </symbol>
102
+
103
+ <symbol id="icon-filter" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
104
+ <path d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z"/>
105
+ </symbol>
106
+
107
+ <symbol id="icon-arrow-left" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
108
+ <path d="M19 12H5M12 19l-7-7 7-7"/>
109
+ </symbol>
110
+
111
+ <symbol id="icon-chevron-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
112
+ <path d="M6 9l6 6 6-6"/>
113
+ </symbol>
114
+
115
+ <symbol id="icon-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
116
+ <circle cx="12" cy="12" r="3"/>
117
+ <path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/>
118
+ </symbol>
119
+ <symbol id="icon-maximize" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
120
+ <polyline points="15 3 21 3 21 9"/>
121
+ <polyline points="9 21 3 21 3 15"/>
122
+ <line x1="21" y1="3" x2="14" y2="10"/>
123
+ <line x1="3" y1="21" x2="10" y2="14"/>
124
+ </symbol>
125
+ </svg>
126
+ </div>