@brainfish-ai/components 0.34.1 → 0.34.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainfish-ai/components",
3
- "version": "0.34.1",
3
+ "version": "0.34.3",
4
4
  "description": "Brainfish Components",
5
5
  "license": "MIT",
6
6
  "main": "./dist/esm/index.js",
@@ -1,25 +0,0 @@
1
- import*as e from"react";import{ScrollArea as t}from"../components/ui/scroll-area.js";import{c as a}from"./utils.C6Qu-kwd.js";import{ArrowBendUpLeft as r,X as n,MagnifyingGlass as i,Folder as l,FolderOpen as s,FolderDashed as o,DotsThree as c,Plus as d,FileDashed as m,File as u,Lightning as f,Article as p}from"@phosphor-icons/react";import{B as h}from"./button.Cs1X8upM.js";import{useToggle as g,useDebounceValue as v}from"usehooks-ts";import{Input as b}from"../components/ui/input.js";import{TreeStateContext as x,Button as E,useTreeData as y,useDragAndDrop as A,DropIndicator as k,Tree as S,TreeItem as N,TreeItemContent as I,Collection as w}from"react-aria-components";import{ButtonGroup as C}from"../components/ui/button-group.js";import{Spinner as M}from"../components/ui/spinner.js";import{u as z}from"./hooks.BQTKhHSv.js";const L=e.createContext(null);function j({collapsed:t,ItemLinkComponent:a,activeArticleId:r,onSelectArticle:n,onExpandArticle:i,onSearchArticles:l,onAddArticle:s,onMoreActions:o,canMoveArticle:c,onMoveArticle:d,children:m}){const[u,f]=e.useState(!1),[p,h]=e.useState(""),[g,v]=e.useState(null),b=e.useCallback(e=>{const t=e.trim();f(""!==t),l?.(e)},[l]),x=e.useMemo(()=>({collapsed:t,ItemLinkComponent:a,activeArticleId:r,onSearchArticles:l,onSelectArticle:n,onExpandArticle:i,onAddArticle:s,onMoreActions:o,canMoveArticle:c,onMoveArticle:d,isSearching:u,searchValue:p,setSearchValue:h,lastSelectedArticleId:g,setLastSelectedArticleId:v,handleSearchArticles:b}),[u,p,g,t,a,r,n,i,l,s,o,c,d,b]);/* @__PURE__ */
2
- return e.createElement(L.Provider,{value:x},m)}function B(){const t=e.useContext(L);if(!t)throw new Error("useSidebar must be used within SidebarProvider");return t}function P({className:t,items:r,activeId:n}){const{ItemLinkComponent:i}=B();/* @__PURE__ */
3
- return e.createElement("nav",{className:a("flex flex-col",t),"aria-label":"App section navigation"},r.map(t=>/* @__PURE__ */e.createElement(i,{key:t.id,href:t.href,"data-sidebar-item-id":t.id,"data-sidebar-item-type":"section","data-sidebar-section-nav-item-selected":t.id===n,className:a("flex p-2 mx-4 text-sm text-default relative rounded-lg","after:transition-opacity after:absolute after:top-0 after:bottom-0 after:-right-4 after:w-2 after:bg-primary after:rounded-l-md after:opacity-0","hover:bg-dark-200 focus:bg-dark-200 hover:text-accent-foreground focus:text-accent-foreground",t.id===n&&"font-bold after:opacity-100")},t.label)))}function $({className:n,items:i,activeId:l,showBack:s,onBack:o,sectionNavItems:c,sectionNavActiveId:d}){const{collapsed:m,ItemLinkComponent:u}=B(),f=!!s&&!!o;/* @__PURE__ */
4
- return e.createElement("nav",{className:a("flex flex-col gap-2",n),"aria-label":"App navigation"},f&&/* @__PURE__ */e.createElement(h,{variant:"link",className:a("py-2 text-base text-default w-full mx-4",m?"justify-center":"justify-start"),size:"icon",onClick:o},
5
- /* @__PURE__ */e.createElement(r,{"aria-hidden":"true"}),
6
- /* @__PURE__ */e.createElement("span",{className:a(m&&"sr-only")},"back to Brainfish")),/* @__PURE__ */e.createElement(t,{className:"min-h-0 max-h-[calc(100dvh-var(--header-nav-height)-5rem)]"},/* @__PURE__ */e.createElement("div",{className:"flex flex-col gap-2 mx-4"},i?.map(t=>/* @__PURE__ */e.createElement(e.Fragment,{key:t.id},/* @__PURE__ */e.createElement(u,{className:a("flex p-2 rounded-lg gap-1 text-base text-default items-center",t.id===l?"bg-primary text-primary-foreground font-bold hover:text-primary-foreground focus:text-primary-foreground":"hover:bg-dark-200 focus:bg-dark-200 hover:text-accent-foreground focus:text-accent-foreground"),href:t.href,"data-sidebar-item-id":t.id,"data-sidebar-item-type":"app","data-sidebar-app-nav-item-selected":t.id===l},t.Icon?/* @__PURE__ */e.createElement(t.Icon,{weight:t.id===l?"fill":"regular",size:16}):null,
7
- /* @__PURE__ */e.createElement("span",{className:a("transition-opacity duration-200",m&&"opacity-0 sr-only")},t.label)),t.id===l&&c&&c.length>0&&!m&&/* @__PURE__ */e.createElement(P,{className:a("flex-shrink-0"),items:c,activeId:d}))))))}function K(){const{handleSearchArticles:t,searchValue:a,setSearchValue:r}=B(),[l,,s]=g(!1),o=e.useRef(null);return e.useEffect(()=>{l&&t?.(a)},[a,t,l]),/* @__PURE__ */e.createElement("div",{className:"mb-2"},/* @__PURE__ */e.createElement(b,{ref:o,className:"h-auto text-subtlest border-border-subtle",startIcon:i,endIcon:a?n:void 0,onEndIconClick:()=>{r(""),t?.("")},value:a,onChange:e=>{const a=e.target.value;l||s(!0),r(a),""===a.trim()&&(t(""),s(!1))},placeholder:"Find an article","aria-label":"Find an article",type:"search"}))}function V({children:t}){/* @__PURE__ */
8
- return e.createElement(E,{slot:"chevron",className:"relative z-30 flex p-0"},/* @__PURE__ */e.createElement("span",{className:"relative inline-flex size-3 items-center justify-center"},t))}function D({item:t,hasChildItems:r,isSelected:n,isExpanded:i,ItemLinkComponent:p,onAddArticle:g,isAddingArticle:v,isLoadingChildren:b,onMoreActions:y,level:A}){const{isSearching:k}=B(),S=e.useContext(x),N=Boolean(n),I=!!(g&&!v||y),w="draft"===t.value.status;let z;switch(t.value.status){case"draft":z="(draft)";break;case"unpublished":z="(unpublished changes)";break;default:z=""}const L=/* @__PURE__ */e.createElement(V,null,/* @__PURE__ */e.createElement(l,{"aria-hidden":"true",weight:"fill",className:a("absolute size-3 transition-opacity duration-200 ease-out",i?"opacity-0":"opacity-100")}),/* @__PURE__ */e.createElement(s,{"aria-hidden":"true",weight:"fill",className:a("absolute size-3 transition-opacity duration-200 ease-out",i?"opacity-100":"opacity-0")})),j=/* @__PURE__ */e.createElement(V,null,/* @__PURE__ */e.createElement(o,{"aria-hidden":"true",weight:"fill",className:a("absolute size-3 transition-opacity duration-200 ease-out",i?"opacity-0":"opacity-100")}),/* @__PURE__ */e.createElement(s,{"aria-hidden":"true",weight:"fill",className:a("absolute size-3 transition-opacity duration-200 ease-out",i?"opacity-100":"opacity-0")})),P=/* @__PURE__ */e.createElement(e.Fragment,null,/* @__PURE__ */e.createElement("span",{className:"sr-only"},"Knowledge Discovery"),/* @__PURE__ */e.createElement(f,{"aria-hidden":"true",weight:"fill",className:"size-3 shrink-0 text-blue-600"})),$=r?/* @__PURE__ */e.createElement(V,null,/* @__PURE__ */e.createElement("span",{className:"text-blue-600"},P)):/* @__PURE__ */e.createElement("span",{className:"flex size-3 shrink-0 items-center justify-center text-blue-600"},P),K=/* @__PURE__ */e.createElement("span",{className:"flex size-3 shrink-0 items-center justify-center"},/* @__PURE__ */e.createElement(u,{"aria-hidden":"true",className:"size-3"})),D=/* @__PURE__ */e.createElement("span",{className:"flex size-3 shrink-0 items-center justify-center"},/* @__PURE__ */e.createElement("span",{className:"sr-only"},"Unpublished Changes"),/* @__PURE__ */e.createElement(m,{"aria-hidden":"true",className:"size-3"})),F=/* @__PURE__ */e.createElement("span",{className:"flex size-3 shrink-0 items-center justify-center",role:"status","aria-label":`Loading nested articles for ${t.value.label}`},
9
- /* @__PURE__ */e.createElement(M,{"aria-hidden":"true",className:"size-3 text-current"}),
10
- /* @__PURE__ */e.createElement("span",{className:"sr-only"},"Loading nested articles for ",t.value.label));/* @__PURE__ */
11
- return e.createElement("div",{style:{"--line-offset":"0.875rem"},"aria-busy":b||void 0,className:a("group relative flex items-center gap-1 rounded-md py-1 pl-1 ml-[calc((var(--tree-item-level)-1)*var(--line-offset))] pr-4 h-9",w&&"text-subtlest italic",n?"bg-lime-100 text-default font-bold after:absolute after:-right-0.5 after:top-1/2 after:h-1/2 after:w-1 after:-translate-y-1/2 after:bg-lime-400":"hover:bg-dark-200 hover:text-accent-foreground")},Array.from({length:A-1},(a,r)=>/* @__PURE__ */e.createElement("span",{key:`${t.value.label}-${r+1}`,style:{"--item-line-level":r+1},className:"absolute w-[1px] bg-dark-400 -top-2 h-9 left-[calc(-4px-var(--line-offset)*(var(--item-line-level)-1))]","aria-hidden":!0})),
12
- /* @__PURE__ */e.createElement(E,{slot:"drag",className:"sr-only"},"Drag ",t.value.label),k?null:b?F:((e,t)=>{if("discovery"===e)return $;const a="unpublished"===e||"draft"===e;return t?a?j:L:a?D:K})(t.value.status,r),
13
- /* @__PURE__ */e.createElement(p,{href:t.value.href,title:`${t.value.label} ${z}`,"data-sidebar-item-id":t.value.id,"data-sidebar-item-type":"article",className:a("min-w-0 text-inherit text-sm truncate flex-1 after:absolute after:inset-0 after:z-10 pr-1",n&&"text-default"),onClick:e=>{N&&(e.preventDefault(),e.stopPropagation()),r&&S&&S.toggleKey(t.key)}},t.value.label),I&&/* @__PURE__ */e.createElement(C,{orientation:"horizontal",className:a("absolute h-4 my-auto right-0.5 top-0 bottom-0 z-20 hidden items-center rounded-xl","group-hover:flex group-focus-within:flex",n?"bg-lime-100 text-default":"bg-dark-200")},y&&/* @__PURE__ */e.createElement(h,{variant:"ghost",size:"icon","aria-label":`More actions for ${t.value.label}`,className:"size-4 [&_svg]:size-3.5",onClick:e=>{e.stopPropagation(),y?.(t.value.id)}},
14
- /* @__PURE__ */e.createElement(c,{"aria-hidden":"true",weight:"bold"})),g&&!v&&/* @__PURE__ */e.createElement(h,{variant:"ghost",size:"icon","aria-label":`Add article to ${t.value.label}`,className:"size-4 [&_svg]:size-3.5",onClick:e=>{e.stopPropagation(),g?.(t.value.id)}},
15
- /* @__PURE__ */e.createElement(d,{"aria-hidden":"true"}))))}function F({articles:t}){const{ItemLinkComponent:r,activeArticleId:n,handleSearchArticles:i,setSearchValue:l,setLastSelectedArticleId:s,onSelectArticle:o}=B(),c=e.useCallback(e=>{s(e),l(""),i?.(""),o?.(e)},[i,o,s,l]);return 0===t.length?null:/* @__PURE__ */e.createElement("ul",{"aria-label":"Search results",className:"flex flex-col list-none"},t.map(t=>{const i=t.id===n;/* @__PURE__ */
16
- return e.createElement("li",{key:t.id},/* @__PURE__ */e.createElement(r,{href:t.href,title:t.label,"data-sidebar-item-id":t.id,"data-sidebar-item-type":"article",onClick:()=>c(t.id),className:a("relative flex h-9 items-center rounded-md px-1 text-sm truncate",i?"bg-lime-100 font-bold text-primary-foreground after:absolute after:-right-0.5 after:top-1/2 after:h-1/2 after:w-1 after:-translate-y-1/2 after:bg-lime-400":"text-inherit hover:bg-dark-200 hover:text-accent-foreground")},t.label))}))}function _(e,t=null,a=0,r=[]){return e.forEach((e,n)=>{r.push({item:e,parentId:t,index:n,depth:a}),e.items?.length&&_(e.items,e.id,a+1,r)}),r}let R=0;function H(e,t,a=[]){for(const r of e){if(r.id===t)return a;if(r.items&&r.items.length>0){const e=H(r.items,t,[...a,r.id]);if(e)return e}}return null}function U(e){return e.map(e=>({...e.value,items:e.children?.length?U(e.children):e.value.items}))}function q({articles:t,isSearching:a,renderEmptyState:r}){const{ItemLinkComponent:n,activeArticleId:i,lastSelectedArticleId:l,setLastSelectedArticleId:s,onSelectArticle:o,onExpandArticle:c,canMoveArticle:d,onMoveArticle:m,onAddArticle:u,onMoreActions:f}=B(),p=i??l??void 0,h=y({initialItems:t,getKey:e=>e.id,getChildren:e=>e.items??[]}),g=e.useRef(t);e.useEffect(()=>{if(g.current===t)return;g.current=t;const e=function(e,t){const a=_(e),r=_(t),n=new Map(a.map(e=>[e.item.id,e])),i=new Map(r.map(e=>[e.item.id,e])),l=[];return a.filter(e=>!i.has(e.item.id)).sort((e,t)=>t.depth-e.depth).forEach(e=>{l.push({type:"remove",id:e.item.id})}),r.forEach(e=>{const t=n.get(e.item.id);var a,r;t?(t.parentId===e.parentId&&t.index===e.index||l.push({type:"move",id:e.item.id,parentId:e.parentId,index:e.index}),a=t.item,r=e.item,(a.label!==r.label||a.href!==r.href||a.status!==r.status||a.hasChildren!==r.hasChildren||a.isLoadingChildren!==r.isLoadingChildren)&&l.push({type:"update",id:e.item.id,item:e.item})):l.push({type:"insert",parentId:e.parentId,index:e.index,item:e.item})}),l}(U(h.items),t);0!==e.length&&e.forEach(e=>{switch(e.type){case"remove":h.remove(e.id);break;case"insert":h.insert(e.parentId,e.index,e.item);break;case"move":h.move(e.id,e.parentId,e.index);break;case"update":h.update(e.id,e.item)}})},[t,h]);const{dragAndDropHooks:v}=A({getItems:(e,t)=>t.map(e=>({"text/plain":e.value.label})),onMove:e=>{if(a)return;const t=Array.from(e.keys)[0];if(!t)return;const r=h.getItem(e.target.key);if(!r)return;let n,i;if("on"===e.target.dropPosition)n=e.target.key.toString(),i=r.children?.length??0;else{n=r.parentKey?.toString()??null;const t=(n?h.getItem(n)?.children??[]:h.items).findIndex(t=>t.key===e.target.key);i="before"===e.target.dropPosition?t:t+1}const l={id:t.toString(),parentId:n,index:i};if(d&&!d(l))return;const s=h.getItem(t),o=s?.parentKey?.toString()??null,c=(o?h.getItem(o)?.children??[]:h.items).findIndex(e=>e.key===t);"on"===e.target.dropPosition?h.move(t,n,i):"before"===e.target.dropPosition?h.moveBefore(e.target.key,e.keys):h.moveAfter(e.target.key,e.keys);const u=m?.(l);u&&"function"==typeof u.then&&u.catch(()=>{h.move(t,o,c)})},renderDropIndicator:t=>/* @__PURE__ */e.createElement(k,{target:t,className:"outline outline-1 outline-lime-200 ml-[calc(var(--tree-item-level)*0.5rem)]"})}),[b,x]=e.useState(/* @__PURE__ */new Set),E=e.useMemo(()=>null!=p?/* @__PURE__ */new Set([p]):b,[p,b]),C=e.useMemo(()=>{if(!p||a||!t.length)return[];return H(t,p)??[]},[t,p,a]),[M,z]=e.useState(/* @__PURE__ */new Set),L=e.useMemo(()=>/* @__PURE__ */new Set([...C,...M]),[C,M]);e.useEffect(()=>{i&&s(null)},[i,l,s]);const j=e.useRef(/* @__PURE__ */new Set),[P,$]=e.useState(/* @__PURE__ */new Set),K=e.useCallback(e=>{if(a||!u||j.current.has(e))return;j.current.add(e),$(new Set(j.current));const t="__placeholder_"+ ++R,r={id:t,label:"Untitled",href:"",status:"draft"};h.append(e,r),z(t=>/* @__PURE__ */new Set([...t,e])),u(e).then(a=>{h.remove(t),h.append(e,a),x(/* @__PURE__ */new Set([a.id])),o?.(a.id),j.current.delete(e),$(new Set(j.current))},()=>{h.remove(t),j.current.delete(e),$(new Set(j.current))})},[a,u,h,o]),V=e.useCallback(e=>{o?.(e),null==i&&x(/* @__PURE__ */new Set([e]))},[i,o]);return a?/* @__PURE__ */e.createElement(F,{articles:t}):/* @__PURE__ */e.createElement(S,{"aria-label":a?"Search results":"Articles tree",items:h.items,renderEmptyState:r,dragAndDropHooks:a?void 0:v,className:"flex flex-col",selectionBehavior:"toggle",selectionMode:"single",expandedKeys:L,onExpandedChange:e=>{(function(e,t){const a=new Set(Array.from(e,e=>e.toString()));return Array.from(t,e=>e.toString()).filter(e=>!a.has(e))})(L,e).forEach(e=>{c?.(e)}),z(e)},selectedKeys:E,disallowEmptySelection:!0,onSelectionChange:e=>{if("all"!==e){null==p&&x(e);const t=Array.from(e)?.[0];t&&V(t.toString())}}},function t(r){/* @__PURE__ */
17
- return e.createElement(N,{className:"list-none",textValue:r.value.label,id:r.value.id,hasChildItems:r.value.hasChildren},
18
- /* @__PURE__ */e.createElement(I,null,t=>/* @__PURE__ */e.createElement(D,{ItemLinkComponent:n,item:r,onAddArticle:!a&&u?K:void 0,isAddingArticle:!a&&P.has(r.value.id),isLoadingChildren:!a&&r.value.isLoadingChildren,onMoreActions:f?e=>f(e):void 0,...t})),!a&&r.children&&/* @__PURE__ */e.createElement(w,{items:r.children},t))})}const G={leading:!0,trailing:!0};function J({articles:t,className:r,isLoadingArticles:n}){const{isSearching:i,searchValue:l,onAddArticle:s}=B(),{contains:o}=z(),c=l.trim(),[d]=v(i?c:"",i?400:0,i?G:void 0),m=!i&&(n??void 0===t),u=e.useCallback(()=>/* @__PURE__ */e.createElement("div",{className:"flex min-h-20 items-center justify-center text-muted-foreground",role:"status","aria-live":"polite","aria-label":"Loading articles list"},
19
- /* @__PURE__ */e.createElement(M,{className:"size-5","aria-hidden":"true"}),
20
- /* @__PURE__ */e.createElement("span",{className:"sr-only"},"Loading articles list")),[]),f=!(m||i||t&&0!==t.length),g=e.useMemo(()=>{if(i){if(""===c||""===d)return[];const e=[],a=d,r=t=>{for(const n of t)o(n.label,a)&&e.push({...n,hasChildren:!1,items:[]}),n.items?.length&&r(n.items)};return r(t??[]),e}return t&&t.length>0?t:[]},[t,i,c,d,o]);/* @__PURE__ */
21
- return e.createElement("div",{className:a("flex flex-col gap-0.5 px-4",r)},f&&/* @__PURE__ */e.createElement(h,{variant:"ghost",className:"text-subtlest",onClick:()=>s?.(null).catch(()=>{})},/* @__PURE__ */e.createElement(p,{"aria-hidden":"true"}),"Create first article or folder"),m?/* @__PURE__ */e.createElement(q,{isSearching:i,articles:[],renderEmptyState:u}):/* @__PURE__ */e.createElement(e.Fragment,null,/* @__PURE__ */e.createElement(K,null),/* @__PURE__ */e.createElement("nav",{"aria-label":"Articles navigation"},/* @__PURE__ */e.createElement(q,{key:i?""===c?"search:empty":`search:${d}`:"browse",isSearching:i,articles:g??[]}))))}const O=e.forwardRef(function({className:r,ItemLinkComponent:n,collapsed:i,showAppBack:l,onAppBack:s,appNavItems:o,appNavActiveId:c,isLoadingArticles:d,sectionNavItems:m,sectionNavActiveId:u,activeArticleId:f,showArticles:p,articles:h,onSelectArticle:g,onExpandArticle:v,onSearchArticles:b,onAddArticle:x,onMoreActions:E,canMoveArticle:y,onMoveArticle:A,...k},S){/* @__PURE__ */
22
- return e.createElement(j,{collapsed:!!i,ItemLinkComponent:n,activeArticleId:f,onSelectArticle:g,onExpandArticle:v,onSearchArticles:b,onAddArticle:x,onMoreActions:E,canMoveArticle:y,onMoveArticle:A},
23
- /* @__PURE__ */e.createElement("aside",{ref:S,...k,className:a("py-4 transition-width duration-200 ease-in-out flex flex-col h-dvh",i?"w-[64px]":"w-[260px]",r),"aria-label":"Application navigation"},
24
- /* @__PURE__ */e.createElement($,{className:a("flex-shrink-0",o&&o.length>0&&"pb-4",!i&&"border-b border-border-subtle"),showBack:l,items:o,activeId:c,onBack:s,sectionNavItems:m,sectionNavActiveId:u}),!i&&/* @__PURE__ */e.createElement(e.Fragment,null,p&&/* @__PURE__ */e.createElement(t,null,/* @__PURE__ */e.createElement(J,{className:"py-4 max-w-[var(--left-nav-width,inherit)]",articles:h,isLoadingArticles:d})))))});export{O as S,B as u};
25
- //# sourceMappingURL=sidebar.BhcrMAU3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sidebar.BhcrMAU3.js","sources":["../../../src/layouts/sidebar/context.tsx","../../../src/layouts/sidebar/section-nav/section-nav.tsx","../../../src/layouts/sidebar/app-nav/app-nav.tsx","../../../src/layouts/sidebar/article-nav/article-search.tsx","../../../src/layouts/sidebar/article-nav/article-item.tsx","../../../src/layouts/sidebar/article-nav/article-search-results.tsx","../../../src/layouts/sidebar/article-nav/tree-sync.ts","../../../src/layouts/sidebar/article-nav/article-tree.tsx","../../../src/layouts/sidebar/article-nav/expansion.ts","../../../src/layouts/sidebar/article-nav/article-nav.tsx","../../../src/layouts/sidebar/sidebar.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { ArticleNavItem, ItemLinkComponent } from './types';\n\nexport interface SidebarContextProps {\n collapsed: boolean;\n ItemLinkComponent: ItemLinkComponent;\n activeArticleId?: string;\n onSelectArticle?: (id: string) => void;\n onExpandArticle?: (id: string) => void;\n onSearchArticles?: (value: string) => void;\n onAddArticle?: (parentId: string | null) => Promise<ArticleNavItem>;\n onMoreActions?: (articleId: string) => void;\n canMoveArticle?: (move: { id: string; parentId: string | null; index: number }) => boolean;\n onMoveArticle?: (moved: { id: string; parentId: string | null; index: number }) => void | Promise<void>;\n}\n\nexport interface SidebarContextValue extends SidebarContextProps {\n handleSearchArticles: (value: string) => void;\n isSearching: boolean;\n searchValue: string;\n setSearchValue: React.Dispatch<React.SetStateAction<string>>;\n /** Set when user selects from search results; tree uses this until activeArticleId catches up */\n lastSelectedArticleId: string | null;\n setLastSelectedArticleId: React.Dispatch<React.SetStateAction<string | null>>;\n}\n\nconst SidebarContext = React.createContext<SidebarContextValue | null>(null);\n\nexport function SidebarProvider({\n collapsed,\n ItemLinkComponent,\n activeArticleId,\n onSelectArticle,\n onExpandArticle,\n onSearchArticles,\n onAddArticle,\n onMoreActions,\n canMoveArticle,\n onMoveArticle,\n children,\n}: React.PropsWithChildren<SidebarContextProps>) {\n const [isSearching, setIsSearching] = React.useState<boolean>(false);\n const [searchValue, setSearchValue] = React.useState<string>('');\n const [lastSelectedArticleId, setLastSelectedArticleId] = React.useState<string | null>(null);\n\n const handleSearchArticles = React.useCallback(\n (value: string) => {\n const trimmedValue = value.trim();\n setIsSearching(trimmedValue !== '');\n onSearchArticles?.(value);\n },\n [onSearchArticles],\n );\n\n const contextValue = React.useMemo<SidebarContextValue>(\n () => ({\n collapsed,\n ItemLinkComponent,\n activeArticleId,\n onSearchArticles,\n onSelectArticle,\n onExpandArticle,\n onAddArticle,\n onMoreActions,\n canMoveArticle,\n onMoveArticle,\n isSearching,\n searchValue,\n setSearchValue,\n lastSelectedArticleId,\n setLastSelectedArticleId,\n handleSearchArticles,\n }),\n [\n isSearching,\n searchValue,\n lastSelectedArticleId,\n collapsed,\n ItemLinkComponent,\n activeArticleId,\n onSelectArticle,\n onExpandArticle,\n onSearchArticles,\n onAddArticle,\n onMoreActions,\n canMoveArticle,\n onMoveArticle,\n handleSearchArticles,\n ],\n );\n\n return <SidebarContext.Provider value={contextValue}>{children}</SidebarContext.Provider>;\n}\n\nexport function useSidebar() {\n const sidebarContext = React.useContext(SidebarContext);\n\n if (!sidebarContext) throw new Error('useSidebar must be used within SidebarProvider');\n\n return sidebarContext;\n}\n","import * as React from 'react';\n\nimport { SidebarNavItem } from '../types';\nimport { useSidebar } from '../context';\n\nimport { cn } from '@/lib/utils';\n\nexport interface SectionNavProps {\n className?: string;\n items: SidebarNavItem[];\n activeId?: string;\n}\n\nexport function SectionNav({ className, items, activeId }: SectionNavProps) {\n const { ItemLinkComponent } = useSidebar();\n\n return (\n <nav className={cn('flex flex-col', className)} aria-label=\"App section navigation\">\n {items.map((item) => (\n <ItemLinkComponent\n key={item.id}\n href={item.href}\n data-sidebar-item-id={item.id}\n data-sidebar-item-type=\"section\"\n data-sidebar-section-nav-item-selected={item.id === activeId}\n className={cn(\n 'flex p-2 mx-4 text-sm text-default relative rounded-lg',\n 'after:transition-opacity after:absolute after:top-0 after:bottom-0 after:-right-4 after:w-2 after:bg-primary after:rounded-l-md after:opacity-0',\n 'hover:bg-dark-200 focus:bg-dark-200 hover:text-accent-foreground focus:text-accent-foreground',\n item.id === activeId && 'font-bold after:opacity-100',\n )}\n >\n {item.label}\n </ItemLinkComponent>\n ))}\n </nav>\n );\n}\n","import * as React from 'react';\nimport { ArrowBendUpLeft } from '@phosphor-icons/react';\n\nimport type { SidebarNavItem } from '../types';\nimport { useSidebar } from '../context';\nimport { SectionNav } from '../section-nav';\n\nimport { Button } from '@/components/ui/button';\nimport { ScrollArea } from '@/components/ui/scroll-area';\nimport { cn } from '@/lib/utils';\n\nexport interface AppNavProps {\n className?: string;\n items?: SidebarNavItem[];\n activeId?: string;\n showBack?: boolean;\n onBack?: () => void;\n sectionNavItems?: SidebarNavItem[];\n sectionNavActiveId?: string;\n}\n\nexport function AppNav({\n className,\n items,\n activeId,\n showBack,\n onBack,\n sectionNavItems,\n sectionNavActiveId,\n}: AppNavProps) {\n const { collapsed, ItemLinkComponent } = useSidebar();\n\n const hasBack = !!showBack && !!onBack;\n\n return (\n <nav className={cn('flex flex-col gap-2', className)} aria-label=\"App navigation\">\n {hasBack && (\n <Button\n variant=\"link\"\n className={cn('py-2 text-base text-default w-full mx-4', collapsed ? 'justify-center' : 'justify-start')}\n size=\"icon\"\n onClick={onBack}\n >\n <ArrowBendUpLeft aria-hidden=\"true\" />\n <span className={cn(collapsed && 'sr-only')}>back to Brainfish</span>\n </Button>\n )}\n <ScrollArea className=\"min-h-0 max-h-[calc(100dvh-var(--header-nav-height)-5rem)]\">\n <div className=\"flex flex-col gap-2 mx-4\">\n {items?.map((item) => (\n <React.Fragment key={item.id}>\n <ItemLinkComponent\n className={cn(\n 'flex p-2 rounded-lg gap-1 text-base text-default items-center',\n item.id === activeId\n ? 'bg-primary text-primary-foreground font-bold hover:text-primary-foreground focus:text-primary-foreground'\n : 'hover:bg-dark-200 focus:bg-dark-200 hover:text-accent-foreground focus:text-accent-foreground',\n )}\n href={item.href}\n data-sidebar-item-id={item.id}\n data-sidebar-item-type=\"app\"\n data-sidebar-app-nav-item-selected={item.id === activeId}\n >\n {item.Icon ? <item.Icon weight={item.id === activeId ? 'fill' : 'regular'} size={16} /> : null}\n <span className={cn('transition-opacity duration-200', collapsed && 'opacity-0 sr-only')}>\n {item.label}\n </span>\n </ItemLinkComponent>\n {item.id === activeId && sectionNavItems && sectionNavItems.length > 0 && !collapsed && (\n <SectionNav className={cn('flex-shrink-0')} items={sectionNavItems} activeId={sectionNavActiveId} />\n )}\n </React.Fragment>\n ))}\n </div>\n </ScrollArea>\n </nav>\n );\n}\n","import * as React from 'react';\nimport { MagnifyingGlass, X } from '@phosphor-icons/react';\nimport { useToggle } from 'usehooks-ts';\n\nimport { useSidebar } from '../context';\n\nimport { Input } from '@/components/ui/input';\n\nexport function ArticleSearch() {\n const { handleSearchArticles, searchValue, setSearchValue } = useSidebar();\n const [searchInteracted, , setSearchInteracted] = useToggle(false);\n const searchInputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (searchInteracted) handleSearchArticles?.(searchValue);\n }, [searchValue, handleSearchArticles, searchInteracted]);\n\n return (\n <div className=\"mb-2\">\n <Input\n ref={searchInputRef}\n className=\"h-auto text-subtlest border-border-subtle\"\n startIcon={MagnifyingGlass}\n endIcon={searchValue ? X : undefined}\n onEndIconClick={() => {\n setSearchValue('');\n handleSearchArticles?.('');\n }}\n value={searchValue}\n onChange={(e) => {\n const value = e.target.value;\n if (!searchInteracted) {\n setSearchInteracted(true);\n }\n setSearchValue(value);\n if (value.trim() === '') {\n handleSearchArticles('');\n setSearchInteracted(false);\n }\n }}\n placeholder=\"Find an article\"\n aria-label=\"Find an article\"\n type=\"search\"\n />\n </div>\n );\n}\n","import * as React from 'react';\nimport { Folder, FolderDashed, FolderOpen, File, FileDashed, DotsThree, Lightning, Plus } from '@phosphor-icons/react';\nimport { Key, TreeItemContentRenderProps, TreeStateContext, Button as AriaButton } from 'react-aria-components';\n\nimport { ArticleNavItem, ItemLinkComponent } from '../types';\nimport { useSidebar } from '../context';\n\nimport { cn } from '@/lib/utils';\nimport { Button } from '@/components/ui/button';\nimport { ButtonGroup } from '@/components/ui/button-group';\nimport { Spinner } from '@/components/ui/spinner';\n\ntype ArticleItemTreeNode = {\n key: Key;\n value: ArticleNavItem;\n children: ArticleItemTreeNode[] | null;\n};\n\nfunction FolderSlotButton({ children }: { children: React.ReactNode }) {\n return (\n <AriaButton slot=\"chevron\" className=\"relative z-30 flex p-0\">\n <span className=\"relative inline-flex size-3 items-center justify-center\">{children}</span>\n </AriaButton>\n );\n}\n\nexport interface ArticleItemProps extends TreeItemContentRenderProps {\n ItemLinkComponent: ItemLinkComponent;\n item: ArticleItemTreeNode;\n onAddArticle?: (parentId: string) => void;\n isAddingArticle?: boolean;\n isLoadingChildren?: boolean;\n onMoreActions?: (articleId: string) => void;\n}\n\nexport function ArticleItem({\n item,\n hasChildItems,\n isSelected,\n isExpanded,\n ItemLinkComponent,\n onAddArticle,\n isAddingArticle,\n isLoadingChildren,\n onMoreActions,\n level,\n}: ArticleItemProps) {\n const { isSearching } = useSidebar();\n const treeState = React.useContext(TreeStateContext);\n const shouldPreventNavigation = Boolean(isSelected);\n\n const handleAddClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n onAddArticle?.(item.value.id);\n };\n\n const handleMoreClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n onMoreActions?.(item.value.id);\n };\n\n const handleItemClick = (e: React.MouseEvent) => {\n if (shouldPreventNavigation) {\n e.preventDefault();\n e.stopPropagation();\n }\n\n if (hasChildItems && treeState) {\n treeState.toggleKey(item.key);\n }\n };\n\n const showActions = !!((onAddArticle && !isAddingArticle) || onMoreActions);\n\n const isDraft = item.value.status === 'draft';\n\n let statusLabel: string;\n switch (item.value.status) {\n case 'draft':\n statusLabel = '(draft)';\n break;\n case 'unpublished':\n statusLabel = '(unpublished changes)';\n break;\n default:\n statusLabel = '';\n }\n\n const folderIcon = (\n <FolderSlotButton>\n <Folder\n aria-hidden=\"true\"\n weight=\"fill\"\n className={cn(\n 'absolute size-3 transition-opacity duration-200 ease-out',\n isExpanded ? 'opacity-0' : 'opacity-100',\n )}\n />\n <FolderOpen\n aria-hidden=\"true\"\n weight=\"fill\"\n className={cn(\n 'absolute size-3 transition-opacity duration-200 ease-out',\n isExpanded ? 'opacity-100' : 'opacity-0',\n )}\n />\n </FolderSlotButton>\n );\n\n const folderWithChangesIcon = (\n <FolderSlotButton>\n <FolderDashed\n aria-hidden=\"true\"\n weight=\"fill\"\n className={cn(\n 'absolute size-3 transition-opacity duration-200 ease-out',\n isExpanded ? 'opacity-0' : 'opacity-100',\n )}\n />\n <FolderOpen\n aria-hidden=\"true\"\n weight=\"fill\"\n className={cn(\n 'absolute size-3 transition-opacity duration-200 ease-out',\n isExpanded ? 'opacity-100' : 'opacity-0',\n )}\n />\n </FolderSlotButton>\n );\n\n const discoveryIconContent = (\n <>\n <span className=\"sr-only\">Knowledge Discovery</span>\n <Lightning aria-hidden=\"true\" weight=\"fill\" className=\"size-3 shrink-0 text-blue-600\" />\n </>\n );\n\n const discoveryIcon = hasChildItems ? (\n <FolderSlotButton>\n <span className=\"text-blue-600\">{discoveryIconContent}</span>\n </FolderSlotButton>\n ) : (\n <span className=\"flex size-3 shrink-0 items-center justify-center text-blue-600\">{discoveryIconContent}</span>\n );\n\n const fileIcon = (\n <span className=\"flex size-3 shrink-0 items-center justify-center\">\n <File aria-hidden=\"true\" className=\"size-3\" />\n </span>\n );\n\n const fileWithChangesIcon = (\n <span className=\"flex size-3 shrink-0 items-center justify-center\">\n <span className=\"sr-only\">Unpublished Changes</span>\n <FileDashed aria-hidden=\"true\" className=\"size-3\" />\n </span>\n );\n\n const loadingFolderIcon = (\n <span\n className=\"flex size-3 shrink-0 items-center justify-center\"\n role=\"status\"\n aria-label={`Loading nested articles for ${item.value.label}`}\n >\n <Spinner aria-hidden=\"true\" className=\"size-3 text-current\" />\n <span className=\"sr-only\">Loading nested articles for {item.value.label}</span>\n </span>\n );\n\n const renderStatusIcon = (status: ArticleNavItem['status'], hasChildItems: boolean) => {\n if (status === 'discovery') {\n return discoveryIcon;\n }\n\n const hasChanges = status === 'unpublished' || status === 'draft';\n\n if (hasChildItems) {\n return hasChanges ? folderWithChangesIcon : folderIcon;\n } else {\n return hasChanges ? fileWithChangesIcon : fileIcon;\n }\n };\n\n return (\n <div\n style={{ ['--line-offset' as string]: '0.875rem' }}\n aria-busy={isLoadingChildren || undefined}\n className={cn(\n 'group relative flex items-center gap-1 rounded-md py-1 pl-1 ml-[calc((var(--tree-item-level)-1)*var(--line-offset))] pr-4 h-9',\n isDraft && 'text-subtlest italic',\n isSelected\n ? 'bg-lime-100 text-default font-bold after:absolute after:-right-0.5 after:top-1/2 after:h-1/2 after:w-1 after:-translate-y-1/2 after:bg-lime-400'\n : 'hover:bg-dark-200 hover:text-accent-foreground',\n )}\n >\n {Array.from({ length: level - 1 }, (_, i) => (\n <span\n key={`${item.value.label}-${i + 1}`}\n style={{ ['--item-line-level' as string]: i + 1 }}\n // Don't mess with the calc: forumula for multiple lines.\n className=\"absolute w-[1px] bg-dark-400 -top-2 h-9 left-[calc(-4px-var(--line-offset)*(var(--item-line-level)-1))]\"\n aria-hidden={true}\n />\n ))}\n\n <AriaButton slot=\"drag\" className=\"sr-only\">\n Drag {item.value.label}\n </AriaButton>\n\n {isSearching ? null : isLoadingChildren ? loadingFolderIcon : renderStatusIcon(item.value.status, hasChildItems)}\n\n <ItemLinkComponent\n href={item.value.href}\n title={`${item.value.label} ${statusLabel}`}\n data-sidebar-item-id={item.value.id}\n data-sidebar-item-type=\"article\"\n className={cn(\n 'min-w-0 text-inherit text-sm truncate flex-1 after:absolute after:inset-0 after:z-10 pr-1',\n isSelected && 'text-default',\n )}\n onClick={handleItemClick}\n >\n {item.value.label}\n </ItemLinkComponent>\n\n {showActions && (\n <ButtonGroup\n orientation=\"horizontal\"\n className={cn(\n 'absolute h-4 my-auto right-0.5 top-0 bottom-0 z-20 hidden items-center rounded-xl',\n 'group-hover:flex group-focus-within:flex',\n isSelected ? 'bg-lime-100 text-default' : 'bg-dark-200',\n )}\n >\n {onMoreActions && (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`More actions for ${item.value.label}`}\n className=\"size-4 [&_svg]:size-3.5\"\n onClick={handleMoreClick}\n >\n <DotsThree aria-hidden=\"true\" weight=\"bold\" />\n </Button>\n )}\n\n {onAddArticle && !isAddingArticle && (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`Add article to ${item.value.label}`}\n className=\"size-4 [&_svg]:size-3.5\"\n onClick={handleAddClick}\n >\n <Plus aria-hidden=\"true\" />\n </Button>\n )}\n </ButtonGroup>\n )}\n </div>\n );\n}\n","import * as React from 'react';\n\nimport { useSidebar } from '../context';\nimport { ArticleNavItem } from '../types';\n\nimport { cn } from '@/lib/utils';\n\ninterface ArticleSearchResultsProps {\n articles: ArticleNavItem[];\n}\n\nexport function ArticleSearchResults({ articles }: ArticleSearchResultsProps) {\n const {\n ItemLinkComponent,\n activeArticleId,\n handleSearchArticles,\n setSearchValue,\n setLastSelectedArticleId,\n onSelectArticle,\n } = useSidebar();\n\n const handleSearchResultClick = React.useCallback(\n (articleId: string) => {\n setLastSelectedArticleId(articleId);\n setSearchValue('');\n handleSearchArticles?.('');\n onSelectArticle?.(articleId);\n },\n [handleSearchArticles, onSelectArticle, setLastSelectedArticleId, setSearchValue],\n );\n\n if (articles.length === 0) {\n return null;\n }\n\n return (\n <ul aria-label=\"Search results\" className=\"flex flex-col list-none\">\n {articles.map((item) => {\n const isActive = item.id === activeArticleId;\n\n return (\n <li key={item.id}>\n <ItemLinkComponent\n href={item.href}\n title={item.label}\n data-sidebar-item-id={item.id}\n data-sidebar-item-type=\"article\"\n onClick={() => handleSearchResultClick(item.id)}\n className={cn(\n 'relative flex h-9 items-center rounded-md px-1 text-sm truncate',\n isActive\n ? 'bg-lime-100 font-bold text-primary-foreground after:absolute after:-right-0.5 after:top-1/2 after:h-1/2 after:w-1 after:-translate-y-1/2 after:bg-lime-400'\n : 'text-inherit hover:bg-dark-200 hover:text-accent-foreground',\n )}\n >\n {item.label}\n </ItemLinkComponent>\n </li>\n );\n })}\n </ul>\n );\n}\n","import type { ArticleNavItem } from '../types';\n\ntype TreeEntry = {\n item: ArticleNavItem;\n parentId: string | null;\n index: number;\n depth: number;\n};\n\nexport type TreeSyncOperation =\n | { type: 'remove'; id: string }\n | { type: 'insert'; parentId: string | null; index: number; item: ArticleNavItem }\n | { type: 'move'; id: string; parentId: string | null; index: number }\n | { type: 'update'; id: string; item: ArticleNavItem };\n\nfunction flattenArticles(items: ArticleNavItem[], parentId: string | null = null, depth = 0, result: TreeEntry[] = []): TreeEntry[] {\n items.forEach((item, index) => {\n result.push({ item, parentId, index, depth });\n if (item.items?.length) {\n flattenArticles(item.items, item.id, depth + 1, result);\n }\n });\n\n return result;\n}\n\nfunction haveSameValue(a: ArticleNavItem, b: ArticleNavItem) {\n return (\n a.label === b.label &&\n a.href === b.href &&\n a.status === b.status &&\n a.hasChildren === b.hasChildren &&\n a.isLoadingChildren === b.isLoadingChildren\n );\n}\n\nexport function buildArticleTreeSyncOperations(currentArticles: ArticleNavItem[], nextArticles: ArticleNavItem[]): TreeSyncOperation[] {\n const currentEntries = flattenArticles(currentArticles);\n const nextEntries = flattenArticles(nextArticles);\n\n const currentMap = new Map(currentEntries.map((entry) => [entry.item.id, entry]));\n const nextMap = new Map(nextEntries.map((entry) => [entry.item.id, entry]));\n const operations: TreeSyncOperation[] = [];\n\n currentEntries\n .filter((entry) => !nextMap.has(entry.item.id))\n .sort((a, b) => b.depth - a.depth)\n .forEach((entry) => {\n operations.push({ type: 'remove', id: entry.item.id });\n });\n\n nextEntries.forEach((entry) => {\n const currentEntry = currentMap.get(entry.item.id);\n\n if (!currentEntry) {\n operations.push({\n type: 'insert',\n parentId: entry.parentId,\n index: entry.index,\n item: entry.item,\n });\n\n return;\n }\n\n if (currentEntry.parentId !== entry.parentId || currentEntry.index !== entry.index) {\n operations.push({\n type: 'move',\n id: entry.item.id,\n parentId: entry.parentId,\n index: entry.index,\n });\n }\n\n if (!haveSameValue(currentEntry.item, entry.item)) {\n operations.push({\n type: 'update',\n id: entry.item.id,\n item: entry.item,\n });\n }\n });\n\n return operations;\n}\n","import * as React from 'react';\nimport {\n useDragAndDrop,\n useTreeData,\n Tree,\n TreeItem,\n TreeItemContent,\n Collection,\n DropIndicator,\n type Key,\n} from 'react-aria-components';\n\nimport { ArticleNavItem } from '../types';\nimport { ArticleItem } from './article-item';\nimport { ArticleSearchResults } from './article-search-results';\nimport { useSidebar } from '../context';\nimport { getNewlyExpandedKeys } from './expansion';\nimport { buildArticleTreeSyncOperations } from './tree-sync';\n\nlet placeholderCounter = 0;\n\ninterface ArticleTreeProps {\n articles: ArticleNavItem[];\n isSearching?: boolean;\n renderEmptyState?: () => React.ReactNode;\n}\n\nexport function getParentKeys(items: ArticleNavItem[], targetId: string, parents: string[] = []): string[] | null {\n for (const item of items) {\n if (item.id === targetId) {\n return parents;\n }\n if (item.items && item.items.length > 0) {\n const result = getParentKeys(item.items, targetId, [...parents, item.id]);\n if (result) return result;\n }\n }\n\n return null;\n}\n\ntype ArticleTreeNode = {\n value: ArticleNavItem;\n children: ArticleTreeNode[] | null;\n};\n\nfunction treeNodesToArticleNavItems(items: ArticleTreeNode[]): ArticleNavItem[] {\n return items.map((item) => ({\n ...item.value,\n items: item.children?.length ? treeNodesToArticleNavItems(item.children) : item.value.items,\n }));\n}\n\nexport function ArticleTree({ articles, isSearching, renderEmptyState }: ArticleTreeProps) {\n const {\n ItemLinkComponent,\n activeArticleId,\n lastSelectedArticleId,\n setLastSelectedArticleId,\n onSelectArticle,\n onExpandArticle,\n canMoveArticle,\n onMoveArticle,\n onAddArticle,\n onMoreActions,\n } = useSidebar();\n\n const effectiveActiveId = activeArticleId ?? lastSelectedArticleId ?? undefined;\n\n const articlesTree = useTreeData({\n initialItems: articles,\n getKey: (articleItem) => articleItem.id,\n getChildren: (articleItem) => articleItem.items ?? [],\n });\n\n const prevArticlesRef = React.useRef(articles);\n React.useEffect(() => {\n if (prevArticlesRef.current === articles) return;\n\n prevArticlesRef.current = articles;\n\n const currentArticles = treeNodesToArticleNavItems(articlesTree.items);\n const operations = buildArticleTreeSyncOperations(currentArticles, articles);\n\n if (operations.length === 0) return;\n\n operations.forEach((operation) => {\n switch (operation.type) {\n case 'remove':\n articlesTree.remove(operation.id);\n break;\n case 'insert':\n articlesTree.insert(operation.parentId, operation.index, operation.item);\n break;\n case 'move':\n articlesTree.move(operation.id, operation.parentId, operation.index);\n break;\n case 'update':\n articlesTree.update(operation.id, operation.item);\n break;\n }\n });\n }, [articles, articlesTree]);\n\n const { dragAndDropHooks } = useDragAndDrop({\n getItems: (_keys, items: typeof articlesTree.items) =>\n items.map((item) => ({\n 'text/plain': item.value.label,\n })),\n onMove: (e) => {\n if (isSearching) return;\n const movedArticleId = Array.from(e.keys)[0];\n if (!movedArticleId) return;\n\n const targetItem = articlesTree.getItem(e.target.key);\n if (!targetItem) return;\n\n let movedToParentId: string | null;\n let movedToIndex: number;\n\n if (e.target.dropPosition === 'on') {\n movedToParentId = e.target.key.toString();\n movedToIndex = targetItem.children?.length ?? 0;\n } else {\n movedToParentId = targetItem.parentKey?.toString() ?? null;\n const siblings = movedToParentId ? (articlesTree.getItem(movedToParentId)?.children ?? []) : articlesTree.items;\n const targetIndex = siblings.findIndex((i) => i.key === e.target.key);\n movedToIndex = e.target.dropPosition === 'before' ? targetIndex : targetIndex + 1;\n }\n\n const moveDetails = {\n id: movedArticleId.toString(),\n parentId: movedToParentId,\n index: movedToIndex,\n };\n\n if (canMoveArticle && !canMoveArticle(moveDetails)) {\n return;\n }\n\n const movedItem = articlesTree.getItem(movedArticleId);\n const originalParentId = movedItem?.parentKey?.toString() ?? null;\n const originalSiblings = originalParentId\n ? (articlesTree.getItem(originalParentId)?.children ?? [])\n : articlesTree.items;\n const originalIndex = originalSiblings.findIndex((i) => i.key === movedArticleId);\n\n if (e.target.dropPosition === 'on') {\n articlesTree.move(movedArticleId, movedToParentId, movedToIndex);\n } else if (e.target.dropPosition === 'before') {\n articlesTree.moveBefore(e.target.key, e.keys);\n } else {\n articlesTree.moveAfter(e.target.key, e.keys);\n }\n\n const result = onMoveArticle?.(moveDetails);\n\n if (result && typeof result.then === 'function') {\n result.catch(() => {\n articlesTree.move(movedArticleId, originalParentId, originalIndex);\n });\n }\n },\n renderDropIndicator: (target) => (\n <DropIndicator\n target={target}\n className=\"outline outline-1 outline-lime-200 ml-[calc(var(--tree-item-level)*0.5rem)]\"\n />\n ),\n });\n\n // Selection when there's no active article from URL/context; used so the tree can show a selection before navigation.\n const [localSelectedKeys, setLocalSelectedKeys] = React.useState<Set<Key>>(new Set());\n const selectedKeys = React.useMemo(\n () => (effectiveActiveId != null ? new Set<Key>([effectiveActiveId]) : localSelectedKeys),\n [effectiveActiveId, localSelectedKeys],\n );\n\n const pathToActive = React.useMemo(() => {\n if (!effectiveActiveId || isSearching || !articles.length) return [];\n const path = getParentKeys(articles, effectiveActiveId);\n\n return path ?? [];\n }, [articles, effectiveActiveId, isSearching]);\n\n const [expandedKeysState, setExpandedKeysState] = React.useState<Set<Key>>(new Set());\n const expandedKeys = React.useMemo(\n () => new Set<Key>([...pathToActive, ...expandedKeysState]),\n [pathToActive, expandedKeysState],\n );\n\n React.useEffect(() => {\n if (!activeArticleId) return;\n setLastSelectedArticleId(null);\n }, [activeArticleId, lastSelectedArticleId, setLastSelectedArticleId]);\n\n const pendingAddIdsRef = React.useRef<Set<string>>(new Set());\n const [pendingAddIds, setPendingAddIds] = React.useState<Set<string>>(new Set());\n\n const handleAddArticle = React.useCallback(\n (parentId: string) => {\n if (isSearching || !onAddArticle || pendingAddIdsRef.current.has(parentId)) return;\n\n pendingAddIdsRef.current.add(parentId);\n setPendingAddIds(new Set(pendingAddIdsRef.current));\n\n const tempId = `__placeholder_${++placeholderCounter}`;\n const placeholder: ArticleNavItem = { id: tempId, label: 'Untitled', href: '', status: 'draft' };\n\n articlesTree.append(parentId, placeholder);\n setExpandedKeysState((prev) => new Set([...prev, parentId]));\n\n onAddArticle(parentId).then(\n (created) => {\n articlesTree.remove(tempId);\n articlesTree.append(parentId, created);\n // Highlight the newly created article in the tree.\n setLocalSelectedKeys(new Set([created.id]));\n onSelectArticle?.(created.id);\n pendingAddIdsRef.current.delete(parentId);\n setPendingAddIds(new Set(pendingAddIdsRef.current));\n },\n () => {\n articlesTree.remove(tempId);\n pendingAddIdsRef.current.delete(parentId);\n setPendingAddIds(new Set(pendingAddIdsRef.current));\n },\n );\n },\n [isSearching, onAddArticle, articlesTree, onSelectArticle],\n );\n\n const navigateToArticleId = React.useCallback(\n (articleId: string) => {\n onSelectArticle?.(articleId);\n // No URL/store id: keep highlight in sync for Storybook and other uncontrolled hosts.\n if (activeArticleId == null) {\n setLocalSelectedKeys(new Set<Key>([articleId]));\n }\n },\n [activeArticleId, onSelectArticle],\n );\n\n if (isSearching) {\n return <ArticleSearchResults articles={articles} />;\n }\n\n return (\n <Tree\n aria-label={isSearching ? 'Search results' : 'Articles tree'}\n items={articlesTree.items}\n renderEmptyState={renderEmptyState}\n dragAndDropHooks={isSearching ? undefined : dragAndDropHooks}\n className=\"flex flex-col\"\n // toggle → selectOnFocus off in React Aria, so ArrowUp/Down only move focus; Space updates selection → onSelectionChange.\n selectionBehavior=\"toggle\"\n selectionMode=\"single\"\n expandedKeys={expandedKeys}\n onExpandedChange={(nextExpandedKeys) => {\n const newlyExpandedKeys = getNewlyExpandedKeys(expandedKeys, nextExpandedKeys);\n\n newlyExpandedKeys.forEach((key) => {\n onExpandArticle?.(key);\n });\n\n setExpandedKeysState(nextExpandedKeys);\n }}\n selectedKeys={selectedKeys}\n disallowEmptySelection\n onSelectionChange={(selection) => {\n if (selection !== 'all') {\n // Keep local selection in sync when we're not driven by URL/active article.\n if (effectiveActiveId == null) setLocalSelectedKeys(selection);\n const selected = Array.from(selection)?.[0];\n\n if (selected) {\n navigateToArticleId(selected.toString());\n }\n }\n }}\n >\n {function renderItem(item) {\n return (\n <TreeItem\n className=\"list-none\"\n textValue={item.value.label}\n id={item.value.id}\n hasChildItems={item.value.hasChildren}\n >\n <TreeItemContent>\n {(props) => (\n <ArticleItem\n ItemLinkComponent={ItemLinkComponent}\n item={item}\n onAddArticle={!isSearching && onAddArticle ? handleAddArticle : undefined}\n isAddingArticle={!isSearching && pendingAddIds.has(item.value.id)}\n isLoadingChildren={!isSearching && item.value.isLoadingChildren}\n onMoreActions={onMoreActions ? (articleId) => onMoreActions(articleId) : undefined}\n {...props}\n />\n )}\n </TreeItemContent>\n\n {!isSearching && item.children && <Collection items={item.children}>{renderItem}</Collection>}\n </TreeItem>\n );\n }}\n </Tree>\n );\n}\n","import type { Key } from 'react-aria-components';\n\nexport function getNewlyExpandedKeys(previous: Iterable<Key>, next: Iterable<Key>): string[] {\n const previousKeys = new Set(Array.from(previous, (key) => key.toString()));\n\n return Array.from(next, (key) => key.toString()).filter((key) => !previousKeys.has(key));\n}\n","import * as React from 'react';\nimport { Article } from '@phosphor-icons/react';\nimport { useDebounceValue } from 'usehooks-ts';\n\nimport { useSidebar } from '../context';\nimport { ArticleNavItem } from '../types';\nimport { ArticleSearch } from './article-search';\nimport { ArticleTree } from './article-tree';\n\nimport { Spinner } from '@/components/ui/spinner';\nimport { cn } from '@/lib/utils';\nimport { Button } from '@/components/ui/button';\nimport { useSubstringFilter } from '@/lib/hooks';\n\n/** Debounce applied to the article tree filter + remount key while searching (avoids work every keystroke). */\nconst ARTICLE_TREE_SEARCH_DEBOUNCE_MS = 400;\n/** Stable ref for useDebounceValue options — inline objects reset debounce each render (usehooks-ts #703). */\nconst ARTICLE_TREE_SEARCH_DEBOUNCE_OPTIONS = { leading: true, trailing: true } as const;\n\nexport interface ArticleNavProps {\n className?: string;\n isLoadingArticles?: boolean;\n articles?: ArticleNavItem[];\n}\n\nexport function ArticleNav({ articles, className, isLoadingArticles }: ArticleNavProps) {\n const { isSearching, searchValue, onAddArticle } = useSidebar();\n const { contains } = useSubstringFilter();\n const trimmedQuery = searchValue.trim();\n const [debouncedTreeQuery] = useDebounceValue(\n isSearching ? trimmedQuery : '',\n isSearching ? ARTICLE_TREE_SEARCH_DEBOUNCE_MS : 0,\n isSearching ? ARTICLE_TREE_SEARCH_DEBOUNCE_OPTIONS : undefined,\n );\n\n const shouldShowLoadingState = !isSearching && (isLoadingArticles ?? articles === undefined);\n const renderLoadingEmptyState = React.useCallback(\n () => (\n <div\n className=\"flex min-h-20 items-center justify-center text-muted-foreground\"\n role=\"status\"\n aria-live=\"polite\"\n aria-label=\"Loading articles list\"\n >\n <Spinner className=\"size-5\" aria-hidden=\"true\" />\n <span className=\"sr-only\">Loading articles list</span>\n </div>\n ),\n [],\n );\n\n const noArticles = !shouldShowLoadingState && !isSearching && (!articles || articles.length === 0);\n\n const filteredArticles = React.useMemo(() => {\n if (isSearching) {\n // `includes('')` is true for every string; avoid showing the full tree with an empty query.\n if (trimmedQuery === '' || debouncedTreeQuery === '') {\n return [];\n }\n const result: ArticleNavItem[] = [];\n const query = debouncedTreeQuery;\n\n const traverse = (nodes: ArticleNavItem[]) => {\n for (const item of nodes) {\n if (contains(item.label, query)) {\n // Strip children so each hit is one row; otherwise the tree would render nested\n // items AND the same nodes again as separate roots (duplicates).\n result.push({ ...item, hasChildren: false, items: [] });\n }\n if (item.items?.length) {\n traverse(item.items);\n }\n }\n };\n\n traverse(articles ?? []);\n\n return result;\n }\n if (articles && articles.length > 0) {\n return articles;\n }\n\n return [];\n }, [articles, isSearching, trimmedQuery, debouncedTreeQuery, contains]);\n\n return (\n <div className={cn('flex flex-col gap-0.5 px-4', className)}>\n {noArticles && (\n <Button variant=\"ghost\" className=\"text-subtlest\" onClick={() => onAddArticle?.(null).catch(() => {})}>\n <Article aria-hidden=\"true\" />\n Create first article or folder\n </Button>\n )}\n\n {shouldShowLoadingState ? (\n <ArticleTree isSearching={isSearching} articles={[]} renderEmptyState={renderLoadingEmptyState} />\n ) : (\n <>\n <ArticleSearch />\n <nav aria-label=\"Articles navigation\">\n <ArticleTree\n key={isSearching ? (trimmedQuery === '' ? 'search:empty' : `search:${debouncedTreeQuery}`) : 'browse'}\n isSearching={isSearching}\n articles={filteredArticles ?? []}\n />\n </nav>\n </>\n )}\n </div>\n );\n}\n","import * as React from 'react';\n\nimport { SidebarNavItem, ArticleNavItem } from './types';\nimport { SidebarContextProps, SidebarProvider } from './context';\nimport { AppNav } from './app-nav';\nimport { ArticleNav } from './article-nav';\n\nimport { ScrollArea } from '@/components/ui/scroll-area';\nimport { cn } from '@/lib/utils';\n\nexport interface SidebarProps extends React.ComponentPropsWithoutRef<'aside'>, SidebarContextProps {\n showAppBack?: boolean;\n onAppBack?: () => void;\n appNavItems?: SidebarNavItem[];\n appNavActiveId?: string;\n showArticles?: boolean;\n isLoadingArticles?: boolean;\n articles?: ArticleNavItem[];\n sectionNavItems?: SidebarNavItem[];\n sectionNavActiveId?: string;\n}\n\nexport const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(function Sidebar(\n {\n className,\n ItemLinkComponent,\n collapsed,\n showAppBack,\n onAppBack,\n appNavItems,\n appNavActiveId,\n isLoadingArticles,\n sectionNavItems,\n sectionNavActiveId,\n activeArticleId,\n showArticles,\n articles,\n onSelectArticle,\n onExpandArticle,\n onSearchArticles,\n onAddArticle,\n onMoreActions,\n canMoveArticle,\n onMoveArticle,\n ...props\n },\n ref,\n) {\n return (\n <SidebarProvider\n collapsed={!!collapsed}\n ItemLinkComponent={ItemLinkComponent}\n activeArticleId={activeArticleId}\n onSelectArticle={onSelectArticle}\n onExpandArticle={onExpandArticle}\n onSearchArticles={onSearchArticles}\n onAddArticle={onAddArticle}\n onMoreActions={onMoreActions}\n canMoveArticle={canMoveArticle}\n onMoveArticle={onMoveArticle}\n >\n <aside\n ref={ref}\n {...props}\n className={cn(\n 'py-4 transition-width duration-200 ease-in-out flex flex-col h-dvh',\n collapsed ? 'w-[64px]' : 'w-[260px]',\n className,\n )}\n aria-label=\"Application navigation\"\n >\n <AppNav\n className={cn(\n 'flex-shrink-0',\n appNavItems && appNavItems.length > 0 && 'pb-4',\n !collapsed && 'border-b border-border-subtle',\n )}\n showBack={showAppBack}\n items={appNavItems}\n activeId={appNavActiveId}\n onBack={onAppBack}\n sectionNavItems={sectionNavItems}\n sectionNavActiveId={sectionNavActiveId}\n />\n\n {!collapsed && (\n <>\n {showArticles && (\n <ScrollArea>\n <ArticleNav\n className=\"py-4 max-w-[var(--left-nav-width,inherit)]\"\n articles={articles}\n isLoadingArticles={isLoadingArticles}\n />\n </ScrollArea>\n )}\n </>\n )}\n </aside>\n </SidebarProvider>\n );\n});\n"],"names":["SidebarContext","React","createContext","SidebarProvider","collapsed","ItemLinkComponent","activeArticleId","onSelectArticle","onExpandArticle","onSearchArticles","onAddArticle","onMoreActions","canMoveArticle","onMoveArticle","children","isSearching","setIsSearching","useState","searchValue","setSearchValue","lastSelectedArticleId","setLastSelectedArticleId","handleSearchArticles","useCallback","value","trimmedValue","trim","contextValue","useMemo","Provider","useSidebar","sidebarContext","useContext","Error","SectionNav","className","items","activeId","createElement","cn","map","item","key","id","href","label","AppNav","showBack","onBack","sectionNavItems","sectionNavActiveId","hasBack","Button","variant","size","onClick","ArrowBendUpLeft","ScrollArea","Fragment","Icon","weight","length","ArticleSearch","searchInteracted","setSearchInteracted","useToggle","searchInputRef","useRef","useEffect","Input","ref","startIcon","MagnifyingGlass","endIcon","X","onEndIconClick","onChange","e","target","placeholder","type","FolderSlotButton","AriaButton","slot","ArticleItem","hasChildItems","isSelected","isExpanded","isAddingArticle","isLoadingChildren","level","treeState","TreeStateContext","shouldPreventNavigation","Boolean","showActions","isDraft","status","statusLabel","folderIcon","Folder","FolderOpen","folderWithChangesIcon","FolderDashed","discoveryIconContent","Lightning","discoveryIcon","fileIcon","File","fileWithChangesIcon","FileDashed","loadingFolderIcon","role","Spinner","style","Array","from","_","i","hasChanges","renderStatusIcon","title","preventDefault","stopPropagation","toggleKey","ButtonGroup","orientation","DotsThree","Plus","ArticleSearchResults","articles","handleSearchResultClick","articleId","isActive","flattenArticles","parentId","depth","result","forEach","index","push","placeholderCounter","getParentKeys","targetId","parents","treeNodesToArticleNavItems","ArticleTree","renderEmptyState","effectiveActiveId","articlesTree","useTreeData","initialItems","getKey","articleItem","getChildren","prevArticlesRef","current","operations","currentArticles","nextArticles","currentEntries","nextEntries","currentMap","Map","entry","nextMap","filter","has","sort","a","b","currentEntry","get","hasChildren","buildArticleTreeSyncOperations","operation","remove","insert","move","update","dragAndDropHooks","useDragAndDrop","getItems","_keys","onMove","movedArticleId","keys","targetItem","getItem","movedToParentId","movedToIndex","dropPosition","toString","parentKey","targetIndex","findIndex","moveDetails","movedItem","originalParentId","originalIndex","moveBefore","moveAfter","then","catch","renderDropIndicator","DropIndicator","localSelectedKeys","setLocalSelectedKeys","Set","selectedKeys","pathToActive","expandedKeysState","setExpandedKeysState","expandedKeys","pendingAddIdsRef","pendingAddIds","setPendingAddIds","handleAddArticle","add","tempId","append","prev","created","delete","navigateToArticleId","Tree","selectionBehavior","selectionMode","onExpandedChange","nextExpandedKeys","previous","next","previousKeys","getNewlyExpandedKeys","disallowEmptySelection","onSelectionChange","selection","selected","renderItem","TreeItem","textValue","TreeItemContent","props","Collection","ARTICLE_TREE_SEARCH_DEBOUNCE_OPTIONS","leading","trailing","ArticleNav","isLoadingArticles","contains","useSubstringFilter","trimmedQuery","debouncedTreeQuery","useDebounceValue","shouldShowLoadingState","renderLoadingEmptyState","noArticles","filteredArticles","query","traverse","nodes","Article","Sidebar","forwardRef","showAppBack","onAppBack","appNavItems","appNavActiveId","showArticles"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"suggestion-card.TDn-dEc0.js","sources":["../../../src/scenes/knowledge-review/review-list/edit-count.tsx","../../../src/scenes/knowledge-review/status-source-summary.tsx","../../../src/scenes/knowledge-review/review-list/review-list-item.tsx","../../../src/scenes/knowledge-review/context.tsx","../../../src/scenes/knowledge-review/review-list/review-list.tsx","../../../src/scenes/knowledge-review/suggestion-card/suggestion-card.tsx"],"sourcesContent":["import React from 'react';\nimport { Circle } from '@phosphor-icons/react';\n\nexport function EditCount({ count }: { count: number }) {\n return (\n <span className=\"inline-flex items-center gap-1 p-1\">\n <Circle size={8} weight=\"fill\" aria-hidden=\"true\" className=\"text-blue\" />\n <span className=\"text-xs leading-4 text-subtle\">\n {count} {count === 1 ? 'edit' : 'edits'}\n </span>\n </span>\n );\n}\n","import * as React from 'react';\n\nimport { EditCount } from './review-list/edit-count';\n\nimport { StatusBadge } from '@/components/convos/status-badge';\nimport { Button } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\n\nexport interface StatusSourceSummaryProps {\n isNew?: boolean;\n isAccepted?: boolean;\n editCount?: number;\n sourceIcon?: React.ReactNode;\n sourceDescription?: string | null;\n onSourceLinkClick?: () => void;\n sourceLabel?: string;\n className?: string;\n}\n\nexport function StatusSourceSummary({\n isNew,\n isAccepted,\n editCount,\n sourceIcon,\n sourceDescription,\n onSourceLinkClick,\n sourceLabel = 'Open source',\n className,\n}: StatusSourceSummaryProps) {\n let status: React.ReactNode | undefined;\n if (isAccepted) {\n status = <StatusBadge variant=\"accepted\">Accepted</StatusBadge>;\n } else if (isNew) {\n status = (\n <StatusBadge variant=\"success\" surface=\"transparent\">\n New\n </StatusBadge>\n );\n } else if (editCount) {\n status = <EditCount count={editCount} />;\n }\n\n if (!status && !sourceIcon && !sourceDescription) {\n return null;\n }\n\n return (\n <span className={cn('flex items-center gap-1 leading-4 min-w-0', className)}>\n {status && <span className=\"shrink-0 flex items-center\">{status}</span>}\n {sourceDescription &&\n (onSourceLinkClick ? (\n <Button\n variant=\"link\"\n onClick={onSourceLinkClick}\n aria-label={sourceLabel}\n className=\"[&_svg]:size-4 h-auto p-0 text-xs font-normal text-subtle gap-1\"\n >\n {sourceIcon && <span className=\"shrink-0 flex items-center\">{sourceIcon}</span>}\n <span className=\"truncate\">{sourceDescription}</span>\n </Button>\n ) : (\n <span className=\"[&_svg]:size-4 flex items-center gap-1 text-xs text-subtle min-w-0\">\n {sourceIcon && <span className=\"shrink-0 flex items-center\">{sourceIcon}</span>}\n <span className=\"truncate\">{sourceDescription}</span>\n </span>\n ))}\n </span>\n );\n}\n","import * as React from 'react';\n\nimport { StatusSourceSummary } from '../status-source-summary';\n\nimport { cn } from '@/lib/utils';\nimport { formatDistance } from '@/lib/formatDate';\n\nexport interface ReviewListItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Unique identifier for the knowledge article */\n id: string;\n /** Primary text displayed in the first row */\n title: string;\n /** Content rendered in the top-right corner (e.g. relative timestamp) */\n timestamp: Date;\n /** Icon rendered at the start of the second row */\n icon?: React.ReactNode;\n /** Text or content next to the icon in the second row */\n description?: string | null;\n /** Whether this item is new */\n isNew?: boolean;\n /** Whether this item has been accepted (e.g. accepted as draft) */\n isAccepted?: boolean;\n /** Number of edits for this item */\n editCount?: number;\n /** Whether this item is currently selected */\n isSelected?: boolean;\n}\n\nexport const ReviewListItem = React.forwardRef<HTMLButtonElement, ReviewListItemProps>(function ReviewListItem(\n { title, timestamp, icon, description, isNew, isAccepted, editCount, isSelected = false, className, ...props },\n ref,\n) {\n return (\n <button\n {...props}\n ref={ref}\n type=\"button\"\n aria-pressed={isSelected}\n className={cn(\n 'relative flex w-full flex-col gap-1 bg-surface rounded p-4 cursor-pointer transition-colors text-left border border-transparent',\n isSelected\n ? [\n 'shadow-[0px_1px_3px_0px_rgba(95,95,95,0.26)]',\n 'after:content-[\"\"] after:block after:absolute after:right-[-2px] after:top-1/2 after:-translate-y-1/2 after:w-1 after:h-14 after:rounded-sm after:bg-primary',\n ]\n : 'hover:bg-dark-100 hover:border-dark-300',\n className,\n )}\n >\n {/* Row 1: Title + Timestamp */}\n <span className=\"flex items-center justify-between w-full gap-2\">\n <span className={cn('text-sm text-default truncate min-w-0 flex-1', isSelected ? 'font-bold' : 'font-normal')}>\n {title}\n </span>\n {timestamp && <span className=\"text-xs leading-4 text-subtlest shrink-0\">{formatDistance(timestamp)}</span>}\n </span>\n\n {/* Row 2: Status + Icon + Description */}\n <StatusSourceSummary\n isNew={isNew}\n isAccepted={isAccepted}\n editCount={editCount}\n sourceIcon={icon}\n sourceDescription={description}\n />\n </button>\n );\n});\n","import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';\n\nimport type { ReviewListItemProps } from './review-list/review-list-item';\n\n/** Passed to `onAcceptAndPublish` from the split Accept dropdown (public vs internal). */\nexport type AcceptAndPublishVisibility = 'public' | 'internal';\n\nexport type LoadedDiffDoc = {\n isNew: boolean;\n diffTitle: string;\n diffContent: React.ReactNode;\n};\n\ntype ReviewsSelectionValue = {\n selectedItem: ReviewListItemProps | null;\n setSelectedItem: (item: ReviewListItemProps | null) => void;\n loadedDiffDoc: LoadedDiffDoc | null;\n setLoadedDiffDoc: (doc: LoadedDiffDoc | null) => void;\n loadingDiff: boolean;\n setLoadingDiff: (loading: boolean) => void;\n onApproveAllSuggestions: () => void | Promise<void>;\n onRejectAllSuggestions: () => void | Promise<void>;\n onAcceptAndPublish?: (visibility: AcceptAndPublishVisibility) => void | Promise<void>;\n onApplyAllInEditor?: () => Promise<void> | void;\n onRejectAllInEditor?: () => Promise<void> | void;\n onOpenArticleLink?: () => void;\n onSourceLinkClick?: () => void;\n breadcrumb?: string;\n suggestionReasonBody?: string;\n /** When true, omit the \"publish publicly\" item from the Accept & publish menu. */\n hideAcceptAndPublishPublic?: boolean;\n};\n\nconst ReviewsContext = createContext<ReviewsSelectionValue | null>(null);\n\nexport type ReviewsProviderProps = {\n children: React.ReactNode;\n /** Optional initial selection (e.g. from server or mock data) */\n defaultSelectedItem?: ReviewListItemProps | null;\n onApproveAllSuggestions: () => void | Promise<void>;\n onRejectAllSuggestions: () => void | Promise<void>;\n onAcceptAndPublish?: (visibility: AcceptAndPublishVisibility) => void | Promise<void>;\n onApplyAllInEditor?: () => Promise<void> | void;\n onRejectAllInEditor?: () => Promise<void> | void;\n onOpenArticleLink?: () => void;\n onSourceLinkClick?: () => void;\n breadcrumb?: string;\n suggestionReasonBody?: string;\n hideAcceptAndPublishPublic?: boolean;\n};\n\nexport function ReviewsSelectionProvider({\n children,\n defaultSelectedItem = null,\n onApproveAllSuggestions,\n onRejectAllSuggestions,\n onAcceptAndPublish,\n onApplyAllInEditor,\n onRejectAllInEditor,\n onOpenArticleLink,\n onSourceLinkClick,\n breadcrumb,\n suggestionReasonBody,\n hideAcceptAndPublishPublic = false,\n}: ReviewsProviderProps) {\n const [userSelectedItem, setUserSelectedItem] = useState<ReviewListItemProps | null>(null);\n const selectedItem = userSelectedItem ?? defaultSelectedItem;\n\n const [loadedDiffDoc, setLoadedDiffDoc] = useState<LoadedDiffDoc | null>(null);\n\n const [loadingDiff, setLoadingDiff] = useState(false);\n\n const handleSelect = useCallback((item: ReviewListItemProps | null) => setUserSelectedItem(item), []);\n const value = useMemo<ReviewsSelectionValue>(\n () => ({\n selectedItem,\n setSelectedItem: handleSelect,\n loadedDiffDoc,\n setLoadedDiffDoc,\n loadingDiff,\n setLoadingDiff,\n onApproveAllSuggestions,\n onRejectAllSuggestions,\n onAcceptAndPublish,\n onApplyAllInEditor,\n onRejectAllInEditor,\n onOpenArticleLink,\n onSourceLinkClick,\n breadcrumb,\n suggestionReasonBody,\n hideAcceptAndPublishPublic,\n }),\n [\n selectedItem,\n handleSelect,\n loadedDiffDoc,\n loadingDiff,\n onApproveAllSuggestions,\n onRejectAllSuggestions,\n onAcceptAndPublish,\n onApplyAllInEditor,\n onRejectAllInEditor,\n onOpenArticleLink,\n onSourceLinkClick,\n breadcrumb,\n suggestionReasonBody,\n hideAcceptAndPublishPublic,\n ],\n );\n\n return <ReviewsContext.Provider value={value}>{children}</ReviewsContext.Provider>;\n}\n\nexport function useReviewsSelection(): ReviewsSelectionValue {\n const ctx = useContext(ReviewsContext);\n if (!ctx) {\n throw new Error('useReviewsSelection must be used within ReviewsSelectionProvider');\n }\n\n return ctx;\n}\n","import React, { forwardRef } from 'react';\n\nimport { ReviewListItem, ReviewListItemProps } from './review-list-item';\nimport { useReviewsSelection } from '../context';\n\nimport { cn } from '@/lib/utils';\n\ninterface ReviewListProps {\n items: ReviewListItemProps[];\n className?: string;\n}\n\nexport const ReviewList = forwardRef<HTMLUListElement, ReviewListProps>(({ items, className, ...props }, ref) => {\n const { selectedItem, setSelectedItem } = useReviewsSelection();\n\n return (\n <ul\n ref={ref}\n className={cn(\n 'min-w-0 w-full overflow-hidden bg-muted border border-dark-300 rounded-lg p-2 space-y-2 list-none m-0',\n className,\n )}\n {...props}\n >\n {items.map((item) => (\n <li key={item.id}>\n <ReviewListItem {...item} isSelected={selectedItem?.id === item.id} onClick={() => setSelectedItem(item)} />\n </li>\n ))}\n </ul>\n );\n});\nReviewList.displayName = 'ReviewList';\n","import * as React from 'react';\nimport { CaretDown, CaretUp, CheckCircle, XCircle } from '@phosphor-icons/react';\n\nimport { Button } from '@/components/ui/button';\nimport { ScrollArea } from '@/components/ui/scroll-area';\nimport { brandShadowEffect } from '@/lib/styles';\nimport { cn } from '@/lib/utils';\n\nexport interface SuggestionCardSource {\n /** Eyebrow label, e.g. \"1 day ago from\" — rendered uppercase by the card. */\n label: string;\n /** Display name of the source, e.g. \"team-meetingFEB.mp4\". */\n name: string;\n /**\n * Optional icon next to the source name. Pass a `@phosphor-icons/react` icon.\n */\n icon?: React.ReactNode;\n}\n\n/**\n * Pagination state for the side paginator column. When omitted, the paginator\n * column (and its divider) is not rendered at all.\n */\nexport interface SuggestionCardPagination {\n /** 1-based index of this suggestion within the set. */\n currentIndex: number;\n /** Total number of suggestions in the set. */\n totalCount: number;\n /** Fires when the user navigates to the previous suggestion. Omit to hide the button. */\n onPrevious?: () => void;\n /** Fires when the user navigates to the next suggestion. Omit to hide the button. */\n onNext?: () => void;\n}\n\n/**\n * Visual diff shown between the source row and the reason. Renders as a\n * single bordered row with the old text struck-through in destructive color\n * and the new text in link color.\n *\n * - For replace ops, set both `oldText` and `newText`.\n * - For delete ops, set only `oldText`.\n * - For insert ops, set only `newText`.\n *\n * When both fields are empty/undefined the diff row is not rendered.\n */\nexport interface SuggestionCardDiff {\n /** Original text being removed. Destructive. */\n oldText?: string;\n /** Proposed replacement / insertion. Link color. */\n newText?: string;\n}\n\nexport interface SuggestionCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect'> {\n /**\n * Source metadata shown at the top of the card. Omit to skip the source\n * row entirely — useful when the host doesn't have source info to surface.\n */\n source?: SuggestionCardSource;\n /**\n * Old vs. new text for the suggestion. Renders the bordered diff row\n * between the source and the reason. Omit to skip the diff row entirely.\n */\n diff?: SuggestionCardDiff;\n /**\n * Host-provided explanation for why this suggestion was raised. Renders\n * as the body paragraph below the diff. Omit to skip the reason line.\n */\n reason?: string;\n\n /**\n * Fires when the user accepts this suggestion. When omitted, the accept\n * button is rendered disabled (so the affordance is visible but inert).\n */\n onAccept?: () => void;\n /**\n * Fires when the user rejects this suggestion. When omitted, the reject\n * button is rendered disabled.\n */\n onReject?: () => void;\n /** Disable the accept button while an accept is in-flight. */\n acceptDisabled?: boolean;\n /** Disable the reject button while a reject is in-flight. */\n rejectDisabled?: boolean;\n\n /**\n * Paginator state. When omitted, the whole paginator column (and its\n * dividing border) is not rendered.\n */\n pagination?: SuggestionCardPagination;\n readOnly?: boolean;\n}\n\n/**\n * Floating card that displays a single review suggestion with its source,\n * accept/reject actions, and an optional paginator for navigating within-article\n * suggestions. Purely presentational — the host wires callbacks to the\n * reviewSuggestions plugin / API layer.\n */\nexport const SuggestionCard = React.forwardRef<HTMLDivElement, SuggestionCardProps>(function SuggestionCard(\n {\n source,\n diff,\n reason,\n onAccept,\n onReject,\n acceptDisabled,\n rejectDisabled,\n pagination,\n className,\n readOnly,\n ...props\n },\n ref,\n) {\n const acceptIsDisabled = acceptDisabled || !onAccept;\n const rejectIsDisabled = rejectDisabled || !onReject;\n const hasDiff = Boolean(diff && diff.oldText && diff.newText);\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label=\"Review suggestion\"\n className={cn(\n // The card grows with short suggestions, then caps so long bodies scroll.\n '!grid max-h-[calc(var(--knowledge-review-scroll-height)-250px)] w-full grid-rows-[auto_minmax(0,1fr)_auto] items-stretch gap-4 rounded-lg bg-surface p-4 border shadow-md dark:shadow-brand-dark',\n className,\n )}\n {...props}\n >\n <div className=\"flex min-w-0 flex-col justify-start gap-1\">\n {source && (\n <>\n <span className=\"text-xs font-bold uppercase leading-4 text-default\">{source.label}</span>\n <div className=\"flex items-center gap-0.5 [&_svg]:size-4 [&_svg]:shrink-0\">\n <span className=\"flex items-center text-subtle\" aria-hidden=\"true\">\n {source.icon}\n </span>\n <span className=\"text-sm leading-4 text-subtle break-all\">{source.name}</span>\n </div>\n </>\n )}\n </div>\n\n <div className=\"flex min-w-0 min-h-0 items-stretch gap-2\">\n {/* min-h-0 lets this flex child shrink below its content size so the ScrollArea actually scrolls. */}\n <ScrollArea className=\"-mr-2 min-h-0 flex-1 pr-2\">\n <div className=\"flex w-full flex-col\">\n <span className=\"text-xs font-bold uppercase leading-4 text-default\">Why</span>\n {reason && <span className=\"text-sm leading-4 text-default\">{reason}</span>}\n\n {hasDiff && <SuggestionCardDiffRow oldText={diff?.oldText} newText={diff?.newText} />}\n </div>\n </ScrollArea>\n\n {pagination && pagination.totalCount > 1 && <SuggestionCardPaginator {...pagination} />}\n </div>\n\n <div className=\"flex min-w-0 items-end\">\n {!readOnly && (\n <div className=\"flex shrink-0 items-center gap-2\">\n {!acceptIsDisabled && (\n <Button\n type=\"button\"\n variant=\"default\"\n elevation=\"shadow\"\n size=\"icon\"\n onClick={onAccept}\n aria-label=\"Accept suggestion\"\n className={cn('rounded-md border-border bg-green-400 text-default', brandShadowEffect)}\n >\n <CheckCircle weight=\"regular\" aria-hidden=\"true\" />\n </Button>\n )}\n {!rejectIsDisabled && (\n <Button\n type=\"button\"\n variant=\"destructive\"\n elevation=\"shadow\"\n size=\"icon\"\n onClick={onReject}\n aria-label=\"Reject suggestion\"\n className={cn('rounded-md border-border bg-red-400 text-default', brandShadowEffect)}\n >\n <XCircle weight=\"regular\" aria-hidden=\"true\" />\n </Button>\n )}\n </div>\n )}\n </div>\n </div>\n );\n});\n\n/**\n * Bordered old/new diff row. Old text is rendered first in destructive color,\n * new text follows in link color. Either side can be\n * omitted (delete-only or insert-only).\n */\nfunction SuggestionCardDiffRow({ oldText, newText }: SuggestionCardDiff) {\n return (\n <div className=\"flex flex-col items-baseline gap-2 border-t border-dark-300 py-4 text-sm leading-4 mt-4\">\n {oldText && (\n <div className=\"text-destructive\">\n <span className=\"font-bold\">Replace: </span>\n <span>{oldText}</span>\n </div>\n )}\n {newText && (\n <div className=\"text-blue\">\n <span className=\"font-bold\">With: </span>\n <span>{newText}</span>\n </div>\n )}\n </div>\n );\n}\n\nfunction SuggestionCardPaginator({ currentIndex, totalCount, onPrevious, onNext }: SuggestionCardPagination) {\n const hasPrevious = currentIndex > 1 && !!onPrevious;\n const hasNext = currentIndex < totalCount && !!onNext;\n\n return (\n <div className=\"flex w-10 shrink-0 flex-col items-center justify-center self-stretch py-2 pl-3\">\n <div className=\"flex flex-col items-center gap-0.5\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={onPrevious}\n disabled={!hasPrevious}\n aria-label=\"Previous suggestion\"\n className=\"text-subtlest disabled:invisible\"\n >\n <CaretUp aria-hidden=\"true\" className=\"size-3.5\" />\n </Button>\n <span className=\"text-xs leading-4 text-subtlest tabular-nums\" aria-hidden=\"true\">\n {currentIndex}/{totalCount}\n </span>\n <span className=\"sr-only\" aria-live=\"polite\">\n Suggestion {currentIndex} of {totalCount}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={onNext}\n disabled={!hasNext}\n aria-label=\"Next suggestion\"\n className=\"text-subtlest disabled:invisible\"\n >\n <CaretDown aria-hidden=\"true\" className=\"size-3.5\" />\n </Button>\n </div>\n </div>\n );\n}\n"],"names":["EditCount","count","React","createElement","className","Circle","size","weight","StatusSourceSummary","isNew","isAccepted","editCount","sourceIcon","sourceDescription","onSourceLinkClick","sourceLabel","status","StatusBadge","variant","surface","cn","Button","onClick","ReviewListItem","forwardRef","title","timestamp","icon","description","isSelected","props","ref","type","formatDistance","ReviewsContext","createContext","ReviewsSelectionProvider","children","defaultSelectedItem","onApproveAllSuggestions","onRejectAllSuggestions","onAcceptAndPublish","onApplyAllInEditor","onRejectAllInEditor","onOpenArticleLink","breadcrumb","suggestionReasonBody","hideAcceptAndPublishPublic","userSelectedItem","setUserSelectedItem","useState","selectedItem","loadedDiffDoc","setLoadedDiffDoc","loadingDiff","setLoadingDiff","handleSelect","useCallback","item","value","useMemo","setSelectedItem","Provider","useReviewsSelection","ctx","useContext","Error","ReviewList","items","map","key","id","displayName","SuggestionCard","source","diff","reason","onAccept","onReject","acceptDisabled","rejectDisabled","pagination","readOnly","acceptIsDisabled","rejectIsDisabled","hasDiff","Boolean","oldText","newText","role","Fragment","label","name","ScrollArea","SuggestionCardDiffRow","totalCount","SuggestionCardPaginator","elevation","brandShadowEffect","CheckCircle","XCircle","currentIndex","onPrevious","onNext","hasPrevious","hasNext","disabled","CaretUp","CaretDown"],"mappings":"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"}