collavre 0.1.1 → 0.2.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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/collavre/comments_popup.css +293 -8
  3. data/app/assets/stylesheets/collavre/mention_menu.css +26 -0
  4. data/app/assets/stylesheets/collavre/popup.css +7 -0
  5. data/app/assets/stylesheets/collavre/print.css +18 -0
  6. data/app/channels/collavre/comments_presence_channel.rb +33 -0
  7. data/app/components/collavre/autocomplete_popup_component.html.erb +3 -0
  8. data/app/components/collavre/autocomplete_popup_component.rb +18 -0
  9. data/app/components/collavre/command_menu_component.rb +7 -0
  10. data/app/components/collavre/plans_timeline_component.html.erb +1 -1
  11. data/app/components/collavre/plans_timeline_component.rb +29 -32
  12. data/app/components/collavre/user_mention_menu_component.rb +4 -5
  13. data/app/controllers/collavre/comments_controller.rb +111 -10
  14. data/app/controllers/collavre/creatives_controller.rb +8 -0
  15. data/app/controllers/collavre/google_auth_controller.rb +5 -1
  16. data/app/controllers/collavre/plans_controller.rb +65 -9
  17. data/app/controllers/collavre/topics_controller.rb +42 -0
  18. data/app/controllers/collavre/users_controller.rb +4 -14
  19. data/app/errors/collavre/approval_pending_error.rb +54 -0
  20. data/app/errors/collavre/cancelled_error.rb +9 -0
  21. data/app/helpers/collavre/navigation_helper.rb +3 -1
  22. data/app/javascript/collavre.js +1 -0
  23. data/app/javascript/controllers/comments/__tests__/popup_controller.test.js +2 -1
  24. data/app/javascript/controllers/comments/form_controller.js +2 -1
  25. data/app/javascript/controllers/comments/list_controller.js +185 -2
  26. data/app/javascript/controllers/comments/popup_controller.js +95 -20
  27. data/app/javascript/controllers/comments/presence_controller.js +30 -1
  28. data/app/javascript/controllers/comments/topics_controller.js +314 -4
  29. data/app/javascript/modules/__tests__/creative_progress.test.js +50 -0
  30. data/app/javascript/modules/command_menu.js +116 -0
  31. data/app/javascript/modules/creative_progress.js +14 -0
  32. data/app/javascript/modules/creative_row_editor.js +104 -20
  33. data/app/javascript/modules/plans_timeline.js +15 -4
  34. data/app/javascript/modules/share_modal.js +3 -0
  35. data/app/jobs/collavre/ai_agent_job.rb +35 -21
  36. data/app/models/collavre/calendar_event.rb +7 -1
  37. data/app/models/collavre/comment.rb +35 -2
  38. data/app/models/collavre/creative.rb +1 -3
  39. data/app/models/collavre/mcp_tool.rb +4 -0
  40. data/app/models/collavre/plan.rb +23 -0
  41. data/app/models/collavre/topic.rb +12 -0
  42. data/app/models/collavre/user.rb +15 -1
  43. data/app/services/collavre/ai_agent_service.rb +174 -66
  44. data/app/services/collavre/ai_client.rb +31 -2
  45. data/app/services/collavre/comments/action_executor.rb +47 -1
  46. data/app/services/collavre/comments/calendar_command.rb +117 -18
  47. data/app/services/collavre/google_calendar_service.rb +38 -15
  48. data/app/services/collavre/markdown_importer.rb +47 -8
  49. data/app/services/collavre/mcp_service.rb +23 -10
  50. data/app/services/collavre/system_events/router.rb +50 -26
  51. data/app/services/collavre/tools/creative_create_service.rb +97 -0
  52. data/app/services/collavre/tools/creative_update_service.rb +116 -0
  53. data/app/views/collavre/comments/_comment.html.erb +2 -2
  54. data/app/views/collavre/comments/_comments_popup.html.erb +40 -6
  55. data/app/views/collavre/comments/fullscreen.html.erb +5 -0
  56. data/app/views/collavre/creatives/_inline_edit_form.html.erb +11 -3
  57. data/app/views/collavre/creatives/_integration_modals.html.erb +6 -0
  58. data/app/views/collavre/creatives/_integration_triggers.html.erb +8 -0
  59. data/app/views/collavre/creatives/_integrations_menu.html.erb +12 -0
  60. data/app/views/collavre/creatives/_mobile_actions_menu.html.erb +13 -1
  61. data/app/views/collavre/creatives/_share_button.html.erb +1 -1
  62. data/app/views/collavre/creatives/index.html.erb +22 -4
  63. data/app/views/collavre/users/edit_ai.html.erb +15 -0
  64. data/app/views/collavre/users/new_ai.html.erb +15 -0
  65. data/app/views/layouts/collavre/chat.html.erb +46 -0
  66. data/config/locales/ai_agent.en.yml +15 -0
  67. data/config/locales/ai_agent.ko.yml +15 -0
  68. data/config/locales/comments.en.yml +15 -3
  69. data/config/locales/comments.ko.yml +15 -3
  70. data/config/locales/creatives.en.yml +3 -31
  71. data/config/locales/creatives.ko.yml +3 -27
  72. data/config/locales/plans.en.yml +4 -0
  73. data/config/locales/plans.ko.yml +4 -0
  74. data/config/locales/users.en.yml +3 -0
  75. data/config/locales/users.ko.yml +3 -0
  76. data/config/routes.rb +8 -3
  77. data/db/migrate/20260120045354_encrypt_oauth_tokens.rb +1 -1
  78. data/db/migrate/20260131100000_migrate_active_storage_attachment_record_types.rb +21 -0
  79. data/db/migrate/20260201100000_make_google_event_id_nullable.rb +5 -0
  80. data/lib/collavre/engine.rb +171 -6
  81. data/lib/collavre/integration_registry.rb +129 -0
  82. data/lib/collavre/version.rb +1 -1
  83. data/lib/collavre.rb +2 -0
  84. data/lib/navigation/registry.rb +130 -0
  85. metadata +22 -15
  86. data/app/components/collavre/user_mention_menu_component.html.erb +0 -3
  87. data/app/controllers/collavre/notion_auth_controller.rb +0 -25
  88. data/app/jobs/collavre/notion_export_job.rb +0 -30
  89. data/app/jobs/collavre/notion_sync_job.rb +0 -48
  90. data/app/models/collavre/notion_account.rb +0 -17
  91. data/app/models/collavre/notion_block_link.rb +0 -10
  92. data/app/models/collavre/notion_page_link.rb +0 -19
  93. data/app/services/collavre/notion_client.rb +0 -231
  94. data/app/services/collavre/notion_creative_exporter.rb +0 -296
  95. data/app/services/collavre/notion_service.rb +0 -249
  96. data/app/views/collavre/creatives/_notion_integration_modal.html.erb +0 -90
  97. data/db/migrate/20241201000000_create_notion_integrations.rb +0 -29
  98. data/db/migrate/20250312000000_create_notion_block_links.rb +0 -16
  99. data/db/migrate/20250312010000_allow_multiple_notion_blocks_per_creative.rb +0 -5
@@ -0,0 +1,6 @@
1
+ <%# Render modals for registered integrations %>
2
+ <% Collavre::IntegrationRegistry.each do |integration| %>
3
+ <% if integration.enabled_for?(creative) && integration.creative_menu_partial.present? %>
4
+ <%= render integration.creative_menu_partial, creative: creative, integration: integration %>
5
+ <% end %>
6
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# Render hidden trigger buttons for registered integrations %>
2
+ <% Collavre::IntegrationRegistry.each do |integration| %>
3
+ <% if integration.enabled_for?(creative) %>
4
+ <button id="<%= integration.name %>-integration-btn"
5
+ data-creative-id="<%= creative&.id %>"
6
+ style="display:none;"></button>
7
+ <% end %>
8
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <%# Render integration menu items from IntegrationRegistry %>
2
+ <% Collavre::IntegrationRegistry.each do |integration| %>
3
+ <% if integration.enabled_for?(creative) %>
4
+ <button type="button"
5
+ class="popup-menu-item"
6
+ data-controller="click-target"
7
+ data-click-target-id-value="<%= integration.name %>-integration-btn"
8
+ data-action="click->click-target#trigger">
9
+ <%= integration.label %>
10
+ </button>
11
+ <% end %>
12
+ <% end %>
@@ -10,8 +10,20 @@
10
10
  <button type="button" class="popup-menu-item" data-controller="click-target" data-click-target-id-value="share-creative-btn" data-action="click->click-target#trigger"><%= t('collavre.creatives.index.share') %></button>
11
11
  <% end %>
12
12
  <% if can_manage_integrations %>
13
+ <%# Legacy hardcoded integrations %>
13
14
  <button type="button" class="popup-menu-item" data-controller="click-target" data-click-target-id-value="github-integration-btn" data-action="click->click-target#trigger"><%= t('collavre.creatives.index.integrations', default: '연동') %> - Github</button>
14
- <button type="button" class="popup-menu-item" data-controller="click-target" data-click-target-id-value="notion-integration-btn" data-action="click->click-target#trigger"><%= t('collavre.creatives.index.integrations', default: '연동') %> - Notion</button>
15
+ <%# Dynamically registered integrations %>
16
+ <% Collavre::IntegrationRegistry.each do |integration| %>
17
+ <% if integration.enabled_for?(current_creative) %>
18
+ <button type="button"
19
+ class="popup-menu-item"
20
+ data-controller="click-target"
21
+ data-click-target-id-value="<%= integration.name %>-integration-btn"
22
+ data-action="click->click-target#trigger">
23
+ <%= t('collavre.creatives.index.integrations', default: '연동') %> - <%= integration.label %>
24
+ </button>
25
+ <% end %>
26
+ <% end %>
15
27
  <% end %>
