plain-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +162 -0
- data/Rakefile +8 -0
- data/app/assets/builds/plain.css +1 -0
- data/app/assets/config/plain_manifest.js +1 -0
- data/app/assets/stylesheets/plain/application.css +15 -0
- data/app/assets/stylesheets/plain/application.tailwind.css +7 -0
- data/app/controllers/plain/application_controller.rb +4 -0
- data/app/controllers/plain/conversations_controller.rb +55 -0
- data/app/controllers/plain/docs_controller.rb +63 -0
- data/app/controllers/plain/home_controller.rb +10 -0
- data/app/controllers/plain/messages_controller.rb +30 -0
- data/app/helpers/plain/application_helper.rb +4 -0
- data/app/helpers/plain/conversations_helper.rb +4 -0
- data/app/helpers/plain/docs_helper.rb +4 -0
- data/app/helpers/plain/home_helper.rb +4 -0
- data/app/helpers/plain/messages_helper.rb +4 -0
- data/app/javascripts/loader_controller.js +27 -0
- data/app/javascripts/scroll_controller.js +8 -0
- data/app/javascripts/toggle_class_controller.js +11 -0
- data/app/jobs/plain/application_job.rb +4 -0
- data/app/jobs/plain/message_processor_job.rb +9 -0
- data/app/mailers/plain/application_mailer.rb +6 -0
- data/app/models/plain/application_record.rb +5 -0
- data/app/models/plain/conversation.rb +53 -0
- data/app/models/plain/message.rb +62 -0
- data/app/services/plain/ai_docs.rb +163 -0
- data/app/services/plain/docs_service.rb +127 -0
- data/app/views/layouts/plain/application.html.erb +51 -0
- data/app/views/plain/conversations/_conversation_item.erb +15 -0
- data/app/views/plain/conversations/_conversation_item.html.erb +21 -0
- data/app/views/plain/conversations/_conversation_list.erb +18 -0
- data/app/views/plain/conversations/_conversation_list.html.erb +13 -0
- data/app/views/plain/conversations/_modal.html.erb +73 -0
- data/app/views/plain/conversations/index.html.erb +17 -0
- data/app/views/plain/conversations/show.html.erb +14 -0
- data/app/views/plain/docs/_footer.erb +61 -0
- data/app/views/plain/docs/_menu.erb +20 -0
- data/app/views/plain/docs/_sections.erb +73 -0
- data/app/views/plain/docs/show.html.erb +126 -0
- data/app/views/plain/home/index.html.erb +35 -0
- data/app/views/plain/messages/_form.html.erb +25 -0
- data/app/views/plain/messages/_message_item.html.erb +26 -0
- data/app/views/plain/messages/_message_list.html.erb +16 -0
- data/app/views/plain/messages/_new_message.turbo_stream.erb +3 -0
- data/app/views/plain/messages/create.turbo_stream.erb +8 -0
- data/app/views/plain/messages/index.html.erb +1 -0
- data/config/database.yml +53 -0
- data/config/routes.rb +11 -0
- data/db/migrate/20230729010703_create_plain_conversations.rb +10 -0
- data/db/migrate/20230729010745_create_plain_messages.rb +11 -0
- data/lib/plain/configuration.rb +12 -0
- data/lib/plain/engine.rb +16 -0
- data/lib/plain/version.rb +3 -0
- data/lib/plain.rb +17 -0
- data/lib/tasks/plain_tasks.rake +21 -0
- 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:[&: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:[&: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,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" %>
|
data/config/database.yml
ADDED
@@ -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,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
|
data/lib/plain/engine.rb
ADDED
@@ -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
|
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
|