From 1ea7b8a32d7d0dee95e7d34d3b4d5424faa71df8 Mon Sep 17 00:00:00 2001 From: Alice Alexandra Moore <86723305+3mdistal@users.noreply.github.com> Date: Mon, 3 Aug 2026 14:01:21 -0400 Subject: [PATCH 1/4] Add compact Content text style selector --- .../components/editor/BubbleToolbar.test.tsx | 74 ++++++- .../app/components/editor/BubbleToolbar.tsx | 208 +++++++++++++++--- ...ing-from-one-compact-formatting-control.md | 6 + .../evidence/content-text-selector/README.md | 26 +++ .../evidence/content-text-selector/after.png | Bin 0 -> 36818 bytes .../evidence/content-text-selector/before.png | Bin 0 -> 32106 bytes 6 files changed, 283 insertions(+), 31 deletions(-) create mode 100644 templates/content/changelog/2026-08-03-choose-text-or-a-heading-from-one-compact-formatting-control.md create mode 100644 templates/content/docs/solutions/evidence/content-text-selector/README.md create mode 100644 templates/content/docs/solutions/evidence/content-text-selector/after.png create mode 100644 templates/content/docs/solutions/evidence/content-text-selector/before.png diff --git a/templates/content/app/components/editor/BubbleToolbar.test.tsx b/templates/content/app/components/editor/BubbleToolbar.test.tsx index 08ee68c75d..974621e666 100644 --- a/templates/content/app/components/editor/BubbleToolbar.test.tsx +++ b/templates/content/app/components/editor/BubbleToolbar.test.tsx @@ -34,7 +34,15 @@ vi.mock("@/components/ui/tooltip", () => ({ TooltipTrigger: ({ children }: { children: ReactNode }) => children, })); -describe("BubbleToolbar link shortcut", () => { +vi.mock("@/components/ui/popover", () => ({ + Popover: ({ children }: { children: ReactNode }) => children, + PopoverTrigger: ({ children }: { children: ReactNode }) => children, + PopoverContent: ({ children }: { children: ReactNode }) => ( +
Selected heading
"); + expect(editor.getHTML()).not.toContain("Stable heading
"); + }); }); diff --git a/templates/content/app/components/editor/BubbleToolbar.tsx b/templates/content/app/components/editor/BubbleToolbar.tsx index 2cccf27e0f..be24c56c5c 100644 --- a/templates/content/app/components/editor/BubbleToolbar.tsx +++ b/templates/content/app/components/editor/BubbleToolbar.tsx @@ -1,15 +1,13 @@ import { useT } from "@agent-native/core/client/i18n"; import { IconBold, + IconCheck, + IconChevronDown, IconItalic, IconStrikethrough, IconCode, IconLink, IconMessageCircle, - IconH1, - IconH2, - IconH3, - IconH4, } from "@tabler/icons-react"; import { NodeSelection, @@ -20,8 +18,13 @@ import { import { Decoration, DecorationSet } from "@tiptap/pm/view"; import type { Editor } from "@tiptap/react"; import { BubbleMenu } from "@tiptap/react/menus"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; import { Tooltip, TooltipContent, @@ -56,6 +59,15 @@ type SelectionFillRange = { to: number; }; +type TextStyle = "paragraph" | 1 | 2 | 3 | 4; + +function activeTextStyle(editor: Editor): TextStyle { + for (const level of [1, 2, 3, 4] as const) { + if (editor.isActive("heading", { level })) return level; + } + return "paragraph"; +} + const selectionFillPluginKey = new PluginKey+I8VNT8&6j6oX{u3MAjkUuGiJTP;o@de`-5VomO&H3L^I
zYJsx?1WMHxt-#|16A};*?6uA<{cO=t;c+qT346W`WIe^{H)sAd6~(&i^mLdW%P9_)
zD+$7MhPH`1F`WL!GQSjCRbF8ih#AFPMwa8Y5kvLvVJt;Yx&T4!lUrrbwxU(sF9*U(
zTf0u1THl=wo=WSnfNbF^>Wj=Ipcw>s#}?x?K`o4a0JHq#b#>fI5r{X)J?#l6@zmY$
zG4n9?q`QooH=&c*81fRm9maR?_it2|dmavq1oLR@yTh)oPHkCa4BauG&~8yq+Kj|e
z((?*4zFj_GvYELHP*FCzJpO+75(Kb{q_^D5OwIj#SRKc@+w~xP!xw0M8z|Y}H0?ad
zUN=k*CuU-LAk?88Cj_aQZg4>c^lC{P5Rk+YH?aXXXk>S89w<@PM<3nRqV2h s_)9d}EWM-S4qU7$*jd>EET=dep=
zspF)`taN=^XJE_dZ0bg(I}x&MfyypA4_eC96ogp-%C}!W90Uh~Q *AkRYg;lv?n85~C?~eUID{Eu`ywth~j+EQ6&MIQjIMgk63-j5B4`<)8iF*6#
znI{o~e9yUVjtB{bqntTdP&{L$5`)Rz{#8}FWyHL87B*6Uyq&Q>=s?+}w*~2ARqaVq
zl#5Z>O656IE(gN`#Az~{1BVgQzZ+S*oDy=G+l+f7{nnWg*3kq`lEPgAMEiEg%vLg|
zryj~n`;>1d9$L#(N^ 3)zz4t3wNFwBXm2+p=<+7BRQjErTB-~}J
z=+G)#xB|#3olE>t8LXD|W4@rGE}Svo5*OKw;Cj}w=re_UGngJKgHDg^dGD_q>qn!^
ze|_l7Pd@|u)x$&R&6{u8MuHHP!GISgS_|^ZXY$7TZRPo?qB<@(U=&-{t*pw0!nK9M
zQoqqL_Xr+&Yp#mC{8rcI`>Du?_0Sh4X4_}4z1v`Ed{&d|yFm&&Ft7eX1w9&T+{$bw
zx`<58(e&7cCQ$kZxu)0Ty3B-MPxj;PsmuL@{xJSdHf<^=sCLhM#%&7)@Q@-`u
z&!dgb%J}8{pa}WXkc`0Q!9kiDtvv^(T?SQD9j&nH&}N5`M|PMqX3eTP8`;66+l;2m
zUaKA^0JtRmGVZ
)}~aV1hBf&w)Lkm@s@MKf3n_Zs5>NxdQ?
z9d&qiX*ESD8A6dNot9=6Hr?=Q6i85)WHBKJxOx974>LGP*dY|}Tcei>x;Z6tx4#L7
zpmL23WWU)}9|DT7GbTUE1xjI}Gmh3v)&xL^X{c<@)P$?e=G$%eM3lDljSZYu68567
zUGQFQ6|Ldsl$-G(i%Kgn=pZsjVbbmC69o3er}@cSXXle9Mi&K$1AP!!h=w*HDBO&8
z9g`{tmmE*brRvV&mV8DMxk#yCps|M49DzV>X}
zjACYFq|8d5zkBR)dJ@sC6H>|>n4Z5$9C(gx+P4o6JYWEUe&0Tx3U*5aw
z%vh2Hh$sXEbhWxfdplHSsv$kd2Io--5E_2@K@(sxT*ZxU;!x?i_=%~fpj?j0+pl6g
zr~l)<-O&*}jNI^ado?6){mVD#Z{7ZTah3n2{vDR2`KFkRg$EE3n8U7KO2Yof`p?Jx
zY!|++vE^<>Bz>_jINe>2rYORB^xDc;5cuW*V|_k#?$)X7Q{7yv6!n8{-l49`SBPAG
zvLw({ihN$6H?{e-;#ECAd*zIsaPTC@N6-Fh@KraZsd&`{|K)oAwvQmz?}wHj)g=0X
z0$^u+EfMUMcX+*uXo5oB=2AdmF1GD%
zDIi)z7SP+)72{45pJ&T=t=7jWIScTIF5&Fac5P9tU)1yGIzBS#@Z!7%XYgqASyzV-
zpe#;_`4`O<*@p)M2!74!A|y&xYAt>;y-W&|C-td6Zs}_0Nh!yCZX^tBgc%Ql3TX_y
zMQ{qa76#8<#cS+Kel|C>ernx)lx<_=HlJ#c3(Qd#i1xKY9k(=LJT{Yge}82RtyzU?
z>=xb`I%z$st@)18GOwanNuG$h!8uL}xm1lqY_47gv9?@gR0Mx}j-8as?YrH?WhbO6
zy28{9YX4XvwJ5f*fwnY`sr2g)HuHNrJJWXTuyrKEYyC8JAoWP_o_f%|bq8CS=#a%6
z;ig4l?iro6c_)d=&g;b)eX&w-N+QP9j6MK>=Up~+&&jP8+xV_e<{}1SrxLPD?L#QF
z-48hZ@|Mo*MdEQ^^N7OIla5qYYqbt(<|c+(WNAK{{jFb%v7?^o3a4{93RP%i6>d{*
zW(tvMz62l7zm5hs{N>2~2Oi%4^RB-~4&}es`Z{*?HvZ)(WS_b@dAYrQw2E;}y9)X#
zj;kO1M~|_?J)93bbOqqh)RI^_3nrI!nLUq*v%2pKC}KriAhHriKUjpR-HIC+Xp4iArog}Dk^W^I=W`KaKV;;u>z6e$hX)a6|`vL@GF~N{n`ccBE2=IJDS;B
zAC%MogtZb;Q{bCfZ_Ub(1|3nV%;6HBUCBm8Z2$eS6l3*d>e`m%y)2Kg(1*j^7<-+&
zJqKTeW-=%wJ2C;U$)1u8u;jV!%K7DoBj!4{P4hX;^q0fkI6`{@b(V^z+2ScCF=WCO
z+OK(+-b48LzRVCtOE=!Xd{?jPm