16
28
  <% if @parent_creative.present? %>
17
29
  <%= button_to t('collavre.creatives.index.slide_view'), slide_view_creative_path(@parent_creative), method: :get, class: 'popup-menu-item' %>
@@ -2,7 +2,7 @@
2
2
  <span aria-hidden="true"><%= svg_tag 'share.svg', class: 'icon-up', width: 22, height: 20 %></span>
3
3
  </button>
4
4
  <!-- Share Creative Modal -->
5
- <div id="share-creative-modal" style="display:none;position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:1000;align-items:center;justify-content:center;" data-creative-id="<%= (@parent_creative || @creative).id %>">
5
+ <div id="share-creative-modal" style="display:none;position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:1100;align-items:center;justify-content:center;" data-creative-id="<%= (@parent_creative || @creative).id %>">
6
6
  <div class="popup-box" style="min-width:320px;max-width:90vw;">
7
7
  <button id="close-share-modal" class="popup-close-btn">&times;</button>
8
8
  <h2><%= t('collavre.creatives.index.share_creative') %></h2>
@@ -33,8 +33,10 @@
33
33
  align: :left,
34
34
  button_classes: 'desktop-only'
35
35
  ) do %>
36
+ <%# Legacy hardcoded integrations (to be migrated to IntegrationRegistry) %>
36
37
  <button type="button" class="popup-menu-item" data-controller="click-target" data-click-target-id-value="github-integration-btn" data-action="click->click-target#trigger">Github</button>
37
- <button type="button" class="popup-menu-item" data-controller="click-target" data-click-target-id-value="notion-integration-btn" data-action="click->click-target#trigger">Notion</button>
38
+ <%# Dynamically registered integrations %>
39
+ <%= render 'integrations_menu', creative: current_creative %>
38
40
  <% end %>
39
41
  <% end %>
40
42
 
@@ -64,8 +66,10 @@
64
66
  <%= render 'import_upload_zone' %>
65
67
 
66
68
  <% if can_manage_integrations %>
69
+ <%# Legacy hardcoded integration triggers %>
67
70
  <button id="github-integration-btn" data-creative-id="<%= current_creative&.id %>" style="display:none;"></button>
68
- <button id="notion-integration-btn" data-creative-id="<%= current_creative&.id %>" style="display:none;"></button>
71
+ <%# Dynamically registered integration triggers %>
72
+ <%= render 'integration_triggers', creative: current_creative %>
69
73
  <% end %>
70
74
 
71
75
 
@@ -81,7 +85,19 @@
81
85
  value="<%= label.id %>">
82
86
 
83
87
  <%= "##{strip_tags(label.name)}" %>
84
- <% if label.type == "Plan" %> 🗓<%= label.target_date %><% end %>
88
+ <% if label.type == "Plan" %>
89
+ <% if @parent_creative&.has_permission?(Current.user, :write) %>
90
+ <%= form_with(model: label, url: collavre.plan_path(label), method: :patch, local: true, class: "plan-tag-date-form") do |form| %>
91
+ <%= hidden_field_tag :creative_id, @parent_creative.id %>
92
+ 🗓
93
+ <%= form.date_field :start_date, value: label.start_date, class: "plan-tag-date-input" %>
94
+ <%= form.date_field :target_date, value: label.target_date, class: "plan-tag-date-input" %>
95
+ <%= form.submit t("collavre.plans.update", default: "Update"), class: "plan-tag-date-submit" %>
96
+ <% end %>
97
+ <% else %>
98
+ 🗓<%= label.target_date %>
99
+ <% end %>
100
+ <% end %>
85
101
 
86
102
  <% end %>
87
103
  <% if labels.present? %>
@@ -186,7 +202,9 @@
186
202
 
187
203
  <%= render 'set_plan_modal' %>
188
204
  <% if can_manage_integrations %>
205
+ <%# Legacy hardcoded integration modals %>
189
206
  <%= render 'github_integration_modal', creative: current_creative %>
190
- <%= render 'notion_integration_modal', creative: current_creative %>
207
+ <%# Dynamically registered integration modals %>
208
+ <%= render 'integration_modals', creative: current_creative %>
191
209
  <% end %>
192
210
  </div>
@@ -17,6 +17,15 @@
17
17
  <small class="form-text text-muted">Liquid expression to determine if this agent should handle an event. Example: <code>chat.mentioned_user.id == agent.id</code></small>
18
18
  </div>
19
19
 
