plain-rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +162 -0
  3. data/Rakefile +8 -0
  4. data/app/assets/builds/plain.css +1 -0
  5. data/app/assets/config/plain_manifest.js +1 -0
  6. data/app/assets/stylesheets/plain/application.css +15 -0
  7. data/app/assets/stylesheets/plain/application.tailwind.css +7 -0
  8. data/app/controllers/plain/application_controller.rb +4 -0
  9. data/app/controllers/plain/conversations_controller.rb +55 -0
  10. data/app/controllers/plain/docs_controller.rb +63 -0
  11. data/app/controllers/plain/home_controller.rb +10 -0
  12. data/app/controllers/plain/messages_controller.rb +30 -0
  13. data/app/helpers/plain/application_helper.rb +4 -0
  14. data/app/helpers/plain/conversations_helper.rb +4 -0
  15. data/app/helpers/plain/docs_helper.rb +4 -0
  16. data/app/helpers/plain/home_helper.rb +4 -0
  17. data/app/helpers/plain/messages_helper.rb +4 -0
  18. data/app/javascripts/loader_controller.js +27 -0
  19. data/app/javascripts/scroll_controller.js +8 -0
  20. data/app/javascripts/toggle_class_controller.js +11 -0
  21. data/app/jobs/plain/application_job.rb +4 -0
  22. data/app/jobs/plain/message_processor_job.rb +9 -0
  23. data/app/mailers/plain/application_mailer.rb +6 -0
  24. data/app/models/plain/application_record.rb +5 -0
  25. data/app/models/plain/conversation.rb +53 -0
  26. data/app/models/plain/message.rb +62 -0
  27. data/app/services/plain/ai_docs.rb +163 -0
  28. data/app/services/plain/docs_service.rb +127 -0
  29. data/app/views/layouts/plain/application.html.erb +51 -0
  30. data/app/views/plain/conversations/_conversation_item.erb +15 -0
  31. data/app/views/plain/conversations/_conversation_item.html.erb +21 -0
  32. data/app/views/plain/conversations/_conversation_list.erb +18 -0
  33. data/app/views/plain/conversations/_conversation_list.html.erb +13 -0
  34. data/app/views/plain/conversations/_modal.html.erb +73 -0
  35. data/app/views/plain/conversations/index.html.erb +17 -0
  36. data/app/views/plain/conversations/show.html.erb +14 -0
  37. data/app/views/plain/docs/_footer.erb +61 -0
  38. data/app/views/plain/docs/_menu.erb +20 -0
  39. data/app/views/plain/docs/_sections.erb +73 -0
  40. data/app/views/plain/docs/show.html.erb +126 -0
  41. data/app/views/plain/home/index.html.erb +35 -0
  42. data/app/views/plain/messages/_form.html.erb +25 -0
  43. data/app/views/plain/messages/_message_item.html.erb +26 -0
  44. data/app/views/plain/messages/_message_list.html.erb +16 -0
  45. data/app/views/plain/messages/_new_message.turbo_stream.erb +3 -0
  46. data/app/views/plain/messages/create.turbo_stream.erb +8 -0
  47. data/app/views/plain/messages/index.html.erb +1 -0
  48. data/config/database.yml +53 -0
  49. data/config/routes.rb +11 -0
  50. data/db/migrate/20230729010703_create_plain_conversations.rb +10 -0
  51. data/db/migrate/20230729010745_create_plain_messages.rb +11 -0
  52. data/lib/plain/configuration.rb +12 -0
  53. data/lib/plain/engine.rb +16 -0
  54. data/lib/plain/version.rb +3 -0
  55. data/lib/plain.rb +17 -0
  56. data/lib/tasks/plain_tasks.rake +21 -0
  57. metadata +196 -0
