collavre 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/images/collavre/landing/interface-en-dark.png +0 -0
- data/app/assets/images/collavre/landing/interface-en.png +0 -0
- data/app/assets/images/collavre/landing/interface-ko-dark.png +0 -0
- data/app/assets/images/collavre/landing/interface-ko.png +0 -0
- data/app/assets/stylesheets/collavre/comments_popup.css +40 -0
- data/app/assets/stylesheets/collavre/landing.css +92 -0
- data/app/assets/stylesheets/collavre/popup.css +4 -2
- data/app/controllers/collavre/admin/settings_controller.rb +52 -67
- data/app/controllers/collavre/api/v1/agents_controller.rb +17 -188
- data/app/controllers/collavre/api/v1/base_controller.rb +9 -27
- data/app/controllers/collavre/attachments_controller.rb +13 -3
- data/app/controllers/collavre/concerns/slide_viewable.rb +8 -32
- data/app/controllers/collavre/creatives_controller.rb +32 -99
- data/app/controllers/collavre/inbox_items_controller.rb +10 -4
- data/app/controllers/collavre/landing_controller.rb +5 -2
- data/app/controllers/collavre/topics_controller.rb +5 -0
- data/app/controllers/concerns/collavre/api/authenticatable.rb +40 -0
- data/app/errors/collavre/api_error.rb +20 -0
- data/app/helpers/collavre/creatives_helper.rb +30 -8
- data/app/javascript/components/InlineLexicalEditor.jsx +2 -34
- data/app/javascript/components/__tests__/creative_tree_row_parent_id.test.js +87 -0
- data/app/javascript/components/creative_tree_row.js +8 -2
- data/app/javascript/components/plugins/markdown_shortcuts_plugin.jsx +15 -4
- data/app/javascript/controllers/__tests__/search_popup_controller.test.js +104 -0
- data/app/javascript/controllers/__tests__/topic_list_controller.test.js +82 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_topic_list.test.js +61 -0
- data/app/javascript/controllers/comments/topics_controller.js +47 -0
- data/app/javascript/controllers/common_popup_controller.js +2 -2
- data/app/javascript/controllers/index.js +3 -0
- data/app/javascript/controllers/search_popup_controller.js +17 -1
- data/app/javascript/controllers/topic_list_controller.js +78 -0
- data/app/javascript/lib/__tests__/common_popup_bounds.test.js +58 -0
- data/app/javascript/lib/common_popup.js +24 -5
- data/app/javascript/lib/lexical/__tests__/code_block_toggle.test.js +326 -0
- data/app/javascript/lib/lexical/__tests__/code_fence_shortcut.test.js +179 -0
- data/app/javascript/lib/lexical/code_block_toggle.js +130 -0
- data/app/javascript/lib/lexical/code_fence_shortcut.js +60 -0
- data/app/javascript/modules/__tests__/creative_inline_payload.test.js +231 -0
- data/app/javascript/modules/__tests__/creative_save_queue.test.js +228 -0
- data/app/javascript/modules/__tests__/creative_tree_dom.test.js +425 -0
- data/app/javascript/modules/creative_inline_payload.js +86 -0
- data/app/javascript/modules/creative_row_editor.js +121 -401
- data/app/javascript/modules/creative_row_editor_helpers.js +76 -0
- data/app/javascript/modules/creative_save_queue.js +95 -0
- data/app/javascript/modules/creative_tree_dom.js +260 -0
- data/app/jobs/collavre/permission_cache_job.rb +20 -2
- data/app/jobs/collavre/trigger_loop_check_job.rb +20 -3
- data/app/jobs/collavre/update_mcp_tools_job.rb +17 -0
- data/app/models/collavre/channel.rb +16 -0
- data/app/models/collavre/comment/approvable.rb +16 -0
- data/app/models/collavre/comment/notifiable.rb +1 -1
- data/app/models/collavre/comment.rb +8 -1
- data/app/models/collavre/comment_version.rb +1 -1
- data/app/models/collavre/creative/describable.rb +15 -9
- data/app/models/collavre/creative/linkable.rb +9 -0
- data/app/models/collavre/creative/permissible.rb +97 -49
- data/app/models/collavre/creative.rb +73 -5
- data/app/models/collavre/creative_share.rb +78 -26
- data/app/models/collavre/inbox_item.rb +2 -2
- data/app/models/collavre/task.rb +12 -0
- data/app/models/collavre/user.rb +33 -9
- data/app/models/concerns/collavre/indexed_json_columns.rb +58 -0
- data/app/services/collavre/agent_type_classifier.rb +23 -0
- data/app/services/collavre/ai_agent/a2a_dispatcher.rb +4 -0
- data/app/services/collavre/ai_agent/message_builder.rb +1 -1
- data/app/services/collavre/ai_agent/session_provisioner.rb +126 -0
- data/app/services/collavre/ai_agent/task_claim_service.rb +96 -0
- data/app/services/collavre/ai_client.rb +53 -2
- data/app/services/collavre/auto_theme_generator.rb +14 -10
- data/app/services/collavre/creatives/children_index.rb +86 -0
- data/app/services/collavre/creatives/comment_badge_index.rb +73 -0
- data/app/services/collavre/creatives/creative_tree_serializer.rb +122 -0
- data/app/services/collavre/creatives/effective_creative_resolution.rb +37 -0
- data/app/services/collavre/creatives/permission_checker.rb +1 -1
- data/app/services/collavre/creatives/permission_filter.rb +144 -17
- data/app/services/collavre/creatives/tree_builder.rb +105 -77
- data/app/services/collavre/http_client.rb +119 -0
- data/app/services/collavre/link_preview_fetcher.rb +129 -38
- data/app/services/collavre/markdown_importer.rb +30 -6
- data/app/services/collavre/mobile/event_summarizer.rb +1 -1
- data/app/services/collavre/orchestration/agent_context_builder.rb +1 -15
- data/app/services/collavre/orchestration/agent_orchestrator.rb +1 -1
- data/app/services/collavre/system_events/context_builder.rb +1 -14
- data/app/services/collavre/tools/preview_attach_service.rb +5 -7
- data/app/views/collavre/comments/_comment.html.erb +4 -4
- data/app/views/collavre/comments/_comments_popup.html.erb +11 -3
- data/app/views/collavre/creatives/_inline_edit_form.html.erb +9 -0
- data/app/views/collavre/landing/show.html.erb +52 -10
- data/config/locales/comments.en.yml +1 -0
- data/config/locales/comments.ko.yml +1 -0
- data/config/locales/creatives.en.yml +6 -0
- data/config/locales/creatives.ko.yml +5 -0
- data/config/locales/landing.en.yml +22 -10
- data/config/locales/landing.ko.yml +22 -10
- data/db/migrate/20260120045354_encrypt_oauth_tokens.rb +13 -4
- data/db/migrate/20260702000001_enforce_system_settings_key_not_null.rb +9 -0
- data/db/migrate/20260702000002_add_creative_index_and_fk_to_tags.rb +11 -0
- data/db/migrate/20260702000003_add_creative_type_index_to_labels.rb +10 -0
- data/db/migrate/20260702000004_add_creative_created_at_index_to_comments.rb +10 -0
- data/db/migrate/20260702000005_promote_channel_config_index_columns.rb +76 -0
- data/db/migrate/20260713000001_add_comments_count_to_creatives.rb +20 -0
- data/db/migrate/20260713000002_add_comment_versions_count_to_comments.rb +20 -0
- data/db/migrate/20260715000000_change_creatives_description_to_plain_text.rb +16 -0
- data/lib/collavre/version.rb +1 -1
- metadata +42 -1
|
@@ -124,6 +124,10 @@ ko:
|
|
|
124
124
|
metadata_save: 저장
|
|
125
125
|
toggle_markdown: 마크다운
|
|
126
126
|
toggle_richtext: 서식 편집
|
|
127
|
+
save_status_pending: 저장 대기중
|
|
128
|
+
save_status_saving: 저장중…
|
|
129
|
+
save_status_saved: 저장됨
|
|
130
|
+
save_status_error: 저장 실패
|
|
127
131
|
markdown_to_richtext_confirm: 서식 편집으로 전환하면 마크다운 원본이 삭제됩니다.
|
|
128
132
|
계속하시겠습니까?
|
|
129
133
|
richtext_to_markdown_confirm: 마크다운으로 전환하면 현재 서식 편집 내용이 삭제됩니다. 계속하시겠습니까?
|
|
@@ -142,6 +146,7 @@ ko:
|
|
|
142
146
|
errors:
|
|
143
147
|
no_permission: 이 작업을 수행할 권한이 없습니다.
|
|
144
148
|
metadata_must_be_object: 메타데이터는 객체여야 합니다.
|
|
149
|
+
description_read_only_source: 읽기 전용 소스 콘텐츠는 직접 변경할 수 없습니다
|
|
145
150
|
inline_editor:
|
|
146
151
|
placeholder: 크리에이티브를 설명해주세요…
|
|
147
152
|
markdown_placeholder: 마크다운으로 작성…
|
|
@@ -3,30 +3,28 @@ en:
|
|
|
3
3
|
landing:
|
|
4
4
|
meta:
|
|
5
5
|
title: "Where Teams Think Together"
|
|
6
|
-
description: "The collaborative workspace where hierarchical
|
|
6
|
+
description: "The collaborative workspace where hierarchical blocks, AI agents, and real-time chat come together. Organize, collaborate, and ship — with AI as your teammate."
|
|
7
7
|
hero:
|
|
8
8
|
headline_html: "A platform for both AI agents and people."
|
|
9
9
|
subline: "Achieve sustainable productivity gains."
|
|
10
10
|
learn_more: "Learn More"
|
|
11
|
-
demo:
|
|
12
|
-
title: "See it in action"
|
|
13
|
-
placeholder: "Product demo video goes here"
|
|
14
11
|
problem:
|
|
15
12
|
title: "Your tools are failing you"
|
|
13
|
+
subtitle: "Docs, trackers, chat — you write the same thing in all three, then spend the day moving between them to keep them in sync. Don't repeat yourself."
|
|
16
14
|
notion:
|
|
17
|
-
title: "
|
|
15
|
+
title: "Docs"
|
|
18
16
|
desc: "Docs become graveyards. Nobody edits someone else's page. Search replaces structure."
|
|
19
17
|
jira:
|
|
20
|
-
title: "
|
|
18
|
+
title: "Trackers"
|
|
21
19
|
desc: "Great for tasks, terrible for knowledge. Results never sync back to the wiki. Information overload."
|
|
22
20
|
slack:
|
|
23
|
-
title: "
|
|
21
|
+
title: "Chat"
|
|
24
22
|
desc: "Conversations vanish in the stream. Flat channel lists explode. Context is lost daily."
|
|
25
23
|
features:
|
|
26
24
|
title: "Everything in one living tree"
|
|
27
|
-
subtitle: "
|
|
25
|
+
subtitle: "Write it once. Every block is a doc, a task, and a discussion at the same time — one source of truth, self-hosted in your own database, that you inject straight into your AI agents."
|
|
28
26
|
tree:
|
|
29
|
-
title: "Hierarchical
|
|
27
|
+
title: "Hierarchical blocks"
|
|
30
28
|
desc: "Organize work as a tree. Drag, link, and reuse blocks across projects. Progress rolls up automatically."
|
|
31
29
|
ai:
|
|
32
30
|
title: "AI Agents as Teammates"
|
|
@@ -36,13 +34,27 @@ en:
|
|
|
36
34
|
desc: "Reduce cognitive load for people, and inject the knowledge and skills AI needs."
|
|
37
35
|
chat:
|
|
38
36
|
title: "Real-time Chat & Topics"
|
|
39
|
-
desc: "Every
|
|
37
|
+
desc: "Every block has a chat. Organize conversations with topics, move messages between blocks, and keep discussions focused."
|
|
40
38
|
integrations:
|
|
41
39
|
title: "Integrations"
|
|
42
40
|
desc: "GitHub PRs auto-analyzed, Slack messages synced, Google Calendar events linked, MCP tools built-in."
|
|
43
41
|
progress:
|
|
44
42
|
title: "Live Progress"
|
|
45
43
|
desc: "Track completion from leaf to root. Filter by tags, plans, or status. See your project's real health at a glance."
|
|
44
|
+
interface:
|
|
45
|
+
title: "The document is the tree"
|
|
46
|
+
subtitle: "A heading, a paragraph, a code block, a table — each one is a block, carrying its own progress on the right and its own thread inside. You do not fill in a tracker. You write the spec, and the plan is already there."
|
|
47
|
+
alt: "A refund spec in Collavre. Prose, a syntax-highlighted code block and a table sit as siblings next to work items, each with its own progress and its own discussion."
|
|
48
|
+
markdown: "Markdown-first. Flip any block between rich text and raw markdown."
|
|
49
|
+
rich: "Fenced code with syntax highlighting, tables, images — rendered in the tree, not hidden behind a detail page."
|
|
50
|
+
drag: "Drag a block and its whole subtree comes with it."
|
|
51
|
+
presence: "See who is in the document and who is editing which block."
|
|
52
|
+
demo:
|
|
53
|
+
title: "See it in action"
|
|
54
|
+
video: "https://collavre.com/public-assets/blobs/eyJfcmFpbHMiOnsiZGF0YSI6NDY2LCJwdXIiOiJibG9iX2lkIn19--724536948e4fa3474e57997958427e1850775c08/launch.mp4"
|
|
55
|
+
poster: "https://collavre.com/public-assets/blobs/eyJfcmFpbHMiOnsiZGF0YSI6NDY3LCJwdXIiOiJibG9iX2lkIn19--7713d27bd154806053991bc8e8abca60475a2ab8/launch-poster.jpg"
|
|
56
|
+
video_dark: "https://collavre.com/public-assets/blobs/eyJfcmFpbHMiOnsiZGF0YSI6NDY4LCJwdXIiOiJibG9iX2lkIn19--63349dc7270a3e6a0503c45a413d3b176f791dc2/launch-dark.mp4"
|
|
57
|
+
poster_dark: "https://collavre.com/public-assets/blobs/eyJfcmFpbHMiOnsiZGF0YSI6NDY5LCJwdXIiOiJibG9iX2lkIn19--25a756ed0d6ff5b4f40ef864b8216c7aa540449c/launch-dark-poster.jpg"
|
|
46
58
|
cta:
|
|
47
59
|
title: "Ready to collaborate smarter?"
|
|
48
60
|
subtitle: "Start organizing your work with AI-powered context."
|
|
@@ -3,30 +3,28 @@ ko:
|
|
|
3
3
|
landing:
|
|
4
4
|
meta:
|
|
5
5
|
title: "팀이 함께 생각하는 곳"
|
|
6
|
-
description: "계층형
|
|
6
|
+
description: "계층형 블록, AI 에이전트, 실시간 채팅이 하나로. 체계적으로 정리하고, 협업하고, AI와 함께 완성하세요."
|
|
7
7
|
hero:
|
|
8
8
|
headline_html: "AI 에이전트와 사람 모두의 플랫폼."
|
|
9
9
|
subline: "지속 가능한 생산성 향상을 이루세요."
|
|
10
10
|
learn_more: "알아보기"
|
|
11
|
-
demo:
|
|
12
|
-
title: "직접 확인하세요"
|
|
13
|
-
placeholder: "실제 작동 화면이 들어갈 자리입니다"
|
|
14
11
|
problem:
|
|
15
12
|
title: "기존 도구의 한계"
|
|
13
|
+
subtitle: "문서에, 트래커에, 대화에 — 같은 걸 세 군데에 쓰고, 하루 종일 그 사이를 오가며 셋을 맞춥니다. 반복하지 마세요."
|
|
16
14
|
notion:
|
|
17
|
-
title: "
|
|
15
|
+
title: "문서"
|
|
18
16
|
desc: "문서는 무덤이 됩니다. 남의 페이지를 고치기 꺼려하고, 검색이 구조를 대체합니다."
|
|
19
17
|
jira:
|
|
20
|
-
title: "
|
|
18
|
+
title: "트래커"
|
|
21
19
|
desc: "작업 관리는 좋지만 지식 관리는 안 됩니다. 결과물이 위키로 돌아오지 않습니다."
|
|
22
20
|
slack:
|
|
23
|
-
title: "
|
|
21
|
+
title: "대화"
|
|
24
22
|
desc: "대화가 흘러가 버립니다. 채널은 폭발적으로 늘어나고, 맥락은 매일 사라집니다."
|
|
25
23
|
features:
|
|
26
24
|
title: "하나의 살아있는 트리로"
|
|
27
|
-
subtitle: "
|
|
25
|
+
subtitle: "한 번만 쓰세요. 모든 블록이 문서이자 일감이자 대화입니다 — 셀프호스트로 자신의 DB에 쌓이는 하나의 SSOT를, 그대로 AI 에이전트에 주입합니다."
|
|
28
26
|
tree:
|
|
29
|
-
title: "계층형
|
|
27
|
+
title: "계층형 블록"
|
|
30
28
|
desc: "작업을 트리로 구성하세요. 드래그, 링크, 재사용이 자유롭고 진행률은 자동 계산됩니다."
|
|
31
29
|
ai:
|
|
32
30
|
title: "AI 에이전트 팀원"
|
|
@@ -36,13 +34,27 @@ ko:
|
|
|
36
34
|
desc: "사람의 인지 부하를 줄이고, AI에게 필요한 지식과 Skill을 주입합니다."
|
|
37
35
|
chat:
|
|
38
36
|
title: "실시간 채팅 & 토픽"
|
|
39
|
-
desc: "모든
|
|
37
|
+
desc: "모든 블록에 채팅이 있습니다. 토픽으로 대화를 정리하고, 메시지를 다른 블록으로 옮기세요."
|
|
40
38
|
integrations:
|
|
41
39
|
title: "외부 연동"
|
|
42
40
|
desc: "GitHub PR 자동 분석, Slack 동기화, Google Calendar 연동, MCP 도구 내장."
|
|
43
41
|
progress:
|
|
44
42
|
title: "실시간 진행률"
|
|
45
43
|
desc: "리프부터 루트까지 완료율 추적. 태그, 계획, 상태별 필터. 프로젝트 건강도를 한눈에."
|
|
44
|
+
interface:
|
|
45
|
+
title: "문서가 곧 트리입니다"
|
|
46
|
+
subtitle: "헤딩도, 문단도, 코드 블록도, 표도 각각 하나의 블록입니다. 저마다 오른쪽에 자기 진행률을 달고, 안에 자기 대화를 품습니다. 트래커를 채우는 게 아니라 스펙을 쓰면, 계획은 이미 거기 있습니다."
|
|
47
|
+
alt: "Collavre의 환불 스펙 화면. 산문과 문법 강조된 코드 블록과 표가 일감들과 나란히 놓여 있고, 각자 자기 진행률과 대화를 가지고 있습니다."
|
|
48
|
+
markdown: "마크다운이 먼저입니다. 어느 블록이든 리치 텍스트와 원본 마크다운을 오갈 수 있습니다."
|
|
49
|
+
rich: "코드 펜스 문법 강조, 표, 이미지 — 상세 페이지 뒤에 숨지 않고 트리에서 바로 보입니다."
|
|
50
|
+
drag: "블록을 끌면 그 아래 서브트리가 통째로 따라옵니다."
|
|
51
|
+
presence: "누가 문서에 들어와 있고 누가 어느 블록을 편집 중인지 보입니다."
|
|
52
|
+
demo:
|
|
53
|
+
title: "직접 확인하세요"
|
|
54
|
+
video: "https://collavre.com/public-assets/blobs/eyJfcmFpbHMiOnsiZGF0YSI6NDcwLCJwdXIiOiJibG9iX2lkIn19--d2e47d9773e9179a4f6a2a69f5740f3551c69e08/launch-ko.mp4"
|
|
55
|
+
poster: "https://collavre.com/public-assets/blobs/eyJfcmFpbHMiOnsiZGF0YSI6NDcxLCJwdXIiOiJibG9iX2lkIn19--9d63bb60ec693416eddd73ea3ff3532bc09ed71e/launch-ko-poster.jpg"
|
|
56
|
+
video_dark: "https://collavre.com/public-assets/blobs/eyJfcmFpbHMiOnsiZGF0YSI6NDcyLCJwdXIiOiJibG9iX2lkIn19--6d635234d2070b43f16c404a9e81e7642062c172/launch-ko-dark.mp4"
|
|
57
|
+
poster_dark: "https://collavre.com/public-assets/blobs/eyJfcmFpbHMiOnsiZGF0YSI6NDczLCJwdXIiOiJibG9iX2lkIn19--094d1e6e8691ea987effeca29195e5fc313bff33/launch-ko-dark-poster.jpg"
|
|
46
58
|
cta:
|
|
47
59
|
title: "더 똑똑하게 협업할 준비 되셨나요?"
|
|
48
60
|
subtitle: "AI 기반 컨텍스트로 작업을 정리하세요."
|
|
@@ -7,12 +7,21 @@ class EncryptOauthTokens < ActiveRecord::Migration[8.1]
|
|
|
7
7
|
encrypt_column(User, :google_refresh_token)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
# Vendor account tokens live in vendor engines. Guard each block so this
|
|
11
|
+
# core migration runs cleanly on an install where the engine is absent
|
|
12
|
+
# (the constant would otherwise raise NameError). When the engine is
|
|
13
|
+
# present on a fresh DB there are no plaintext rows yet, so the block is a
|
|
14
|
+
# no-op; encryption of new writes is handled by the model's `encrypts`.
|
|
15
|
+
if defined?(CollavreGithub::Account)
|
|
16
|
+
say_with_time "Encrypting CollavreGithub::Account tokens" do
|
|
17
|
+
encrypt_column(CollavreGithub::Account, :token)
|
|
18
|
+
end
|
|
12
19
|
end
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
if defined?(CollavreNotion::NotionAccount)
|
|
22
|
+
say_with_time "Encrypting CollavreNotion::NotionAccount tokens" do
|
|
23
|
+
encrypt_column(CollavreNotion::NotionAccount, :token)
|
|
24
|
+
end
|
|
16
25
|
end
|
|
17
26
|
end
|
|
18
27
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class EnforceSystemSettingsKeyNotNull < ActiveRecord::Migration[8.1]
|
|
2
|
+
# `key` is the lookup identifier for every setting and the model already
|
|
3
|
+
# validates its presence; the unique index exists but the column was left
|
|
4
|
+
# nullable, allowing a NULL-keyed row to slip in outside the model. Enforce
|
|
5
|
+
# the invariant at the database level to match the existing UNIQUE index.
|
|
6
|
+
def change
|
|
7
|
+
change_column_null :system_settings, :key, false
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class AddCreativeIndexAndFkToTags < ActiveRecord::Migration[8.1]
|
|
2
|
+
# tags.creative_id is NOT NULL and queried on every tag lookup, but only
|
|
3
|
+
# label_id was indexed and there was no referential integrity to creatives.
|
|
4
|
+
# Add the missing index and foreign key.
|
|
5
|
+
def change
|
|
6
|
+
add_index :tags, :creative_id unless index_exists?(:tags, :creative_id)
|
|
7
|
+
unless foreign_key_exists?(:tags, :creatives, column: :creative_id)
|
|
8
|
+
add_foreign_key :tags, :creatives, column: :creative_id
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class AddCreativeTypeIndexToLabels < ActiveRecord::Migration[8.1]
|
|
2
|
+
# Labels are almost always fetched for a creative filtered by their kind
|
|
3
|
+
# (`type`). A composite index on [creative_id, type] serves those lookups
|
|
4
|
+
# far better than the standalone creative_id index alone.
|
|
5
|
+
def change
|
|
6
|
+
unless index_exists?(:labels, [ :creative_id, :type ])
|
|
7
|
+
add_index :labels, [ :creative_id, :type ], name: "index_labels_on_creative_id_and_type"
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class AddCreativeCreatedAtIndexToComments < ActiveRecord::Migration[8.1]
|
|
2
|
+
# Comments are rendered per creative in chronological order. A composite
|
|
3
|
+
# index on [creative_id, created_at] lets the DB satisfy both the filter and
|
|
4
|
+
# the sort from the index instead of filtering on creative_id then sorting.
|
|
5
|
+
def change
|
|
6
|
+
unless index_exists?(:comments, [ :creative_id, :created_at ])
|
|
7
|
+
add_index :comments, [ :creative_id, :created_at ], name: "index_comments_on_creative_id_and_created_at"
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
class PromoteChannelConfigIndexColumns < ActiveRecord::Migration[8.1]
|
|
2
|
+
# The channels uniqueness guarantees were enforced by UNIQUE indexes over
|
|
3
|
+
# JSON expressions: `json_extract(config, '$.repo_full_name')` on SQLite and
|
|
4
|
+
# `config->>'repo_full_name'` on PostgreSQL. Those expressions serialize
|
|
5
|
+
# differently per adapter, so schema.rb (dumped from the SQLite dev DB)
|
|
6
|
+
# carries json_extract, which is not a PostgreSQL function -- `db:schema:load`
|
|
7
|
+
# on PostgreSQL crashes. Promote the extracted keys to real columns kept in
|
|
8
|
+
# sync from `config` (see Collavre::Channel#sync_indexed_config_columns) and
|
|
9
|
+
# replace the expression indexes with plain-column indexes that dump
|
|
10
|
+
# identically on both adapters.
|
|
11
|
+
def up
|
|
12
|
+
postgres = connection.adapter_name == "PostgreSQL"
|
|
13
|
+
|
|
14
|
+
add_column :channels, :repo_full_name, :string
|
|
15
|
+
add_column :channels, :pr_number, :integer
|
|
16
|
+
add_column :channels, :worktree_id, :string
|
|
17
|
+
|
|
18
|
+
if postgres
|
|
19
|
+
execute <<~SQL.squish
|
|
20
|
+
UPDATE channels SET
|
|
21
|
+
repo_full_name = config->>'repo_full_name',
|
|
22
|
+
pr_number = NULLIF(config->>'pr_number', '')::integer,
|
|
23
|
+
worktree_id = config->>'worktree_id'
|
|
24
|
+
SQL
|
|
25
|
+
else
|
|
26
|
+
execute <<~SQL.squish
|
|
27
|
+
UPDATE channels SET
|
|
28
|
+
repo_full_name = json_extract(config, '$.repo_full_name'),
|
|
29
|
+
pr_number = json_extract(config, '$.pr_number'),
|
|
30
|
+
worktree_id = json_extract(config, '$.worktree_id')
|
|
31
|
+
SQL
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
remove_index :channels, name: "index_channels_on_type_topic_repo_pr"
|
|
35
|
+
remove_index :channels, name: "index_channels_on_topic_preview_worktree"
|
|
36
|
+
|
|
37
|
+
add_index :channels, [ :type, :topic_id, :repo_full_name, :pr_number ],
|
|
38
|
+
unique: true, name: "index_channels_on_type_topic_repo_pr"
|
|
39
|
+
add_index :channels, [ :topic_id, :worktree_id ],
|
|
40
|
+
unique: true, name: "index_channels_on_topic_preview_worktree",
|
|
41
|
+
where: "type = 'Collavre::PreviewChannel'"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def down
|
|
45
|
+
postgres = connection.adapter_name == "PostgreSQL"
|
|
46
|
+
|
|
47
|
+
remove_index :channels, name: "index_channels_on_type_topic_repo_pr"
|
|
48
|
+
remove_index :channels, name: "index_channels_on_topic_preview_worktree"
|
|
49
|
+
|
|
50
|
+
remove_column :channels, :repo_full_name
|
|
51
|
+
remove_column :channels, :pr_number
|
|
52
|
+
remove_column :channels, :worktree_id
|
|
53
|
+
|
|
54
|
+
if postgres
|
|
55
|
+
execute <<~SQL
|
|
56
|
+
CREATE UNIQUE INDEX index_channels_on_type_topic_repo_pr
|
|
57
|
+
ON channels (type, topic_id, (config->>'repo_full_name'), (config->>'pr_number'))
|
|
58
|
+
SQL
|
|
59
|
+
execute <<~SQL
|
|
60
|
+
CREATE UNIQUE INDEX index_channels_on_topic_preview_worktree
|
|
61
|
+
ON channels (topic_id, (config->>'worktree_id'))
|
|
62
|
+
WHERE type = 'Collavre::PreviewChannel'
|
|
63
|
+
SQL
|
|
64
|
+
else
|
|
65
|
+
execute <<~SQL
|
|
66
|
+
CREATE UNIQUE INDEX index_channels_on_type_topic_repo_pr
|
|
67
|
+
ON channels (type, topic_id, json_extract(config, '$.repo_full_name'), json_extract(config, '$.pr_number'))
|
|
68
|
+
SQL
|
|
69
|
+
execute <<~SQL
|
|
70
|
+
CREATE UNIQUE INDEX index_channels_on_topic_preview_worktree
|
|
71
|
+
ON channels (topic_id, json_extract(config, '$.worktree_id'))
|
|
72
|
+
WHERE type = 'Collavre::PreviewChannel'
|
|
73
|
+
SQL
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class AddCommentsCountToCreatives < ActiveRecord::Migration[8.1]
|
|
2
|
+
def up
|
|
3
|
+
unless column_exists?(:creatives, :comments_count)
|
|
4
|
+
add_column :creatives, :comments_count, :integer, default: 0, null: false
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# Backfill: straight COUNT (no soft-delete on comments). Matches house
|
|
8
|
+
# raw-SQL backfill convention.
|
|
9
|
+
execute <<~SQL.squish
|
|
10
|
+
UPDATE creatives
|
|
11
|
+
SET comments_count = (
|
|
12
|
+
SELECT COUNT(*) FROM comments WHERE comments.creative_id = creatives.id
|
|
13
|
+
)
|
|
14
|
+
SQL
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def down
|
|
18
|
+
remove_column :creatives, :comments_count if column_exists?(:creatives, :comments_count)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class AddCommentVersionsCountToComments < ActiveRecord::Migration[8.1]
|
|
2
|
+
def up
|
|
3
|
+
unless column_exists?(:comments, :comment_versions_count)
|
|
4
|
+
add_column :comments, :comment_versions_count, :integer, default: 0, null: false
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# Backfill counts only the owning :comment association (comment_id),
|
|
8
|
+
# NOT review_comment_id (review_versions).
|
|
9
|
+
execute <<~SQL.squish
|
|
10
|
+
UPDATE comments
|
|
11
|
+
SET comment_versions_count = (
|
|
12
|
+
SELECT COUNT(*) FROM comment_versions WHERE comment_versions.comment_id = comments.id
|
|
13
|
+
)
|
|
14
|
+
SQL
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def down
|
|
18
|
+
remove_column :comments, :comment_versions_count if column_exists?(:comments, :comment_versions_count)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class ChangeCreativesDescriptionToPlainText < ActiveRecord::Migration[8.1]
|
|
2
|
+
# The original AddDescriptionToCreatives migration created the column as
|
|
3
|
+
# `:text, limit: 4294967295` (a MySQL LONGTEXT-style byte cap). SQLite ignores
|
|
4
|
+
# the limit, but it gets recorded in db/schema.rb, and `db:schema:load` then
|
|
5
|
+
# crashes on PostgreSQL (production) with:
|
|
6
|
+
# ArgumentError: No text type has byte size 4294967295.
|
|
7
|
+
# Postgres `text` is already unbounded, so drop the limit to keep the schema
|
|
8
|
+
# loadable on every adapter.
|
|
9
|
+
def up
|
|
10
|
+
change_column :creatives, :description, :text, limit: nil
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def down
|
|
14
|
+
change_column :creatives, :description, :text, limit: 4294967295
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/collavre/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: collavre
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.24.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Collavre
|
|
@@ -173,6 +173,10 @@ extra_rdoc_files: []
|
|
|
173
173
|
files:
|
|
174
174
|
- README.md
|
|
175
175
|
- Rakefile
|
|
176
|
+
- app/assets/images/collavre/landing/interface-en-dark.png
|
|
177
|
+
- app/assets/images/collavre/landing/interface-en.png
|
|
178
|
+
- app/assets/images/collavre/landing/interface-ko-dark.png
|
|
179
|
+
- app/assets/images/collavre/landing/interface-ko.png
|
|
176
180
|
- app/assets/stylesheets/collavre/actiontext.css
|
|
177
181
|
- app/assets/stylesheets/collavre/activity_logs.css
|
|
178
182
|
- app/assets/stylesheets/collavre/code_highlight.css
|
|
@@ -258,6 +262,7 @@ files:
|
|
|
258
262
|
- app/controllers/collavre/user_creative_preferences_controller.rb
|
|
259
263
|
- app/controllers/collavre/user_themes_controller.rb
|
|
260
264
|
- app/controllers/collavre/users_controller.rb
|
|
265
|
+
- app/controllers/concerns/collavre/api/authenticatable.rb
|
|
261
266
|
- app/controllers/concerns/collavre/comments/approval_actions.rb
|
|
262
267
|
- app/controllers/concerns/collavre/comments/batch_operations.rb
|
|
263
268
|
- app/controllers/concerns/collavre/comments/comment_scoping.rb
|
|
@@ -270,6 +275,7 @@ files:
|
|
|
270
275
|
- app/controllers/concerns/collavre/users_controller/contact_management.rb
|
|
271
276
|
- app/controllers/concerns/collavre/users_controller/profile_and_settings.rb
|
|
272
277
|
- app/controllers/concerns/collavre/users_controller/registration.rb
|
|
278
|
+
- app/errors/collavre/api_error.rb
|
|
273
279
|
- app/errors/collavre/approval_pending_error.rb
|
|
274
280
|
- app/errors/collavre/cancelled_error.rb
|
|
275
281
|
- app/helpers/collavre/application_helper.rb
|
|
@@ -281,6 +287,7 @@ files:
|
|
|
281
287
|
- app/javascript/components/ImageResizer.jsx
|
|
282
288
|
- app/javascript/components/InlineLexicalEditor.jsx
|
|
283
289
|
- app/javascript/components/LinkPopup.jsx
|
|
290
|
+
- app/javascript/components/__tests__/creative_tree_row_parent_id.test.js
|
|
284
291
|
- app/javascript/components/creative_tree_row.js
|
|
285
292
|
- app/javascript/components/plugins/attachment_cleanup_plugin.jsx
|
|
286
293
|
- app/javascript/components/plugins/image_upload_plugin.jsx
|
|
@@ -290,6 +297,8 @@ files:
|
|
|
290
297
|
- app/javascript/controllers/__tests__/inbox_badge_controller.test.js
|
|
291
298
|
- app/javascript/controllers/__tests__/link_creative_controller.test.js
|
|
292
299
|
- app/javascript/controllers/__tests__/popup_menu_controller.test.js
|
|
300
|
+
- app/javascript/controllers/__tests__/search_popup_controller.test.js
|
|
301
|
+
- app/javascript/controllers/__tests__/topic_list_controller.test.js
|
|
293
302
|
- app/javascript/controllers/agent_trigger_controller.js
|
|
294
303
|
- app/javascript/controllers/click_target_controller.js
|
|
295
304
|
- app/javascript/controllers/comment_badge_controller.js
|
|
@@ -303,6 +312,7 @@ files:
|
|
|
303
312
|
- app/javascript/controllers/comments/__tests__/review_quotes_store.test.js
|
|
304
313
|
- app/javascript/controllers/comments/__tests__/topics_controller_delete.test.js
|
|
305
314
|
- app/javascript/controllers/comments/__tests__/topics_controller_override.test.js
|
|
315
|
+
- app/javascript/controllers/comments/__tests__/topics_controller_topic_list.test.js
|
|
306
316
|
- app/javascript/controllers/comments/contexts_controller.js
|
|
307
317
|
- app/javascript/controllers/comments/drop_trigger_controller.js
|
|
308
318
|
- app/javascript/controllers/comments/form_controller.js
|
|
@@ -336,6 +346,7 @@ files:
|
|
|
336
346
|
- app/javascript/controllers/share_modal_controller.js
|
|
337
347
|
- app/javascript/controllers/share_user_search_controller.js
|
|
338
348
|
- app/javascript/controllers/tabs_controller.js
|
|
349
|
+
- app/javascript/controllers/topic_list_controller.js
|
|
339
350
|
- app/javascript/controllers/topic_search_controller.js
|
|
340
351
|
- app/javascript/creatives/drag_drop/dom.js
|
|
341
352
|
- app/javascript/creatives/drag_drop/event_handlers.js
|
|
@@ -345,6 +356,7 @@ files:
|
|
|
345
356
|
- app/javascript/creatives/topic_move_members_popup.js
|
|
346
357
|
- app/javascript/creatives/tree_renderer.js
|
|
347
358
|
- app/javascript/lib/__tests__/chat_history.test.js
|
|
359
|
+
- app/javascript/lib/__tests__/common_popup_bounds.test.js
|
|
348
360
|
- app/javascript/lib/__tests__/html_code_block_wrapper.test.js
|
|
349
361
|
- app/javascript/lib/__tests__/turbo_confirm.test.js
|
|
350
362
|
- app/javascript/lib/__tests__/typo_correction.test.js
|
|
@@ -367,6 +379,8 @@ files:
|
|
|
367
379
|
- app/javascript/lib/gnb_popup_manager.js
|
|
368
380
|
- app/javascript/lib/html_code_block_wrapper.js
|
|
369
381
|
- app/javascript/lib/lexical/__tests__/attachment_payload.test.js
|
|
382
|
+
- app/javascript/lib/lexical/__tests__/code_block_toggle.test.js
|
|
383
|
+
- app/javascript/lib/lexical/__tests__/code_fence_shortcut.test.js
|
|
370
384
|
- app/javascript/lib/lexical/__tests__/color_import.test.js
|
|
371
385
|
- app/javascript/lib/lexical/__tests__/image_focus.test.js
|
|
372
386
|
- app/javascript/lib/lexical/__tests__/list_tab_indent.test.js
|
|
@@ -377,6 +391,8 @@ files:
|
|
|
377
391
|
- app/javascript/lib/lexical/__tests__/trailing_paragraph.test.js
|
|
378
392
|
- app/javascript/lib/lexical/attachment_node.jsx
|
|
379
393
|
- app/javascript/lib/lexical/attachment_payload.js
|
|
394
|
+
- app/javascript/lib/lexical/code_block_toggle.js
|
|
395
|
+
- app/javascript/lib/lexical/code_fence_shortcut.js
|
|
380
396
|
- app/javascript/lib/lexical/color_import.js
|
|
381
397
|
- app/javascript/lib/lexical/image_node.jsx
|
|
382
398
|
- app/javascript/lib/lexical/list_tab_indent.js
|
|
@@ -403,16 +419,23 @@ files:
|
|
|
403
419
|
- app/javascript/lib/utils/sanitize_description.js
|
|
404
420
|
- app/javascript/lib/utils/table_download.js
|
|
405
421
|
- app/javascript/modules/__tests__/command_args_form.test.js
|
|
422
|
+
- app/javascript/modules/__tests__/creative_inline_payload.test.js
|
|
406
423
|
- app/javascript/modules/__tests__/creative_progress.test.js
|
|
424
|
+
- app/javascript/modules/__tests__/creative_save_queue.test.js
|
|
425
|
+
- app/javascript/modules/__tests__/creative_tree_dom.test.js
|
|
407
426
|
- app/javascript/modules/__tests__/html_content_empty.test.js
|
|
408
427
|
- app/javascript/modules/__tests__/markdown_source_reconcile.test.js
|
|
409
428
|
- app/javascript/modules/__tests__/typo_corrector.test.js
|
|
410
429
|
- app/javascript/modules/command_args_form.js
|
|
411
430
|
- app/javascript/modules/command_menu.js
|
|
412
431
|
- app/javascript/modules/creative_guide.js
|
|
432
|
+
- app/javascript/modules/creative_inline_payload.js
|
|
413
433
|
- app/javascript/modules/creative_progress.js
|
|
414
434
|
- app/javascript/modules/creative_row_editor.js
|
|
435
|
+
- app/javascript/modules/creative_row_editor_helpers.js
|
|
415
436
|
- app/javascript/modules/creative_row_swipe.js
|
|
437
|
+
- app/javascript/modules/creative_save_queue.js
|
|
438
|
+
- app/javascript/modules/creative_tree_dom.js
|
|
416
439
|
- app/javascript/modules/creatives.js
|
|
417
440
|
- app/javascript/modules/export_to_markdown.js
|
|
418
441
|
- app/javascript/modules/html_content_empty.js
|
|
@@ -446,6 +469,7 @@ files:
|
|
|
446
469
|
- app/jobs/collavre/stuck_detector_job.rb
|
|
447
470
|
- app/jobs/collavre/trigger_loop_check_job.rb
|
|
448
471
|
- app/jobs/collavre/trigger_loop_verify_job.rb
|
|
472
|
+
- app/jobs/collavre/update_mcp_tools_job.rb
|
|
449
473
|
- app/jobs/concerns/collavre/comment_serializable.rb
|
|
450
474
|
- app/jobs/concerns/collavre/trigger_loop_helpers.rb
|
|
451
475
|
- app/mailers/collavre/application_mailer.rb
|
|
@@ -501,7 +525,9 @@ files:
|
|
|
501
525
|
- app/models/collavre/webauthn_credential.rb
|
|
502
526
|
- app/models/concerns/collavre/ai_agent_resolvable.rb
|
|
503
527
|
- app/models/concerns/collavre/has_inbox_creative.rb
|
|
528
|
+
- app/models/concerns/collavre/indexed_json_columns.rb
|
|
504
529
|
- app/services/collavre/agent_session_abort.rb
|
|
530
|
+
- app/services/collavre/agent_type_classifier.rb
|
|
505
531
|
- app/services/collavre/ai_agent/a2a_dispatcher.rb
|
|
506
532
|
- app/services/collavre/ai_agent/agent_lifecycle_manager.rb
|
|
507
533
|
- app/services/collavre/ai_agent/approval_handler.rb
|
|
@@ -511,6 +537,8 @@ files:
|
|
|
511
537
|
- app/services/collavre/ai_agent/response_streamer.rb
|
|
512
538
|
- app/services/collavre/ai_agent/review_handler.rb
|
|
513
539
|
- app/services/collavre/ai_agent/session_context_resolver.rb
|
|
540
|
+
- app/services/collavre/ai_agent/session_provisioner.rb
|
|
541
|
+
- app/services/collavre/ai_agent/task_claim_service.rb
|
|
514
542
|
- app/services/collavre/ai_agent_service.rb
|
|
515
543
|
- app/services/collavre/ai_client.rb
|
|
516
544
|
- app/services/collavre/ai_system_prompt_renderer.rb
|
|
@@ -533,8 +561,12 @@ files:
|
|
|
533
561
|
- app/services/collavre/comments/work_command.rb
|
|
534
562
|
- app/services/collavre/comments/workflow_executor.rb
|
|
535
563
|
- app/services/collavre/creatives/breadcrumb_resolver.rb
|
|
564
|
+
- app/services/collavre/creatives/children_index.rb
|
|
565
|
+
- app/services/collavre/creatives/comment_badge_index.rb
|
|
536
566
|
- app/services/collavre/creatives/create_service.rb
|
|
567
|
+
- app/services/collavre/creatives/creative_tree_serializer.rb
|
|
537
568
|
- app/services/collavre/creatives/destroy_service.rb
|
|
569
|
+
- app/services/collavre/creatives/effective_creative_resolution.rb
|
|
538
570
|
- app/services/collavre/creatives/filter_pipeline.rb
|
|
539
571
|
- app/services/collavre/creatives/filters/assignee_filter.rb
|
|
540
572
|
- app/services/collavre/creatives/filters/base_filter.rb
|
|
@@ -556,6 +588,7 @@ files:
|
|
|
556
588
|
- app/services/collavre/crons/recurring_task_arguments.rb
|
|
557
589
|
- app/services/collavre/gemini_parent_recommender.rb
|
|
558
590
|
- app/services/collavre/google_calendar_service.rb
|
|
591
|
+
- app/services/collavre/http_client.rb
|
|
559
592
|
- app/services/collavre/inbox_reply_service.rb
|
|
560
593
|
- app/services/collavre/link_preview_fetcher.rb
|
|
561
594
|
- app/services/collavre/markdown_converter.rb
|
|
@@ -835,6 +868,14 @@ files:
|
|
|
835
868
|
- db/migrate/20260609190659_backfill_creative_files_into_description.rb
|
|
836
869
|
- db/migrate/20260612000000_add_topic_concurrency_defer_to_comments.rb
|
|
837
870
|
- db/migrate/20260617090000_add_typo_correction_settings_to_users.rb
|
|
871
|
+
- db/migrate/20260702000001_enforce_system_settings_key_not_null.rb
|
|
872
|
+
- db/migrate/20260702000002_add_creative_index_and_fk_to_tags.rb
|
|
873
|
+
- db/migrate/20260702000003_add_creative_type_index_to_labels.rb
|
|
874
|
+
- db/migrate/20260702000004_add_creative_created_at_index_to_comments.rb
|
|
875
|
+
- db/migrate/20260702000005_promote_channel_config_index_columns.rb
|
|
876
|
+
- db/migrate/20260713000001_add_comments_count_to_creatives.rb
|
|
877
|
+
- db/migrate/20260713000002_add_comment_versions_count_to_comments.rb
|
|
878
|
+
- db/migrate/20260715000000_change_creatives_description_to_plain_text.rb
|
|
838
879
|
- db/seeds.rb
|
|
839
880
|
- lib/collavre.rb
|
|
840
881
|
- lib/collavre/aws_credentials.rb
|