20
+ <div class="form-group">
21
+ <%= form.label :llm_vendor, t('collavre.users.new_ai.vendor_label', default: 'LLM Provider') %>
22
+ <%= form.select :llm_vendor, options_for_select([
23
+ ["Google (Gemini)", "google"],
24
+ ["OpenClaw", "openclaw"]
25
+ ], @user.llm_vendor || "google"), {}, class: "stacked-form-control" %>
26
+ <small class="form-text text-muted"><%= t('collavre.users.new_ai.vendor_help', default: 'Select the AI provider for this agent') %></small>
27
+ </div>
28
+
20
29
  <div class="form-group">
21
30
  <%= form.label :llm_model, t('collavre.users.new_ai.model_label') %>
22
31
  <div style="position: relative;"
@@ -41,6 +50,12 @@
41
50
  <small class="form-text text-muted"><%= t('collavre.users.new_ai.api_key_help') %></small>
42
51
  </div>
43
52
 
53
+ <div class="form-group">
54
+ <%= form.label :gateway_url, t('collavre.users.new_ai.gateway_url_label', default: 'Gateway URL') %>
55
+ <%= form.url_field :gateway_url, class: "stacked-form-control", placeholder: "http://localhost:18789" %>
56
+ <small class="form-text text-muted"><%= t('collavre.users.new_ai.gateway_url_help', default: 'Required for OpenClaw. The URL of your OpenClaw gateway.') %></small>
57
+ </div>
58
+
44
59
  <div class="form-group">
45
60
  <label class="form-label"><%= t('collavre.users.edit_ai.tools_title') %></label>
46
61
  <p class="text-muted small"><%= t('collavre.users.edit_ai.tools_description') %></p>
@@ -22,6 +22,15 @@
22
22
  <small class="form-text text-muted">Liquid expression to determine if this agent should handle an event. Example: <code>chat.mentioned_user.id == agent.id</code></small>
23
23
  </div>
24
24
 
25
+ <div class="form-group">
26
+ <%= form.label :llm_vendor, t('collavre.users.new_ai.vendor_label', default: 'LLM Provider') %>
27
+ <%= form.select :llm_vendor, options_for_select([
28
+ ["Google (Gemini)", "google"],
29
+ ["OpenClaw", "openclaw"]
30
+ ], "google"), {}, class: "stacked-form-control" %>
31
+ <small class="form-text text-muted"><%= t('collavre.users.new_ai.vendor_help', default: 'Select the AI provider for this agent') %></small>
32
+ </div>
33
+
25
34
  <div class="form-group">
26
35
  <%= form.label :llm_model, t('collavre.users.new_ai.model_label') %>
27
36
  <div style="position: relative;"
@@ -46,6 +55,12 @@
46
55
  <small class="form-text text-muted"><%= t('collavre.users.new_ai.api_key_help') %></small>
47
56
  </div>
48
57
 
58
+ <div class="form-group">
59
+ <%= form.label :gateway_url, t('collavre.users.new_ai.gateway_url_label', default: 'Gateway URL') %>
60
+ <%= form.url_field :gateway_url, class: "stacked-form-control", placeholder: "http://localhost:18789" %>
61
+ <small class="form-text text-muted"><%= t('collavre.users.new_ai.gateway_url_help', default: 'Required for OpenClaw. The URL of your OpenClaw gateway.') %></small>
62
+ </div>
63
+
49
64
  <div class="form-group">
50
65
  <label class="form-label"><%= t('collavre.users.edit_ai.tools_title') %></label>
51
66
  <p class="text-muted small"><%= t('collavre.users.edit_ai.tools_description') %></p>
