DEV: improve artifact presentation (#932)

1. Keep source in a "details" block after rendered so it does
not overwhelm users

2. Ensure artifacts are never indexed by robots

3. Cache break our CSS that changed recently
This commit is contained in:
Sam 2024-11-20 18:53:19 +11:00 committed by GitHub
parent 2652716398
commit 52c644798d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 48 additions and 5 deletions

View File

@ -68,6 +68,7 @@ module DiscourseAi
response.headers.delete("X-Frame-Options") response.headers.delete("X-Frame-Options")
response.headers["Content-Security-Policy"] = "script-src 'unsafe-inline';" response.headers["Content-Security-Policy"] = "script-src 'unsafe-inline';"
response.headers["X-Robots-Tag"] = "noindex"
# Render the content # Render the content
render html: trusted_html.html_safe, layout: false, content_type: "text/html" render html: trusted_html.html_safe, layout: false, content_type: "text/html"

View File

@ -10,7 +10,7 @@
<meta name="twitter:title" content="<%= I18n.t("discourse_ai.share_ai.title", title: @shared_conversation.title, site_name: SiteSetting.title) %>"> <meta name="twitter:title" content="<%= I18n.t("discourse_ai.share_ai.title", title: @shared_conversation.title, site_name: SiteSetting.title) %>">
<meta name="twitter:description" content="<%= @shared_conversation.formatted_excerpt %>"> <meta name="twitter:description" content="<%= @shared_conversation.formatted_excerpt %>">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="<%= ::UrlHelper.absolute("/plugins/discourse-ai/ai-share/share.css") %>"> <link rel="stylesheet" href="<%= ::UrlHelper.absolute("/plugins/discourse-ai/ai-share/share.css?v=1") %>">
</head> </head>
<body> <body>
<header class="site-header"> <header class="site-header">

View File

@ -175,6 +175,7 @@ en:
discourse_ai: discourse_ai:
ai_artifact: ai_artifact:
link: "Show Artifact in new tab" link: "Show Artifact in new tab"
view_source: "View Source"
unknown_model: "Unknown AI model" unknown_model: "Unknown AI model"
tools: tools:

View File

@ -93,7 +93,7 @@ module DiscourseAi
js = parameters[:js].to_s js = parameters[:js].to_s
artifact_div = artifact_div =
"<div class=\"ai-artifact\" data-ai-artifact-id=#{artifact.id}></div>" if artifact "<div class=\"ai-artifact\" data-ai-artifact-id=\"#{artifact.id}\"></div>" if artifact
content = [] content = []
@ -103,10 +103,14 @@ module DiscourseAi
content << [:js, "### JavaScript\n\n```javascript\n#{js}\n```"] if js.present? content << [:js, "### JavaScript\n\n```javascript\n#{js}\n```"] if js.present?
content << [:preview, "### Preview\n\n#{artifact_div}"] if artifact_div
content.sort_by! { |c| c[0] === @selected_tab ? 1 : 0 } if !artifact content.sort_by! { |c| c[0] === @selected_tab ? 1 : 0 } if !artifact
if artifact
content.unshift([nil, "[details='#{I18n.t("discourse_ai.ai_artifact.view_source")}']"])
content << [nil, "[/details]"]
end
content << [:preview, "### Preview\n\n#{artifact_div}"] if artifact_div
self.custom_raw = content.map { |c| c[1] }.join("\n\n") self.custom_raw = content.map { |c| c[1] }.join("\n\n")
end end

View File

@ -4,10 +4,46 @@ RSpec.describe DiscourseAi::AiBot::Tools::CreateArtifact do
fab!(:llm_model) fab!(:llm_model)
let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) }
let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") }
fab!(:post)
before { SiteSetting.ai_bot_enabled = true } before { SiteSetting.ai_bot_enabled = true }
describe "#process" do describe "#process" do
it "correctly adds details block on final invoke" do
tool =
described_class.new(
{ html_body: "hello" },
bot_user: Fabricate(:user),
llm: llm,
context: {
post_id: post.id,
},
)
tool.parameters = { html_body: "hello" }
tool.invoke {}
artifact_id = AiArtifact.order("id desc").limit(1).pluck(:id).first
expected = <<~MD
[details='View Source']
### HTML
```html
hello
```
[/details]
### Preview
<div class="ai-artifact" data-ai-artifact-id="#{artifact_id}"></div>
MD
expect(tool.custom_raw.strip).to eq(expected.strip)
end
it "can correctly handle partial updates" do it "can correctly handle partial updates" do
tool = described_class.new({}, bot_user: bot_user, llm: llm) tool = described_class.new({}, bot_user: bot_user, llm: llm)

View File

@ -67,11 +67,12 @@ RSpec.describe DiscourseAi::AiBot::ArtifactsController do
end end
end end
it "removes security headers" do it "removes security headers and disables crawling" do
sign_in(user) sign_in(user)
get "/discourse-ai/ai-bot/artifacts/#{artifact.id}" get "/discourse-ai/ai-bot/artifacts/#{artifact.id}"
expect(response.headers["X-Frame-Options"]).to eq(nil) expect(response.headers["X-Frame-Options"]).to eq(nil)
expect(response.headers["Content-Security-Policy"]).to eq("script-src 'unsafe-inline';") expect(response.headers["Content-Security-Policy"]).to eq("script-src 'unsafe-inline';")
expect(response.headers["X-Robots-Tag"]).to eq("noindex")
end end
end end
end end