From cea47f10e2e74f7a9371220520092bd9b25eebd8 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Mon, 22 Jun 2026 11:17:03 +0200 Subject: [PATCH] feat: ccloseout of changes for audit --- lib/bds/ai/openai_compatible_runtime.ex | 26 +++------ lib/bds/desktop/shell_live/chat_editor.ex | 7 ++- lib/bds/desktop/shell_live/misc_editor.ex | 7 ++- lib/bds/desktop/shell_live/sidebar_events.ex | 55 ++++++-------------- lib/bds/rendering/filters.ex | 11 ++-- test/bds/desktop/shell_live_test.exs | 12 +++++ test/bds/gallery_pipeline_test.exs | 31 +++++++++++ 7 files changed, 85 insertions(+), 64 deletions(-) diff --git a/lib/bds/ai/openai_compatible_runtime.ex b/lib/bds/ai/openai_compatible_runtime.ex index 97296a0..ffabc44 100644 --- a/lib/bds/ai/openai_compatible_runtime.ex +++ b/lib/bds/ai/openai_compatible_runtime.ex @@ -35,9 +35,11 @@ defmodule BDS.AI.OpenAICompatibleRuntime do end defp generate_blocking(url, headers, payload, request) do - payload_json = encode_payload(payload) + payload_json = Jason.encode!(payload) - log_request(:blocking, url, request, payload, payload_json) + Logger.debug( + "AI OpenAI-compatible request operation=#{inspect(Map.get(request, :operation))} model=#{inspect(request.model)} url=#{url} tools=#{payload |> Map.get("tools", []) |> length()} payload_size=#{byte_size(payload_json)}" + ) case HttpClient.post(url, headers, payload_json) do {:ok, %{status: 200, body: body}} -> @@ -75,9 +77,11 @@ defmodule BDS.AI.OpenAICompatibleRuntime do # snapshots to `on_stream` as they arrive. The assembled message goes # through the same normalization as the blocking path. defp generate_streaming(url, headers, payload, request, on_stream) do - payload_json = payload |> streaming_payload() |> encode_payload() + payload_json = payload |> streaming_payload() |> Jason.encode!() - log_request(:streaming, url, request, payload, payload_json) + Logger.debug( + "AI OpenAI-compatible streaming request operation=#{inspect(Map.get(request, :operation))} model=#{inspect(request.model)} url=#{url} payload_size=#{byte_size(payload_json)}" + ) sse = SSE.new(on_stream, emit_interval_ms: stream_emit_interval_ms()) @@ -234,20 +238,6 @@ defmodule BDS.AI.OpenAICompatibleRuntime do |> Map.put("stream_options", %{"include_usage" => true}) end - defp encode_payload(payload), do: Jason.encode!(payload) - - defp log_request(mode, url, request, payload, payload_json) do - tools_segment = - case mode do - :blocking -> " tools=#{payload |> Map.get("tools", []) |> length()}" - :streaming -> "" - end - - Logger.debug( - "AI OpenAI-compatible#{if mode == :streaming, do: " streaming", else: ""} request operation=#{inspect(Map.get(request, :operation))} model=#{inspect(request.model)} url=#{url}#{tools_segment} payload_size=#{byte_size(payload_json)}" - ) - end - defp log_http_status_error(prefix, status, body) do Logger.error("#{prefix} HTTP error status=#{status} body=#{String.slice(body, 0, 2000)}") end diff --git a/lib/bds/desktop/shell_live/chat_editor.ex b/lib/bds/desktop/shell_live/chat_editor.ex index 0ae78ea..c4ee88c 100644 --- a/lib/bds/desktop/shell_live/chat_editor.ex +++ b/lib/bds/desktop/shell_live/chat_editor.ex @@ -545,8 +545,13 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do @spec markdown_html(binary()) :: Phoenix.HTML.Safe.t() def markdown_html(content) when is_binary(content) do + # Match Earmark's defaults (GFM tables, strikethrough, autolinks); escape + # raw HTML to text rather than rendering it, as the old escape: true did. html = - case MDEx.to_html(content) do + case MDEx.to_html(content, + extension: [table: true, strikethrough: true, autolink: true], + render: [escape: true] + ) do {:ok, rendered} -> rendered {:error, _reason} -> "" end diff --git a/lib/bds/desktop/shell_live/misc_editor.ex b/lib/bds/desktop/shell_live/misc_editor.ex index 03ea60b..5b8eaf2 100644 --- a/lib/bds/desktop/shell_live/misc_editor.ex +++ b/lib/bds/desktop/shell_live/misc_editor.ex @@ -278,8 +278,13 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do @spec markdown_html(String.t()) :: Phoenix.HTML.safe() def markdown_html(content) do + # Match Earmark's defaults (GFM tables, strikethrough, autolinks); escape + # raw HTML to text rather than rendering it, as the old escape: true did. html = - case MDEx.to_html(content || "") do + case MDEx.to_html(content || "", + extension: [table: true, strikethrough: true, autolink: true], + render: [escape: true] + ) do {:ok, rendered} -> rendered {:error, _reason} -> "" end diff --git a/lib/bds/desktop/shell_live/sidebar_events.ex b/lib/bds/desktop/shell_live/sidebar_events.ex index 2b5eb0d..fe2b573 100644 --- a/lib/bds/desktop/shell_live/sidebar_events.ex +++ b/lib/bds/desktop/shell_live/sidebar_events.ex @@ -3,24 +3,6 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do alias BDS.Desktop.ShellLive.SidebarState, as: ShellSidebarState - @event_handlers %{ - "toggle_sidebar_filters" => :toggle_sidebar_filters, - "toggle_sidebar_archive" => :toggle_sidebar_archive, - "toggle_sidebar_tags" => :toggle_sidebar_tags, - "toggle_sidebar_categories" => :toggle_sidebar_categories, - "update_sidebar_search" => :update_sidebar_search, - "clear_sidebar_search" => :clear_sidebar_search, - "clear_sidebar_tags" => :clear_sidebar_tags, - "clear_sidebar_categories" => :clear_sidebar_categories, - "toggle_sidebar_tag" => :toggle_sidebar_tag, - "toggle_sidebar_category" => :toggle_sidebar_category, - "select_sidebar_year" => :select_sidebar_year, - "select_sidebar_month" => :select_sidebar_month, - "clear_sidebar_month" => :clear_sidebar_month, - "clear_sidebar_filters" => :clear_sidebar_filters, - "load_more_sidebar" => :load_more_sidebar - } - @spec handle(Phoenix.LiveView.Socket.t(), String.t(), map(), (Phoenix.LiveView.Socket.t(), term() -> Phoenix.LiveView.Socket.t())) :: @@ -29,14 +11,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do {:noreply, dispatch(event, socket, params, reload)} end - defp dispatch(event, socket, params, reload) do - case Map.fetch(@event_handlers, event) do - {:ok, handler} -> handle_event(handler, socket, params, reload) - :error -> socket - end - end - - defp handle_event(:toggle_sidebar_filters, socket, _params, reload) do + defp dispatch("toggle_sidebar_filters", socket, _params, reload) do socket = ShellSidebarState.put_filter_panel_state(socket, fn state -> if state.visible do @@ -55,7 +30,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do reload.(socket, socket.assigns.workbench) end - defp handle_event(:toggle_sidebar_archive, socket, _params, reload) do + defp dispatch("toggle_sidebar_archive", socket, _params, reload) do socket |> ShellSidebarState.put_filter_panel_state(fn state -> %{state | archive_collapsed: not state.archive_collapsed} @@ -63,7 +38,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do |> reload.(socket.assigns.workbench) end - defp handle_event(:toggle_sidebar_tags, socket, _params, reload) do + defp dispatch("toggle_sidebar_tags", socket, _params, reload) do socket |> ShellSidebarState.put_filter_panel_state(fn state -> %{state | tags_collapsed: not state.tags_collapsed} @@ -71,7 +46,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do |> reload.(socket.assigns.workbench) end - defp handle_event(:toggle_sidebar_categories, socket, _params, reload) do + defp dispatch("toggle_sidebar_categories", socket, _params, reload) do socket |> ShellSidebarState.put_filter_panel_state(fn state -> %{state | categories_collapsed: not state.categories_collapsed} @@ -79,7 +54,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do |> reload.(socket.assigns.workbench) end - defp handle_event(:update_sidebar_search, socket, %{"sidebar_filters" => params}, reload) do + defp dispatch("update_sidebar_search", socket, %{"sidebar_filters" => params}, reload) do socket |> ShellSidebarState.put_filters(fn filters -> Map.put( @@ -91,25 +66,25 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do |> reload.(socket.assigns.workbench) end - defp handle_event(:clear_sidebar_search, socket, _params, reload) do + defp dispatch("clear_sidebar_search", socket, _params, reload) do socket |> ShellSidebarState.put_filters(fn filters -> Map.put(filters, :search, nil) end) |> reload.(socket.assigns.workbench) end - defp handle_event(:clear_sidebar_tags, socket, _params, reload) do + defp dispatch("clear_sidebar_tags", socket, _params, reload) do socket |> ShellSidebarState.put_filters(fn filters -> Map.put(filters, :tags, []) end) |> reload.(socket.assigns.workbench) end - defp handle_event(:clear_sidebar_categories, socket, _params, reload) do + defp dispatch("clear_sidebar_categories", socket, _params, reload) do socket |> ShellSidebarState.put_filters(fn filters -> Map.put(filters, :categories, []) end) |> reload.(socket.assigns.workbench) end - defp handle_event(:toggle_sidebar_tag, socket, %{"tag" => tag}, reload) do + defp dispatch("toggle_sidebar_tag", socket, %{"tag" => tag}, reload) do socket |> ShellSidebarState.put_filters(fn filters -> ShellSidebarState.toggle_filter_value(filters, :tags, tag) @@ -117,7 +92,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do |> reload.(socket.assigns.workbench) end - defp handle_event(:toggle_sidebar_category, socket, %{"category" => category}, reload) do + defp dispatch("toggle_sidebar_category", socket, %{"category" => category}, reload) do socket |> ShellSidebarState.put_filters(fn filters -> ShellSidebarState.toggle_filter_value(filters, :categories, category) @@ -125,7 +100,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do |> reload.(socket.assigns.workbench) end - defp handle_event(:select_sidebar_year, socket, %{"year" => year}, reload) do + defp dispatch("select_sidebar_year", socket, %{"year" => year}, reload) do parsed_year = ShellSidebarState.parse_optional_integer(year) socket @@ -144,7 +119,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do |> reload.(socket.assigns.workbench) end - defp handle_event(:select_sidebar_month, socket, %{"year" => year, "month" => month}, reload) do + defp dispatch("select_sidebar_month", socket, %{"year" => year, "month" => month}, reload) do socket |> ShellSidebarState.put_filter_panel_state(fn state -> %{ @@ -161,7 +136,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do |> reload.(socket.assigns.workbench) end - defp handle_event(:clear_sidebar_month, socket, _params, reload) do + defp dispatch("clear_sidebar_month", socket, _params, reload) do socket |> ShellSidebarState.put_filter_panel_state(fn state -> %{state | archive_collapsed: false} @@ -172,7 +147,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do |> reload.(socket.assigns.workbench) end - defp handle_event(:clear_sidebar_filters, socket, _params, reload) do + defp dispatch("clear_sidebar_filters", socket, _params, reload) do socket |> ShellSidebarState.put_filters(fn filters -> filters @@ -189,7 +164,7 @@ defmodule BDS.Desktop.ShellLive.SidebarEvents do |> reload.(socket.assigns.workbench) end - defp handle_event(:load_more_sidebar, socket, _params, reload) do + defp dispatch("load_more_sidebar", socket, _params, reload) do socket |> ShellSidebarState.put_filters(fn filters -> Map.update( diff --git a/lib/bds/rendering/filters.ex b/lib/bds/rendering/filters.ex index 10546ee..42244cb 100644 --- a/lib/bds/rendering/filters.ex +++ b/lib/bds/rendering/filters.ex @@ -180,10 +180,13 @@ defmodule BDS.Rendering.Filters do end defp render_markdown_html(markdown) do - # Macros above inject raw HTML (gallery, video embeds), so the render path - # must pass HTML through untouched. unsafe: true matches the prior Earmark - # no-escape behavior. - case MDEx.to_html(markdown, render: [unsafe: true]) do + # Match Earmark's defaults: GFM tables, strikethrough and bare-URL autolinks + # were all on. Macros above inject raw HTML (gallery, video embeds), so + # unsafe: true keeps that HTML untouched as Earmark's no-escape path did. + case MDEx.to_html(markdown, + extension: [table: true, strikethrough: true, autolink: true], + render: [unsafe: true] + ) do {:ok, html} -> html {:error, _reason} -> markdown end diff --git a/test/bds/desktop/shell_live_test.exs b/test/bds/desktop/shell_live_test.exs index db365b9..eb0b4bc 100644 --- a/test/bds/desktop/shell_live_test.exs +++ b/test/bds/desktop/shell_live_test.exs @@ -5465,6 +5465,18 @@ defmodule BDS.Desktop.ShellLiveTest do refute html =~ ~r/alert(1)" + ) + + assert html =~ "old" + assert html =~ "" + refute html =~ "