plain-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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