@factorialco/f0-react 6.104.0 → 6.106.0

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.
Files changed (27) hide show
  1. package/dist/{ChatPdfThumbnail-CV6YlrJT.js → ChatPdfThumbnail-BkhAKV0r.js} +1 -1
  2. package/dist/ChatPostComposerDialog-D2b_bvdN.js +142 -0
  3. package/dist/{CoachmarkProvider-DPVNJLZU.js → CoachmarkProvider-DDU2DOmn.js} +75 -78
  4. package/dist/{DocumentToolbar-BrZ13rVL.js → DocumentToolbar-BwV5Q0rV.js} +2 -2
  5. package/dist/{DocxViewer-CF3W_zjV.js → DocxViewer-BP1HAgxW.js} +2 -2
  6. package/dist/{F0AiProcessingOverlay-B-MR6cMY.js → F0AiProcessingOverlay-jkio5JGp.js} +728 -766
  7. package/dist/{F0CanvasPanel-DT64c3E-.js → F0CanvasPanel-C8sTxHKI.js} +30476 -30171
  8. package/dist/{F0VideoPlayer-CFHQr9KH.js → F0VideoPlayer-C_ibK2BV.js} +1 -1
  9. package/dist/{LocationMap-96J4YbOy.js → LocationMap-CGP3eGZK.js} +1 -1
  10. package/dist/{SheetViewer-BMj7kc1c.js → SheetViewer-D5BRnR0I.js} +2 -2
  11. package/dist/{TextViewer-CgLPN38a.js → TextViewer-CIIk5GoS.js} +2 -2
  12. package/dist/ai.d.ts +183 -8
  13. package/dist/ai.js +2 -2
  14. package/dist/component-status.js +1 -1
  15. package/dist/{experimental-DjvvLdnE.js → experimental-Ds-2DStT.js} +18468 -17455
  16. package/dist/experimental.d.ts +915 -6
  17. package/dist/experimental.js +161 -159
  18. package/dist/f0.d.ts +190 -8
  19. package/dist/f0.js +205 -205
  20. package/dist/i18n-provider-defaults.d.ts +172 -0
  21. package/dist/i18n-provider-defaults.js +210 -3
  22. package/dist/{index-CLLyZsKL.js → index-BER_ZOwy.js} +3 -3
  23. package/dist/index-BcRGcyyC.js +4 -0
  24. package/dist/{pdfWorker-BgsVA819.js → pdfWorker-KK_gurE8.js} +2 -2
  25. package/dist/styles.css +1 -1
  26. package/package.json +1 -1
  27. package/dist/index-B1S8Pxtc.js +0 -4
@@ -1072,16 +1072,38 @@ declare const ANCHOR_ATTRIBUTE = "data-f0-coachmark";
1072
1072
  */
1073
1073
  export declare const ApplicationFrame: typeof _ApplicationFrame;
1074
1074
 
1075
- declare function _ApplicationFrame({ children, sidebar, banner, ai, aiPromotion, }: ApplicationFrameProps): JSX_2.Element;
1075
+ declare function _ApplicationFrame({ children, sidebar, banner, ai, aiPromotion, sidePanel, }: ApplicationFrameProps): JSX_2.Element;
1076
1076
 
1077
1077
  export declare interface ApplicationFrameProps {
1078
1078
  ai?: Omit<AiChatProviderProps, "children">;
1079
1079
  aiPromotion?: Omit<AiPromotionChatProviderProps, "children">;
1080
+ /**
1081
+ * The side panel, independently of the AI chat. Omit it and the panel
1082
+ * behaves exactly as before: present when `ai.enabled`, absent otherwise.
1083
+ */
1084
+ sidePanel?: ApplicationFrameSidePanelProps;
1080
1085
  banner?: React.ReactNode;
1081
1086
  sidebar: React.ReactNode;
1082
1087
  children: React.ReactNode;
1083
1088
  }
1084
1089
 
1090
+ export declare interface ApplicationFrameSidePanelProps {
1091
+ /**
1092
+ * What may occupy the side panel. The AI chat adds itself when `ai.enabled`;
1093
+ * anything else — a conversation list, an inspector — declares itself here.
1094
+ *
1095
+ * When nothing is available the panel does not exist: no chrome, no reserved
1096
+ * width, no DOM. That is what keeps a product with no assistant from getting
1097
+ * an empty column, and a product with an assistant it cannot use from
1098
+ * getting no column at all.
1099
+ */
1100
+ views?: SidePanelViewDefinition[];
1101
+ /** Edge the panel docks to. @default "right" */
1102
+ side?: "left" | "right";
1103
+ /** Whether the panel may be resized. */
1104
+ resizable?: boolean;
1105
+ }
1106
+
1085
1107
  /**
1086
1108
  * The slice of persisted collection state that was actually applied to the
1087
1109
  * data source during hydration. `sortings: null` means the user had
@@ -1352,7 +1374,7 @@ declare interface BaseChipProps extends VariantProps<typeof chipVariants> {
1352
1374
 
1353
1375
  declare type BaseColor = keyof typeof baseColors;
1354
1376
 
1355
- export declare const BaseCommunityPost: ({ id, author, group, createdAt, title, description, onClick, mediaUrl, event, counters, reactions, inLabel, comment, actions, dropdownItems, noReactionsButton, descriptionExpandable, noDescriptionClamp, hideTitle, }: CommunityPostProps) => JSX_2.Element;
1377
+ export declare const BaseCommunityPost: ({ id, author, group, createdAt, title, description, onClick, mediaUrl, event, counters, reactions, inLabel, comment, actions, dropdownItems, noReactionsButton, descriptionExpandable, noDescriptionClamp, hideTitle, hideGroup, pinned, pinnedLabel, relativeDate, }: CommunityPostProps) => JSX_2.Element;
1356
1378
 
1357
1379
  /**
1358
1380
  * Base data adapter configuration for non-paginated collections
@@ -3984,7 +4006,7 @@ export declare type CommandRunContext = {
3984
4006
  ask: (prompt: string) => void;
3985
4007
  };
3986
4008
 
3987
- export declare const CommunityPost: (({ id, author, group, createdAt, title, description, onClick, mediaUrl, event, counters, reactions, inLabel, comment, actions, dropdownItems, noReactionsButton, descriptionExpandable, noDescriptionClamp, hideTitle, }: CommunityPostProps) => JSX_2.Element) & {
4009
+ export declare const CommunityPost: (({ id, author, group, createdAt, title, description, onClick, mediaUrl, event, counters, reactions, inLabel, comment, actions, dropdownItems, noReactionsButton, descriptionExpandable, noDescriptionClamp, hideTitle, hideGroup, pinned, pinnedLabel, relativeDate, }: CommunityPostProps) => JSX_2.Element) & {
3988
4010
  Skeleton: ({ withEvent, withImage, }: CommunityPostSkeletonProps) => JSX_2.Element;
3989
4011
  };
3990
4012
 
@@ -4046,6 +4068,16 @@ export declare type CommunityPostProps = {
4046
4068
  * unskimmable, which is what the clamp is for.
4047
4069
  */
4048
4070
  noDescriptionClamp?: boolean;
4071
+ /**
4072
+ * Drops "in <community>" from the header — for a container that already names
4073
+ * the community, like a single community's feed, whose channel header carries
4074
+ * that name an inch above every card. Without this the link repeats what you
4075
+ * are already reading and leads nowhere you aren't.
4076
+ *
4077
+ * `group` stays required: it is still the post's community, and a caller that
4078
+ * hides the line today may show it tomorrow without changing what it passes.
4079
+ */
4080
+ hideGroup?: boolean;
4049
4081
  /**
4050
4082
  * Keeps the title as the post's ACCESSIBLE NAME but takes it out of the card —
4051
4083
  * for a container that already shows it, like a dialog carrying the post's
@@ -4057,6 +4089,25 @@ export declare type CommunityPostProps = {
4057
4089
  * break that as well as the post's name.
4058
4090
  */
4059
4091
  hideTitle?: boolean;
4092
+ /**
4093
+ * Marks the post as pinned in its community: a pin beside the date.
4094
+ *
4095
+ * A BADGE, not a control — pinning and unpinning are decisions the container
4096
+ * owns (they need to know who may), and this only says what is already true.
4097
+ */
4098
+ pinned?: boolean;
4099
+ /** Accessible name for the pin badge, e.g. "Pinned post". Required with
4100
+ * `pinned`, since the icon alone says nothing to a screen reader. */
4101
+ pinnedLabel?: string;
4102
+ /**
4103
+ * "2 days ago" instead of "August 25th, 2026 at 3:00 PM".
4104
+ *
4105
+ * For a FEED, where the question a date answers is "how fresh is this" and
4106
+ * the posts scroll past in one column — a full timestamp on every row is
4107
+ * four lines of clerical detail nobody reads. A page or a dialog showing one
4108
+ * post keeps the exact date, which is the default.
4109
+ */
4110
+ relativeDate?: boolean;
4060
4111
  };
4061
4112
 
4062
4113
  export declare const CommunityPostSkeleton: ({ withEvent, withImage, }: CommunityPostSkeletonProps) => JSX_2.Element;
@@ -5138,6 +5189,10 @@ declare const defaultTranslations: {
5138
5189
  readonly placeholder: "Select a company";
5139
5190
  };
5140
5191
  };
5192
+ readonly sidePanel: {
5193
+ readonly resize: "Resize side panel";
5194
+ readonly width: "{{width}} pixels";
5195
+ };
5141
5196
  readonly previous: "Previous";
5142
5197
  readonly next: "Next";
5143
5198
  };
@@ -5803,6 +5858,174 @@ declare const defaultTranslations: {
5803
5858
  readonly emptyConversationDescription: "Send a message to start the conversation.";
5804
5859
  readonly error: "Couldn't load this conversation";
5805
5860
  readonly loadingOlder: "Loading earlier messages…";
5861
+ readonly newPosts: "New posts";
5862
+ readonly newPostsCount: {
5863
+ readonly one: "{{count}} new post";
5864
+ readonly other: "{{count}} new posts";
5865
+ };
5866
+ readonly unreadMentionCount: {
5867
+ readonly one: "{{count}} unread, mentions you";
5868
+ readonly other: "{{count}} unread, mentions you";
5869
+ };
5870
+ readonly post: {
5871
+ readonly in: "in";
5872
+ readonly comment: "Comment";
5873
+ readonly views: {
5874
+ readonly one: "{{count}} view";
5875
+ readonly other: "{{count}} views";
5876
+ };
5877
+ readonly comments: {
5878
+ readonly one: "{{count}} comment";
5879
+ readonly other: "{{count}} comments";
5880
+ };
5881
+ };
5882
+ readonly community: {
5883
+ readonly readOnly: "You can't post in this community";
5884
+ readonly writePost: "Write a post…";
5885
+ readonly newPost: "New post";
5886
+ readonly postTitle: "Title";
5887
+ readonly postTitlePlaceholder: "Add a title";
5888
+ readonly postBodyPlaceholder: "Share something with the community…";
5889
+ readonly publish: "Publish";
5890
+ readonly cancel: "Cancel";
5891
+ readonly discardTitle: "Discard this post?";
5892
+ readonly discardDescription: "What you've written won't be saved.";
5893
+ readonly discard: "Discard";
5894
+ readonly keepEditing: "Keep editing";
5895
+ readonly publishError: "Couldn't publish this post";
5896
+ readonly emptyTitle: "No posts yet";
5897
+ readonly emptyDescription: "Posts shared in this community will appear here.";
5898
+ readonly emptyDescriptionCanPost: "Be the first to post here.";
5899
+ readonly pinnedPost: "Pinned post";
5900
+ readonly pinnedPosts: "Pinned";
5901
+ readonly pinnedPostsCount: {
5902
+ readonly one: "{{count}} pinned post";
5903
+ readonly other: "{{count}} pinned posts";
5904
+ };
5905
+ readonly pinPost: "Pin post";
5906
+ readonly unpinPost: "Unpin post";
5907
+ readonly goToPost: "Go to post";
5908
+ readonly noPinnedPosts: "No pinned posts";
5909
+ readonly noPinnedPostsDescription: "Pin a post and it stays at the top of this community.";
5910
+ readonly scheduledPosts: "Scheduled";
5911
+ readonly scheduledPostsCount: {
5912
+ readonly one: "{{count}} scheduled post";
5913
+ readonly other: "{{count}} scheduled posts";
5914
+ };
5915
+ readonly scheduledEvent: "Event";
5916
+ /** The preview's bar, e.g. "Publishes 4 Jun at 09:00". */
5917
+ readonly publishesAt: "Publishes {{when}}";
5918
+ readonly noScheduledPosts: "Nothing scheduled";
5919
+ readonly noScheduledPostsDescription: "Posts you schedule will wait here until their time.";
5920
+ readonly scheduledActions: "Scheduled post actions";
5921
+ readonly publishNow: "Publish now";
5922
+ readonly cancelScheduled: "Cancel";
5923
+ readonly draftPosts: "Drafts";
5924
+ readonly draftPostsCount: {
5925
+ readonly one: "{{count}} draft";
5926
+ readonly other: "{{count}} drafts";
5927
+ };
5928
+ /** Where a scheduled row prints its date, a draft prints this. */
5929
+ readonly draftUntitled: "Untitled post";
5930
+ readonly draftSavedAt: "Saved {{when}}";
5931
+ readonly publishDraft: "Publish";
5932
+ readonly deleteDraft: "Delete draft";
5933
+ readonly shelfLabel: "Pinned, scheduled and draft posts";
5934
+ };
5935
+ };
5936
+ readonly communities: {
5937
+ readonly composer: {
5938
+ readonly createPost: "Create post";
5939
+ readonly createEvent: "Create event";
5940
+ readonly editPost: "Edit post";
5941
+ readonly basicInformation: "Basic information";
5942
+ readonly postSettings: "Post settings";
5943
+ readonly postCover: "Post cover";
5944
+ readonly addMedia: "Drag and drop or click here";
5945
+ readonly addMediaSubtitle: "any image, video or GIF";
5946
+ readonly addMediaSize: "1200x600px";
5947
+ readonly deletePhoto: "Delete photo";
5948
+ readonly deleteVideo: "Delete video";
5949
+ readonly coverActions: "Cover options";
5950
+ readonly title: "Title";
5951
+ readonly titlePlaceholder: "e.g. World Mental Health Day";
5952
+ readonly description: "Description";
5953
+ readonly descriptionPlaceholder: "Share what's special about it";
5954
+ readonly attachmentsSizePerPost: "Up to 150 MB in attachments per post";
5955
+ readonly previousAttachments: "Previously added attachments";
5956
+ readonly isEventLabel: "This is an event";
5957
+ readonly isEventDescription: "Select this to include a date, time, and location for this event.";
5958
+ readonly eventStartDate: "Event start date";
5959
+ readonly eventStartTime: "Start time";
5960
+ readonly eventLocation: "Event location";
5961
+ readonly eventLocationPlaceholder: "Where will this take place?";
5962
+ readonly requireAction: "Require action";
5963
+ readonly requireActionDescription: "Select how you want to track that users have taken the required action";
5964
+ readonly actionType: "Action type";
5965
+ readonly actionTypePlaceholder: "Select action type";
5966
+ readonly actionTypeAcknowledge: "Acknowledge post";
5967
+ readonly actionTypeAcknowledgeDescription: "Employees read and acknowledge the post";
5968
+ readonly actionTypeWatchVideo: "Watch video";
5969
+ readonly actionTypeWatchVideoDescription: "Employees watch the entire video";
5970
+ readonly actionTypeClickedLink: "Clicked a link";
5971
+ readonly actionTypeClickedLinkDescription: "Employees click the first link in the post";
5972
+ readonly actionTypeComingSoon: "Coming soon";
5973
+ readonly continue: "Continue";
5974
+ readonly publishTitle: "Publish";
5975
+ readonly publishDescription: "When publishing the post, it will be visible in the dashboard of each employee.";
5976
+ readonly publish: "Publish";
5977
+ readonly selectCommunity: "Select community";
5978
+ readonly allowCommentsAndReactions: "Allow comments and reactions.";
5979
+ readonly sendEmailNotification: "Send email notification";
5980
+ readonly schedulePost: "Schedule post";
5981
+ readonly scheduleDate: "Date";
5982
+ readonly scheduleTime: "Time";
5983
+ readonly confirm: "Confirm";
5984
+ readonly cancel: "Cancel";
5985
+ readonly saveAsDraft: "Save as draft";
5986
+ readonly save: "Save";
5987
+ readonly cantBeEmpty: "can't be empty";
5988
+ readonly invalidInput: "Invalid input. Only alphanumeric characters are allowed";
5989
+ readonly publishedSuccess: "The post has been published";
5990
+ readonly scheduledSuccess: "The post has been scheduled";
5991
+ readonly draftSuccess: "The post has been saved as draft";
5992
+ readonly publishError: "There was an error creating the post, please try again.";
5993
+ };
5994
+ readonly detail: {
5995
+ readonly postActions: "Post actions";
5996
+ readonly edit: "Edit post";
5997
+ readonly delete: "Delete post";
5998
+ readonly deleteDescription: "Are you sure you want to delete this post? This action cannot be undone.";
5999
+ readonly proceed: "Proceed";
6000
+ readonly turnInteractionsOff: "Turn comments and reactions off";
6001
+ readonly turnInteractionsOn: "Turn comments and reactions on";
6002
+ readonly insights: "Insights";
6003
+ readonly visits: {
6004
+ readonly one: "Visit";
6005
+ readonly other: "Visits";
6006
+ };
6007
+ readonly postViews: "Post views";
6008
+ readonly anonymous: "Anonymous";
6009
+ readonly comments: {
6010
+ readonly one: "{{count}} comment";
6011
+ readonly other: "{{count}} comments";
6012
+ };
6013
+ readonly commentPlaceholder: "Click here to write a comment...";
6014
+ readonly submit: "Submit";
6015
+ readonly deleteComment: "Delete comment";
6016
+ readonly deleteCommentDescription: "Are you sure you want to delete this comment? This action cannot be undone.";
6017
+ readonly editComment: "Edit";
6018
+ readonly acknowledgeRequired: "Post acknowledgement required";
6019
+ readonly acknowledge: "I acknowledge";
6020
+ readonly acknowledgeLater: "Acknowledge later";
6021
+ readonly acknowledgedOn: "Acknowledged on {{date}}, at {{time}}";
6022
+ readonly closePostTitle: "Close post?";
6023
+ readonly closePostDescription: "You haven't acknowledged this post yet. You can acknowledge it later.";
6024
+ readonly closePost: "Close post";
6025
+ readonly continueReading: "Continue reading";
6026
+ readonly close: "Close";
6027
+ readonly home: "Home";
6028
+ };
5806
6029
  };
5807
6030
  readonly dataChart: {
5808
6031
  readonly heatmapNotSupported: "Heatmap not supported at this size";
@@ -6432,6 +6655,14 @@ declare type DropdownInternalProps = {
6432
6655
  * @default false
6433
6656
  */
6434
6657
  disabled?: boolean;
6658
+ /**
6659
+ * Where the menu is portalled. Defaults to the document body; pass the
6660
+ * element of a surrounding modal layer — a dialog's own content node, which
6661
+ * it publishes as `portalContainer` — so that layer's focus trap CONTAINS
6662
+ * the menu instead of fighting it. Two traps over the same document push
6663
+ * focus back and forth until the call stack gives out.
6664
+ */
6665
+ container?: HTMLElement | null;
6435
6666
  } & DataAttributes_3;
6436
6667
 
6437
6668
  export declare type DropdownItem = DropdownItemObject | DropdownItemSeparator | DropdownItemLabel;
@@ -7694,6 +7925,28 @@ export declare type F0ChatChannel = {
7694
7925
  * generic i18n line.
7695
7926
  */
7696
7927
  readOnlyNotice?: string;
7928
+ /**
7929
+ * Community only. The posts kept at the top of this community, newest pin
7930
+ * first — SUMMARIES, not the posts themselves.
7931
+ *
7932
+ * Not derived from `messages` on purpose: the whole point of pinning is that
7933
+ * the post is far enough back to be hard to find, which usually means outside
7934
+ * the loaded window. Deriving it would make the bar appear and disappear as
7935
+ * the reader scrolls. Empty or absent ⇒ no bar.
7936
+ */
7937
+ pinnedPosts?: F0ChatPinnedPost[];
7938
+ /**
7939
+ * Community only. Posts written but not yet visible to anyone — scheduled for
7940
+ * a moment that hasn't arrived. They are NOT in `messages` (nobody can read
7941
+ * them yet), so this is the only place they exist for the panel.
7942
+ */
7943
+ scheduledPosts?: F0ChatScheduledPost[];
7944
+ /**
7945
+ * Community only. The current user's own unfinished posts — see
7946
+ * {@link F0ChatDraftPost} for why they are not scheduled posts without a
7947
+ * date. Only ever THEIRS: nobody sees anyone else's drafts.
7948
+ */
7949
+ draftPosts?: F0ChatDraftPost[];
7697
7950
  };
7698
7951
 
7699
7952
  /** A status badge shown in the header next to the title (e.g. on vacation, away).
@@ -7711,7 +7964,15 @@ export declare type F0ChatChannelStatus = {
7711
7964
  * {@link F0ChatCapabilities}), so a read-only channel needs no configuration at
7712
7965
  * all while a host that has a poster still turns `canSend` back on.
7713
7966
  */
7714
- export declare type F0ChatChannelType = "dm" | "group" | "announcement";
7967
+ /**
7968
+ * `community` is a FEED: its items are {@link F0ChatPost}s — a title, a body
7969
+ * with formatting, a cover — rather than chat bubbles, and what you do at the
7970
+ * bottom is publish rather than send. Reading and reacting are on by default;
7971
+ * posting is off until the host grants it, because a community is a place most
7972
+ * people read and a few write in. It differs from an `announcement` in exactly
7973
+ * that: a noticeboard is written by the product, a community by its members.
7974
+ */
7975
+ export declare type F0ChatChannelType = "dm" | "group" | "announcement" | "community";
7715
7976
 
7716
7977
  /**
7717
7978
  * What the composer can produce. Cards are authored by the host (a seeded
@@ -7720,6 +7981,66 @@ export declare type F0ChatChannelType = "dm" | "group" | "announcement";
7720
7981
  */
7721
7982
  export declare type F0ChatComposableAttachment = Exclude<F0ChatAttachment, F0ChatCardAttachment>;
7722
7983
 
7984
+ /**
7985
+ * What the post composer produces — the whole form, not a subset.
7986
+ *
7987
+ * Files go RAW, un-uploaded: a post's media belongs in its own storage rather
7988
+ * than the chat's attachment bucket, so reusing `uploadFiles` would leave it in
7989
+ * the wrong place — and this lets the host publish atomically (upload + create)
7990
+ * in one transaction it can roll back whole.
7991
+ */
7992
+ export declare type F0ChatCreatePostInput = {
7993
+ title: string;
7994
+ /** Sanitized HTML from the editor. Empty is valid: a headline with a photo
7995
+ * is a post. */
7996
+ description?: string;
7997
+ /** The cover, raw. One file: an image or a video, never both. */
7998
+ cover?: File | null;
7999
+ /** Files attached to the body. */
8000
+ files?: File[];
8001
+ mentions?: F0ChatMention[];
8002
+ /** Which community it goes to. The composer only ever offers the ones the
8003
+ * host said the user can post in. */
8004
+ communityId?: string;
8005
+ /** Present ⇒ the post announces an event, and its card draws that instead of
8006
+ * the cover. */
8007
+ event?: F0ChatPostEvent;
8008
+ /** @default true */
8009
+ allowCommentsAndReactions?: boolean;
8010
+ /** @default false — mailing everyone is opt-in, not opt-out. */
8011
+ sendNotifications?: boolean;
8012
+ /** Present ⇒ readers must acknowledge it. */
8013
+ requiredAction?: F0ChatPostRequiredAction["type"];
8014
+ /**
8015
+ * When it becomes visible. `undefined` publishes now, an ISO string schedules
8016
+ * it, and `null` saves a draft — the three modes the composer offers, in one
8017
+ * field, exactly as the product's own API models them.
8018
+ */
8019
+ publishedAt?: string | null;
8020
+ };
8021
+
8022
+ /**
8023
+ * A post written and kept, with no date and no audience yet.
8024
+ *
8025
+ * ITS OWN LIST, not a scheduled post without a date. Pinned and scheduled are
8026
+ * facts about the CHANNEL — everyone who publishes there sees the same queue —
8027
+ * while a draft is one person's, and folding them together leaves "whose
8028
+ * drafts?" unanswered in the one place it must not be. The verbs differ for the
8029
+ * same reason: a draft has no moment to bring forward, so it is published or
8030
+ * deleted, never "published NOW" or "cancelled".
8031
+ */
8032
+ declare type F0ChatDraftPost = {
8033
+ id: string;
8034
+ /** May be empty — a draft is unfinished by definition, title included. */
8035
+ title: string;
8036
+ /** ISO — when it was last written to, which is how drafts are ordered. */
8037
+ savedAt: string;
8038
+ /** As {@link F0ChatPinnedPost.excerpt}. */
8039
+ excerpt?: string;
8040
+ /** As {@link F0ChatPinnedPost.thumbnailUrl}. */
8041
+ thumbnailUrl?: string;
8042
+ };
8043
+
7723
8044
  /**
7724
8045
  * Edits applied to an existing message. Text, mentions and attachments are all
7725
8046
  * editable; the host maps this to the transport's partial-update (factorial →
@@ -7831,12 +8152,37 @@ export declare type F0ChatEvents = {
7831
8152
  onCardActivated?: (p: {
7832
8153
  source: "card" | "action";
7833
8154
  }) => void;
8155
+ /** Opening a post from the feed. `source` separates a click on the card from
8156
+ * one on the Comment button. Carries no title. */
8157
+ onPostOpened?: (p: {
8158
+ source: "card" | "comment";
8159
+ }) => void;
8160
+ /** An entry of a post's overflow menu was chosen. The host already knows what
8161
+ * its own action does — this is here so the MENU's use is measurable. */
8162
+ onPostActionInvoked?: (p: {
8163
+ actionId: string;
8164
+ }) => void;
8165
+ /** The post composer was opened. Pair with `onPostCompositionCancelled` to
8166
+ * measure abandonment — the host only sees posts that were published. */
8167
+ onPostCompositionStarted?: () => void;
8168
+ /** The composer was dismissed without publishing. `hadDraft` distinguishes
8169
+ * an accidental open from giving up on written text. */
8170
+ onPostCompositionCancelled?: (p: {
8171
+ hadDraft: boolean;
8172
+ }) => void;
7834
8173
  onSearchOpened?: () => void;
7835
8174
  onSearchResultNavigated?: (p: {
7836
8175
  direction: "next" | "prev";
7837
8176
  }) => void;
7838
8177
  onJumpedToQuotedMessage?: () => void;
7839
8178
  onJumpedToBottom?: () => void;
8179
+ /** The community shelf was opened on one of its two lists. */
8180
+ onShelfOpened?: (p: {
8181
+ list: "pinned" | "scheduled" | "draft";
8182
+ }) => void;
8183
+ /** A scheduled post was opened from the shelf. Its counterpart for pinned
8184
+ * posts is `openPost`'s `source: "pinned"`. */
8185
+ onScheduledPostOpened?: () => void;
7840
8186
  };
7841
8187
 
7842
8188
  export declare type F0ChatFileAttachment = {
@@ -7909,7 +8255,7 @@ export declare type F0ChatImageAttachment = {
7909
8255
  };
7910
8256
 
7911
8257
  /** Anything that can appear in the transcript, oldest → newest. */
7912
- export declare type F0ChatItem = F0ChatMessage | F0ChatSystemMessage;
8258
+ export declare type F0ChatItem = F0ChatMessage | F0ChatSystemMessage | F0ChatPost;
7913
8259
 
7914
8260
  /**
7915
8261
  * Open Graph preview of a URL in the body (WhatsApp-style card above the text).
@@ -8049,6 +8395,230 @@ export declare type F0ChatMessageReply = {
8049
8395
  */
8050
8396
  export declare type F0ChatMessageStatus = "sending" | "sent" | "delivered" | "read" | "failed";
8051
8397
 
8398
+ /**
8399
+ * A pinned post as the bar needs it: enough to name it, and its id to jump to.
8400
+ * The post itself is fetched by the jump, not carried here.
8401
+ */
8402
+ declare type F0ChatPinnedPost = {
8403
+ id: string;
8404
+ title: string;
8405
+ /** ISO — when it was pinned, which is what orders the list. */
8406
+ pinnedAt: string;
8407
+ /**
8408
+ * The opening of the body as PLAIN TEXT — the row clamps it to two lines.
8409
+ * Titles alone read as a list of headlines, and two lines of the post is
8410
+ * usually what tells you whether this is the one you were looking for.
8411
+ *
8412
+ * Plain text, not the post's HTML: this is a preview, so the host strips
8413
+ * (`stripHtml`) rather than F0 rendering markup it would then have to clamp.
8414
+ */
8415
+ excerpt?: string;
8416
+ /**
8417
+ * The post's cover, as a THUMBNAIL beside the title. A feed of photographs is
8418
+ * remembered as photographs — the picture finds the post faster than the
8419
+ * headline does. Absent ⇒ the row is text, full width.
8420
+ */
8421
+ thumbnailUrl?: string;
8422
+ };
8423
+
8424
+ /**
8425
+ * A COMMUNITY POST: the third kind of transcript item, and the only one that is
8426
+ * neither a bubble nor a centered line.
8427
+ *
8428
+ * It's an item rather than a separate `runtime.posts` list because everything a
8429
+ * feed needs is already built around {@link F0ChatItem}: day separators, the
8430
+ * unread divider, `markRead`, pagination, jump-to-item, the sticky date pill
8431
+ * and the sidebar badge all fall out of `id` + `createdAt`, and each would need
8432
+ * a second implementation if posts lived outside.
8433
+ *
8434
+ * The payload is DATA AND NOTHING ELSE — no handlers, no ReactNode: it is what
8435
+ * a transport can carry, the same rule as {@link F0ChatCardAttachment}. What a
8436
+ * click does comes from {@link F0ChatRuntime.openPost}; what its menu offers,
8437
+ * from {@link F0ChatRuntime.postActions}.
8438
+ *
8439
+ * What F0 DERIVES and the host must not send: the words "in" and "Comment"
8440
+ * (i18n), and the wording of the counters (numbers on the wire,
8441
+ * `{{count}} comments` at the edge; otherwise the host translates what F0
8442
+ * already translates). The community's name is the one exception, and only in
8443
+ * an aggregated feed — see {@link F0ChatPost.community}.
8444
+ */
8445
+ export declare type F0ChatPost = {
8446
+ type: "post";
8447
+ id: string;
8448
+ /** ISO — feeds day separators, ordering and unread exactly like a message's. */
8449
+ createdAt: string;
8450
+ /**
8451
+ * Who wrote it. The SAME identity type as a message's author, so hover cards,
8452
+ * mentions and read receipts keep meaning one thing across the union. Omit it
8453
+ * when the community itself publishes (there is no person to name).
8454
+ */
8455
+ author?: F0ChatUser;
8456
+ /**
8457
+ * Whether the current user wrote it. Feeds the default edit/delete policy in
8458
+ * `postActions` — NOT any alignment: a post is never tinted "mine", it takes
8459
+ * the full width for everyone.
8460
+ */
8461
+ isMine?: boolean;
8462
+ /** One-line headline, always visible (the card clamps it to two lines). */
8463
+ title: string;
8464
+ /**
8465
+ * The body, as SANITIZED HTML — posts are written in a rich text editor, so
8466
+ * unlike a message's plain `body` this one carries formatting. Sanitizing is
8467
+ * the host's job: F0 hands it straight to the card (`PostDescription`), and a
8468
+ * transport that returns raw user HTML would inject it here.
8469
+ */
8470
+ description?: string;
8471
+ /**
8472
+ * An image or a video. Drawn in a 16:9 box RESERVED BEFORE it loads, so a
8473
+ * post never changes height after being measured — which is what a
8474
+ * virtualized transcript demands of any row (see `chatRowHeightEstimate`).
8475
+ */
8476
+ mediaUrl?: string;
8477
+ /** An event announcement, drawn INSTEAD of `mediaUrl`. */
8478
+ event?: F0ChatPostEvent;
8479
+ /** How many people opened it. Omit when the host doesn't count visits — the
8480
+ * counters line then shows comments alone. */
8481
+ viewCount?: number;
8482
+ /**
8483
+ * How many comments it has. Always present: "0 comments" is information (it's
8484
+ * an invitation), whereas a missing counter reads as a broken card.
8485
+ */
8486
+ commentCount: number;
8487
+ /**
8488
+ * Reactions, in the SAME shape as a message's — so they go through the same
8489
+ * {@link F0ChatRuntime.toggleReaction} and {@link F0ChatRuntime.loadReactionUsers}
8490
+ * with the post's id as the item id. A post with no reactions still shows the
8491
+ * picker: that is the affordance for adding the first.
8492
+ */
8493
+ reactions?: F0ChatReaction[];
8494
+ /**
8495
+ * Files hanging off the post. Not shown on the feed card — a row of download
8496
+ * chips is not something you skim past — so these only ever paint in the
8497
+ * detail view.
8498
+ */
8499
+ attachments?: F0ChatPostAttachment[];
8500
+ /**
8501
+ * Whether this post takes comments and reactions at all. The author can turn
8502
+ * them off per post, and when they are off BOTH disappear — the reaction bar
8503
+ * and the comment section are one block, and half of it is worse than none.
8504
+ * @default true
8505
+ */
8506
+ allowCommentsAndReactions?: boolean;
8507
+ /**
8508
+ * An action the reader has to take before the post counts as done. Present
8509
+ * ⇒ the detail view shows the acknowledgement bar; `completedAt` decides
8510
+ * whether it asks or confirms.
8511
+ */
8512
+ requiredAction?: F0ChatPostRequiredAction;
8513
+ /**
8514
+ * Whether the current user may edit, delete or reconfigure this post — the
8515
+ * host's own policy, not something F0 can infer from `isMine` (a moderator
8516
+ * manages posts that aren't theirs). Drives the detail view's overflow menu.
8517
+ */
8518
+ canManage?: boolean;
8519
+ /**
8520
+ * ISO ⇒ this post is pinned in its community, and its card says so.
8521
+ *
8522
+ * Only the badge: the BAR reads {@link F0ChatChannel.pinnedPosts}, because a
8523
+ * pinned post is usually not in the loaded window at all.
8524
+ */
8525
+ pinnedAt?: string;
8526
+ /**
8527
+ * Where the post was published, named on the card as "… in Barcelona".
8528
+ *
8529
+ * ONLY for an aggregated feed — a channel that gathers posts from several
8530
+ * communities at once. In a single community's channel the answer is already
8531
+ * the channel title an inch above every card, so sending it there prints the
8532
+ * same word twice per post; omit it and F0 draws no origin at all.
8533
+ *
8534
+ * That makes this the one field where the host tells F0 something F0 would
8535
+ * otherwise derive, and the reason is that an aggregated feed is the one
8536
+ * place where the derivation is wrong: "which of my communities is this
8537
+ * from?" is the question the reader actually has, and the channel can't
8538
+ * answer it.
8539
+ *
8540
+ * Clicking it calls {@link F0ChatRuntime.openCommunity}; without that
8541
+ * handler the name is still drawn, just not as a link.
8542
+ */
8543
+ community?: F0ChatPostCommunity;
8544
+ };
8545
+
8546
+ /**
8547
+ * A single entry in a post's overflow menu (edit, delete, pin, report). Already
8548
+ * localized, like {@link F0ChatHeaderAction.label} — F0 never knows which of
8549
+ * these the current user is allowed, so the host builds the list per post.
8550
+ */
8551
+ export declare type F0ChatPostAction = {
8552
+ id: string;
8553
+ label: string;
8554
+ icon?: IconType;
8555
+ onClick: (post: F0ChatPost) => void;
8556
+ /** Renders the entry in red (delete, report). */
8557
+ critical?: boolean;
8558
+ };
8559
+
8560
+ /** A file attached to a post: the chip is a download link, nothing more. */
8561
+ export declare type F0ChatPostAttachment = {
8562
+ id: string;
8563
+ filename: string;
8564
+ url: string;
8565
+ };
8566
+
8567
+ /** A comment on a post. */
8568
+ export declare type F0ChatPostComment = {
8569
+ id: string;
8570
+ author: F0ChatUser;
8571
+ /** Sanitized HTML — comments carry mentions, so they are not plain text. */
8572
+ text: string;
8573
+ createdAt: string;
8574
+ /** Whether the current user may edit or delete it. */
8575
+ isMine?: boolean;
8576
+ };
8577
+
8578
+ /** The community a post came from, for an aggregated feed's origin label. */
8579
+ declare type F0ChatPostCommunity = {
8580
+ id: string;
8581
+ /** As the reader knows it — "Barcelona", "Company news". No `#`. */
8582
+ name: string;
8583
+ };
8584
+
8585
+ /**
8586
+ * An event a post announces (a talk, an offsite, a townhall). It takes the
8587
+ * card's MEDIA SLOT — it replaces the cover rather than stacking under it —
8588
+ * which is why both are optional and only one is ever drawn.
8589
+ *
8590
+ * `date` is ISO like every other timestamp in this file: the item has to
8591
+ * survive a transport, and a `Date` doesn't. F0 parses it once, at the render
8592
+ * edge.
8593
+ */
8594
+ export declare type F0ChatPostEvent = {
8595
+ title: string;
8596
+ /** When the event HAPPENS — not when the post was published. */
8597
+ date: string;
8598
+ /** Where: a room, an office, a URL if it's remote. */
8599
+ place?: string;
8600
+ mediaUrl?: string;
8601
+ };
8602
+
8603
+ /**
8604
+ * "Read and acknowledge" — the only action type that exists. A union of one on
8605
+ * purpose: the product ships two more as "Coming soon", and a bare string here
8606
+ * would let a host send one that nothing renders.
8607
+ */
8608
+ export declare type F0ChatPostRequiredAction = {
8609
+ type: "acknowledge";
8610
+ /** ISO, when the CURRENT user completed it. Absent ⇒ still pending. */
8611
+ completedAt?: string;
8612
+ };
8613
+
8614
+ /** One entry of "who has opened this post". */
8615
+ export declare type F0ChatPostVisit = {
8616
+ id: string;
8617
+ /** Absent when the host cannot resolve the visitor — rendered as "Anonymous". */
8618
+ author?: F0ChatUser;
8619
+ createdAt: string;
8620
+ };
8621
+
8052
8622
  export declare type F0ChatProps = {
8053
8623
  /** Whether the hosting panel is in fullscreen (controls the header toggle icon). */
8054
8624
  isFullscreen?: boolean;
@@ -8231,6 +8801,107 @@ export declare type F0ChatRuntime = {
8231
8801
  * ignore the parameter.
8232
8802
  */
8233
8803
  markRead?: (untilMessageId?: string) => void | Promise<void>;
8804
+ /**
8805
+ * Publish a post (community channels). Same optimistic contract as
8806
+ * `sendMessage` — client-side id, synchronous echo with `type: "post"`,
8807
+ * reconcile by id — with ONE deliberate difference: it returns a promise the
8808
+ * composer AWAITS. A post is written in a dialog, and the dialog has to stay
8809
+ * open until it knows the post landed; losing a page of text to a dropped
8810
+ * request is a different order of loss from losing a line.
8811
+ *
8812
+ * Omit it and the publish affordance never mounts, even where `canSend` is
8813
+ * true — there would be nowhere for the post to go.
8814
+ */
8815
+ createPost?: (input: F0ChatCreatePostInput) => Promise<void>;
8816
+ /**
8817
+ * Open a post — its page or its dialog, with the comments. F0 owns no
8818
+ * navigation, so the card, the Comment button and the counter all call here;
8819
+ * `source` tells them apart for the host's telemetry.
8820
+ *
8821
+ * Without it the card is not clickable — and `CommunityPost` drops the
8822
+ * pointer cursor, the hover tint and the focus ring on its own, which is
8823
+ * exactly what should happen when there is nowhere to go.
8824
+ */
8825
+ openPost?: (id: string, context: {
8826
+ source: "card" | "comment" | "pinned";
8827
+ }) => void;
8828
+ /**
8829
+ * Go to a community, from the origin label an aggregated feed puts on each
8830
+ * card (see {@link F0ChatPost.community}).
8831
+ *
8832
+ * Separate from `openPost` because it is a different destination — the
8833
+ * community, not the post — and hosts wire the two to different routes.
8834
+ * Omit it and the name is still drawn, just not clickable: an aggregated feed
8835
+ * that cannot navigate should still say where each post came from.
8836
+ */
8837
+ openCommunity?: (communityId: string) => void;
8838
+ /**
8839
+ * Each post's overflow menu. FUNCTION form, like `headerActions`: every post
8840
+ * offers exactly what the user may do to THAT post, and only the host knows.
8841
+ * F0 keeps it behind a ref (like {@link F0ChatEvents}), so it can be rebuilt
8842
+ * every render without re-rendering the feed.
8843
+ */
8844
+ postActions?: (post: F0ChatPost) => F0ChatPostAction[];
8845
+ /**
8846
+ * The post whose page is open right now, beside the feed. Its card is drawn
8847
+ * SELECTED, so the feed keeps saying where you are in it.
8848
+ *
8849
+ * The host's to tell, not F0's: `openPost` hands over and F0 hears nothing
8850
+ * back — where it opened, whether the reader then navigated somewhere else,
8851
+ * whether they arrived on that post by a link instead of from this feed. A
8852
+ * card that lit up on click and stayed lit would be lying by the second page.
8853
+ *
8854
+ * Omit and no card is ever selected.
8855
+ */
8856
+ activePostId?: string;
8857
+ /**
8858
+ * Take a post off the community's pinned list.
8859
+ *
8860
+ * PINNING is not here: it is a decision about one post, taken where that post
8861
+ * is, so it belongs in `postActions` alongside edit and delete — where the
8862
+ * host already decides who may do what. Unpinning is taken looking at ALL the
8863
+ * pins at once, and that place — the pinned list — exists only inside F0, so
8864
+ * there is nowhere else to put it.
8865
+ *
8866
+ * Omit and the pinned list is read-only (still a way to find them).
8867
+ */
8868
+ unpinPost?: (postId: string) => void | Promise<void>;
8869
+ /**
8870
+ * Each scheduled post's menu — publish now, edit, cancel. Same story as
8871
+ * `postActions`: only the host knows what its API allows, and every one of
8872
+ * these writes to the post backend rather than the chat transport.
8873
+ */
8874
+ scheduledActions?: (post: F0ChatScheduledPost) => F0ChatShelfAction[];
8875
+ /**
8876
+ * Open a scheduled post. Separate from `openPost` because it is NOT an
8877
+ * {@link F0ChatPost}: it has no counters, no reactions and no comments —
8878
+ * nothing has happened to it yet. The host decides what it shows; a
8879
+ * PREVIEW of the post as the community will see it is the useful answer.
8880
+ *
8881
+ * Without it the shelf's scheduled rows are not clickable, and the row's
8882
+ * menu is the only way in.
8883
+ */
8884
+ openScheduledPost?: (postId: string) => void;
8885
+ /**
8886
+ * Each draft's menu — publish, delete. Kept apart from `scheduledActions`
8887
+ * because the verbs are not the same ones: there is no moment to bring
8888
+ * forward and nothing to cancel.
8889
+ */
8890
+ draftActions?: (post: F0ChatDraftPost) => F0ChatShelfAction[];
8891
+ /**
8892
+ * Open a draft. The useful answer is the COMPOSER with the draft loaded —
8893
+ * what you want from something half-written is to keep writing it — which is
8894
+ * also why this is not `openScheduledPost` with a different argument: that
8895
+ * one previews, this one edits.
8896
+ *
8897
+ * Without it the shelf's draft rows are not clickable.
8898
+ */
8899
+ openDraftPost?: (postId: string) => void;
8900
+ /**
8901
+ * Escape hatch: a host with its own post-creation flow opens it here, and F0
8902
+ * does not mount its dialog. Present ⇒ `createPost` is never called.
8903
+ */
8904
+ composePost?: () => void;
8234
8905
  /**
8235
8906
  * Per-channel permissions (frozen / read-only channels, moderation…). Omit
8236
8907
  * for the default policy — see {@link F0ChatCapabilities}.
@@ -8283,6 +8954,20 @@ export declare type F0ChatRuntime = {
8283
8954
  loadMessageContext?: (idOrLatest: string) => Promise<void>;
8284
8955
  };
8285
8956
 
8957
+ /** A post waiting for its moment. */
8958
+ declare type F0ChatScheduledPost = {
8959
+ id: string;
8960
+ title: string;
8961
+ /** ISO — when it becomes visible. */
8962
+ scheduledFor: string;
8963
+ /** Present ⇒ it announces an event, and the row says so. */
8964
+ event?: F0ChatPostEvent;
8965
+ /** As {@link F0ChatPinnedPost.excerpt} — plain text, clamped to two lines. */
8966
+ excerpt?: string;
8967
+ /** As {@link F0ChatPinnedPost.thumbnailUrl}. An event's own image counts. */
8968
+ thumbnailUrl?: string;
8969
+ };
8970
+
8286
8971
  /** A message that matched an in-conversation search (room to grow: preview, author…). */
8287
8972
  export declare type F0ChatSearchResult = {
8288
8973
  id: string;
@@ -8304,6 +8989,23 @@ export declare type F0ChatSendInput = {
8304
8989
  mentionedEveryone?: boolean;
8305
8990
  };
8306
8991
 
8992
+ /**
8993
+ * An entry in a shelf row's menu — publish now, edit, cancel, delete.
8994
+ *
8995
+ * Same shape as {@link F0ChatPostAction} minus the post argument: neither a
8996
+ * scheduled post nor a draft is an {@link F0ChatPost} (no author line, no
8997
+ * counters, no reactions — nothing has happened yet), so the host closes over
8998
+ * the one it is building the menu for instead of being handed it back.
8999
+ */
9000
+ declare type F0ChatShelfAction = {
9001
+ id: string;
9002
+ label: string;
9003
+ icon?: IconType;
9004
+ onClick: () => void;
9005
+ /** Renders the entry in red (cancel). */
9006
+ critical?: boolean;
9007
+ };
9008
+
8307
9009
  /**
8308
9010
  * Conversation lifecycle as the host reports it. `connecting` (first load)
8309
9011
  * shows the skeleton and `error` the error state (with a Retry button when
@@ -8559,6 +9261,23 @@ declare type F0DialogInternalProps = {
8559
9261
  * @default true
8560
9262
  */
8561
9263
  dismissable?: boolean;
9264
+ /**
9265
+ * Whether pressing OUTSIDE the dialog closes it. Escape and the close button
9266
+ * are unaffected — this is the narrow version of {@link dismissable}, which
9267
+ * takes all three away at once.
9268
+ *
9269
+ * Turn it off for a SIDE PANEL that sits beside something the reader is meant
9270
+ * to keep using. A left/right dialog is already non-modal — no overlay, no
9271
+ * focus trap — so the surface next to it is live; but Radix still treats a
9272
+ * click there as "dismiss", which means using the thing beside the panel
9273
+ * closes the panel. That is the case this exists for: a post open next to an
9274
+ * open chat.
9275
+ *
9276
+ * Leave it on (the default) for an ordinary drawer, where clicking away IS
9277
+ * how you close it.
9278
+ * @default true
9279
+ */
9280
+ dismissOnInteractOutside?: boolean;
8562
9281
  asBottomSheetInMobile?: boolean;
8563
9282
  position?: DialogPosition;
8564
9283
  width?: DialogWidth;
@@ -11854,8 +12573,18 @@ declare const internalAvatarTypes: readonly ["base", "rounded"];
11854
12573
 
11855
12574
  export declare const isPossiblePhoneValue: (value: F0PhoneInputValue | undefined, fallbackCountry?: CountryCode) => boolean;
11856
12575
 
12576
+ export declare const isPost: (item: F0ChatItem) => item is F0ChatPost;
12577
+
11857
12578
  export declare const isSystemMessage: (item: F0ChatItem) => item is F0ChatSystemMessage;
11858
12579
 
12580
+ /**
12581
+ * A WHITELIST, not "anything that isn't a system row". The transcript's item
12582
+ * union grows (posts, and whatever comes after), and every growth would
12583
+ * otherwise be classified as a message here — silently, with no compile error,
12584
+ * across the ~40 call sites that narrow through this guard. A post would come
12585
+ * out with a delivery footer, editable with ↑, and would crash the client-side
12586
+ * search on a `body` it doesn't have.
12587
+ */
11859
12588
  export declare const isUserMessage: (item: F0ChatItem) => item is F0ChatMessage;
11860
12589
 
11861
12590
  /**
@@ -12062,6 +12791,25 @@ export declare type ListItem<S extends ListSchema = ListSchema> = {
12062
12791
  id: string | number;
12063
12792
  /** An accent dot on the left glyph — unseen/pending. */
12064
12793
  unread?: boolean;
12794
+ /**
12795
+ * What this row says ON HOVER: its own line of plain text, drawn as a tooltip
12796
+ * over the whole row.
12797
+ *
12798
+ * For what the row could not fit. A second line is ONE truncating line —
12799
+ * around 40 characters at the rail's width — so a row with more to say ends
12800
+ * in an ellipsis; this is where the rest can live. Written SEPARATELY rather
12801
+ * than repeating the description, so the tooltip can say the fuller thing (a
12802
+ * task's actual detail, an expense's full breakdown) instead of the
12803
+ * abbreviation the line had room for.
12804
+ *
12805
+ * Per ROW, like `unread` and `actions`: whether there is more to say is a
12806
+ * state of the row's own data. Rows without it hover silently — an empty
12807
+ * tooltip promises information that isn't there.
12808
+ *
12809
+ * It also OVERRIDES what a {@link ListSchema.compact} row would otherwise
12810
+ * surface, which is its folded-away description.
12811
+ */
12812
+ tooltipDescription?: string;
12065
12813
  /**
12066
12814
  * What can be DONE to this row, revealed on hover (and on focus, so they are
12067
12815
  * reachable by keyboard) behind a fade over whatever the row trails. Keep it
@@ -13664,6 +14412,22 @@ export declare type PaginationInfo = Omit<PageBasedPaginatedResponse<unknown>, "
13664
14412
  */
13665
14413
  export declare type PaginationType = "pages" | "infinite-scroll" | "no-pagination";
13666
14414
 
14415
+ declare type PanelBounds = {
14416
+ min: number;
14417
+ /** How far a deliberate drag may go — bounded by the content's hard floor. */
14418
+ max: number;
14419
+ /**
14420
+ * Where the panel sits when the user has not said otherwise: the content
14421
+ * keeps `mainMin` and the panel takes what is left, down to `min`.
14422
+ *
14423
+ * Separate from `max` so that "served the content first" is the default
14424
+ * without also being a cage — see `resolvePanelWidth`.
14425
+ */
14426
+ autoMax: number;
14427
+ /** The frame is too narrow to split: the panel should cover it instead. */
14428
+ shouldOverlay: boolean;
14429
+ };
14430
+
13667
14431
  declare type PathsToStringProps<T> = T extends string ? [] : {
13668
14432
  [K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K]>];
13669
14433
  }[Extract<keyof T, string>];
@@ -14489,6 +15253,11 @@ declare function _Sidebar({ header, body, footer, onFooterDropdownClick, }: Side
14489
15253
  export declare type SidebarChat = {
14490
15254
  id: string;
14491
15255
  label: string;
15256
+ /**
15257
+ * What the row stands for — see {@link SidebarChatKind}.
15258
+ * @default "conversation"
15259
+ */
15260
+ kind?: SidebarChatKind;
14492
15261
  /**
14493
15262
  * Person / team / company avatar (F0Avatar variant). Optional: omit it for
14494
15263
  * avatar-less rows (e.g. an AI chat history that shows titles only).
@@ -14591,6 +15360,14 @@ export declare type SidebarChatGroup = {
14591
15360
  title: string;
14592
15361
  /** Initial open state of the collapsible group. @default true */
14593
15362
  isOpen?: boolean;
15363
+ /**
15364
+ * One action on the group's own header, revealed on hover like a row's pin
15365
+ * — "new channel" beside Channels, "new community" beside Communities.
15366
+ *
15367
+ * Distinct from the panel's top-of-list `actions`: those belong to the whole
15368
+ * tab, this one belongs to the group it sits on, and says so by being there.
15369
+ */
15370
+ action?: SidebarSectionAction;
14594
15371
  chats: SidebarChat[];
14595
15372
  };
14596
15373
 
@@ -14610,6 +15387,17 @@ export declare const SidebarChatItemSkeleton: ({ className, }: {
14610
15387
  className?: string;
14611
15388
  }) => JSX_2.Element;
14612
15389
 
15390
+ /**
15391
+ * What a row stands for. `community` is a channel whose contents are POSTS
15392
+ * rather than messages: its badge counts posts and says so, and the row never
15393
+ * carries presence, typing or a mention prefix — none of which mean anything
15394
+ * for a place rather than a person.
15395
+ *
15396
+ * Purely semantic. The layout is identical, so a host can set it without
15397
+ * redesigning anything.
15398
+ */
15399
+ export declare type SidebarChatKind = "conversation" | "community";
15400
+
14613
15401
  /**
14614
15402
  * Body of the "Messages" tab: chat groups read from `SidebarChatProvider`,
14615
15403
  * rendered through the shared `SidebarTabPanel` (search + actions + collapsible
@@ -14699,7 +15487,7 @@ export declare type SidebarChatStore = {
14699
15487
  * Collapsible titled section used across the Sidebar (navigation categories,
14700
15488
  * chat groups). Title + rotating chevron + animated height.
14701
15489
  */
14702
- export declare const SidebarCollapsibleSection: ({ title, isOpen: initialIsOpen, isRoot, onCollapse, children, highlightWhenCollapsed, collapsedBadge, isDragging, wasDragging, }: SidebarCollapsibleSectionProps) => JSX_2.Element;
15490
+ export declare const SidebarCollapsibleSection: ({ title, isOpen: initialIsOpen, isRoot, onCollapse, children, highlightWhenCollapsed, collapsedBadge, action, isDragging, wasDragging, }: SidebarCollapsibleSectionProps) => JSX_2.Element;
14703
15491
 
14704
15492
  export declare interface SidebarCollapsibleSectionProps {
14705
15493
  title: string;
@@ -14719,6 +15507,8 @@ export declare interface SidebarCollapsibleSectionProps {
14719
15507
  * unread badge) — surfaces what's hidden inside without expanding.
14720
15508
  */
14721
15509
  collapsedBadge?: ReactNode;
15510
+ /** Shown on hover at the end of the header — see {@link SidebarSectionAction}. */
15511
+ action?: SidebarSectionAction;
14722
15512
  /** Drag-aware guards used by the sortable Menu; safe to omit elsewhere. */
14723
15513
  isDragging?: boolean;
14724
15514
  wasDragging?: RefObject<boolean>;
@@ -14756,6 +15546,18 @@ declare interface SidebarProps {
14756
15546
  onFooterDropdownClick?: () => void;
14757
15547
  }
14758
15548
 
15549
+ /**
15550
+ * One action on a section's own header — "new channel" beside Channels, "new
15551
+ * community" beside Communities. Icon-only, and revealed on hover like the
15552
+ * row's pin, so the header stays a title until somebody reaches for it.
15553
+ */
15554
+ export declare type SidebarSectionAction = {
15555
+ /** Names the button for a screen reader, and shows as its tooltip. */
15556
+ label: string;
15557
+ icon: IconType;
15558
+ onClick: () => void;
15559
+ };
15560
+
14759
15561
  declare type SidebarState = "locked" | "unlocked" | "hidden";
14760
15562
 
14761
15563
  export declare type SidebarTab = {
@@ -14810,6 +15612,8 @@ export declare type SidebarTabPanelGroup = {
14810
15612
  highlightWhenCollapsed?: boolean;
14811
15613
  /** Content shown at the end of the header only while collapsed. */
14812
15614
  collapsedBadge?: ReactNode;
15615
+ /** Hover-revealed action on the group's own header. */
15616
+ action?: SidebarSectionAction;
14813
15617
  items: SidebarTabPanelItem[];
14814
15618
  };
14815
15619
 
@@ -14875,6 +15679,103 @@ export declare type SidebarTabsProps = {
14875
15679
  persistKey?: string;
14876
15680
  };
14877
15681
 
15682
+ /**
15683
+ * A single piece of content hosted in the side panel — the resizable,
15684
+ * fullscreen-able space beside the page. Only one is mounted at a time: the
15685
+ * `id` keys the content, so switching views unmounts the previous one and
15686
+ * mounts the next.
15687
+ */
15688
+ export declare type SidePanelContent = {
15689
+ id: string;
15690
+ content: React.ReactNode;
15691
+ };
15692
+
15693
+ declare type SidePanelContextValue = {
15694
+ /** Every view declared for this frame, in declaration order. */
15695
+ views: SidePanelViewDefinition[];
15696
+ /**
15697
+ * Whether anything can occupy the panel. False means the panel does not
15698
+ * exist: no chrome, no reserved width, no DOM.
15699
+ */
15700
+ hasAvailableView: boolean;
15701
+ /** Whether the panel is showing at all. Persisted. */
15702
+ open: boolean;
15703
+ setOpen: React.Dispatch<React.SetStateAction<boolean>>;
15704
+ /** Docked beside the page, or covering it. Persisted. */
15705
+ layout: SidePanelLayout;
15706
+ setLayout: React.Dispatch<React.SetStateAction<SidePanelLayout>>;
15707
+ /** Edge the panel docks to. @default "right" */
15708
+ side: "left" | "right";
15709
+ setSide: React.Dispatch<React.SetStateAction<"left" | "right">>;
15710
+ /**
15711
+ * Edge hosted content docks to. Defaults to `side`; when the two differ the
15712
+ * frame renders a window on each edge and keeps them mutually exclusive.
15713
+ */
15714
+ contentSide: "left" | "right";
15715
+ setContentSide: React.Dispatch<React.SetStateAction<"left" | "right">>;
15716
+ /** Content currently hosted in the panel, or `null`. */
15717
+ activeContent: SidePanelContent | null;
15718
+ /** Mount content (replacing whatever was there) and open the panel. */
15719
+ present: (content: SidePanelContent | null) => void;
15720
+ /** Remove the hosted content without closing the panel. */
15721
+ clear: () => void;
15722
+ /**
15723
+ * Id persisted from the last session, waiting for its host to re-mount it.
15724
+ * The panel holds a placeholder until the host calls `present`, calls
15725
+ * `cancelRestore` (the content is gone), or a safety timeout fires.
15726
+ */
15727
+ restoringViewId: string | null;
15728
+ cancelRestore: () => void;
15729
+ /** The user's width PREFERENCE, against the absolute range. Persisted. */
15730
+ width: number;
15731
+ setWidth: React.Dispatch<React.SetStateAction<number>>;
15732
+ resetWidth: () => void;
15733
+ /** `width` held inside what the measured frame can actually give it. */
15734
+ effectiveWidth: number;
15735
+ /** The range the panel may be dragged to at the frame's current width. */
15736
+ widthBounds: PanelBounds;
15737
+ /** True when the panel covers the frame rather than sitting beside it. */
15738
+ panelOverlays: boolean;
15739
+ /** Published by the frame from its measured content box. */
15740
+ setFrameWidth: React.Dispatch<React.SetStateAction<number>>;
15741
+ /** Live state of a pointer drag, not a preference. */
15742
+ isResizing: boolean;
15743
+ setIsResizing: React.Dispatch<React.SetStateAction<boolean>>;
15744
+ /** Whether the panel may be resized at all. */
15745
+ resizable: boolean;
15746
+ shouldPlayEntranceAnimation: boolean;
15747
+ setShouldPlayEntranceAnimation: React.Dispatch<React.SetStateAction<boolean>>;
15748
+ };
15749
+
15750
+ /**
15751
+ * How the panel is laid out. Deliberately two values: the AI chat's `canvas`
15752
+ * is a third thing the AI kit derives on top of this, not a panel concern.
15753
+ */
15754
+ export declare type SidePanelLayout = "sidepanel" | "fullscreen";
15755
+
15756
+ /**
15757
+ * A product's claim on the panel.
15758
+ *
15759
+ * Declaring this is what lets the frame answer "is there anything at all to
15760
+ * show?" without the host computing it. The distinction that matters is
15761
+ * between a view that COULD open and one that is not installed at all — with
15762
+ * only `present()` those look identical (no content either way), which is why
15763
+ * the panel used to need a global `enabled` flag and why a customer with
15764
+ * communications and no assistant got no panel.
15765
+ */
15766
+ export declare type SidePanelViewDefinition = {
15767
+ id: string;
15768
+ /** Can this view occupy the panel at all? @default true */
15769
+ available?: boolean;
15770
+ /** Edge this view docks to. Falls back to the panel's `side`. */
15771
+ side?: "left" | "right";
15772
+ /**
15773
+ * Static renderer, for views that own their whole surface (the AI chat).
15774
+ * Omit for views whose content is pushed at runtime via `present()`.
15775
+ */
15776
+ render?: () => React.ReactNode;
15777
+ };
15778
+
14878
15779
  /**
14879
15780
  * Response structure for non-paginated data
14880
15781
  */
@@ -16237,6 +17138,14 @@ export declare const useSidebarChatActions: () => SidebarChatActions;
16237
17138
  /** Read the chat state (groups, active chat) and the imperative store API. */
16238
17139
  export declare const useSidebarChats: () => SidebarChatStore;
16239
17140
 
17141
+ /**
17142
+ * Read and control the frame's side panel.
17143
+ *
17144
+ * Returns an inert value when no panel is mounted, so a component can be used
17145
+ * both inside and outside a frame without guarding.
17146
+ */
17147
+ export declare function useSidePanel(): SidePanelContextValue;
17148
+
16240
17149
  export declare const useWidgetIsWide: () => boolean;
16241
17150
 
16242
17151
  /**