@@ -0,0 +1,73 @@
1
+ <div class="my-16 xl:max-w-none">
2
+
3
+ <h2 id="resources" class="scroll-mt-24">
4
+ <a class="group text-inherit no-underline hover:text-inherit" href="/#resources">
5
+ <div class="absolute ml-[calc(-1*var(--width))] mt-1 hidden w-[var(--width)] opacity-0 transition [--width:calc(2.625rem+0.5px+50%-min(50%,calc(theme(maxWidth.lg)+theme(spacing.8))))] group-hover:opacity-100 group-focus:opacity-100 md:block lg:z-50 2xl:[--width:theme(spacing.10)]">
6
+ <div class="group/anchor block h-5 w-5 rounded-lg bg-zinc-50 ring-1 ring-inset ring-zinc-300 transition hover:ring-zinc-500 dark:bg-zinc-800 dark:ring-zinc-700 dark:hover:bg-zinc-700 dark:hover:ring-zinc-600">
7
+ <svg viewBox="0 0 20 20" fill="none" stroke-linecap="round" aria-hidden="true" class="h-5 w-5 stroke-zinc-500 transition dark:stroke-zinc-400 dark:group-hover/anchor:stroke-white">
8
+ <path d="m6.5 11.5-.964-.964a3.535 3.535 0 1 1 5-5l.964.964m2 2 .964.964a3.536 3.536 0 0 1-5 5L8.5 13.5m0-5 3 3"></path>
9
+ </svg>
10
+ </div>
11
+ </div>
12
+ <%= section["name"] %>
13
+ </a>
14
+ </h2>
15
+
16
+ <div class="not-prose mt-4 grid grid-cols-1 gap-8 border-t border-zinc-900/5 pt-10 dark:border-white/5 sm:grid-cols-2 xl:grid-cols-4">
17
+ <% section["items"].each do |item| %>
18
+ <div class="group relative flex rounded-2xl bg-zinc-50 transition-shadow hover:shadow-md hover:shadow-zinc-900/5 dark:bg-white/2.5 dark:hover:shadow-black/5">
19
+ <div class="pointer-events-none">
20
+ <div class="absolute inset-0 rounded-2xl transition duration-300 [mask-image:linear-gradient(white,transparent)] group-hover:opacity-50">
21
+ <svg aria-hidden="true" class="absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/[0.02] stroke-black/5 dark:fill-white/1 dark:stroke-white/2.5">
22
+ <defs>
23
+ <pattern id=":r22:" width="72" height="56" patternUnits="userSpaceOnUse" x="50%" y="16">
24
+ <path d="M.5 56V.5H72" fill="none"></path>
25
+ </pattern>
26
+ </defs>
27
+ <rect width="100%" height="100%" stroke-width="0" fill="url(#:r22:)"></rect>
28
+ <svg x="50%" y="16" class="overflow-visible">
29
+ <rect stroke-width="0" width="73" height="57" x="0" y="56"></rect>
30
+ <rect stroke-width="0" width="73" height="57" x="72" y="168"></rect>
31
+ </svg>
32
+ </svg>
33
+ </div>
34
+ <div class="absolute inset-0 rounded-2xl bg-gradient-to-r from-[#D7EDEA] to-[#F4FBDF] opacity-0 transition duration-300 group-hover:opacity-100 dark:from-[#202D2E] dark:to-[#303428]" data-projection-id="17" style="-webkit-mask-image: radial-gradient(180px at 14px 46px, white, transparent);"></div>
35
+ <div class="absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay transition duration-300 group-hover:opacity-100" data-projection-id="18" style="-webkit-mask-image: radial-gradient(180px at 14px 46px, white, transparent);">
36
+ <svg aria-hidden="true" class="absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/50 stroke-black/70 dark:fill-white/2.5 dark:stroke-white/10">
37
+ <defs>
38
+ <pattern id=":r23:" width="72" height="56" patternUnits="userSpaceOnUse" x="50%" y="16">
39
+ <path d="M.5 56V.5H72" fill="none"></path>
40
+ </pattern>
41
+ </defs>
42
+ <rect width="100%" height="100%" stroke-width="0" fill="url(#:r23:)"></rect>
43
+ <svg x="50%" y="16" class="overflow-visible">
44
+ <rect stroke-width="0" width="73" height="57" x="0" y="56"></rect>
45
+ <rect stroke-width="0" width="73" height="57" x="72" y="168"></rect>
46
+ </svg>
47
+ </svg>
48
+ </div>
49
+ </div>
50
+ <div class="absolute inset-0 rounded-2xl ring-1 ring-inset ring-zinc-900/7.5 group-hover:ring-zinc-900/10 dark:ring-white/10 dark:group-hover:ring-white/20"></div>
51
+ <div class="relative rounded-2xl px-4 pb-4 pt-16">
52
+ <div class="flex h-7 w-7 items-center justify-center rounded-full bg-zinc-900/5 ring-1 ring-zinc-900/25 backdrop-blur-[2px] transition duration-300 group-hover:bg-white/50 group-hover:ring-zinc-900/25 dark:bg-white/7.5 dark:ring-white/15 dark:group-hover:bg-emerald-300/10 dark:group-hover:ring-emerald-400">
53
+ <svg viewBox="0 0 20 20" aria-hidden="true" class="h-5 w-5 fill-zinc-700/10 stroke-zinc-700 transition-colors duration-300 group-hover:stroke-zinc-900 dark:fill-white/10 dark:stroke-zinc-400 dark:group-hover:fill-emerald-300/10 dark:group-hover:stroke-emerald-400">
54
+ <path stroke-width="0" fill-rule="evenodd" clip-rule="evenodd" d="M10 .5a9.5 9.5 0 0 1 5.598 17.177C14.466 15.177 12.383 13.5 10 13.5s-4.466 1.677-5.598 4.177A9.5 9.5 0 0 1 10 .5ZM12.5 8a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z"></path>
55
+ <path fill="none" stroke-linecap="round" stroke-linejoin="round" d="M10 .5a9.5 9.5 0 0 1 5.598 17.177A9.458 9.458 0 0 1 10 19.5a9.458 9.458 0 0 1-5.598-1.823A9.5 9.5 0 0 1 10 .5Z"></path>
56
+ <path fill="none" stroke-linecap="round" stroke-linejoin="round" d="M4.402 17.677C5.534 15.177 7.617 13.5 10 13.5s4.466 1.677 5.598 4.177M10 5.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Z"></path>
57
+ </svg>
58
+ </div>
59
+ <h3 class="mt-4 text-sm font-semibold leading-7 text-zinc-900 dark:text-white">
60
+ <a href="<%= item["path"] %>">
61
+ <span class="absolute inset-0 rounded-2xl"></span>
62
+ <%= item["name"] %>
63
+ </a>
64
+ </h3>
65
+ <p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
66
+ <%= item["description"] %>
67
+ </p>
68
+ </div>
69
+ </div>
70
+ <% end %>
71
+
72
+ </div>
73
+ </div>
@@ -0,0 +1,126 @@
1
+
2
+ <div id="pp" data-turbo-permanent>
3
+ <%= turbo_frame_tag "plain" %>
4
+ </div>
5
+
6
+ <div class="lg:ml-72 xl:ml-80">
7
+ <header class="contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex">
8
+ <div class="contents lg:pointer-events-auto lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-zinc-900/10 lg:px-6 lg:pb-8 lg:pt-4 lg:dark:border-white/10 xl:w-80">
9
+ <div class="hidden lg:flex">
10
+ <a aria-label="Home" href="/" class="flex space-x-2 items-center">
11
+ <% if @config["logo"] %>
12
+ <%= image_tag(@config["logo"], class: "h-10") %>
13
+ <% end %>
14
+ <span class="font-extrabold uppercase text-xl">
15
+ <%= @config["name"] || "PLAIN" %>
16
+ </span>
17
+ </a>
18
+ </div>
19
+
20
+ <div class="fixed inset-x-0 top-0 z-50 flex h-14 items-center justify-between gap-12 px-4 transition sm:px-6 lg:left-72 lg:z-30 lg:px-8 xl:left-80 backdrop-blur-sm dark:backdrop-blur lg:left-72 xl:left-80 bg-white/[var(--bg-opacity-light)] dark:bg-zinc-900/[var(--bg-opacity-dark)]" style="--bg-opacity-light: 0.5; --bg-opacity-dark: 0.2;">
21
+
22
+ <div class="absolute inset-x-0 top-full h-px transition bg-zinc-900/7.5 dark:bg-white/7.5"></div>
23
+ <!--<div class="hidden lg:block lg:max-w-md lg:flex-auto">
24
+ <button type="button" class="hidden h-8 w-full items-center gap-2 rounded-full bg-white pl-2 pr-3 text-sm text-zinc-500 ring-1 ring-zinc-900/10 transition hover:ring-zinc-900/20 dark:bg-white/5 dark:text-zinc-400 dark:ring-inset dark:ring-white/10 dark:hover:ring-white/20 lg:flex focus:[&amp;:not(:focus-visible)]:outline-none">
25
+ <svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="h-5 w-5 stroke-current">
26
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25"></path>
27
+ </svg>Find something... <kbd class="ml-auto text-2xs text-zinc-400 dark:text-zinc-500">
28
+ <kbd class="font-sans">⌘</kbd>
29
+ <kbd class="font-sans">K</kbd>
30
+ </kbd>
31
+ </button>
32
+ </div>
33
+ <div class="flex items-center gap-5 lg:hidden">
34
+ <button type="button" class="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5" aria-label="Toggle navigation">
35
+ <svg viewBox="0 0 10 9" fill="none" stroke-linecap="round" aria-hidden="true" class="w-2.5 stroke-zinc-900 dark:stroke-white">
36
+ <path d="M.5 1h9M.5 8h9M.5 4.5h9"></path>
37
+ </svg>
38
+ </button>
39
+ <a aria-label="Home" href="/plain" class="flex space-x-2 items-center">
40
+
41
+ <% # image_tag("add-logo-here", class: "h-6") %>
42
+ <span class="font-extrabold uppercase text-sm">Plain</span>
43
+ </a>
44
+ </div>
45
+ -->
46
+ <div class="flex items-center gap-5">
47
+ <nav class="hidden md:block">
48
+ <ul role="list" class="flex items-center gap-8">
49
+ <% if @config["chat_envs"].present? and @config["chat_envs"].include?(Rails.env.to_s) %>
50
+ <li>
51
+ <a href="/plain"
52
+ data-turbo-frame="plain"
53
+ class="bg-gray-900 text-white rounded-md px-3 py-2 text-sm font-medium" aria-current="page">
54
+ Chat
55
+ </a>
56
+ </li>
57
+ <% end %>
58
+ <!--<li>
59
+ <a class="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/">API</a>
60
+ </li>
61
+ <li>
62
+ <a class="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/#">Documentation</a>
63
+ </li>-->
64
+ <% if @config["links"] %>
65
+ <% @config["links"].each do |link| %>
66
+ <li>
67
+ <a class="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white"
68
+ href="<%= link["url"] %>" target="blank">
69
+ <%= link["name"] %>
70
+ </a>
71
+ </li>
72
+ <% end %>
73
+ <% end %>
74
+ </ul>
75
+ </nav>
76
+ <div class="hidden md:block md:h-5 md:w-px md:bg-zinc-900/10 md:dark:bg-white/15"></div>
77
+ <div class="hidden flex gap-4">
78
+ <div class="contents lg:hidden">
79
+ <button type="button" class="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5 lg:hidden focus:[&amp;:not(:focus-visible)]:outline-none" aria-label="Find something...">
80
+ <svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="h-5 w-5 stroke-zinc-900 dark:stroke-white">
81
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25"></path>
82
+ </svg>
83
+ </button>
84
+ </div>
85
+ <button type="button" class="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5" aria-label="Toggle dark mode">
86
+ <svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="h-5 w-5 stroke-zinc-900 dark:hidden">
87
+ <path d="M12.5 10a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0Z"></path>
88
+ <path stroke-linecap="round" d="M10 5.5v-1M13.182 6.818l.707-.707M14.5 10h1M13.182 13.182l.707.707M10 15.5v-1M6.11 13.889l.708-.707M4.5 10h1M6.11 6.111l.708.707"></path>
89
+ </svg>
90
+ <svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="hidden h-5 w-5 stroke-white dark:block">
91
+ <path d="M15.224 11.724a5.5 5.5 0 0 1-6.949-6.949 5.5 5.5 0 1 0 6.949 6.949Z"></path>
92
+ </svg>
93
+ </button>
94
+ </div>
95
+ <!--
96
+ <div class="hidden min-[416px]:contents">
97
+ <a class="inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-emerald-400/10 dark:text-emerald-400 dark:ring-1 dark:ring-inset dark:ring-emerald-400/20 dark:hover:bg-emerald-400/10 dark:hover:text-emerald-300 dark:hover:ring-emerald-300"
98
+ href="/#">Sign in</a>
99
+ </div>
100
+ -->
101
+ </div>
102
+
103
+ </div>
104
+
105
+ <nav class="hidden lg:mt-10 lg:block">
106
+ <%= render partial: 'menu', locals: { nodes: @docs_structure[:children], level: 1 } %>
107
+ </nav>
108
+
109
+ </div>
110
+ </header>
111
+
112
+ <div class="relative px-4 pt-14 sm:px-6 lg:px-8">
113
+ <main class="py-16">
114
+ <article class="prose dark:prose-invert">
115
+ <%= @content.html_safe unless @content.nil? %>
116
+ </article>
117
+ </main>
118
+
119
+
120
+ <% if @main_config.present? %>
121
+ <%= render partial: "sections", collection: @main_config, as: :section %>
122
+ <% end %>
123
+
124
+ <%= render "footer" %>
125
+ </div>
126
+ </div>
@@ -0,0 +1,35 @@
1
+ <%= turbo_frame_tag "plain" do %>
2
+ <%= render "plain/conversations/modal" do %>
3
+ <div class="border shadow-sm rounded-md bg-white m-4">
4
+ <h2 class=" p-4 inline-block text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200">
5
+ Continue conversations
6
+ </h2>
7
+
8
+ <div class="divide divide-y">
9
+ <% @conversations.each do |c| %>
10
+ <div class="p-2 rounded-sm hover:bg-black/5">
11
+
12
+ <div class="flex space-x-2">
13
+ <% if c.pinned? %>
14
+ <div class="w-5 h-5">
15
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
16
+ <path stroke-linecap="round" stroke-linejoin="round" d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0111.186 0z" />
17
+ </svg>
18
+ </div>
19
+ <% end %>
20
+ <%= link_to c.subject || "new chat", conversation_path(c), data: {"turbo-frame": "plain"} %>
21
+ </div>
22
+ <p class="text-xs">
23
+ <%= time_ago_in_words(c.created_at) %>
24
+ </p>
25
+ </div>
26
+ <% end %>
27
+ </div>
28
+ <div class="m-2 flex justify-between">
29
+ <%= link_to "New conversation", new_conversation_path, class: "rounded-md bg-brand-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-600" %>
30
+ <%= link_to "See previous chats", conversations_path, class: "rounded-lg text-slate-900 font-semibold transition flex items-center gap-3 text-[0.8125rem] leading-6 py-1 px-1.5 hover:bg-slate-900/[0.03] -my-1 -mx-1.5" %>
31
+
32
+ </div>
33
+ </div>
34
+ <% end %>
35
+ <% end %>
@@ -0,0 +1,25 @@
1
+
2
+ <%= turbo_frame_tag "message-form" do %>
3
+ <%= form_for Plain::Message.new,
4
+ url: plain.conversation_messages_path(conversation),
5
+ html: {
6
+ data: {
7
+ controller: "loader",
8
+ action: "turbo:submit-start->loader#formSubmitting turbo:submit-end->loader#formSubmitted"
9
+ }
10
+ } do |form| %>
11
+ <div class="absolute bottom-0 border-t pt-2 bg-white h-[52px]">
12
+ <div class="flex space-x-2 w-[448px] px-2">
13
+ <%= form.text_field :content,
14
+ label: false,
15
+ class: "flex-grow p-2 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-brand-600 sm:text-sm sm:leading-6"
16
+ %>
17
+
18
+ <div class="">
19
+ <%= form.submit "Ask Plain", "data-loader-target": "loader", class: "ml-4 inline-flex justify-center rounded-md bg-brand-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-600" %>
20
+ </div>
21
+
22
+ </div>
23
+ </div>
24
+ <% end %>
25
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <div id="message-item-<%= message.id %>"
2
+ class="message-role <%= message.assistant? ? "bg-white" : "bg-brand-200" %>
3
+ mx-4 flex flex-col pb-1 p-2 my-1 shadow-sm rounded-md border border-black/10">
4
+
5
+ <%= "Plain bot:" if message.assistant? %>
6
+
7
+ <div class="prose prose-sm space-y-2">
8
+ <% unless message.content.nil? %>
9
+ <%= raw Plain::AiDocs.convert_markdown message.content %>
10
+ <% end %>
11
+ </div>
12
+
13
+ <div class="flex space-x-2">
14
+ <% if message.assistant? %>
15
+ <button class="rounded bg-white px-2 py-1 text-xs font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
16
+ create doc
17
+ </button>
18
+ <button class="rounded bg-white px-2 py-1 text-xs font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
19
+ OTHER action?
20
+ </button>
21
+ <% end %>
22
+ </div>
23
+ <% if local_assigns[:scroll] %>
24
+ <span data-controller="scroll-to"></span>
25
+ <% end %>
26
+ </div>
@@ -0,0 +1,16 @@
1
+ <%= turbo_frame_tag "message-list-#{@current_page}" do %>
2
+ <div class="flex flex-col-reverse">
3
+ <% if @current_page.zero? %>
4
+ <span data-controller="scroll-to"></span>
5
+ <% end %>
6
+ <%= render partial: "plain/messages/message_item", collection: @messages, as: :message %>
7
+ <% if @messages.size.positive? %>
8
+ <%= turbo_frame_tag "message-list-#{@next_page}", loading: :lazy,
9
+ src: conversation_messages_path(@conversation, page: @next_page),
10
+ class: 'empty:mt-[600px] empty:mb-[-600px] empty:block group' %>
11
+ <div class="mt-4 group-[&:not(:empty)]:hidden">
12
+ <%= t('conversations.loading') %>
13
+ </div>
14
+ <% end %>
15
+ </div>
16
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= turbo_stream.append "new-messages-container" do %>
2
+ <%= render "plain/messages/message_item", message: message, scroll: true %>
3
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%= turbo_stream.append "new-messages-container" do %>
2
+ <%= render partial: "message_item", collection: @messages, as: :message, locals: { data: "asd" } %>
3
+ <% end %>
4
+
5
+
6
+ <%= turbo_stream.update "message-form" do %>
7
+ <%= render "form", conversation: @conversation %>
8
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= render "plain/messages/message_list" %>
@@ -0,0 +1,53 @@
1
+ # SQLite. Versions 3.8.0 and up are supported.
2
+ # gem install sqlite3
3
+ #
4
+ # Ensure the SQLite 3 gem is defined in your Gemfile
5
+ # gem "sqlite3"
6
+ #
7
+ default: &default
8
+ adapter: sqlite3
9
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10
+ timeout: 5000
11
+
12
+ development_sqlite:
13
+ <<: *default
14
+ database: db/development.sqlite3
15
+
16
+ development:
17
+ prepared_statements: false
18
+ adapter: postgresql
19
+ database: <%= ENV.fetch('POSTGRES_DATABASE', 'ruby_on_plain_development') %>
20
+ username: <%= ENV.fetch('POSTGRES_USERNAME', 'postgres') %>
21
+ password: <%= ENV.fetch('POSTGRES_PASSWORD', '') %>
22
+ min_messages: warning
23
+ pool: 15
24
+ timeout: 5000
25
+ host_names:
26
+ ### Don't include the port number here. Change the "port" site setting instead, at /admin/site_settings.
27
+ ### If you change this setting you will need to
28
+ ### - restart sidekiq if you change this setting
29
+ ### - rebake all to posts using: `RAILS_ENV=production bundle exec rake posts:rebake`
30
+ - 'localhost'
31
+
32
+ # Warning: The database defined as "test" will be erased and
33
+ # re-generated from your development database when you run "rake".
34
+ # Do not set this db to the same as development or production.
35
+ test:
36
+ prepared_statements: false
37
+ adapter: postgresql
38
+ database: <%= ENV.fetch('POSTGRES_DATABASE', 'ruby_on_plain_test') %>
39
+ username: <%= ENV.fetch('POSTGRES_USERNAME', 'postgres') %>
40
+ password: <%= ENV.fetch('POSTGRES_PASSWORD', '') %>
41
+ min_messages: warning
42
+ pool: 15
43
+ timeout: 5000
44
+ host_names:
45
+ ### Don't include the port number here. Change the "port" site setting instead, at /admin/site_settings.
46
+ ### If you change this setting you will need to
47
+ ### - restart sidekiq if you change this setting
48
+ ### - rebake all to posts using: `RAILS_ENV=production bundle exec rake posts:rebake`
49
+ - 'localhost'
50
+
51
+ production:
52
+ <<: *default
53
+ database: db/production.sqlite3
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ Plain::Engine.routes.draw do
2
+
3
+ root 'home#index'
4
+
5
+ resources :conversations do
6
+ resources :messages
7
+ end
8
+
9
+ get '/docs/*file_path', to: 'docs#show', as: :docs
10
+ get '/docs/', to: 'docs#show'
11
+ end
@@ -0,0 +1,10 @@
1
+ class CreatePlainConversations < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :plain_conversations do |t|
4
+ t.string :subject
5
+ t.datetime :pinned_at
6
+ t.boolean :pinned
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ class CreatePlainMessages < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :plain_messages do |t|
4
+ t.string :role
5
+ t.text :content
6
+ t.references :plain_conversation, null: false, foreign_key: true
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module Plain
2
+ class Configuration
3
+ attr_accessor :paths, :chat_environments, :vector_search, :extensions
4
+
5
+ def initialize
6
+ @paths = []
7
+ @chat_environments = []
8
+ @vector_search = nil
9
+ @extensions = ["rb", "js", "erb", "md", "json"]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ require "openai"
2
+ require "qdrant"
3
+ require "langchainrb"
4
+ require "coderay"
5
+ require "redcarpet"
6
+
7
+ module Plain
8
+ class Engine < ::Rails::Engine
9
+ isolate_namespace Plain
10
+
11
+ # NOTE: add engine manifest to precompile assets in production, if you don't have this yet.
12
+ initializer "plain.assets" do |app|
13
+ app.config.assets.precompile += %w[plain_manifest]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Plain
2
+ VERSION = "0.1.0"
3
+ end
data/lib/plain.rb ADDED
@@ -0,0 +1,17 @@
1
+ require "plain/version"
2
+ require "plain/engine"
3
+ require "plain/configuration"
4
+
5
+ module Plain
6
+ class << self
7
+ attr_writer :configuration
8
+
9
+ def configuration
10
+ @configuration ||= Configuration.new
11
+ end
12
+
13
+ def configure
14
+ yield(configuration)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ namespace :plain do
2
+ desc "Indexes the contents"
3
+ task :load => :environment do
4
+ puts "loading contents of the project"
5
+ Plain::AiDocs.new.load_all
6
+ puts "contents indexed"
7
+ end
8
+
9
+
10
+ task :tailwind_engine_watch do
11
+ require "tailwindcss-rails"
12
+ # NOTE: tailwindcss-rails is an engine
13
+ Dir.chdir(Plain::Engine.root.to_s) do
14
+ system "tailwindcss \
15
+ -i #{Plain::Engine.root.join("app/assets/stylesheets/plain/application.tailwind.css")} \
16
+ -o #{Plain::Engine.root.join("app/assets/builds/plain.css")} \
17
+ -c #{Plain::Engine.root.join("tailwind.config.js")} \
18
+ --minify -w"
19
+ end
20
+ end
21
+ end