@@ -0,0 +1,46 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= content_for(:title) || t('app.name') %></title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <meta name="apple-mobile-web-app-capable" content="yes">
7
+ <meta name="mobile-web-app-capable" content="yes">
8
+ <meta name="app-version" content="<%= Rails.application.config.app_version %>">
9
+ <%= csrf_meta_tags %>
10
+ <%= csp_meta_tag %>
11
+
12
+ <%= yield :head %>
13
+
14
+ <link rel="icon" href="/icon-1e3cf549d2.png" type="image/png">
15
+ <link rel="icon" href="/icon-1e3cf549d2.svg" type="image/svg+xml">
16
+ <link rel="apple-touch-icon" href="/icon-1e3cf549d2.png">
17
+
18
+ <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
19
+ <%= stylesheet_link_tag "collavre/creatives" %>
20
+ <%= stylesheet_link_tag "collavre/actiontext" %>
21
+ <%= stylesheet_link_tag "collavre/dark_mode" %>
22
+ <%= stylesheet_link_tag "collavre/popup" %>
23
+ <%= stylesheet_link_tag "collavre/comments_popup" %>
24
+
25
+ <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true, type: "module" %>
26
+
27
+ <% if Current.user&.theme.present? && !%w[light dark].include?(Current.user.theme) %>
28
+ <% if (custom_theme = UserTheme.find_by(id: Current.user.theme)) %>
29
+ <style id="user-theme-styles" data-turbo-track="reload">
30
+ body {
31
+ <% custom_theme.variables.each do |key, value| %>
32
+ <%= key %>: <%= value %> !important;
33
+ <% end %>
34
+ }
35
+ </style>
36
+ <% end %>
37
+ <% end %>
38
+ </head>
39
+ <body class="chat-fullscreen<%= ' dark-mode' if Current.user&.theme == 'dark' %><%= ' light-mode' if Current.user&.theme == 'light' %>" data-current-user-id="<%= Current.user&.id %>" data-controller="action-text-attachment-link" data-action="click->action-text-attachment-link#download">
40
+ <main>
41
+ <%= yield %>
42
+ </main>
43
+
44
+ <%= render "collavre/comments/reaction_picker" %>
45
+ </body>
46
+ </html>
@@ -0,0 +1,15 @@
1
+ en:
2
+ collavre:
3
+ ai_agent:
4
+ approval:
5
+ message: |
6
+ 🔧 **Tool Approval Required**
7
+
8
+ **%{tool_name}** wants to execute with the following arguments:
9
+
10
+ ```json
11
+ %{arguments}
12
+ ```
13
+
14
+ Please approve or reject this action.
15
+ no_arguments: "(no arguments)"
@@ -0,0 +1,15 @@
1
+ ko:
2
+ collavre:
3
+ ai_agent:
4
+ approval:
5
+ message: |
6
+ 🔧 **도구 실행 승인 필요**
7
+
8
+ **%{tool_name}** 도구가 다음 인자로 실행을 요청합니다:
9
+
10
+ ```json
11
+ %{arguments}
12
+ ```
13
+
14
+ 승인 또는 거부해 주세요.
15
+ no_arguments: "(인자 없음)"
@@ -8,9 +8,6 @@ en:
8
8
  github_auth:
9
9
  login_first: Please sign in first.
10
10
  connected: Github account connected successfully.
11
- notion_auth:
12
- login_first: Please sign in first.
13
- connected: Notion account connected successfully.
14
11
  comments:
15
12
  reaction_invalid: Invalid reaction.
16
13
  invalid_topic: Topic does not belong to this creative.
@@ -70,17 +67,31 @@ en:
70
67
  move_button: Move
71
68
  move_no_selection: Select at least one message to move.
72
69
  move_error: Unable to move messages.
70
+ add_participant: Add user
71
+ hint_drag_topic: "🎯 Drag → Move to topic"
72
+ hint_move_button: "📤 Move button → Another chat"
73
+ fullscreen: Full screen
74
+ exit_fullscreen: Exit full screen
73
75
  move_invalid_target: Select a valid creative to move messages to.
76
+ move_invalid_topic: Invalid topic selected.
74
77
  move_not_allowed: You do not have permission to move these messages.
75
78
  select_label: Select message
76
79
  add_reaction: Add reaction
77
80
  add_reaction_button: "+"
81
+ command_menu:
82
+ calendar_description: "Create a Google Calendar event."
83
+ calendar_args: "YYYY-MM-DD@HH:MM memo (today/tomorrow, +3days, +2weeks, +1months, +1years, +mon)"
78
84
  calendar_command:
79
85
  event_created: 'event created: [Google Calendar event](%{url})'
86
+ event_created_local: 'event created (local only - connect Google Calendar to sync)'
87
+ event_created_sync_failed: 'event created (Google Calendar sync failed - please reconnect your Google account)'
80
88
  read_by: Read by %{name}
81
89
  activity_logs_summary: Activity Logs
82
90
  calendar_events:
83
91
  deleted: Calendar event deleted.
92
+ google_calendar:
93
+ errors:
94
+ not_connected: Google Calendar is not connected. Please connect your Google account first.
84
95
  inbox:
85
96
  mark_read: Read
86
97
  mark_unread: Unread
@@ -96,6 +107,7 @@ en:
96
107
  comment_content_unavailable: "(comment unavailable)"
97
108
  tool_approval_needed: Tool '%{tool_name}' has been detected/updated. Please approve
98
109
  it to activate.
110
+ approval_requested: '%{user} requested approval to execute tool "%{tool_name}" in "%{creative}".'
99
111
  emails:
100
112
  index_title: Emails
101
113
  list:
@@ -8,9 +8,6 @@ ko:
8
8
  github_auth:
9
9
  login_first: 먼저 로그인해주세요.
10
10
  connected: Github 계정이 연동되었습니다.
11
- notion_auth:
12
- login_first: 먼저 로그인해주세요.
13
- connected: Notion 계정이 연동되었습니다.
14
11
  comments:
15
12
  reaction_invalid: 잘못된 리액션입니다.
16
13
  invalid_topic: 토픽이 이 크리에이티브에 속하지 않습니다.
@@ -67,17 +64,31 @@ ko:
67
64
  move_button: 이동
68
65
  move_no_selection: 이동할 메시지를 선택해주세요.
69
66
  move_error: 메시지를 이동할 수 없습니다.
67
+ add_participant: 사용자 추가
68
+ hint_drag_topic: "🎯 드래그 → 토픽 이동"
69
+ hint_move_button: "📤 이동 버튼 → 다른 채팅창"
70
+ fullscreen: 전체 화면
71
+ exit_fullscreen: 전체 화면 종료
70
72
  move_invalid_target: 이동할 크리에이티브를 선택해주세요.
73
+ move_invalid_topic: 유효하지 않은 토픽입니다.
71
74
  move_not_allowed: 이 메시지를 이동할 권한이 없습니다.
72
75
  select_label: 메시지 선택
73
76
  add_reaction: 리액션 추가
74
77
  add_reaction_button: "+"
78
+ command_menu:
79
+ calendar_description: 구글 캘린더 이벤트를 생성합니다.
80
+ calendar_args: "YYYY-MM-DD@HH:MM 메모 (today/tomorrow, +3days, +2weeks, +1months, +1years, +mon/+월)"
75
81
  calendar_command:
76
82
  event_created: '이벤트가 생성되었습니다: [구글 캘린더 이벤트](%{url})'
83
+ event_created_local: '이벤트가 생성되었습니다 (로컬 전용 - 구글 캘린더 연동 시 동기화됩니다)'
84
+ event_created_sync_failed: '이벤트가 생성되었습니다 (구글 캘린더 동기화 실패 - 구글 계정을 다시 연동해주세요)'
77
85
  read_by: "%{name} 님이 읽음"
78
86
  activity_logs_summary: 활동 기록
79
87
  calendar_events:
80
88
  deleted: 캘린더 이벤트가 삭제되었습니다.
89
+ google_calendar:
90
+ errors:
91
+ not_connected: 구글 캘린더가 연동되지 않았습니다. 먼저 구글 계정을 연동해주세요.
81
92
  inbox:
82
93
  mark_read: 읽음
83
94
  mark_unread: 안읽음
@@ -92,6 +103,7 @@ ko:
92
103
  삭제했습니다: %{comment_content}'
93
104
  comment_content_unavailable: "(삭제된 댓글 내용을 표시할 수 없습니다)"
94
105
  tool_approval_needed: 도구 '%{tool_name}'가 감지/업데이트되었습니다. 활성화하려면 승인해주세요.
106
+ approval_requested: '%{user}님이 "%{creative}"에서 도구 "%{tool_name}" 실행 승인을 요청했습니다.'
95
107
  emails:
96
108
  index_title: 이메일
97
109
  list:
@@ -98,37 +98,6 @@ en:
98
98
  it if needed.
99
99
  github_integration_prompt_help: 'Use the placeholders below to inject runtime
100
100
  details as needed:'
101
- notion_integration_saved: Notion integration saved successfully.
102
- notion_integration_login_required: Sign in with your Notion account to start
103
- the integration.
104
- notion_integration_missing_creative: No Creative selected for integration.
105
- notion_integration_existing_message: You're already connected to Notion pages
106
- below.
107
- notion_integration_delete_confirm: Do you want to remove the Notion integration?
108
- notion_integration_delete_success: Notion integration removed successfully.
109
- notion_integration_delete_error: Failed to remove the Notion integration.
110
- notion_integration_delete_button: Remove integration
111
- notion_export_success: Creative exported to Notion successfully.
112
- notion_sync_success: Creative synced to Notion successfully.
113
- notion_integration_title: Configure Notion integration
114
- notion_integration_connect: Sign in with your Notion account to start exporting.
115
- notion_login_button: Sign in with Notion
116
- notion_integration_existing_intro: 'Linked Notion pages:'
117
- notion_sync_button: Sync to Notion
118
- notion_integration_workspace: Your Notion workspace is connected. Choose how
119
- to export your creative.
120
- notion_export_option: 'Export option:'
121
- notion_export_new_page: Create new page
122
- notion_export_under_page: Create as subpage under existing page
123
- notion_parent_page: 'Parent page:'
124
- notion_loading: Loading pages...
125
- notion_integration_summary: 'Ready to export your creative tree to Notion:'
126
- notion_creative_title: 'Root Creative:'
127
- notion_workspace: 'Workspace:'
128
- notion_export_as: 'Export as:'
129
- notion_tree_note: This will export the selected creative and all its descendants
130
- as a structured document.
131
- notion_export_button: Export to Notion
132
101
  plan_tags_applied: Plan tags applied to selected creatives.
133
102
  plan_tag_failed: Please select a plan and at least one creative.
134
103
  plan_tags_removed: Plan tag removed from selected creatives.
@@ -136,6 +105,9 @@ en:
136
105
  select_plan: Select Plan
137
106
  remove_plan: Remove Plan
138
107
  add_plan: Add Plan
108
+ progress_complete: Complete
109
+ progress_incomplete: Incomplete
110
+ progress_complete_children_alert: Marking this creative complete will also mark all child creatives complete.
139
111
  set_plan_title: Set Plan for Selected Creative
