Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2973375
[AI-101] Add the editor seam for AI text transform actions
tangopium Aug 31, 2026
f3f85c0
[AI-101] Resolve editor actions through the API v3 list endpoints
tangopium Sep 8, 2026
8240ce4
[AI-101] Add an echo gateway for local development
tangopium Sep 8, 2026
f5cecef
[AI-101] Execute the selected action and apply the result
tangopium Sep 8, 2026
9aa3a77
[AI-101] Wire the text transform gateway to the LLM connection
tangopium Oct 2, 2026
00b0d31
[AI-101] Refuse a second run while one is active on the editor
tangopium Oct 2, 2026
1aea9a8
[AI-101] Keep implementation rationale out of the source
tangopium Oct 2, 2026
8b338ca
[AI-101] Switch the assistant and the AI features on for previews
tangopium Oct 2, 2026
42f89c0
[AI-101] Leave the admin toggles to the preview's administration
tangopium Oct 2, 2026
8780b57
Merge branch 'feature/66020-pr12-llm-module' into feature/ai-101-ai-a…
tangopium Oct 6, 2026
042f585
Merge branch 'feature/66020-pr12-llm-module' into feature/ai-101-ai-a…
tangopium Oct 8, 2026
f779342
Merge branch 'feature/66020-pr12-llm-module' into feature/ai-101-ai-a…
tangopium Oct 8, 2026
604e4fd
Migrate the AttributeHelpText table to a BorderBoxTableComponent
HDinger Oct 12, 2026
4daaae0
Update screenshots in new AttributeHelpText index page
HDinger Oct 12, 2026
5e618ab
Harmonize templates for global search and workPackage autocompleter
HDinger Oct 12, 2026
00f725f
Update CKEditor to 49.0.0 without the Watchdog
thykel Oct 12, 2026
026b85d
Give the WP subject more space in the boards column header
HDinger Oct 12, 2026
43bf22d
Use the refreshed default theme of CKEditor 49
thykel Oct 12, 2026
f75c872
Type the createCustomized config as unknown
thykel Oct 12, 2026
beb676d
Make the source mode read-only after a CKEditor error
thykel Oct 12, 2026
020bf44
Merge branch 'dev' into code-maintenance/ckeditor-49
thykel Oct 12, 2026
10324f1
Handle a failed CKEditor teardown
thykel Oct 12, 2026
4f9504e
Merge remote-tracking branch 'origin/code-maintenance/ckeditor-49' in…
thykel Oct 12, 2026
b99c9cb
Merge branch 'dev' into code-maintenance/ckeditor-49
thykel Oct 12, 2026
ca8d599
Merge pull request #25966 from opf/code-maintenance/ckeditor-49
oliverguenther Oct 13, 2026
8ba75f2
Merge pull request #25950 from opf/feature/dream-811-harmonise-user-d…
HDinger Oct 13, 2026
1486f86
Merge pull request #25953 from opf/feature/op-20413-migrate-the-attri…
HDinger Oct 13, 2026
5f5e4d5
Merge pull request #25965 from opf/feature/op-17691-display-full-work…
HDinger Oct 13, 2026
9ea8e3d
Merge branch 'feature/66020-pr12-llm-module' into feature/ai-101-ai-a…
tangopium Oct 13, 2026
5423c11
[AI-101] Rebuild the CKEditor bundle with the Assistant dropdown
tangopium Oct 13, 2026
da2f1a1
[AI-101] Update the copyright headers to the current wording
tangopium Oct 13, 2026
5971cf2
Merge remote-tracking branch 'origin/dev' into feature/ai-101-ai-acti…
tangopium Oct 13, 2026
b6323e0
[AI-101] Rebuild the CKEditor bundle with the Assistant dropdown
tangopium Oct 13, 2026
d415742
Merge remote-tracking branch 'origin/feature/ai-101-ai-actions-dropdo…
tangopium Oct 13, 2026
d6b0d3b
[AI-101] Rebuild the bundle with the Assistant menu opening inwards
tangopium Oct 13, 2026
abcccc1
[AI-101] Show the last used action on the Assistant button
tangopium Oct 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions app/components/attribute_help_texts/row_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# frozen_string_literal: true

# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++

class AttributeHelpTexts::RowComponent < OpPrimer::BorderBoxRowComponent
alias_method :attribute_help_text, :model

def row_data
{ test_selector: "attribute-help-text-row-#{attribute_help_text.id}" }
end

def attribute_name
render(Primer::Beta::Link.new(href: edit_attribute_help_text_path(attribute_help_text))) do
attribute_help_text.attribute_field_name
end
end

def help_text
render(OpenProject::Common::AttributeHelpTextComponent.new(help_text: attribute_help_text)) do |help_text|
help_text.with_additional_label_content(t(:"attribute_help_texts.show_preview"))
end
end

def button_links
[delete_link]
end

private

