@crossworks/share-ui 0.230.43
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/LICENSE.md +135 -0
- package/package.json +68 -0
- package/src/app-bridge-protocol.ts +115 -0
- package/src/app-presenter.tsx +25 -0
- package/src/app-sandbox.tsx +552 -0
- package/src/appearance.ts +192 -0
- package/src/avatar.test.ts +229 -0
- package/src/avatar.ts +731 -0
- package/src/backgrounds.test.ts +119 -0
- package/src/backgrounds.ts +118 -0
- package/src/draw-presenter.tsx +39 -0
- package/src/event-presenter.tsx +62 -0
- package/src/file-presenter.tsx +76 -0
- package/src/formula-calculator.tsx +209 -0
- package/src/formula-presenter.test.ts +128 -0
- package/src/formula-presenter.tsx +301 -0
- package/src/help-topics.ts +104 -0
- package/src/lib/ink-audit.test.ts +314 -0
- package/src/lib/theme-css-blocks.ts +26 -0
- package/src/lib/theme-generator.test.ts +179 -0
- package/src/lib/theme-registry.gen.ts +352 -0
- package/src/lib/themes.test.ts +308 -0
- package/src/lib/themes.ts +75 -0
- package/src/lib/utils.ts +6 -0
- package/src/nav-items.ts +225 -0
- package/src/note-presenter.tsx +14 -0
- package/src/page-outline.tsx +127 -0
- package/src/table-presenter.tsx +226 -0
- package/src/task-presenter.tsx +60 -0
- package/src/ui/button.tsx +50 -0
- package/src/ui/input.tsx +18 -0
- package/src/ui/label.tsx +20 -0
- package/src/view-payload.ts +82 -0
- package/styles/app.css +1098 -0
- package/styles/themes.css +6198 -0
- package/themes/generate.d.mts +11 -0
- package/themes/generate.mjs +618 -0
- package/themes/model.d.mts +24 -0
- package/themes/model.mjs +213 -0
- package/themes/preview.html +145 -0
- package/themes/seeds.d.mts +16 -0
- package/themes/seeds.mjs +3694 -0
- package/tsconfig.json +15 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import ReactMarkdown from 'react-markdown';
|
|
2
|
+
import remarkGfm from 'remark-gfm';
|
|
3
|
+
import { Check, Circle } from 'lucide-react';
|
|
4
|
+
import { formatDateTime } from '@mantle/client-types/lib/format-datetime';
|
|
5
|
+
|
|
6
|
+
/** Public task render — a clean status card. */
|
|
7
|
+
export function TaskPresenter({
|
|
8
|
+
view,
|
|
9
|
+
}: {
|
|
10
|
+
view: { title: string; body: string; status: string; priority: string; dueAt: string | null };
|
|
11
|
+
}) {
|
|
12
|
+
const done = view.status === 'done';
|
|
13
|
+
return (
|
|
14
|
+
<div className="mx-auto max-w-2xl px-6 py-12 md:py-16">
|
|
15
|
+
<div className="rounded-xl border border-border bg-card p-6">
|
|
16
|
+
<div className="flex items-start gap-3">
|
|
17
|
+
<span
|
|
18
|
+
className={
|
|
19
|
+
'mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full border ' +
|
|
20
|
+
(done
|
|
21
|
+
? 'border-primary bg-primary text-primary-foreground'
|
|
22
|
+
: 'border-border text-transparent')
|
|
23
|
+
}
|
|
24
|
+
aria-hidden
|
|
25
|
+
>
|
|
26
|
+
{done ? <Check className="size-4" /> : <Circle className="size-3" />}
|
|
27
|
+
</span>
|
|
28
|
+
<div className="min-w-0 flex-1">
|
|
29
|
+
<h1
|
|
30
|
+
className={
|
|
31
|
+
'text-2xl font-bold tracking-tight ' +
|
|
32
|
+
(done ? 'text-muted-foreground line-through' : '')
|
|
33
|
+
}
|
|
34
|
+
>
|
|
35
|
+
{view.title}
|
|
36
|
+
</h1>
|
|
37
|
+
<div className="mt-3 flex flex-wrap items-center gap-2 text-xs">
|
|
38
|
+
<span className="rounded-full border border-border px-2 py-0.5 capitalize text-muted-foreground">
|
|
39
|
+
{view.status}
|
|
40
|
+
</span>
|
|
41
|
+
<span className="rounded-full border border-border px-2 py-0.5 capitalize text-muted-foreground">
|
|
42
|
+
{view.priority} priority
|
|
43
|
+
</span>
|
|
44
|
+
{view.dueAt && (
|
|
45
|
+
<span className="rounded-full border border-border px-2 py-0.5 text-muted-foreground">
|
|
46
|
+
Due {formatDateTime(view.dueAt)}
|
|
47
|
+
</span>
|
|
48
|
+
)}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
{view.body && (
|
|
53
|
+
<div className="prose prose-sm dark:prose-invert mt-5 max-w-none border-t border-border pt-5">
|
|
54
|
+
<ReactMarkdown remarkPlugins={[remarkGfm]}>{view.body}</ReactMarkdown>
|
|
55
|
+
</div>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
import { cn } from '../lib/utils';
|
|
5
|
+
|
|
6
|
+
const buttonVariants = cva(
|
|
7
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
12
|
+
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
13
|
+
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
14
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
15
|
+
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
16
|
+
link: 'text-primary-ink underline-offset-4 hover:underline',
|
|
17
|
+
// Semantic approve/deny aliases — used by sender curation buttons.
|
|
18
|
+
// Theme-aware: approve uses the primary action colour, deny the
|
|
19
|
+
// destructive colour, so they recolour with the active theme instead
|
|
20
|
+
// of forcing green/red.
|
|
21
|
+
approve: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
22
|
+
deny: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
default: 'h-10 px-4 py-2',
|
|
26
|
+
sm: 'h-9 rounded-md px-3',
|
|
27
|
+
lg: 'h-11 rounded-md px-8',
|
|
28
|
+
icon: 'h-10 w-10',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: { variant: 'default', size: 'default' },
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export interface ButtonProps
|
|
36
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
37
|
+
asChild?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
41
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
42
|
+
const Comp = asChild ? Slot : 'button';
|
|
43
|
+
return (
|
|
44
|
+
<Comp ref={ref} className={cn(buttonVariants({ variant, size, className }))} {...props} />
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
Button.displayName = 'Button';
|
|
49
|
+
|
|
50
|
+
export { buttonVariants };
|
package/src/ui/input.tsx
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { cn } from '../lib/utils';
|
|
3
|
+
|
|
4
|
+
export const Input = React.forwardRef<
|
|
5
|
+
HTMLInputElement,
|
|
6
|
+
React.InputHTMLAttributes<HTMLInputElement>
|
|
7
|
+
>(({ className, type, ...props }, ref) => (
|
|
8
|
+
<input
|
|
9
|
+
type={type}
|
|
10
|
+
ref={ref}
|
|
11
|
+
className={cn(
|
|
12
|
+
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
|
13
|
+
className,
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
));
|
|
18
|
+
Input.displayName = 'Input';
|
package/src/ui/label.tsx
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
5
|
+
import { cn } from '../lib/utils';
|
|
6
|
+
|
|
7
|
+
export const Label = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
|
10
|
+
>(({ className, ...props }, ref) => (
|
|
11
|
+
<LabelPrimitive.Root
|
|
12
|
+
ref={ref}
|
|
13
|
+
className={cn(
|
|
14
|
+
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
|
15
|
+
className,
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
));
|
|
20
|
+
Label.displayName = 'Label';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The JSON payload of GET /s/<token>/view — the share content door for the
|
|
3
|
+
* /team inline reader (no iframe). One shape per share kind, mirroring the
|
|
4
|
+
* server's ShareView except where the server must pre-compute:
|
|
5
|
+
*
|
|
6
|
+
* - `page` ships pre-rendered SANITIZED html + toc (renderPageDoc runs
|
|
7
|
+
* server-side only, so escaping stays on the server and katex/lowlight
|
|
8
|
+
* stay out of the client bundle);
|
|
9
|
+
* - `folder` ships the listing for the requested `?p=` sub-path (the
|
|
10
|
+
* listing is a DB read, re-validated against the shared root per request).
|
|
11
|
+
*
|
|
12
|
+
* Everything else is the presenter's own props, verbatim. Consumed by the
|
|
13
|
+
* client ShareReader and produced by server/web/app/s/[token]/view.
|
|
14
|
+
*/
|
|
15
|
+
import type { TocEntry } from '@mantle/content-core/page-toc';
|
|
16
|
+
import type { CoverageGap, FormulaSpec } from '@mantle/content-core/formula-spec';
|
|
17
|
+
import type { TargetSignature } from '@mantle/content-core/formula-signature';
|
|
18
|
+
import type { DimensionIssue } from '@mantle/content-core/formula-dimensions';
|
|
19
|
+
import type { Column, Row } from '@mantle/content-core/table-model';
|
|
20
|
+
|
|
21
|
+
export type ShareFolderListing = {
|
|
22
|
+
/** ltree path currently being listed (the shared root or a descendant). */
|
|
23
|
+
currentPath: string;
|
|
24
|
+
folders: Array<{ id: string; path: string; slug: string; fileCount: number }>;
|
|
25
|
+
files: Array<{ id: string; filename: string; mimeType: string; sizeBytes: number }>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type ShareViewPayload =
|
|
29
|
+
| {
|
|
30
|
+
kind: 'page';
|
|
31
|
+
title: string;
|
|
32
|
+
icon: string | null;
|
|
33
|
+
width: 'narrow' | 'wide';
|
|
34
|
+
/** Sanitized HTML from renderPageDoc — safe to inject as-is. */
|
|
35
|
+
html: string;
|
|
36
|
+
toc: TocEntry[];
|
|
37
|
+
}
|
|
38
|
+
| { kind: 'note'; title: string; content: string }
|
|
39
|
+
| {
|
|
40
|
+
kind: 'task';
|
|
41
|
+
title: string;
|
|
42
|
+
body: string;
|
|
43
|
+
status: string;
|
|
44
|
+
priority: string;
|
|
45
|
+
dueAt: string | null;
|
|
46
|
+
}
|
|
47
|
+
| {
|
|
48
|
+
kind: 'event';
|
|
49
|
+
title: string;
|
|
50
|
+
body: string;
|
|
51
|
+
startsAt: string | null;
|
|
52
|
+
endsAt: string | null;
|
|
53
|
+
location: string | null;
|
|
54
|
+
}
|
|
55
|
+
| { kind: 'file'; fileId: string; filename: string; mimeType: string; size: number }
|
|
56
|
+
| { kind: 'app'; appId: string; title: string }
|
|
57
|
+
| {
|
|
58
|
+
kind: 'table';
|
|
59
|
+
tableId: string;
|
|
60
|
+
title: string;
|
|
61
|
+
icon: string | null;
|
|
62
|
+
tabs: Array<{
|
|
63
|
+
id: string;
|
|
64
|
+
name: string;
|
|
65
|
+
rowCount: number;
|
|
66
|
+
columns: Array<{ id: string; name: string; type: string }>;
|
|
67
|
+
}> | null;
|
|
68
|
+
legacyDoc: { columns: Column[]; rows: Row[] } | null;
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
kind: 'formula';
|
|
72
|
+
title: string;
|
|
73
|
+
spec: FormulaSpec;
|
|
74
|
+
signature: TargetSignature[];
|
|
75
|
+
coverageGaps: CoverageGap[];
|
|
76
|
+
dimensionIssues: DimensionIssue[];
|
|
77
|
+
}
|
|
78
|
+
| { kind: 'folder'; title: string; path: string; listing: ShareFolderListing }
|
|
79
|
+
// Only WHETHER a committed snapshot exists. The bytes are fetched as an
|
|
80
|
+
// image from /s/<token>/draw, so they never ride in this payload and never
|
|
81
|
+
// become markup on the page.
|
|
82
|
+
| { kind: 'draw'; title: string; hasSvg: boolean };
|