140
112
  are_you_sure_delete_share: Are you sure delete share?
141
113
  share_deleted: The share are deleted.
@@ -85,33 +85,6 @@ ko:
85
85
  github_integration_existing_intro: '이미 연동된 Repository 목록입니다:'
86
86
  github_integration_prompt_title: Gemini 분석 프롬프트를 확인하고 필요시 수정하세요.
87
87
  github_integration_prompt_help: '아래 플레이스홀더를 사용하여 런타임 정보를 삽입할 수 있습니다:'
88
- notion_integration_saved: Notion 연동이 저장되었습니다.
89
- notion_integration_login_required: Notion 계정으로 로그인하여 연동을 시작하세요.
90
- notion_integration_missing_creative: 연동할 Creative가 선택되지 않았습니다.
91
- notion_integration_existing_message: 이미 아래 Notion 페이지와 연동 중입니다.
92
- notion_integration_delete_confirm: Notion 연동을 삭제하시겠습니까?
93
- notion_integration_delete_success: Notion 연동이 삭제되었습니다.
94
- notion_integration_delete_error: Notion 연동 삭제에 실패했습니다.
95
- notion_integration_delete_button: 연동 삭제
96
- notion_export_success: Creative가 Notion으로 내보내기 되었습니다.
97
- notion_sync_success: Creative가 Notion과 동기화되었습니다.
98
- notion_integration_title: Notion 연동 설정
99
- notion_integration_connect: Notion 계정으로 로그인하여 내보내기를 시작하세요.
100
- notion_login_button: Notion 계정으로 로그인
101
- notion_integration_existing_intro: '연동된 Notion 페이지:'
102
- notion_sync_button: Notion으로 동기화
103
- notion_integration_workspace: Notion 워크스페이스가 연결되었습니다. 내보내기 방법을 선택하세요.
104
- notion_export_option: '내보내기 옵션:'
105
- notion_export_new_page: 새 페이지 생성
106
- notion_export_under_page: 기존 페이지 하위에 생성
107
- notion_parent_page: '상위 페이지:'
108
- notion_loading: 페이지 로딩 중...
109
- notion_integration_summary: 'Creative 트리를 Notion으로 내보낼 준비가 되었습니다:'
110
- notion_creative_title: '루트 Creative:'
111
- notion_workspace: '워크스페이스:'
112
- notion_export_as: '내보내기 형태:'
113
- notion_tree_note: 선택한 크리에이티브와 모든 하위 항목이 구조화된 문서로 내보내집니다.
114
- notion_export_button: Notion으로 내보내기
115
88
  search_placeholder: 크리에이티브 검색...
116
89
  plan_tags_applied: 선택한 크리에이티브에 플랜 태그가 적용되었습니다.
117
90
  plan_tag_failed: 플랜과 하나 이상의 크리에이티브를 선택하세요.
@@ -120,6 +93,9 @@ ko:
120
93
  select_plan: 계획 선택
121
94
  remove_plan: 계획 제거
122
95
  add_plan: 계획 추가
96
+ progress_complete: 완료
97
+ progress_incomplete: 미완료
98
+ progress_complete_children_alert: 하위 크리에이티브도 모두 완료로 표시됩니다.
123
99
  set_plan_title: 선택된 크리에이티브에 대한 계획 설정
124
100
  are_you_sure_delete_share: 공유를 삭제하시겠습니까?
125
101
  share_deleted: 공유가 삭제 되었습니다.
@@ -3,10 +3,14 @@ en:
3
3
  collavre:
4
4
  plans:
5
5
  add_plan: Add Plan
6
+ start_date: Start Date
6
7
  target_date: Target Date
7
8
  plan_name: Plan Name
8
9
  created: Plan was successfully created.
9
10
  deleted: Plan deleted.
11
+ updated: Plan updated.
12
+ update: Update
13
+ update_forbidden: You do not have permission to update this plan.
10
14
  delete_confirm: Are you sure you want to delete this plan?
11
15
  today: Today
12
16
  select_creative: Select Creative
@@ -3,10 +3,14 @@ ko:
3
3
  collavre:
4
4
  plans:
5
5
  add_plan: 계획 추가
6
+ start_date: 시작 날짜
6
7
  target_date: 목표 날짜
7
8
  plan_name: 계획 이름
8
9
  created: 계획이 성공적으로 생성되었습니다.
9
10
  deleted: 계획이 삭제되었습니다.
11
+ updated: 계획이 업데이트되었습니다.
12
+ update: 업데이트
13
+ update_forbidden: 이 계획을 업데이트할 권한이 없습니다.
10
14
  delete_confirm: 이 계획을 삭제하시겠습니까?
11
15
  today: 오늘
12
16
  select_creative: 크리에이티브 선택
@@ -22,10 +22,13 @@ en:
22
22
  name_label: Name