def delete_link
render(
Primer::Beta::IconButton.new(
icon: :trash,
scheme: :danger,
tag: :a,
href: attribute_help_text_path(attribute_help_text),
"aria-label": t(:button_delete),
tooltip_direction: :w,
test_selector: "attribute-help-text-delete-#{attribute_help_text.id}",
data: {
turbo_method: :delete,
turbo_confirm: t(:text_are_you_sure)
}
)
)
end
end
47 changes: 47 additions & 0 deletions app/components/attribute_help_texts/table_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++

class AttributeHelpTexts::TableComponent < OpPrimer::BorderBoxTableComponent
columns :attribute_name, :help_text
main_column :attribute_name

def has_actions? = true

def sortable? = false

def headers
[
[:attribute_name, { caption: AttributeHelpText.human_attribute_name(:attribute_name) }],
[:help_text, { caption: AttributeHelpText.human_attribute_name(:help_text) }]
]
end

def mobile_title = AttributeHelpText.human_plural_model_name
end
75 changes: 75 additions & 0 deletions app/services/ai/text_transforms/echo_gateway.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#

module AI
module TextTransforms
# Development stand-in that needs no LLM connection: reports ready and
# streams the submitted content back word by word.
class EchoGateway < Gateway
ENV_KEY = "OPENPROJECT_AI_TEXT_TRANSFORM_ECHO_GATEWAY"

def self.enabled?
!Rails.env.production? && ActiveModel::Type::Boolean.new.cast(ENV.fetch(ENV_KEY, false)) == true
end

def initialize(delay: 0.05)
super()
@delay = delay
end

def readiness
Readiness.new(true, nil)
end

def stream(system:, user:, **)
text = echo_text(system:, user:)

text.scan(/\S+\s*|\s+/).each do |chunk|
yield chunk
sleep(@delay) if @delay.positive?
end

text
end

private

def echo_text(system:, user:)
<<~TEXT.chomp
#{user}

---

_Echo gateway: no LLM connected. The action's instructions were:_

> #{system.to_s.lines.map(&:chomp).join("\n> ")}
TEXT
end
end
end
end
10 changes: 9 additions & 1 deletion app/services/ai/text_transforms/gateway.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,15 @@ class Gateway
def ready? = ready
end

def self.build = NullGateway.new
class << self
attr_accessor :factory

def build
return EchoGateway.new if EchoGateway.enabled?

factory&.call || NullGateway.new
end
end
end
end
end
70 changes: 1 addition & 69 deletions app/views/attribute_help_texts/_tab.html.erb
Original file line number Diff line number Diff line change
@@ -1,69 +1 @@
<% if @attribute_help_texts.any? %>
<div class="generic-table--container">
<div class="generic-table--results-container">
<table class="generic-table" data-controller="table-highlighting">
<colgroup>
<col>
<col>
<col>
<col data-highlight="false">
</colgroup>
<thead>
<tr>
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<span>
<%= AttributeHelpText.human_attribute_name(:attribute_name) %>
</span>
</div>
</div>
</th>
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<span>
<%= AttributeHelpText.human_attribute_name(:help_text) %>
</span>
</div>
</div>
</th>
<th>
<div class="generic-table--empty-header"></div>
</th>
</tr>
</thead>
<tbody>
<% @attribute_help_texts.each do |attribute_help_text| -%>
<tr class="attribute-help-text--entry">
<td>
<%= link_to h(attribute_help_text.attribute_field_name),
edit_attribute_help_text_path(attribute_help_text) %>
</td>
<td>
<%=
render OpenProject::Common::AttributeHelpTextComponent.new(help_text: attribute_help_text) do |help_text|
help_text.with_additional_label_content(t(:"attribute_help_texts.show_preview"))
end
%>
</td>
<td class="buttons">
<%= link_to(
op_icon("icon icon-delete"),
attribute_help_text_path(attribute_help_text),
data: {
turbo_method: :delete,
turbo_confirm: I18n.t(:text_are_you_sure)
},
title: t(:button_delete)
) %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<% else %>
<%= no_results_box %>
<% end %>
<%= render(AttributeHelpTexts::TableComponent.new(rows: @attribute_help_texts)) %>
7 changes: 7 additions & 0 deletions config/locales/js-en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,14 @@ en:
description_select_work_package: "Select work package #%{id}"
description_subwork_package: "Child of work package #%{id}"
editor:
ai_actions:
caption: "Assistant"
failed: "The assistant could not transform the text."
running: "The assistant is working on \"%{action}\"…"
succeeded: "\"%{action}\" applied. Use undo to restore the previous text."
timeout: "The assistant did not answer in time."
ckeditor_error: "An error occurred within CKEditor"
ckeditor_error_reload: "The editor stopped working. Your text is still here, but you cannot change it. Copy any text you want to keep, then reload the page."
error_saving_failed: "Saving the document failed with the following error: %{error}"
macro:
attribute_reference:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading