@flamingo-stack/openframe-frontend-core 0.0.847 → 0.0.849
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/dist/build/export-map.json +1 -1
- package/dist/{case-study-card-DJOHIHYU.cjs → case-study-card-X2HKPKT7.cjs} +5 -5
- package/dist/{chunk-CNMXP77I.cjs → chunk-36DUCHI7.cjs} +4 -4
- package/dist/{chunk-MPVOPSU6.cjs → chunk-36XWCVSM.cjs} +96 -96
- package/dist/{chunk-J2IXYCEH.cjs → chunk-3W4ASAXN.cjs} +15 -15
- package/dist/{chunk-6LDL3UZE.cjs → chunk-4YBS4HIO.cjs} +20 -20
- package/dist/{chunk-ABL5OBSF.cjs → chunk-533QK4R6.cjs} +296 -250
- package/dist/{chunk-QK2A334V.cjs → chunk-5CBI2ZYV.cjs} +12 -12
- package/dist/{chunk-D5DMWXF2.cjs → chunk-5PIR77V5.cjs} +4 -4
- package/dist/{chunk-HPXET3LH.cjs → chunk-6AJYGXXS.cjs} +27 -27
- package/dist/{chunk-4R4HAFXI.cjs → chunk-ALRYVZ3E.cjs} +6 -6
- package/dist/{chunk-FLW4WULE.cjs → chunk-BU2PSRAS.cjs} +17 -61
- package/dist/{chunk-HBDLJUI3.cjs → chunk-C3P7IUTU.cjs} +39 -14
- package/dist/{chunk-SSSI2RBV.cjs → chunk-FNQWL4W4.cjs} +14 -14
- package/dist/{chunk-TBOWFTEN.cjs → chunk-FRG724DU.cjs} +22 -22
- package/dist/{chunk-SIFRIUPW.cjs → chunk-FXLROQUT.cjs} +345 -356
- package/dist/{chunk-3V5JWG27.cjs → chunk-GG466XFQ.cjs} +2 -2
- package/dist/{chunk-XCVH5BCZ.cjs → chunk-GRCOEICK.cjs} +6 -6
- package/dist/{chunk-5GBLXY5F.cjs → chunk-HTYJUMFB.cjs} +7 -7
- package/dist/{chunk-URP6GYTD.cjs → chunk-IG6ROHPS.cjs} +56 -56
- package/dist/{chunk-DGQDGCQH.cjs → chunk-IHY7E4EX.cjs} +2 -2
- package/dist/{chunk-WCK5T3UD.cjs → chunk-IKOL6NJY.cjs} +101 -69
- package/dist/{chunk-RAJ5FYFL.cjs → chunk-IN3LA2FU.cjs} +14 -14
- package/dist/{chunk-ZTO7KNYV.cjs → chunk-KXBZCUDW.cjs} +35 -35
- package/dist/{chunk-57JYB3E2.cjs → chunk-MC3L525K.cjs} +3 -3
- package/dist/{chunk-BIOSHDXQ.cjs → chunk-MDLT6D46.cjs} +61 -61
- package/dist/{chunk-3TOWCFIK.cjs → chunk-PMUCEUFQ.cjs} +13 -1
- package/dist/{chunk-PV3NKXWS.cjs → chunk-PV2XQJ2R.cjs} +99 -178
- package/dist/{chunk-J2ZSGOUC.cjs → chunk-R5JPZ6TY.cjs} +27 -27
- package/dist/{chunk-744JXTDP.cjs → chunk-SA2NNWIJ.cjs} +23 -23
- package/dist/{chunk-DEEQSVLX.cjs → chunk-SHQY4GOE.cjs} +90 -90
- package/dist/{chunk-BHOVF4KP.cjs → chunk-SOSIXRQ3.cjs} +2 -2
- package/dist/{chunk-Y2OEC7UW.cjs → chunk-TEKN6TJ5.cjs} +40 -40
- package/dist/{chunk-PZNWIJRZ.cjs → chunk-TIVPOVIM.cjs} +2 -2
- package/dist/{chunk-BZZE4KIV.cjs → chunk-TX4NTH22.cjs} +155 -136
- package/dist/{chunk-NFNSPLGC.cjs → chunk-W7OFAROY.cjs} +14 -9
- package/dist/{chunk-N2A24FDF.cjs → chunk-YLETC7LP.cjs} +994 -953
- package/dist/{chunk-YC52LSPP.cjs → chunk-YYHQY3K6.cjs} +40 -40
- package/dist/{chunk-2FUTZKLB.cjs → chunk-Z5UCDEKZ.cjs} +6 -6
- package/dist/{chunk-SWF6TNZ3.cjs → chunk-ZGEWT3SF.cjs} +2 -2
- package/dist/{chunk-PQDCSZ3W.cjs → chunk-ZY34R37A.cjs} +85 -85
- package/dist/{chunk-5SZ4ILU3.cjs → chunk-ZZDFMUEU.cjs} +3 -3
- package/dist/{cloud-tenants-XEUUMABT.cjs → cloud-tenants-HKUUEUAY.cjs} +21 -21
- package/dist/components/case-studies/index.cjs +36 -36
- package/dist/components/chat/chat-composer.d.ts.map +1 -1
- package/dist/components/chat/chat-composer.js +4 -3
- package/dist/components/chat/chat-container.d.ts.map +1 -1
- package/dist/components/chat/chat-container.js +2 -2
- package/dist/components/chat/entity-cards/design-doc-card.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/design-doc-card.js +5 -17
- package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/dispatch.js +5 -7
- package/dist/components/chat/index.cjs +15 -15
- package/dist/components/contact/index.cjs +22 -22
- package/dist/components/docs/doc-viewer.d.ts +7 -7
- package/dist/components/docs/doc-viewer.d.ts.map +1 -1
- package/dist/components/docs/doc-viewer.js +29 -16
- package/dist/components/docs/docs-hub-page.d.ts +1 -1
- package/dist/components/docs/docs-hub-page.d.ts.map +1 -1
- package/dist/components/docs/docs-hub-page.js +4 -11
- package/dist/components/docs/index.cjs +25 -25
- package/dist/components/docs/use-document-tree.d.ts.map +1 -1
- package/dist/components/docs/use-document-tree.js +24 -24
- package/dist/components/embeds/claude-embed.d.ts +2 -2
- package/dist/components/embeds/claude-embed.d.ts.map +1 -1
- package/dist/components/embeds/claude-embed.js +3 -3
- package/dist/components/embeds/embed-iframe.d.ts +5 -3
- package/dist/components/embeds/embed-iframe.d.ts.map +1 -1
- package/dist/components/embeds/embed-iframe.js +4 -3
- package/dist/components/embeds/embed-viewer-frame.d.ts +12 -9
- package/dist/components/embeds/embed-viewer-frame.d.ts.map +1 -1
- package/dist/components/embeds/embed-viewer-frame.js +21 -7
- package/dist/components/embeds/figma-embed.d.ts +4 -2
- package/dist/components/embeds/figma-embed.d.ts.map +1 -1
- package/dist/components/embeds/figma-embed.js +4 -3
- package/dist/components/embeds/file-download-card.d.ts +3 -1
- package/dist/components/embeds/file-download-card.d.ts.map +1 -1
- package/dist/components/embeds/file-download-card.js +18 -3
- package/dist/components/embeds/google-sheets-viewer.d.ts +3 -1
- package/dist/components/embeds/google-sheets-viewer.d.ts.map +1 -1
- package/dist/components/embeds/google-sheets-viewer.js +7 -4
- package/dist/components/embeds/index.cjs +22 -22
- package/dist/components/embeds/pdf-viewer.d.ts +3 -1
- package/dist/components/embeds/pdf-viewer.d.ts.map +1 -1
- package/dist/components/embeds/pdf-viewer.js +6 -2
- package/dist/components/faq/index.cjs +23 -23
- package/dist/components/features/index.cjs +21 -21
- package/dist/components/features/status-filter-component.d.ts +7 -1
- package/dist/components/features/status-filter-component.d.ts.map +1 -1
- package/dist/components/features/status-filter-component.js +9 -3
- package/dist/components/help-center-pages/index.cjs +140 -140
- package/dist/components/index.cjs +137 -135
- package/dist/components/layout/back-button.d.ts +7 -1
- package/dist/components/layout/back-button.d.ts.map +1 -1
- package/dist/components/layout/back-button.js +34 -2
- package/dist/components/loading/unified-skeleton.d.ts +6 -1
- package/dist/components/loading/unified-skeleton.d.ts.map +1 -1
- package/dist/components/loading/unified-skeleton.js +13 -1
- package/dist/components/meeting-scheduler/index.cjs +119 -119
- package/dist/components/navigation/index.cjs +24 -24
- package/dist/components/onboarding-guides/index.cjs +24 -24
- package/dist/components/product-demo/index.cjs +33 -33
- package/dist/components/related-content/index.cjs +11 -11
- package/dist/components/tickets/index.cjs +28 -28
- package/dist/components/ui/file-manager/file-manager-table.d.ts.map +1 -1
- package/dist/components/ui/file-manager/file-manager-table.js +59 -50
- package/dist/components/ui/file-manager/index.cjs +71 -62
- package/dist/components/ui/index.cjs +23 -21
- package/dist/components/ui/markdown/engine.d.ts +5 -4
- package/dist/components/ui/markdown/engine.d.ts.map +1 -1
- package/dist/components/ui/markdown/engine.js +15 -15
- package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +1 -1
- package/dist/components/ui/markdown/rich/rich-markdown-renderer.js +7 -3
- package/dist/components/ui/people-cell.d.ts +9 -2
- package/dist/components/ui/people-cell.d.ts.map +1 -1
- package/dist/components/ui/people-cell.js +39 -23
- package/dist/components/ui/progress-bar.d.ts +6 -0
- package/dist/components/ui/progress-bar.d.ts.map +1 -1
- package/dist/components/ui/progress-bar.js +2 -0
- package/dist/components/ui/tag.d.ts +8 -0
- package/dist/components/ui/tag.d.ts.map +1 -1
- package/dist/components/ui/tag.js +5 -0
- package/dist/{devices-7YDIHVVP.cjs → devices-P7M4Z7VP.cjs} +22 -22
- package/dist/embed-shims/index.cjs +3 -3
- package/dist/embed-shims/next-image.cjs +3 -3
- package/dist/{embeddable-chat-Z3S37SSM.cjs → embeddable-chat-322NDFP5.cjs} +94 -94
- package/dist/hooks/index.cjs +6 -6
- package/dist/index.cjs +32 -30
- package/dist/{knowledge-SQVPQSKM.cjs → knowledge-TC7JLOWI.cjs} +22 -22
- package/dist/{logs-M5DFGUBC.cjs → logs-GPOYCP4B.cjs} +28 -28
- package/dist/{policies-T3VQEJUD.cjs → policies-ZUHOUMDD.cjs} +25 -25
- package/dist/{remote-session-EE6477O3.cjs → remote-session-NT4NG7EF.cjs} +10 -10
- package/dist/{scripts-JFRFJKPH.cjs → scripts-FOEVDGTH.cjs} +28 -28
- package/dist/{software-update-E7W3AAIY.cjs → software-update-XGTMNFST.cjs} +25 -25
- package/dist/{tickets-board-44HO3LR4.cjs → tickets-board-7FLGQEHF.cjs} +10 -10
- package/dist/types/design-doc.d.ts +45 -8
- package/dist/types/design-doc.d.ts.map +1 -1
- package/dist/types/doc-source.d.ts +2 -2
- package/dist/types/doc-source.d.ts.map +1 -1
- package/dist/utils/design-doc-readiness.d.ts +17 -15
- package/dist/utils/design-doc-readiness.d.ts.map +1 -1
- package/dist/utils/design-doc-readiness.js +5 -11
- package/dist/utils/doc-tree-nav.d.ts +7 -0
- package/dist/utils/doc-tree-nav.d.ts.map +1 -1
- package/dist/utils/doc-tree-nav.js +19 -0
- package/dist/utils/index.cjs +30 -13
- package/dist/utils/list-url.d.ts.map +1 -1
- package/dist/utils/list-url.js +2 -1
- package/dist/utils/source-grouping.d.ts +2 -2
- package/dist/utils/source-grouping.d.ts.map +1 -1
- package/dist/utils/source-grouping.js +1 -1
- package/dist/utils/source-icons.d.ts.map +1 -1
- package/dist/utils/source-icons.js +3 -0
- package/package.json +1 -1
- package/src/components/chat/chat-composer.tsx +10 -3
- package/src/components/chat/chat-container.tsx +6 -4
- package/src/components/chat/entity-cards/design-doc-card.tsx +12 -17
- package/src/components/chat/entity-cards/dispatch.tsx +10 -10
- package/src/components/docs/doc-viewer.tsx +56 -35
- package/src/components/docs/docs-hub-page.tsx +13 -37
- package/src/components/docs/use-document-tree.ts +33 -23
- package/src/components/embeds/claude-embed.tsx +4 -4
- package/src/components/embeds/embed-iframe.tsx +8 -5
- package/src/components/embeds/embed-viewer-frame.tsx +35 -15
- package/src/components/embeds/figma-embed.tsx +9 -4
- package/src/components/embeds/file-download-card.tsx +28 -4
- package/src/components/embeds/google-sheets-viewer.tsx +9 -3
- package/src/components/embeds/pdf-viewer.tsx +9 -2
- package/src/components/features/status-filter-component.tsx +15 -0
- package/src/components/layout/back-button.tsx +54 -2
- package/src/components/loading/unified-skeleton.tsx +18 -2
- package/src/components/ui/file-manager/file-manager-table.tsx +8 -3
- package/src/components/ui/markdown/engine.tsx +21 -30
- package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +4 -0
- package/src/components/ui/people-cell.tsx +53 -17
- package/src/components/ui/progress-bar.tsx +8 -0
- package/src/components/ui/tag.tsx +14 -0
- package/src/types/design-doc.ts +59 -10
- package/src/types/doc-source.ts +2 -2
- package/src/utils/design-doc-readiness.ts +22 -23
- package/src/utils/doc-tree-nav.ts +27 -0
- package/src/utils/list-url.ts +2 -1
- package/src/utils/source-grouping.ts +2 -2
- package/src/utils/source-icons.ts +3 -0
- package/dist/components/docs/skeletons.d.ts +0 -25
- package/dist/components/docs/skeletons.d.ts.map +0 -1
- package/dist/components/docs/skeletons.js +0 -83
- package/src/components/docs/skeletons.tsx +0 -131
|
@@ -35,7 +35,11 @@ import type { PluggableList } from 'unified';
|
|
|
35
35
|
import type { ResolveLinkResult } from '../../../types/doc-source';
|
|
36
36
|
import { readAppOrigins, SERVER_APP_ORIGINS, subscribeAppOrigins } from '../../../utils/app-origins';
|
|
37
37
|
import { cn } from '../../../utils/cn';
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
TextLineSkeleton,
|
|
40
|
+
TextLinesByBreakpointSkeleton,
|
|
41
|
+
type TextLinesByBreakpoint,
|
|
42
|
+
} from '../../loading/unified-skeleton';
|
|
39
43
|
import { LoadingItems, useLoadingScope } from '../loading-scope';
|
|
40
44
|
import { buildBaseComponents, MARKDOWN_H1_CLASS, MARKDOWN_H2_CLASS, MARKDOWN_PARAGRAPH_CLASS } from './base-components';
|
|
41
45
|
import { MarkdownMediaOriginsContext } from './external-media-notice';
|
|
@@ -193,8 +197,8 @@ export interface MarkdownEngineProps {
|
|
|
193
197
|
* sections, each a heading and its paragraphs. Unset, one paragraph of `skeletonLines` lines.
|
|
194
198
|
*/
|
|
195
199
|
skeletonSections?: number;
|
|
196
|
-
/** Lines of the one paragraph a loading body draws when it names no sections. Default 3. */
|
|
197
|
-
skeletonLines?:
|
|
200
|
+
/** Lines of the one paragraph a loading body draws when it names no sections, per width. Default 3 at every width. */
|
|
201
|
+
skeletonLines?: TextLinesByBreakpoint;
|
|
198
202
|
/** A loading body opens with its own `# ` title (a legal document's): its title row is drawn first. */
|
|
199
203
|
skeletonTitle?: boolean;
|
|
200
204
|
/** The content is still being read: the body draws its skeleton (the `skeleton*` shape). A loading scope does the
|
|
@@ -203,16 +207,19 @@ export interface MarkdownEngineProps {
|
|
|
203
207
|
}
|
|
204
208
|
|
|
205
209
|
/** Lines a loading body draws when its caller names no shape. */
|
|
206
|
-
export const MARKDOWN_SKELETON_LINES = 3;
|
|
210
|
+
export const MARKDOWN_SKELETON_LINES: TextLinesByBreakpoint = { phone: 3, md: 3, lg: 3 };
|
|
207
211
|
/** Paragraphs under each heading of a loading body, and their lines. */
|
|
208
|
-
const SKELETON_SECTION_PARAGRAPHS
|
|
212
|
+
const SKELETON_SECTION_PARAGRAPHS: readonly TextLinesByBreakpoint[] = [
|
|
213
|
+
{ phone: 3, md: 3, lg: 3 },
|
|
214
|
+
{ phone: 2, md: 2, lg: 2 },
|
|
215
|
+
];
|
|
209
216
|
/**
|
|
210
|
-
* Lines a typical body's opening line set takes,
|
|
217
|
+
* Lines a typical body's opening line set takes, per width: a document's
|
|
211
218
|
* `# ` title wraps on a phone, and the opening paragraph every post and legal document starts with runs a few lines.
|
|
212
219
|
* Typical lengths, never one record's: a longer record growing past them is a settle below its own lines.
|
|
213
220
|
*/
|
|
214
|
-
const SKELETON_TITLE_LINES = { phone: 2,
|
|
215
|
-
const SKELETON_LEAD_LINES = { phone: 5,
|
|
221
|
+
const SKELETON_TITLE_LINES: TextLinesByBreakpoint = { phone: 2, md: 1, lg: 1 };
|
|
222
|
+
const SKELETON_LEAD_LINES: TextLinesByBreakpoint = { phone: 5, md: 3, lg: 3 };
|
|
216
223
|
/** The renderer's own frame: read by the engine and by its skeleton. */
|
|
217
224
|
/** The renderer's root class, on the rendered document and on every skeleton of one: it names a markdown body. */
|
|
218
225
|
export const MARKDOWN_RENDERER_CLASS = 'simple-markdown-renderer';
|
|
@@ -467,31 +474,15 @@ export const MarkdownEngine = memo(function MarkdownEngineGate(props: MarkdownEn
|
|
|
467
474
|
return <MarkdownEngineImpl {...props} />;
|
|
468
475
|
});
|
|
469
476
|
|
|
470
|
-
/** A paragraph of `lines`
|
|
471
|
-
function ParagraphBar({ lines, className }: { lines:
|
|
477
|
+
/** A paragraph of its `lines` per width at the body's own paragraph type. */
|
|
478
|
+
function ParagraphBar({ lines, className }: { lines: TextLinesByBreakpoint; className: string }) {
|
|
472
479
|
return (
|
|
473
480
|
<div className={className}>
|
|
474
|
-
<
|
|
481
|
+
<TextLinesByBreakpointSkeleton lines={lines} />
|
|
475
482
|
</div>
|
|
476
483
|
);
|
|
477
484
|
}
|
|
478
485
|
|
|
479
|
-
/** Lines of a text run per breakpoint: on a phone, and from `content-md`. */
|
|
480
|
-
interface SkeletonLinesByBreakpoint {
|
|
481
|
-
phone: number;
|
|
482
|
-
wide: number;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/** A text run of the lines it takes on a phone and from `content-md` (the content area's own steps). */
|
|
486
|
-
function LinesByBreakpoint({ lines, as = 'div' }: { lines: SkeletonLinesByBreakpoint; as?: 'div' | 'span' }) {
|
|
487
|
-
return (
|
|
488
|
-
<>
|
|
489
|
-
<TextLineSkeleton type="inherit" as={as} lines={lines.phone} className="w-full content-md:hidden" />
|
|
490
|
-
<TextLineSkeleton type="inherit" as={as} lines={lines.wide} className="hidden w-full content-md:flex" />
|
|
491
|
-
</>
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
486
|
/**
|
|
496
487
|
* A markdown body while it is read: the renderer's own frame and type. With `sections`, the body's opening paragraph
|
|
497
488
|
* (a typical length per breakpoint), then each section a REAL `<h2>` (the renderer's heading classes) holding a bar,
|
|
@@ -506,7 +497,7 @@ export function MarkdownEngineSkeleton({
|
|
|
506
497
|
className = '',
|
|
507
498
|
}: {
|
|
508
499
|
sections?: number;
|
|
509
|
-
lines?:
|
|
500
|
+
lines?: TextLinesByBreakpoint;
|
|
510
501
|
textSize?: TextSizeConfig;
|
|
511
502
|
/** The body opens with its own `# ` title (a legal document's, demoted to an `h2`): its H1 row is drawn first. */
|
|
512
503
|
title?: boolean;
|
|
@@ -522,12 +513,12 @@ export function MarkdownEngineSkeleton({
|
|
|
522
513
|
<div className={ARTICLE_CLASS}>
|
|
523
514
|
{title && (
|
|
524
515
|
<h2 className={cn(MARKDOWN_H1_CLASS, textSizes.h1)}>
|
|
525
|
-
<
|
|
516
|
+
<TextLinesByBreakpointSkeleton lines={SKELETON_TITLE_LINES} as="span" />
|
|
526
517
|
</h2>
|
|
527
518
|
)}
|
|
528
519
|
{sections ? (
|
|
529
520
|
<div className={paragraphClass}>
|
|
530
|
-
<
|
|
521
|
+
<TextLinesByBreakpointSkeleton lines={SKELETON_LEAD_LINES} />
|
|
531
522
|
</div>
|
|
532
523
|
) : null}
|
|
533
524
|
{sections ? (
|
|
@@ -101,6 +101,7 @@ const RichMarkdownInner: React.FC<InnerProps> = ({
|
|
|
101
101
|
demoteMarkdownH1ToH2,
|
|
102
102
|
skeletonSections,
|
|
103
103
|
skeletonLines,
|
|
104
|
+
skeletonTitle,
|
|
104
105
|
}) => {
|
|
105
106
|
const { ogScraperUrl } = useRichMarkdownRuntime();
|
|
106
107
|
|
|
@@ -158,6 +159,7 @@ const RichMarkdownInner: React.FC<InnerProps> = ({
|
|
|
158
159
|
allowRemoteMedia
|
|
159
160
|
skeletonSections={skeletonSections}
|
|
160
161
|
skeletonLines={skeletonLines}
|
|
162
|
+
skeletonTitle={skeletonTitle}
|
|
161
163
|
/>
|
|
162
164
|
);
|
|
163
165
|
};
|
|
@@ -174,6 +176,7 @@ const RichMarkdownRendererImpl: React.FC<RichMarkdownRendererProps> = ({
|
|
|
174
176
|
demoteMarkdownH1ToH2 = false,
|
|
175
177
|
skeletonSections,
|
|
176
178
|
skeletonLines,
|
|
179
|
+
skeletonTitle,
|
|
177
180
|
// Runtime overrides; provider fills the defaults
|
|
178
181
|
redditProxyUrl,
|
|
179
182
|
twitterProxyUrl,
|
|
@@ -199,6 +202,7 @@ const RichMarkdownRendererImpl: React.FC<RichMarkdownRendererProps> = ({
|
|
|
199
202
|
demoteMarkdownH1ToH2={demoteMarkdownH1ToH2}
|
|
200
203
|
skeletonSections={skeletonSections}
|
|
201
204
|
skeletonLines={skeletonLines}
|
|
205
|
+
skeletonTitle={skeletonTitle}
|
|
202
206
|
/>
|
|
203
207
|
</RichMarkdownRuntimeProvider>
|
|
204
208
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { cn } from '../../utils/cn';
|
|
3
4
|
import { personFirstName } from '../../utils/format';
|
|
4
5
|
import { TextLineSkeleton } from '../loading/unified-skeleton';
|
|
5
6
|
import { AvatarStack } from './avatar-stack';
|
|
@@ -48,6 +49,11 @@ export interface PersonCellProps {
|
|
|
48
49
|
size?: 'sm' | 'lg';
|
|
49
50
|
/** The person is still being read: the cell draws its skeleton at its size. A loading scope does the same. */
|
|
50
51
|
loading?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* `2` (default): the name and its second line. `1`: the name alone, one line, with the second line in a tooltip:
|
|
54
|
+
* for a table whose rows are one line in every column but the first.
|
|
55
|
+
*/
|
|
56
|
+
lines?: 1 | 2;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
const PERSON_CELL_SIZE = {
|
|
@@ -67,29 +73,35 @@ export function PersonCell({
|
|
|
67
73
|
actionTitle,
|
|
68
74
|
size = 'sm',
|
|
69
75
|
loading,
|
|
76
|
+
lines = 2,
|
|
70
77
|
}: PersonCellProps) {
|
|
71
78
|
// In a page's loading state (or while the host reads the person) the cell is its skeleton.
|
|
72
|
-
if (useLoadingScope() || loading) return <PersonCellSkeleton size={size} />;
|
|
79
|
+
if (useLoadingScope() || loading) return <PersonCellSkeleton size={size} lines={lines} />;
|
|
73
80
|
const displayName = name || fallbackName;
|
|
74
81
|
const scale = PERSON_CELL_SIZE[size];
|
|
82
|
+
const nameLine = (
|
|
83
|
+
<TruncateText
|
|
84
|
+
variant={scale.primary}
|
|
85
|
+
className={onClick ? 'text-ods-accent group-hover/person-cell:underline' : undefined}
|
|
86
|
+
>
|
|
87
|
+
{displayName}
|
|
88
|
+
</TruncateText>
|
|
89
|
+
);
|
|
75
90
|
const row = (
|
|
76
91
|
<div className={`group/person-cell ${PERSON_ROW_CLASS} ${scale.gap}`}>
|
|
77
92
|
<SquareAvatar size={scale.avatar} src={avatarUrl ?? undefined} alt={displayName} fallback={displayName} />
|
|
78
93
|
<div className="min-w-0 flex-1">
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
<TruncateText variant="h6" tone="secondary">
|
|
86
|
-
{secondary === undefined ? ' ' : secondary || '·'}
|
|
87
|
-
</TruncateText>
|
|
94
|
+
{nameLine}
|
|
95
|
+
{lines === 2 ? (
|
|
96
|
+
<TruncateText variant="h6" tone="secondary">
|
|
97
|
+
{secondary === undefined ? ' ' : secondary || '·'}
|
|
98
|
+
</TruncateText>
|
|
99
|
+
) : null}
|
|
88
100
|
</div>
|
|
89
101
|
</div>
|
|
90
102
|
);
|
|
91
|
-
|
|
92
|
-
|
|
103
|
+
// A clickable cell is a button in both layouts, so `onClick` and `actionTitle` are never dropped by `lines`.
|
|
104
|
+
const cell = onClick ? (
|
|
93
105
|
<Button
|
|
94
106
|
variant="transparent"
|
|
95
107
|
onClick={onClick}
|
|
@@ -100,18 +112,34 @@ export function PersonCell({
|
|
|
100
112
|
>
|
|
101
113
|
{row}
|
|
102
114
|
</Button>
|
|
115
|
+
) : (
|
|
116
|
+
row
|
|
117
|
+
);
|
|
118
|
+
// One line: the second line is not drawn, so it is the cell's tooltip.
|
|
119
|
+
return lines === 1 && secondary ? (
|
|
120
|
+
<FloatingTooltip content={`${displayName} · ${secondary}`} triggerClassName="min-w-0 max-w-full">
|
|
121
|
+
{cell}
|
|
122
|
+
</FloatingTooltip>
|
|
123
|
+
) : (
|
|
124
|
+
cell
|
|
103
125
|
);
|
|
104
126
|
}
|
|
105
127
|
|
|
106
128
|
/** The cell while it loads: the SAME avatar box, gap and two lines (the name's type step, then the caption's). */
|
|
107
|
-
function PersonCellSkeleton({
|
|
129
|
+
function PersonCellSkeleton({
|
|
130
|
+
size = 'sm',
|
|
131
|
+
lines = 2,
|
|
132
|
+
}: {
|
|
133
|
+
size?: PersonCellProps['size'];
|
|
134
|
+
lines?: PersonCellProps['lines'];
|
|
135
|
+
}) {
|
|
108
136
|
const scale = PERSON_CELL_SIZE[size];
|
|
109
137
|
return (
|
|
110
138
|
<div className={`${PERSON_ROW_CLASS} ${scale.gap}`} aria-hidden>
|
|
111
139
|
<SquareAvatar loading size={scale.avatar} />
|
|
112
140
|
<div className="flex min-w-0 flex-1 flex-col">
|
|
113
141
|
<TextLineSkeleton type={scale.primary} className="w-3/4" />
|
|
114
|
-
<TextLineSkeleton type="h6" className="w-1/2" />
|
|
142
|
+
{lines === 2 ? <TextLineSkeleton type="h6" className="w-1/2" /> : null}
|
|
115
143
|
</div>
|
|
116
144
|
</div>
|
|
117
145
|
);
|
|
@@ -130,14 +158,16 @@ export interface PeopleCellProps {
|
|
|
130
158
|
people: PeopleCellPerson[];
|
|
131
159
|
/** Shown when the list is empty — the same slot `PersonCell` fills. */
|
|
132
160
|
fallbackName?: string;
|
|
161
|
+
/** `1`: one line for one person or several (see `PersonCellProps.lines`). */
|
|
162
|
+
lines?: 1 | 2;
|
|
133
163
|
}
|
|
134
164
|
|
|
135
165
|
/** Positions the multi-person stack reserves: two faces, or one face and "+N". */
|
|
136
166
|
const PEOPLE_CELL_STACK_SLOTS = 2;
|
|
137
167
|
|
|
138
|
-
export function PeopleCell({ people, fallbackName = 'Unassigned' }: PeopleCellProps) {
|
|
168
|
+
export function PeopleCell({ people, fallbackName = 'Unassigned', lines = 2 }: PeopleCellProps) {
|
|
139
169
|
// In a page's loading state the people are read: the cell is a person cell's skeleton.
|
|
140
|
-
if (useLoadingScope()) return <PersonCellSkeleton />;
|
|
170
|
+
if (useLoadingScope()) return <PersonCellSkeleton lines={lines} />;
|
|
141
171
|
if (people.length <= 1) {
|
|
142
172
|
const [person] = people;
|
|
143
173
|
return (
|
|
@@ -146,6 +176,7 @@ export function PeopleCell({ people, fallbackName = 'Unassigned' }: PeopleCellPr
|
|
|
146
176
|
avatarUrl={person?.avatarUrl}
|
|
147
177
|
secondary={person ? person.secondary : undefined}
|
|
148
178
|
fallbackName={fallbackName}
|
|
179
|
+
lines={lines}
|
|
149
180
|
/>
|
|
150
181
|
);
|
|
151
182
|
}
|
|
@@ -163,7 +194,12 @@ export function PeopleCell({ people, fallbackName = 'Unassigned' }: PeopleCellPr
|
|
|
163
194
|
people={people.map(p => ({ key: p.key, name: p.name || 'Unknown', avatarUrl: p.avatarUrl }))}
|
|
164
195
|
/>
|
|
165
196
|
</div>
|
|
166
|
-
<p
|
|
197
|
+
<p
|
|
198
|
+
className={cn(
|
|
199
|
+
'min-w-0 flex-1 break-words text-ods-text-primary text-h6',
|
|
200
|
+
lines === 1 ? 'line-clamp-1' : 'line-clamp-2',
|
|
201
|
+
)}
|
|
202
|
+
>
|
|
167
203
|
<span aria-hidden>{people.map(p => personFirstName(p.name, 'Unknown')).join(' & ')}</span>
|
|
168
204
|
<span className="sr-only">{fullNames}</span>
|
|
169
205
|
</p>
|
|
@@ -27,6 +27,12 @@ type ProgressBarProps = {
|
|
|
27
27
|
height?: number; // desktop height (px)
|
|
28
28
|
mobileHeight?: number; // mobile height (px)
|
|
29
29
|
inverted?: boolean; // if true, high values are good (green), low values are bad (red)
|
|
30
|
+
/**
|
|
31
|
+
* `threshold` (default): the fill takes the success, warning or error colour by the thresholds. `neutral`: the fill
|
|
32
|
+
* is the secondary text colour at every value, for a count that is progress, not a health reading (a sign-off
|
|
33
|
+
* count), so a partial bar never reads as an error.
|
|
34
|
+
*/
|
|
35
|
+
tone?: 'threshold' | 'neutral';
|
|
30
36
|
/** The value is still being read: the bar draws its `ProgressBarSkeleton` at its own height. A loading scope does
|
|
31
37
|
* the same. */
|
|
32
38
|
loading?: boolean;
|
|
@@ -42,6 +48,7 @@ export const ProgressBar: React.FC<ProgressBarProps> = ({
|
|
|
42
48
|
height = PROGRESS_BAR_HEIGHT.desktop,
|
|
43
49
|
mobileHeight = PROGRESS_BAR_HEIGHT.mobile,
|
|
44
50
|
inverted = false,
|
|
51
|
+
tone = 'threshold',
|
|
45
52
|
loading: loadingProp,
|
|
46
53
|
}) => {
|
|
47
54
|
// ONE switch: in a page's loading state (or while the host reads the value) the bar is its skeleton.
|
|
@@ -76,6 +83,7 @@ export const ProgressBar: React.FC<ProgressBarProps> = ({
|
|
|
76
83
|
|
|
77
84
|
// Pick color based on thresholds using ODS design tokens
|
|
78
85
|
const getColor = () => {
|
|
86
|
+
if (tone === 'neutral') return 'var(--color-text-secondary)';
|
|
79
87
|
if (inverted) {
|
|
80
88
|
// Inverted: high values = good (green), low values = bad (red)
|
|
81
89
|
// For battery health: 100% = green, <30% = red
|
|
@@ -201,6 +201,20 @@ function isEmptyTagLabel(label: React.ReactNode): boolean {
|
|
|
201
201
|
return label == null || label === false || (typeof label === 'string' && label.trim() === '');
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
/** The tones a badge colour keeps when it is drawn as a tag. */
|
|
205
|
+
const BADGE_COLOR_TAG_TONES = ['success', 'warning', 'error'] as const;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* THE tag variant for a badge colour that is data (a `StatusBadge` colour scheme a vocabulary row stores): the three
|
|
209
|
+
* semantic tones carry over, every other colour reads grey. Every card that draws such a colour as a tag reads it
|
|
210
|
+
* here, so a status is the same tone in a badge and in a chat card.
|
|
211
|
+
*/
|
|
212
|
+
export function tagVariantForBadgeColor(
|
|
213
|
+
color: string | null | undefined,
|
|
214
|
+
): (typeof BADGE_COLOR_TAG_TONES)[number] | 'grey' {
|
|
215
|
+
return BADGE_COLOR_TAG_TONES.find(tone => tone === color) ?? 'grey';
|
|
216
|
+
}
|
|
217
|
+
|
|
204
218
|
export { Tag, tagVariants };
|
|
205
219
|
|
|
206
220
|
/**
|
package/src/types/design-doc.ts
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Participant sections are keyed by the employee DEPARTMENT (FK to the ONE
|
|
5
5
|
// `departments` table — see department.ts); there is no function enum.
|
|
6
|
-
// A doc
|
|
7
|
-
// blocking comment is open. That math lives in the hub's
|
|
6
|
+
// A doc's READINESS is derived: it is ready when every section is signed off and
|
|
7
|
+
// no blocking comment is open. That math lives in the hub's
|
|
8
8
|
// `lib/utils/design-doc-gate.ts` (isomorphic, shared by server + client).
|
|
9
|
+
// Beside it a PERSON may set a lifecycle status (`lifecycle_status`: postponed,
|
|
10
|
+
// shipped, archived), which replaces readiness on every surface while it is set.
|
|
9
11
|
|
|
10
12
|
// MIGRATION NOTE (2026-09-11). This file dropped `status`, `DesignDocStatus`,
|
|
11
13
|
// `DesignDocStatusTransition`, `approved_at`, `dri_vacated_by`, `by_status` and
|
|
@@ -53,7 +55,15 @@ import type { EntityAuthor } from './entity-author';
|
|
|
53
55
|
/** One value of `design_doc_vocabulary.kind` — the STRUCTURAL list (one per
|
|
54
56
|
* value column), the only design-doc vocabulary that is code. */
|
|
55
57
|
export type DesignDocVocabularyKind =
|
|
56
|
-
|
|
58
|
+
| 'tier'
|
|
59
|
+
| 'spec_mode'
|
|
60
|
+
| 'participant_status'
|
|
61
|
+
| 'comment_type'
|
|
62
|
+
| 'comment_status'
|
|
63
|
+
| 'link_type'
|
|
64
|
+
| 'media_type'
|
|
65
|
+
| 'lifecycle_status'
|
|
66
|
+
| 'readiness';
|
|
57
67
|
|
|
58
68
|
/** A `design_doc_vocabulary` row, as the DAL and the vocabulary API return it. */
|
|
59
69
|
export interface DesignDocVocabularyOption {
|
|
@@ -107,6 +117,24 @@ export type DesignDocCommentStatus = string;
|
|
|
107
117
|
export type DesignDocLinkType = string;
|
|
108
118
|
/** A `design_doc_vocabulary.value` of kind `media_type`. */
|
|
109
119
|
export type DesignDocMediaType = string;
|
|
120
|
+
/**
|
|
121
|
+
* A `design_doc_vocabulary.value` of kind `lifecycle_status`: a status a PERSON sets (postponed, shipped, archived),
|
|
122
|
+
* beside the readiness the sign-offs decide. Null on a doc = active, and its readiness shows.
|
|
123
|
+
*/
|
|
124
|
+
export type DesignDocLifecycleStatus = string;
|
|
125
|
+
/**
|
|
126
|
+
* A `design_doc_vocabulary.value` of kind `readiness`: the stage the sign-offs put a doc in (not ready, in active
|
|
127
|
+
* work, blocked, ready to build). The hub picks the row by its role; the words and the colour are the row's.
|
|
128
|
+
*/
|
|
129
|
+
export type DesignDocReadiness = string;
|
|
130
|
+
|
|
131
|
+
/** A vocabulary value as a card shows it, resolved by the hub so a surface without the vocabulary can draw it. */
|
|
132
|
+
export interface DesignDocVocabularyDisplay {
|
|
133
|
+
value: string;
|
|
134
|
+
label: string;
|
|
135
|
+
/** A `StatusBadge` colorScheme name. */
|
|
136
|
+
color_scheme: string;
|
|
137
|
+
}
|
|
110
138
|
|
|
111
139
|
// ---------------------------------------------------------------------------
|
|
112
140
|
// Rows (as returned by the hub DAL — hydrated)
|
|
@@ -258,6 +286,15 @@ export interface DesignDoc {
|
|
|
258
286
|
comments: DesignDocComment[];
|
|
259
287
|
completion: DesignDocCompletion;
|
|
260
288
|
my_open_work?: DesignDocMyOpenWork;
|
|
289
|
+
/** The status a person set (postponed, shipped, archived); null = active, its readiness shows. */
|
|
290
|
+
lifecycle_status: DesignDocLifecycleStatus | null;
|
|
291
|
+
/** That status as the vocabulary words it, resolved by the hub (null when none is set). */
|
|
292
|
+
lifecycle?: DesignDocVocabularyDisplay | null;
|
|
293
|
+
/** When the status was last set or cleared, and by whom. */
|
|
294
|
+
lifecycle_status_changed_at: string | null;
|
|
295
|
+
lifecycle_status_changed_by?: DesignDocPerson | null;
|
|
296
|
+
/** The stage its sign-offs put it in, as the vocabulary words it, resolved by the hub. */
|
|
297
|
+
readiness?: DesignDocVocabularyDisplay | null;
|
|
261
298
|
}
|
|
262
299
|
|
|
263
300
|
// ---------------------------------------------------------------------------
|
|
@@ -328,6 +365,8 @@ export interface UpdateDesignDocData {
|
|
|
328
365
|
*/
|
|
329
366
|
feature_lead_ids?: string[];
|
|
330
367
|
links?: DesignDocLinkInput[];
|
|
368
|
+
/** A `lifecycle_status` value, or null to make the doc active again. Anyone who may edit the doc may set it. */
|
|
369
|
+
lifecycle_status?: DesignDocLifecycleStatus | null;
|
|
331
370
|
/** OCC token — the `updated_at` the editor seeded from; mismatch → 409. */
|
|
332
371
|
expected_updated_at?: string;
|
|
333
372
|
}
|
|
@@ -358,10 +397,18 @@ export interface AddDesignDocCommentData {
|
|
|
358
397
|
body: string;
|
|
359
398
|
}
|
|
360
399
|
|
|
361
|
-
/** The dashboard's readiness filter (`?ready=`). */
|
|
362
|
-
export type DesignDocReadyFilter =
|
|
400
|
+
/** The dashboard's readiness filter (`?ready=`): one `readiness` value. */
|
|
401
|
+
export type DesignDocReadyFilter = DesignDocReadiness;
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* The dashboard's status filter (`?status=`): `active` (no lifecycle status set, the default) or one
|
|
405
|
+
* `lifecycle_status` value.
|
|
406
|
+
*/
|
|
407
|
+
export type DesignDocLifecycleFilter = 'active' | DesignDocLifecycleStatus;
|
|
363
408
|
|
|
364
409
|
export interface DesignDocFilters {
|
|
410
|
+
/** Defaults to `active`: a doc a person postponed, shipped or archived is listed only under its own status. */
|
|
411
|
+
lifecycle?: DesignDocLifecycleFilter;
|
|
365
412
|
ready?: DesignDocReadyFilter;
|
|
366
413
|
/** One or more `spec_mode` values (CSV on the wire). */
|
|
367
414
|
spec_mode?: string;
|
|
@@ -378,16 +425,18 @@ export interface DesignDocListResponse {
|
|
|
378
425
|
* filter itself — so a filtered row still shows what the other value holds. */
|
|
379
426
|
facets: {
|
|
380
427
|
spec_mode: Record<string, number>;
|
|
381
|
-
/** The same over readiness (`
|
|
382
|
-
ready: Record<
|
|
428
|
+
/** The same over readiness (each `readiness` value), MINUS the ready filter itself: the filter row's options. */
|
|
429
|
+
ready: Record<DesignDocReadiness, number>;
|
|
430
|
+
/** The same over the status filter (`active` and each `lifecycle_status` value), MINUS the status filter itself. */
|
|
431
|
+
lifecycle: Record<string, number>;
|
|
383
432
|
};
|
|
384
433
|
}
|
|
385
434
|
|
|
435
|
+
/** Counted over ACTIVE docs (no lifecycle status), so postponed, shipped and archived docs never inflate open work. */
|
|
386
436
|
export interface DesignDocStats {
|
|
387
437
|
total: number;
|
|
388
|
-
/**
|
|
389
|
-
|
|
390
|
-
not_ready: number;
|
|
438
|
+
/** How many active docs each `readiness` value holds, zeros included. */
|
|
439
|
+
readiness: Record<DesignDocReadiness, number>;
|
|
391
440
|
waiting_on_me_docs: number;
|
|
392
441
|
waiting_on_me_tasks: number;
|
|
393
442
|
}
|
package/src/types/doc-source.ts
CHANGED
|
@@ -75,12 +75,12 @@ export interface DocSourceDal<Client = unknown> {
|
|
|
75
75
|
* Doc-source registry id. The lib's `<DocViewer>` is generic over this union
|
|
76
76
|
* — the hub's `DOC_SOURCES` registry defines the actual ids. Listed here as a
|
|
77
77
|
* type-only union so the lib can flow the id into `DocRenderHandlers.sourceId`
|
|
78
|
-
* without callers having to cast `string`
|
|
78
|
+
* without callers having to cast `string` to the union.
|
|
79
79
|
*
|
|
80
80
|
* Adding a new source = add the id here AND register it in the hub. The lib
|
|
81
81
|
* never reaches into the registry — this is purely a type-narrowing handle.
|
|
82
82
|
*/
|
|
83
|
-
export type DocSourceId = 'openframe-docs' | 'data-room-docs';
|
|
83
|
+
export type DocSourceId = 'openframe-docs' | 'data-room-docs' | 'company-policies';
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Result returned by the resolve-link endpoint. Discriminator-style: `success`
|
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A design doc's
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* A design doc's status as every surface draws it (the hub's design-docs screens, its doc page, the lib's design-doc
|
|
3
|
+
* cards), and its sign-off caption.
|
|
4
|
+
*
|
|
5
|
+
* Both halves of the status are vocabulary rows the hub resolves onto the doc, so no surface holds a word or a colour:
|
|
6
|
+
* - its READINESS (`doc.readiness`): the stage the sign-offs put it in (not ready, in active work, blocked, ready to
|
|
7
|
+
* build), picked by the hub from the `readiness` rows by role;
|
|
8
|
+
* - a LIFECYCLE status a person set (`doc.lifecycle`: postponed, shipped, archived). While one is set it is what the
|
|
9
|
+
* doc shows; cleared, the readiness shows again.
|
|
10
|
+
*
|
|
11
|
+
* The sign-off caption is counts only ("4/6 signed off · 1 blocking"): the status has its own badge.
|
|
12
|
+
* Pure, zero-import beyond the type.
|
|
5
13
|
*/
|
|
6
|
-
import type { DesignDocCompletion } from '../types/design-doc';
|
|
14
|
+
import type { DesignDoc, DesignDocCompletion, DesignDocVocabularyDisplay } from '../types/design-doc';
|
|
7
15
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
> = {
|
|
16
|
-
ready: { label: 'Ready to build', scheme: 'success' },
|
|
17
|
-
not_ready: { label: 'Not ready', scheme: 'default' },
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/** Ready to build ⇔ `completion.isComplete` (every section signed off, no open blocking comment). */
|
|
21
|
-
export const designDocReadiness = (completion: Pick<DesignDocCompletion, 'isComplete'>): DesignDocReadiness =>
|
|
22
|
-
completion.isComplete ? 'ready' : 'not_ready';
|
|
16
|
+
/** What the doc's status badge says: its lifecycle status when a person set one, else its readiness (null from a hub
|
|
17
|
+
* that resolves neither). */
|
|
18
|
+
export function designDocStatusDisplay(
|
|
19
|
+
doc: Pick<DesignDoc, 'lifecycle' | 'readiness'>,
|
|
20
|
+
): DesignDocVocabularyDisplay | null {
|
|
21
|
+
return doc.lifecycle ?? doc.readiness ?? null;
|
|
22
|
+
}
|
|
23
23
|
|
|
24
|
-
/** "4/6
|
|
24
|
+
/** "4/6 signed off" */
|
|
25
25
|
export function formatCompletionLabel(completion: Pick<DesignDocCompletion, 'total' | 'completed'>): string {
|
|
26
|
-
return `${completion.completed}/${completion.total}
|
|
26
|
+
return `${completion.completed}/${completion.total} signed off`;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
/** The SAME
|
|
29
|
+
/** The SAME count plus the blocking veto ("4/6 signed off · 1 blocking"): the dashboard row, the doc page, the card. */
|
|
30
30
|
export function formatCompletionLabelWithBlockers(completion: DesignDocCompletion): string {
|
|
31
31
|
const base = formatCompletionLabel(completion);
|
|
32
|
-
if (completion.isComplete) return `${DESIGN_DOC_READINESS_DISPLAY.ready.label} · ${base}`;
|
|
33
32
|
return completion.openBlocking > 0 ? `${base} · ${completion.openBlocking} blocking` : base;
|
|
34
33
|
}
|
|
@@ -102,3 +102,30 @@ export function getDocSourceDefaultPath(
|
|
|
102
102
|
const firstFolderWithReadme = structure.find(node => node.type === 'folder' && node.hasReadme && !!node.path);
|
|
103
103
|
return firstFolderWithReadme?.path ?? null;
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
/** The first document under a folder (its own files first, then each subfolder's, in order), never a diagram source. */
|
|
107
|
+
export function findFirstDocPath(folder: DocNode): string | null {
|
|
108
|
+
const children = folder.children ?? [];
|
|
109
|
+
for (const child of children) {
|
|
110
|
+
if (child.type === 'file' && !child.path.toLowerCase().endsWith('.mmd')) return child.path;
|
|
111
|
+
}
|
|
112
|
+
for (const child of children) {
|
|
113
|
+
if (child.type === 'folder') {
|
|
114
|
+
const nested = findFirstDocPath(child);
|
|
115
|
+
if (nested) return nested;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The document a source's root shows: its folder index when the tree has one, else its first document (the rule a
|
|
123
|
+
* folder with no README follows), else null (a tree with no document at all).
|
|
124
|
+
*/
|
|
125
|
+
export function rootDocumentPath(
|
|
126
|
+
structure: DocNode[],
|
|
127
|
+
folderIndexFile: string = DEFAULT_FOLDER_INDEX_FILE,
|
|
128
|
+
): string | null {
|
|
129
|
+
if (structure.some(node => node.type === 'file' && node.path === folderIndexFile)) return folderIndexFile;
|
|
130
|
+
return findFirstDocPath({ id: '', name: '', slug: '', path: '', type: 'folder', children: structure });
|
|
131
|
+
}
|
package/src/utils/list-url.ts
CHANGED
|
@@ -99,7 +99,8 @@ const BUILDERS: Record<string, (ids: string[], base: string) => string> = {
|
|
|
99
99
|
slack_message: (ids, b) => `${b}/api/slack-community/messages?ids=${ids.join(',')}`,
|
|
100
100
|
hubspot_ticket: (ids, b) => `${b}/api/tickets/internal?ids=${ids.join(',')}`,
|
|
101
101
|
hubspot_ticket_anon: (ids, b) => `${b}/api/tickets/known-issues?ids=${ids.join(',')}`,
|
|
102
|
-
data_room_doc: (ids, b) => `${b}/api/data-room/documents?ids=${ids.join(',')}`,
|
|
102
|
+
data_room_doc: (ids, b) => `${b}/api/libraries/data-room/documents?ids=${ids.join(',')}`,
|
|
103
|
+
policy_doc: (ids, b) => `${b}/api/libraries/company-policies/documents?ids=${ids.join(',')}`,
|
|
103
104
|
markdown: (ids, b) => `${b}/api/docs/pages?ids=${ids.join(',')}`,
|
|
104
105
|
financial_kpi: (ids, b) => `${b}/api/financials/kpis?ids=${ids.join(',')}`,
|
|
105
106
|
cap_table: (ids, b) => `${b}/api/financials/cap-table?ids=${ids.join(',')}`,
|
|
@@ -27,13 +27,13 @@ import { defaultDocumentTypeForTableId, getSourceLabel } from './source-icons';
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Doc-table documentTypes: whole documents with their own viewer (markdown =
|
|
30
|
-
* product docs, data_room_doc = data room). They carry an in-app `path` (handed
|
|
30
|
+
* product docs, data_room_doc = data room, policy_doc = company policies). They carry an in-app `path` (handed
|
|
31
31
|
* to the host's composer as the slug), and a server never folds them
|
|
32
32
|
* into one citation (`groupsByTable`); the strip still draws them as one chip
|
|
33
33
|
* per table. The hub's per-source contract test pins this list to its
|
|
34
34
|
* doc-table configs.
|
|
35
35
|
*/
|
|
36
|
-
export const DOC_TABLE_TYPES = ['markdown', 'data_room_doc'] as const;
|
|
36
|
+
export const DOC_TABLE_TYPES = ['markdown', 'data_room_doc', 'policy_doc'] as const;
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Which tables a SERVER folds into one citation: rows of a knowledge table
|
|
@@ -29,6 +29,7 @@ export const SOURCE_ICON_NAMES: Record<string, string> = {
|
|
|
29
29
|
// Doc tables
|
|
30
30
|
'openframe-docs': 'openframe',
|
|
31
31
|
'data-room-docs': 'shield',
|
|
32
|
+
'company-policies': 'book-open',
|
|
32
33
|
|
|
33
34
|
// CMS / programs
|
|
34
35
|
'blog-posts': 'newspaper',
|
|
@@ -136,6 +137,7 @@ export const SOURCE_LABELS_BY_TABLE: Record<string, string> = {
|
|
|
136
137
|
// Doc tables
|
|
137
138
|
'openframe-docs': 'OpenFrame Docs',
|
|
138
139
|
'data-room-docs': 'Data Room',
|
|
140
|
+
'company-policies': 'Company Policies',
|
|
139
141
|
|
|
140
142
|
// CMS / programs
|
|
141
143
|
'blog-posts': 'Blog Posts',
|
|
@@ -242,6 +244,7 @@ export const DEFAULT_DOCUMENT_TYPE_TO_TABLE_ID: Record<string, string> = {
|
|
|
242
244
|
// Doc tables
|
|
243
245
|
markdown: 'openframe-docs',
|
|
244
246
|
data_room_doc: 'data-room-docs',
|
|
247
|
+
policy_doc: 'company-policies',
|
|
245
248
|
|
|
246
249
|
// CMS / programs
|
|
247
250
|
blog_post: 'blog-posts',
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export declare function MarkdownSkeleton({ region, }?: {
|
|
2
|
-
/** The loading state announces itself (`LoadingRegion`). `false` inside another loading region. Default `true`. */
|
|
3
|
-
region?: boolean;
|
|
4
|
-
}): import("react").JSX.Element;
|
|
5
|
-
/**
|
|
6
|
-
* Embed-style skeleton — matches the iframe loading state for `pdf`,
|
|
7
|
-
* `google_sheet`, `figma`, and `file` document types. Used by
|
|
8
|
-
* `<DocsHubPage>` as the default for non-markdown documentTypes.
|
|
9
|
-
*
|
|
10
|
-
* The skeleton is documentType-aware so its layout matches the actual
|
|
11
|
-
* viewer that will replace it:
|
|
12
|
-
* - `pdf` → header with title + TWO buttons (Preview, Download)
|
|
13
|
-
* - `google_sheet`/`figma` → header with title + ONE button/toggle
|
|
14
|
-
* - `file` → centered FileDownloadCard-style box
|
|
15
|
-
* - undefined / others → generic (1-button header)
|
|
16
|
-
*/
|
|
17
|
-
export interface EmbedSkeletonProps {
|
|
18
|
-
/** When provided, the header layout matches the eventual viewer's
|
|
19
|
-
* button count + arrangement, so the layout doesn't shift on load. */
|
|
20
|
-
documentType?: 'pdf' | 'google_sheet' | 'figma' | 'file' | string;
|
|
21
|
-
/** The loading state announces itself (`LoadingRegion`). `false` inside another loading region. Default `true`. */
|
|
22
|
-
region?: boolean;
|
|
23
|
-
}
|
|
24
|
-
export declare function EmbedSkeleton({ documentType, region }?: EmbedSkeletonProps): import("react").JSX.Element;
|
|
25
|
-
//# sourceMappingURL=skeletons.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"skeletons.d.ts","sourceRoot":"","sources":["../../../src/components/docs/skeletons.tsx"],"names":[],"mappings":"AA2BA,wBAAgB,gBAAgB,CAAC,EAC/B,MAAa,GACd,GAAE;IACD,mHAAmH;IACnH,MAAM,CAAC,EAAE,OAAO,CAAC;CACb,+BA2BL;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IACjC;2EACuE;IACvE,YAAY,CAAC,EAAE,KAAK,GAAG,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAClE,mHAAmH;IACnH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,MAAa,EAAE,GAAE,kBAAuB,+BAiDrF"}
|