@flamingo-stack/openframe-frontend-core 0.0.422 → 0.0.423-snapshot.20260714123224
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/{chunk-WAQH35VG.cjs → chunk-445ZGRPE.cjs} +30 -30
- package/dist/{chunk-WAQH35VG.cjs.map → chunk-445ZGRPE.cjs.map} +1 -1
- package/dist/{chunk-HBTKCED7.js → chunk-4ACSUJ7Q.js} +13 -6
- package/dist/chunk-4ACSUJ7Q.js.map +1 -0
- package/dist/{chunk-DUHM44CQ.js → chunk-56NEIHTC.js} +3 -3
- package/dist/{chunk-XFYQV2K2.js → chunk-5TCOKIAS.js} +2 -2
- package/dist/{chunk-4TMMYZTY.js → chunk-6PB5HGFM.js} +2 -2
- package/dist/{chunk-WQEEGFDM.js → chunk-AD4A44US.js} +3 -3
- package/dist/{chunk-BF5BXKM7.cjs → chunk-AEUFBPB3.cjs} +82 -82
- package/dist/{chunk-BF5BXKM7.cjs.map → chunk-AEUFBPB3.cjs.map} +1 -1
- package/dist/{chunk-LHM2VUTO.js → chunk-APWAFTFL.js} +3 -3
- package/dist/{chunk-RGKGRS2T.cjs → chunk-BM5RER5N.cjs} +59 -59
- package/dist/{chunk-RGKGRS2T.cjs.map → chunk-BM5RER5N.cjs.map} +1 -1
- package/dist/{chunk-A2IRUIUJ.cjs → chunk-C7AAFHP5.cjs} +37 -37
- package/dist/{chunk-A2IRUIUJ.cjs.map → chunk-C7AAFHP5.cjs.map} +1 -1
- package/dist/{chunk-LWC2U5OC.cjs → chunk-FVL25VKT.cjs} +9 -9
- package/dist/{chunk-LWC2U5OC.cjs.map → chunk-FVL25VKT.cjs.map} +1 -1
- package/dist/{chunk-YJDVAT7J.js → chunk-H2ECFX4G.js} +43 -22
- package/dist/chunk-H2ECFX4G.js.map +1 -0
- package/dist/{chunk-E6WANQ6K.cjs → chunk-IMK6RUEC.cjs} +13 -6
- package/dist/chunk-IMK6RUEC.cjs.map +1 -0
- package/dist/{chunk-PO4PXWZN.cjs → chunk-J2WSWXJN.cjs} +5 -5
- package/dist/{chunk-PO4PXWZN.cjs.map → chunk-J2WSWXJN.cjs.map} +1 -1
- package/dist/{chunk-2JNFQEVX.cjs → chunk-JSO2EOOV.cjs} +26 -26
- package/dist/{chunk-2JNFQEVX.cjs.map → chunk-JSO2EOOV.cjs.map} +1 -1
- package/dist/{chunk-ULUH2X53.cjs → chunk-LYO2FUKU.cjs} +11 -11
- package/dist/{chunk-ULUH2X53.cjs.map → chunk-LYO2FUKU.cjs.map} +1 -1
- package/dist/{chunk-B62DI2SC.cjs → chunk-M3ROXRUZ.cjs} +19 -19
- package/dist/{chunk-B62DI2SC.cjs.map → chunk-M3ROXRUZ.cjs.map} +1 -1
- package/dist/{chunk-YZQNKMFQ.cjs → chunk-NMKIS3JX.cjs} +31 -31
- package/dist/{chunk-YZQNKMFQ.cjs.map → chunk-NMKIS3JX.cjs.map} +1 -1
- package/dist/{chunk-JSL3VXGJ.js → chunk-ORKNKKOH.js} +2 -2
- package/dist/{chunk-3BGWK5QI.js → chunk-OXE2PNKV.js} +4 -4
- package/dist/{chunk-HFK33T6A.js → chunk-RUQ4MVBN.js} +5 -5
- package/dist/{chunk-O3FSSWV4.cjs → chunk-TMITO43G.cjs} +12 -12
- package/dist/{chunk-O3FSSWV4.cjs.map → chunk-TMITO43G.cjs.map} +1 -1
- package/dist/{chunk-KUDLV25M.cjs → chunk-X3IHSMPD.cjs} +388 -367
- package/dist/chunk-X3IHSMPD.cjs.map +1 -0
- package/dist/{chunk-YUMFRC3D.js → chunk-X7UBB6F6.js} +4 -4
- package/dist/{chunk-6F5OKJTW.js → chunk-XMCURSQ7.js} +4 -4
- package/dist/{chunk-4BWQYGIY.js → chunk-Y4WY2AF6.js} +2 -2
- package/dist/components/case-studies/index.cjs +9 -9
- package/dist/components/case-studies/index.js +3 -3
- package/dist/components/chat/index.cjs +3 -3
- package/dist/components/chat/index.js +2 -2
- package/dist/components/contact/index.cjs +4 -4
- package/dist/components/contact/index.js +3 -3
- package/dist/components/docs/index.cjs +6 -6
- package/dist/components/docs/index.js +5 -5
- package/dist/components/embeds/index.cjs +4 -4
- package/dist/components/embeds/index.js +3 -3
- package/dist/components/faq/index.cjs +4 -4
- package/dist/components/faq/index.js +3 -3
- package/dist/components/features/index.cjs +3 -3
- package/dist/components/features/index.js +2 -2
- package/dist/components/features/seo-editor-preview.d.ts.map +1 -1
- package/dist/components/features/time-tracker/time-tracker-panel.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +21 -21
- package/dist/components/help-center-pages/index.js +12 -12
- package/dist/components/index.cjs +153 -153
- package/dist/components/index.js +10 -10
- package/dist/components/navigation/index.cjs +3 -3
- package/dist/components/navigation/index.js +2 -2
- package/dist/components/onboarding-guides/index.cjs +6 -6
- package/dist/components/onboarding-guides/index.js +5 -5
- package/dist/components/related-content/index.cjs +4 -4
- package/dist/components/related-content/index.js +3 -3
- package/dist/components/tickets/index.cjs +6 -6
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/actions-menu.d.ts.map +1 -1
- package/dist/components/ui/button/split-button.d.ts +6 -0
- package/dist/components/ui/button/split-button.d.ts.map +1 -1
- package/dist/components/ui/file-manager/index.cjs +50 -50
- package/dist/components/ui/file-manager/index.js +1 -1
- package/dist/components/ui/index.cjs +3 -3
- package/dist/components/ui/index.js +2 -2
- package/dist/components/ui/page-actions.d.ts.map +1 -1
- package/dist/index.cjs +5 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -2
- package/dist/utils/index.cjs +4 -0
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/seo-description.d.ts +14 -0
- package/dist/utils/seo-description.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/.tool-icon.md +3 -3
- package/src/components/features/seo-editor-preview.tsx +24 -1
- package/src/components/features/time-tracker/time-tracker-panel.tsx +1 -0
- package/src/components/icons-v2-generated/brand-logos/.index.md +1 -2
- package/src/components/platform/.ShellTypeBadge.md +1 -1
- package/src/components/platform/.ToolBadge.md +2 -2
- package/src/components/ui/actions-menu.tsx +1 -0
- package/src/components/ui/button/split-button.tsx +18 -3
- package/src/components/ui/page-actions.tsx +16 -14
- package/src/stories/.OSTypeBadge.stories.md +1 -1
- package/src/stories/.ShellTypeBadge.stories.md +1 -1
- package/src/stories/.Toaster.stories.md +1 -1
- package/src/stories/.ToolBadge.stories.md +2 -2
- package/src/stories/.ToolExecutionDisplay.stories.md +4 -4
- package/src/types/.os.types.md +1 -1
- package/src/types/.shell.types.md +1 -1
- package/src/types/.tool.types.md +3 -3
- package/src/utils/.tool-utils.md +1 -3
- package/src/utils/index.ts +1 -0
- package/src/utils/seo-description.ts +13 -0
- package/dist/chunk-E6WANQ6K.cjs.map +0 -1
- package/dist/chunk-HBTKCED7.js.map +0 -1
- package/dist/chunk-KUDLV25M.cjs.map +0 -1
- package/dist/chunk-YJDVAT7J.js.map +0 -1
- /package/dist/{chunk-DUHM44CQ.js.map → chunk-56NEIHTC.js.map} +0 -0
- /package/dist/{chunk-XFYQV2K2.js.map → chunk-5TCOKIAS.js.map} +0 -0
- /package/dist/{chunk-4TMMYZTY.js.map → chunk-6PB5HGFM.js.map} +0 -0
- /package/dist/{chunk-WQEEGFDM.js.map → chunk-AD4A44US.js.map} +0 -0
- /package/dist/{chunk-LHM2VUTO.js.map → chunk-APWAFTFL.js.map} +0 -0
- /package/dist/{chunk-JSL3VXGJ.js.map → chunk-ORKNKKOH.js.map} +0 -0
- /package/dist/{chunk-3BGWK5QI.js.map → chunk-OXE2PNKV.js.map} +0 -0
- /package/dist/{chunk-HFK33T6A.js.map → chunk-RUQ4MVBN.js.map} +0 -0
- /package/dist/{chunk-YUMFRC3D.js.map → chunk-X7UBB6F6.js.map} +0 -0
- /package/dist/{chunk-6F5OKJTW.js.map → chunk-XMCURSQ7.js.map} +0 -0
- /package/dist/{chunk-4BWQYGIY.js.map → chunk-Y4WY2AF6.js.map} +0 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the `seo_description` length budget.
|
|
3
|
+
*
|
|
4
|
+
* Unlike `seo_title` (soft search-engine guidance), this is a HARD cap: the
|
|
5
|
+
* `seo_description` DB columns are `varchar(160)` and Postgres rejects longer
|
|
6
|
+
* values outright ("value too long for type character varying(160)").
|
|
7
|
+
*
|
|
8
|
+
* This number lives in exactly ONE place. Consumers:
|
|
9
|
+
* - `SEOEditorPreview` — input `maxLength` + the too-long alert.
|
|
10
|
+
* - the hub — imports it (via `@flamingo-stack/openframe-frontend-core/utils`)
|
|
11
|
+
* for the AI generation prompt guidance and the rewrite-to-fit helper.
|
|
12
|
+
*/
|
|
13
|
+
export declare const SEO_DESCRIPTION_MAX_LENGTH = 160;
|
|
14
|
+
//# sourceMappingURL=seo-description.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-description.d.ts","sourceRoot":"","sources":["../../src/utils/seo-description.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- source-hash: 2db0b6673e5d8f02f2a3713fb6528d5d -->
|
|
2
|
-
Renders the appropriate logo icon for a given MSP tool type (Fleet MDM, MeshCentral,
|
|
2
|
+
Renders the appropriate logo icon for a given MSP tool type (Fleet MDM, MeshCentral, Authentik, Osquery, or OpenFrame variants).
|
|
3
3
|
|
|
4
4
|
## Key Components
|
|
5
5
|
|
|
@@ -25,9 +25,9 @@ import { ToolTypeValues } from "../types/tool.types";
|
|
|
25
25
|
// Render a Fleet MDM icon at default size (16px)
|
|
26
26
|
<ToolIcon toolType={ToolTypeValues.FLEET_MDM} />
|
|
27
27
|
|
|
28
|
-
// Render a
|
|
28
|
+
// Render a MeshCentral icon at 24px with a custom class
|
|
29
29
|
<ToolIcon
|
|
30
|
-
toolType={ToolTypeValues.
|
|
30
|
+
toolType={ToolTypeValues.MESHCENTRAL}
|
|
31
31
|
size={24}
|
|
32
32
|
className="h-6 w-6 text-accent"
|
|
33
33
|
/>
|
|
@@ -10,6 +10,7 @@ import Image from '../../embed-shims/next-image';
|
|
|
10
10
|
// SSOT for the field cap (server-safe constant). The seo_title renders as the
|
|
11
11
|
// page <title> verbatim (no brand suffix), so this is the full ~60-char budget.
|
|
12
12
|
import { SEO_TITLE_MAX_LENGTH } from '../../utils/seo-title';
|
|
13
|
+
import { SEO_DESCRIPTION_MAX_LENGTH } from '../../utils/seo-description';
|
|
13
14
|
|
|
14
15
|
export interface SEOEditorPreviewProps {
|
|
15
16
|
// SEO fields - must be strings (not undefined)
|
|
@@ -76,6 +77,11 @@ export function SEOEditorPreview({
|
|
|
76
77
|
const seoTitleLength = (seoTitle || '').length;
|
|
77
78
|
const seoTitleTooLong = seoTitleLength > SEO_TITLE_MAX_LENGTH;
|
|
78
79
|
|
|
80
|
+
// SEO description length state — the column is varchar(160); flag over-length so
|
|
81
|
+
// the editor (or a programmatically-set value) can be brought within budget.
|
|
82
|
+
const seoDescriptionLength = (seoDescription || '').length;
|
|
83
|
+
const seoDescriptionTooLong = seoDescriptionLength > SEO_DESCRIPTION_MAX_LENGTH;
|
|
84
|
+
|
|
79
85
|
// Use fallback values if OG fields are empty
|
|
80
86
|
const displayTitle = seoTitle.trim() || title || 'Untitled';
|
|
81
87
|
const displayDescription = seoDescription.trim() || summary || 'No description';
|
|
@@ -137,7 +143,7 @@ export function SEOEditorPreview({
|
|
|
137
143
|
<div className="flex items-center justify-between gap-2">
|
|
138
144
|
<span className="text-[11px] text-ods-error font-['DM_Sans']">
|
|
139
145
|
{seoTitleTooLong
|
|
140
|
-
? `Too long
|
|
146
|
+
? `Too long: search engines may truncate this title (keep it under ${SEO_TITLE_MAX_LENGTH})`
|
|
141
147
|
: ''}
|
|
142
148
|
</span>
|
|
143
149
|
<span
|
|
@@ -206,10 +212,27 @@ export function SEOEditorPreview({
|
|
|
206
212
|
value={seoDescription || ''}
|
|
207
213
|
onChange={(e) => onSeoDescriptionChange(e.target.value)}
|
|
208
214
|
disabled={disabled}
|
|
215
|
+
maxLength={SEO_DESCRIPTION_MAX_LENGTH}
|
|
216
|
+
invalid={seoDescriptionTooLong}
|
|
209
217
|
placeholder="Enter SEO meta description..."
|
|
210
218
|
className="bg-ods-bg border-ods-border text-ods-text-primary flex-1 resize-none"
|
|
211
219
|
rows={6}
|
|
212
220
|
/>
|
|
221
|
+
<div className="flex items-center justify-between gap-2">
|
|
222
|
+
<span className="text-[11px] text-ods-error font-['DM_Sans']">
|
|
223
|
+
{seoDescriptionTooLong
|
|
224
|
+
? `Too long: search engines may truncate this description (keep it under ${SEO_DESCRIPTION_MAX_LENGTH})`
|
|
225
|
+
: ''}
|
|
226
|
+
</span>
|
|
227
|
+
<span
|
|
228
|
+
className={cn(
|
|
229
|
+
"text-[11px] font-['DM_Sans'] tabular-nums shrink-0",
|
|
230
|
+
seoDescriptionTooLong ? 'text-ods-error font-semibold' : 'text-ods-text-secondary'
|
|
231
|
+
)}
|
|
232
|
+
>
|
|
233
|
+
{seoDescriptionLength}/{SEO_DESCRIPTION_MAX_LENGTH}
|
|
234
|
+
</span>
|
|
235
|
+
</div>
|
|
213
236
|
{!seoDescription && summary && (
|
|
214
237
|
<p className="text-[11px] text-ods-accent font-['DM_Sans']">
|
|
215
238
|
Auto-populated from summary
|
|
@@ -13,7 +13,7 @@ A flat collection of named icon exports organized by brand/platform category:
|
|
|
13
13
|
| **Microsoft** | `MicrosoftLogoIcon`, `MicrosoftLogoGreyIcon`, `Office365LogoIcon`, `WindowsIcon`, `WindowsLogoIcon` |
|
|
14
14
|
| **AI Platforms** | `AnthropicLogoIcon`, `GeminiLogoIcon`, `OpenaiLogoIcon` (+ grey variants) |
|
|
15
15
|
| **Social** | `FacebookIcon`, `InstagramIcon`, `LinkedinIcon`, `TwitterIcon`, `TwitterXIcon`, `TiktokIcon`, `YoutubeIcon` |
|
|
16
|
-
| **MSP/IT Tools** | `
|
|
16
|
+
| **MSP/IT Tools** | `MeshcentralLogoIcon`, `FleetMdmLogoIcon`, `OsqueryLogoIcon`, `PowershellLogoIcon` |
|
|
17
17
|
| **Dev Tools** | `GithubIcon`, `FigmaIcon`, `FramerIcon`, `TrelloIcon`, `ClickupLogoIcon` |
|
|
18
18
|
|
|
19
19
|
> Many brands include both a standard and a `Grey` variant (e.g., `SlackLogoIcon` / `SlackLogoGreyIcon`), useful for active/inactive states.
|
|
@@ -26,7 +26,6 @@ import {
|
|
|
26
26
|
GithubIcon,
|
|
27
27
|
SlackLogoIcon,
|
|
28
28
|
SlackLogoGreyIcon,
|
|
29
|
-
TacticalRmmLogoIcon,
|
|
30
29
|
OpenaiLogoIcon,
|
|
31
30
|
} from '@/components/icons/brands';
|
|
32
31
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- source-hash: f614dd9faeadf368597d094822bcd3c7 -->
|
|
2
|
-
Displays a badge combining an icon and label for a given shell/script type, supporting all
|
|
2
|
+
Displays a badge combining an icon and label for a given shell/script type, supporting all OpenFrame RMM shell types (PowerShell, CMD, Bash, Python, Nushell, Deno, Shell).
|
|
3
3
|
|
|
4
4
|
## Key Components
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- source-hash: 5668aa064d8c050beff751a96d6469be -->
|
|
2
|
-
A React component that renders a visual badge combining a tool icon and label for OpenFrame-integrated MSP tools (e.g.,
|
|
2
|
+
A React component that renders a visual badge combining a tool icon and label for OpenFrame-integrated MSP tools (e.g., Fleet MDM, MeshCentral).
|
|
3
3
|
|
|
4
4
|
## Key Components
|
|
5
5
|
|
|
@@ -15,7 +15,7 @@ A React component that renders a visual badge combining a tool icon and label fo
|
|
|
15
15
|
import { ToolBadge } from './components/ToolBadge'
|
|
16
16
|
|
|
17
17
|
// Basic usage in a table cell
|
|
18
|
-
<ToolBadge toolType="
|
|
18
|
+
<ToolBadge toolType="OPENFRAME_RMM" />
|
|
19
19
|
|
|
20
20
|
// With custom styling
|
|
21
21
|
<ToolBadge
|
|
@@ -90,6 +90,7 @@ const SecondaryAction: React.FC<{ action: ActionsMenuItemIconAction }> = ({ acti
|
|
|
90
90
|
|
|
91
91
|
const classes = cn(
|
|
92
92
|
SECONDARY_ACTION_CLASSES,
|
|
93
|
+
action.openInNewTab && "max-md:hidden",
|
|
93
94
|
action.disabled && "cursor-not-allowed opacity-60 pointer-events-none",
|
|
94
95
|
);
|
|
95
96
|
|
|
@@ -32,6 +32,7 @@ const splitHalfVariants = cva(splitHalfBase, {
|
|
|
32
32
|
small: "h-6 md:h-8 px-[var(--spacing-system-xs)] text-h5 [&_svg]:h-3 [&_svg]:w-3 md:[&_svg]:h-4 md:[&_svg]:w-4",
|
|
33
33
|
},
|
|
34
34
|
side: { main: "", icon: "" },
|
|
35
|
+
soloOnMobile: { true: "", false: "" },
|
|
35
36
|
},
|
|
36
37
|
compoundVariants: [
|
|
37
38
|
// Rounded corners + per-variant seam. The icon-side's left border is the divider.
|
|
@@ -55,8 +56,12 @@ const splitHalfVariants = cva(splitHalfBase, {
|
|
|
55
56
|
// Icon half: per Figma, narrower than main height (default: 40×48; small: 32×32).
|
|
56
57
|
{ side: "icon", size: "default", class: "w-10 px-0" },
|
|
57
58
|
{ side: "icon", size: "small", class: "w-6 md:w-8 px-0" },
|
|
59
|
+
|
|
60
|
+
{ side: "icon", soloOnMobile: true, class: "max-md:hidden" },
|
|
61
|
+
{ side: "main", soloOnMobile: true, class: "max-md:rounded-r-md" },
|
|
62
|
+
{ variant: "outline", side: "main", soloOnMobile: true, class: "max-md:border-r" },
|
|
58
63
|
],
|
|
59
|
-
defaultVariants: { variant: "accent", size: "default", side: "main" },
|
|
64
|
+
defaultVariants: { variant: "accent", size: "default", side: "main", soloOnMobile: false },
|
|
60
65
|
})
|
|
61
66
|
|
|
62
67
|
type SplitButtonVariant = NonNullable<VariantProps<typeof splitHalfVariants>["variant"]>
|
|
@@ -68,6 +73,11 @@ interface SplitButtonIconAction {
|
|
|
68
73
|
"aria-label": string
|
|
69
74
|
onClick?: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>
|
|
70
75
|
href?: string
|
|
76
|
+
/**
|
|
77
|
+
* Opens `href` with `target="_blank"`. Also marks the half as a new-tab
|
|
78
|
+
* affordance, which hides it below `md` (set it for `onClick` handlers that
|
|
79
|
+
* open a new tab/window themselves).
|
|
80
|
+
*/
|
|
71
81
|
openInNewTab?: boolean
|
|
72
82
|
prefetch?: boolean
|
|
73
83
|
disabled?: boolean
|
|
@@ -101,6 +111,7 @@ interface HalfOptions {
|
|
|
101
111
|
variant: SplitButtonVariant
|
|
102
112
|
size: SplitButtonSize
|
|
103
113
|
side: "main" | "icon"
|
|
114
|
+
soloOnMobile?: boolean
|
|
104
115
|
href?: string
|
|
105
116
|
openInNewTab?: boolean
|
|
106
117
|
prefetch?: boolean
|
|
@@ -113,8 +124,8 @@ interface HalfOptions {
|
|
|
113
124
|
children: React.ReactNode
|
|
114
125
|
}
|
|
115
126
|
|
|
116
|
-
function Half({ variant, size, side, href, openInNewTab, prefetch, onClick, disabled, grow, type = "button", ariaLabel, children }: HalfOptions) {
|
|
117
|
-
const classes = cn(splitHalfVariants({ variant, size, side }), grow && "flex-1")
|
|
127
|
+
function Half({ variant, size, side, soloOnMobile, href, openInNewTab, prefetch, onClick, disabled, grow, type = "button", ariaLabel, children }: HalfOptions) {
|
|
128
|
+
const classes = cn(splitHalfVariants({ variant, size, side, soloOnMobile }), grow && "flex-1")
|
|
118
129
|
|
|
119
130
|
if (href) {
|
|
120
131
|
return (
|
|
@@ -169,6 +180,8 @@ const SplitButton = React.forwardRef<HTMLDivElement, SplitButtonProps>(function
|
|
|
169
180
|
},
|
|
170
181
|
ref,
|
|
171
182
|
) {
|
|
183
|
+
const soloOnMobile = !!iconAction.openInNewTab
|
|
184
|
+
|
|
172
185
|
return (
|
|
173
186
|
<div
|
|
174
187
|
ref={ref}
|
|
@@ -180,6 +193,7 @@ const SplitButton = React.forwardRef<HTMLDivElement, SplitButtonProps>(function
|
|
|
180
193
|
variant={variant}
|
|
181
194
|
size={size}
|
|
182
195
|
side="main"
|
|
196
|
+
soloOnMobile={soloOnMobile}
|
|
183
197
|
href={href}
|
|
184
198
|
openInNewTab={openInNewTab}
|
|
185
199
|
prefetch={prefetch}
|
|
@@ -197,6 +211,7 @@ const SplitButton = React.forwardRef<HTMLDivElement, SplitButtonProps>(function
|
|
|
197
211
|
variant={variant}
|
|
198
212
|
size={size}
|
|
199
213
|
side="icon"
|
|
214
|
+
soloOnMobile={soloOnMobile}
|
|
200
215
|
href={iconAction.href}
|
|
201
216
|
openInNewTab={iconAction.openInNewTab}
|
|
202
217
|
prefetch={iconAction.prefetch}
|
|
@@ -245,6 +245,12 @@ function IconButtonsVariant({
|
|
|
245
245
|
const singleAction = isSingleAction ? actions[0] : null
|
|
246
246
|
const useSingleActionMobile = isSingleAction && !hasMenuActions
|
|
247
247
|
|
|
248
|
+
const mobileMenuGroups = [
|
|
249
|
+
{ items: actions.flatMap(actionToMenuItems) },
|
|
250
|
+
...(menuActions ?? []),
|
|
251
|
+
]
|
|
252
|
+
const hasMobileMenuItems = mobileMenuGroups.some(g => g.items.length > 0)
|
|
253
|
+
|
|
248
254
|
return (
|
|
249
255
|
<>
|
|
250
256
|
{/* Desktop: every action as an icon button + optional overflow menu */}
|
|
@@ -262,14 +268,9 @@ function IconButtonsVariant({
|
|
|
262
268
|
<div className={cn('flex md:hidden', className)}>
|
|
263
269
|
{useSingleActionMobile && singleAction ? (
|
|
264
270
|
renderActionButton(singleAction, { iconOnly: true })
|
|
265
|
-
) : (
|
|
266
|
-
<ActionsMenuDropdown
|
|
267
|
-
|
|
268
|
-
{ items: actions.flatMap(actionToMenuItems) },
|
|
269
|
-
...(menuActions ?? []),
|
|
270
|
-
]}
|
|
271
|
-
/>
|
|
272
|
-
)}
|
|
271
|
+
) : hasMobileMenuItems ? (
|
|
272
|
+
<ActionsMenuDropdown groups={mobileMenuGroups} />
|
|
273
|
+
) : null}
|
|
273
274
|
</div>
|
|
274
275
|
</>
|
|
275
276
|
)
|
|
@@ -328,6 +329,12 @@ function MenuPrimaryVariant({
|
|
|
328
329
|
const desktopActions = actions.filter(a => !a.showOnlyMobile)
|
|
329
330
|
const hasMenuActions = menuActions.some(g => g.items.length > 0)
|
|
330
331
|
|
|
332
|
+
const mobileMenuGroups = [
|
|
333
|
+
{ items: actions.flatMap(actionToMenuItems) },
|
|
334
|
+
...menuActions,
|
|
335
|
+
]
|
|
336
|
+
const hasMobileMenuItems = mobileMenuGroups.some(g => g.items.length > 0)
|
|
337
|
+
|
|
331
338
|
return (
|
|
332
339
|
<>
|
|
333
340
|
<div className={cn('hidden md:flex items-center', ACTIONS_GAP, className)}>
|
|
@@ -341,12 +348,7 @@ function MenuPrimaryVariant({
|
|
|
341
348
|
</div>
|
|
342
349
|
|
|
343
350
|
<div className={cn('flex md:hidden', className)}>
|
|
344
|
-
<ActionsMenuDropdown
|
|
345
|
-
groups={[
|
|
346
|
-
{ items: actions.flatMap(actionToMenuItems) },
|
|
347
|
-
...menuActions,
|
|
348
|
-
]}
|
|
349
|
-
/>
|
|
351
|
+
{hasMobileMenuItems ? <ActionsMenuDropdown groups={mobileMenuGroups} /> : null}
|
|
350
352
|
</div>
|
|
351
353
|
</>
|
|
352
354
|
)
|
|
@@ -31,4 +31,4 @@ export const AllOSTypes: Story = {
|
|
|
31
31
|
};
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
The stories demonstrate OS type normalization from various MDM sources (Fleet
|
|
34
|
+
The stories demonstrate OS type normalization from various MDM sources (Fleet) with support for aliases like "Darwin" → "macOS" and "win32" → "Windows". Includes examples for icon-only, label-only, and combined display modes with customizable sizing options.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- source-hash: fe5ee5e74229fbc813833a5d6c81b725 -->
|
|
2
|
-
Storybook configuration for the ShellTypeBadge component, defining visual documentation and interactive examples for all supported
|
|
2
|
+
Storybook configuration for the ShellTypeBadge component, defining visual documentation and interactive examples for all supported OpenFrame RMM shell types including PowerShell, CMD, Bash, Python, Nushell, Deno, and generic Shell.
|
|
3
3
|
|
|
4
4
|
## Key Components
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ Storybook stories for the `Toaster` / `ToastCard` component system, covering sta
|
|
|
9
9
|
| `Playground` | Story | Arg-driven interactive card — use Storybook Controls to tweak `variant`, `title`, `description`, `duration`, and `dismissible` |
|
|
10
10
|
| `CommandApprovalCollapsed` | Story | `CommandApprovalToast` in its default collapsed state |
|
|
11
11
|
| `CommandApprovalExpanded` | Story | `CommandApprovalToast` with `defaultExpanded` showing the command preview and Approve/Reject actions |
|
|
12
|
-
| `CommandApprovalPerTool` | Story | One expanded approval toast per supported tool type (`FLEET_MDM`, `
|
|
12
|
+
| `CommandApprovalPerTool` | Story | One expanded approval toast per supported tool type (`FLEET_MDM`, `MESHCENTRAL`, `AUTHENTIK`, `OSQUERY`, `OPENFRAME`) to verify icon mapping |
|
|
13
13
|
| `CommandApprovalLive` | Story | Fires real Sonner toasts via `showCommandApprovalToast()` with `onApprove`/`onReject` callbacks |
|
|
14
14
|
| `LiveTriggers` | Story | Button panel that fires every variant as a live Sonner toast using both `toast()` and `showToast()` |
|
|
15
15
|
|
|
@@ -4,7 +4,7 @@ Storybook configuration file for the ToolBadge component, providing interactive
|
|
|
4
4
|
## Key Components
|
|
5
5
|
|
|
6
6
|
- **meta**: Main Storybook configuration with component metadata, controls, and documentation
|
|
7
|
-
- **Story exports**: Individual stories showcasing different tool types (
|
|
7
|
+
- **Story exports**: Individual stories showcasing different tool types (Fleet MDM, MeshCentral, etc.)
|
|
8
8
|
- **AllTools story**: Comparison view showing multiple tool badges together
|
|
9
9
|
- **argTypes**: Interactive controls for `toolType` selection and `className` customization
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ Storybook configuration file for the ToolBadge component, providing interactive
|
|
|
14
14
|
// Individual tool badge stories
|
|
15
15
|
export const Default: Story = {
|
|
16
16
|
args: {
|
|
17
|
-
toolType: '
|
|
17
|
+
toolType: 'OPENFRAME_RMM',
|
|
18
18
|
},
|
|
19
19
|
};
|
|
20
20
|
|
|
@@ -5,7 +5,7 @@ Storybook stories for the `ToolExecutionDisplay` chat component, covering execut
|
|
|
5
5
|
|
|
6
6
|
| Export | Description |
|
|
7
7
|
|--------|-------------|
|
|
8
|
-
| `CollapsedExecuting` | Spinner state while a tool (e.g. `run_script` via
|
|
8
|
+
| `CollapsedExecuting` | Spinner state while a tool (e.g. `run_script` via OpenFrame RMM) is in progress |
|
|
9
9
|
| `CollapsedSuccess` | Green check state after successful tool execution, with result output |
|
|
10
10
|
| `CollapsedFailure` | Red error icon state after a failed tool execution (e.g. Fleet MDM profile deploy) |
|
|
11
11
|
| `constrainedDecorator` | Helper decorator that wraps stories in a `max-width` container (default 400px) |
|
|
@@ -18,7 +18,7 @@ import type { ToolExecutionData } from "../components/chat/types";
|
|
|
18
18
|
|
|
19
19
|
const message: ToolExecutionData = {
|
|
20
20
|
type: "EXECUTED_TOOL",
|
|
21
|
-
integratedToolType: "
|
|
21
|
+
integratedToolType: "OPENFRAME_RMM",
|
|
22
22
|
toolFunction: "run_script",
|
|
23
23
|
parameters: { RunAsUser: "False", timeoutSeconds: 60 },
|
|
24
24
|
result: "Script output here...",
|
|
@@ -34,8 +34,8 @@ Three fixture payloads cover the core display states:
|
|
|
34
34
|
|
|
35
35
|
| Fixture | `type` | `integratedToolType` | `success` |
|
|
36
36
|
|---------|--------|----------------------|-----------|
|
|
37
|
-
| `executingMessage` | `EXECUTING_TOOL` | `
|
|
38
|
-
| `executedSuccessMessage` | `EXECUTED_TOOL` | `
|
|
37
|
+
| `executingMessage` | `EXECUTING_TOOL` | `OPENFRAME_RMM` | — |
|
|
38
|
+
| `executedSuccessMessage` | `EXECUTED_TOOL` | `OPENFRAME_RMM` | `true` |
|
|
39
39
|
| `executedFailureMessage` | `EXECUTED_TOOL` | `FLEET_MDM` | `false` |
|
|
40
40
|
|
|
41
41
|
The component renders a collapsed card by default; clicking toggles full details including parameters and result output.
|
package/src/types/.os.types.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- source-hash: 6581d93b3edee981ded786a177e99959 -->
|
|
2
|
-
Defines the centralized source of truth for operating system type metadata across the Flamingo platform, normalizing OS values from Fleet MDM
|
|
2
|
+
Defines the centralized source of truth for operating system type metadata across the Flamingo platform, normalizing OS values from Fleet MDM and GraphQL into a unified structure.
|
|
3
3
|
|
|
4
4
|
## Key Components
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- source-hash: b8477e619839e162b7edcfaaf835462d -->
|
|
2
|
-
Centralized type definitions for shell/script types supported across the Flamingo platform. Provides a single source of truth for shell metadata, icons, and labels based on
|
|
2
|
+
Centralized type definitions for shell/script types supported across the Flamingo platform. Provides a single source of truth for shell metadata, icons, and labels based on OpenFrame RMM supported types.
|
|
3
3
|
|
|
4
4
|
## Key Components
|
|
5
5
|
|
package/src/types/.tool.types.md
CHANGED
|
@@ -3,7 +3,7 @@ Centralized type definitions for MSP tool types used throughout the Flamingo/Ope
|
|
|
3
3
|
|
|
4
4
|
## Key Components
|
|
5
5
|
|
|
6
|
-
- **`ToolTypeValues`** - Const assertion object containing all available tool type constants (
|
|
6
|
+
- **`ToolTypeValues`** - Const assertion object containing all available tool type constants (FLEET_MDM, MESHCENTRAL, etc.)
|
|
7
7
|
- **`ToolType`** - Union type derived from ToolTypeValues for type-safe tool type references
|
|
8
8
|
- **`toolLabels`** - Record mapping tool types to human-readable display names
|
|
9
9
|
|
|
@@ -13,10 +13,10 @@ Centralized type definitions for MSP tool types used throughout the Flamingo/Ope
|
|
|
13
13
|
import { ToolType, ToolTypeValues, toolLabels } from './tool.types'
|
|
14
14
|
|
|
15
15
|
// Type-safe tool type usage
|
|
16
|
-
const currentTool: ToolType = ToolTypeValues.
|
|
16
|
+
const currentTool: ToolType = ToolTypeValues.MESHCENTRAL
|
|
17
17
|
|
|
18
18
|
// Get display label for UI
|
|
19
|
-
const displayName = toolLabels[currentTool] // "
|
|
19
|
+
const displayName = toolLabels[currentTool] // "MeshCentral"
|
|
20
20
|
|
|
21
21
|
// Use in component props
|
|
22
22
|
interface ToolBadgeProps {
|
package/src/utils/.tool-utils.md
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
} from './tool-utils'
|
|
23
23
|
|
|
24
24
|
// Normalize various input formats
|
|
25
|
-
normalizeToolType('tactical') // => 'TACTICAL_RMM'
|
|
26
25
|
normalizeToolType('FLEET-MDM') // => 'FLEET_MDM'
|
|
27
26
|
normalizeToolType('unknown') // => undefined
|
|
28
27
|
|
|
@@ -31,7 +30,6 @@ normalizeToolTypeWithFallback('mesh') // => 'MESHCENTRAL'
|
|
|
31
30
|
normalizeToolTypeWithFallback('invalid') // => 'SYSTEM'
|
|
32
31
|
|
|
33
32
|
// Convert to display labels
|
|
34
|
-
toToolLabel('tactical') // => 'Tactical'
|
|
35
33
|
toToolLabel('fleet_mdm') // => 'Fleet'
|
|
36
34
|
|
|
37
35
|
// Validation
|
|
@@ -39,4 +37,4 @@ isValidToolType('authentik') // => true
|
|
|
39
37
|
isValidToolType('invalid') // => false
|
|
40
38
|
```
|
|
41
39
|
|
|
42
|
-
The utility supports all major MSP tools including
|
|
40
|
+
The utility supports all major MSP tools including Fleet MDM, MeshCentral, Authentik, and OpenFrame components with comprehensive alias mapping for different naming conventions.
|
package/src/utils/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ export { getBaseUrl } from '../utils/cn'
|
|
|
29
29
|
// SEO title length budget — server-safe constant (SSOT). Consumed by the hub
|
|
30
30
|
// (prompt guidance + DB check value) and by SEOEditorPreview (input maxLength).
|
|
31
31
|
export { SEO_TITLE_MAX_LENGTH } from './seo-title'
|
|
32
|
+
export { SEO_DESCRIPTION_MAX_LENGTH } from './seo-description'
|
|
32
33
|
|
|
33
34
|
// Brand silhouette SVG registry (CSS mask-image tinting) — centralized so
|
|
34
35
|
// consumers (e.g. company-hub deck) don't ship their own copies of the marks.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the `seo_description` length budget.
|
|
3
|
+
*
|
|
4
|
+
* Unlike `seo_title` (soft search-engine guidance), this is a HARD cap: the
|
|
5
|
+
* `seo_description` DB columns are `varchar(160)` and Postgres rejects longer
|
|
6
|
+
* values outright ("value too long for type character varying(160)").
|
|
7
|
+
*
|
|
8
|
+
* This number lives in exactly ONE place. Consumers:
|
|
9
|
+
* - `SEOEditorPreview` — input `maxLength` + the too-long alert.
|
|
10
|
+
* - the hub — imports it (via `@flamingo-stack/openframe-frontend-core/utils`)
|
|
11
|
+
* for the AI generation prompt guidance and the rewrite-to-fit helper.
|
|
12
|
+
*/
|
|
13
|
+
export const SEO_DESCRIPTION_MAX_LENGTH = 160;
|