23
23
  system_prompt_label: System Prompt
24
24
  vendor_label: LLM Vendor
25
+ vendor_help: Select the AI provider for this agent
25
26
  model_label: Model
26
27
  api_key_label: API Key
27
28
  api_key_help: If provided, this key will be used instead of the system default.
28
29
  Stored encrypted.
30
+ gateway_url_label: Gateway URL
31
+ gateway_url_help: Required for OpenClaw. The URL of your OpenClaw gateway.
29
32
  searchable_label: Allow this AI to be searchable and mentionable
30
33
  searchable_help: When enabled, others can find and mention this AI by name.
31
34
  submit: Add AI Agent
@@ -21,9 +21,12 @@ ko:
21
21
  name_label: 이름
22
22
  system_prompt_label: 시스템 프롬프트
23
23
  vendor_label: LLM 벤더
24
+ vendor_help: 이 에이전트에 사용할 AI 제공업체를 선택하세요
24
25
  model_label: 모델
25
26
  api_key_label: API Key
26
27
  api_key_help: 입력하면 시스템 기본값 대신 이 키가 사용됩니다. 암호화되어 저장됩니다.
28
+ gateway_url_label: Gateway URL
29
+ gateway_url_help: OpenClaw 사용 시 필수입니다. OpenClaw 게이트웨이의 URL을 입력하세요.
27
30
  searchable_label: 이 AI를 검색 및 멘션 가능하게 허용
28
31
  searchable_help: 활성화하면 다른 사용자가 이름으로 이 AI를 찾고 멘션할 수 있습니다.
29
32
  submit: AI 에이전트 추가
data/config/routes.rb CHANGED
@@ -25,7 +25,6 @@ Collavre::Engine.routes.draw do
25
25
  # OAuth callback routes (paths match OmniAuth provider names)
26
26
  match "/auth/google_oauth2/callback", to: "google_auth#callback", via: [ :get, :post ]
27
27
  match "/auth/github/callback", to: "github_auth#callback", via: [ :get, :post ]
28
- match "/auth/notion/callback", to: "notion_auth#callback", via: [ :get, :post ]
29
28
 
30
29
  delete "/attachments/:signed_id", to: "attachments#destroy", as: :attachment
31
30
 
@@ -37,7 +36,7 @@ Collavre::Engine.routes.draw do
37
36
  get :count, on: :collection
38
37
  end
39
38
 
40
- resources :plans, only: [ :create, :destroy, :index ]
39
+ resources :plans, only: [ :create, :destroy, :index, :update ]
41
40
 
42
41
  resources :user_themes, only: [ :index, :create, :destroy ] do
43
42
  member do
@@ -50,7 +49,11 @@ Collavre::Engine.routes.draw do
50
49
 
51
50
  resources :creatives do
52
51
  resources :creative_shares, only: [ :create, :destroy ]
53
- resources :topics, only: [ :index, :create, :destroy ]
52
+ resources :topics, only: [ :index, :create, :update, :destroy ] do
53
+ collection do
54
+ post :reorder
55
+ end
56
+ end
54
57
  resources :comments, only: [ :index, :create, :destroy, :show, :update ] do
55
58
  member do
56
59
  post :convert
@@ -64,7 +67,9 @@ Collavre::Engine.routes.draw do
64
67
 
65
68
  collection do
66
69
  get :participants
70
+ get :fullscreen
67
71
  post :move
72
+ get :commands
68
73
  end
69
74
  end
70
75
  collection do
@@ -12,7 +12,7 @@ class EncryptOauthTokens < ActiveRecord::Migration[8.1]
12
12
  end
13
13
 
14
14
  say_with_time "Encrypting NotionAccount tokens" do
15
- encrypt_column(NotionAccount, :token)
15
+ encrypt_column(CollavreNotion::NotionAccount, :token)
16
16
  end
17
17
  end
18
18
 
@@ -0,0 +1,21 @@
1
+ class MigrateActiveStorageAttachmentRecordTypes < ActiveRecord::Migration[8.1]
2
+ def up
3
+ return unless table_exists?(:active_storage_attachments)
4
+
5
+ migrate_record_type("User", "Collavre::User")
6
+ migrate_record_type("Comment", "Collavre::Comment")
7
+ end
8
+
9
+ def down
10
+ return unless table_exists?(:active_storage_attachments)
11
+
12
+ migrate_record_type("Collavre::User", "User")
13
+ migrate_record_type("Collavre::Comment", "Comment")
14
+ end
15
+
16
+ private
17
+
18
+ def migrate_record_type(from, to)
19
+ ActiveStorage::Attachment.where(record_type: from).update_all(record_type: to)
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ class MakeGoogleEventIdNullable < ActiveRecord::Migration[8.0]
2
+ def change
3
+ change_column_null :calendar_events, :google_event_id, true
4
+ end
5
+ end