tycho-components 0.43.6 → 0.44.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.
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Build-time whitelabel: name, logo, favicon only.
3
+ * Brand packs are served at site root: {VITE_APP_ORIGIN}/brands/{id}/
4
+ * (not bundled per app — upload packs on each host / shared nginx).
5
+ */
6
+ export type BrandId = 'tycho' | 'corpushub';
7
+ export type AppCode = 'admin' | 'auth' | 'cs-analyzer' | 'dictionary' | 'editor' | 'io' | 'lexicon' | 'parser' | 'psd-reindexer' | 'reserved' | 'search' | 'syntrees' | 'viewer';
8
+ export type BrandConfig = {
9
+ id: BrandId;
10
+ name: string;
11
+ completeName: string;
12
+ logo: string;
13
+ documentTitle: string;
14
+ /** Absolute URL prefix: https://host/brands/{id} */
15
+ faviconBase: string;
16
+ socialMedia?: {
17
+ youtube?: string;
18
+ };
19
+ };
20
+ export declare function brandAssetBase(brandId: BrandId, originRaw: string | undefined): string;
21
+ /**
22
+ * Resolve platform branding for a front-end app.
23
+ * Pass `options` from Vite plugins (Node) where `import.meta.env` may be unavailable.
24
+ */
25
+ export declare function createBrand(app: AppCode, options?: {
26
+ brandId?: string;
27
+ origin?: string;
28
+ }): BrandConfig;
29
+ /** Shared helper for tests / tooling. */
30
+ export declare function brandFromEnv(raw: string | undefined): BrandId;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Build-time whitelabel: name, logo, favicon only.
3
+ * Brand packs are served at site root: {VITE_APP_ORIGIN}/brands/{id}/
4
+ * (not bundled per app — upload packs on each host / shared nginx).
5
+ */
6
+ const APP_TITLE_SUFFIX = {
7
+ admin: 'Admin',
8
+ auth: 'Auth',
9
+ 'cs-analyzer': 'CS Analyzer',
10
+ dictionary: 'Dictionary',
11
+ editor: 'Editor',
12
+ io: 'IO',
13
+ lexicon: 'Lexicon',
14
+ parser: 'Parser',
15
+ 'psd-reindexer': 'PSD Re-Indexer',
16
+ reserved: 'Reserved Area',
17
+ search: 'Search',
18
+ syntrees: 'Syntrees',
19
+ viewer: 'Viewer',
20
+ };
21
+ const DEFAULT_YOUTUBE = 'https://www.youtube.com/@tychoplatform';
22
+ function trimOrigin(raw) {
23
+ return (raw || '').replace(/\/+$/, '');
24
+ }
25
+ export function brandAssetBase(brandId, originRaw) {
26
+ const origin = trimOrigin(originRaw);
27
+ if (!origin) {
28
+ // Last resort for misconfigured builds — prefer setting VITE_APP_ORIGIN.
29
+ return `/brands/${brandId}`;
30
+ }
31
+ return `${origin}/brands/${brandId}`;
32
+ }
33
+ function resolveBrandId(raw) {
34
+ return raw?.toLowerCase() === 'corpushub' ? 'corpushub' : 'tycho';
35
+ }
36
+ function readEnvBrandId() {
37
+ try {
38
+ return import.meta.env?.VITE_APP_BRAND;
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ }
44
+ function readEnvOrigin() {
45
+ try {
46
+ return import.meta.env?.VITE_APP_ORIGIN;
47
+ }
48
+ catch {
49
+ return undefined;
50
+ }
51
+ }
52
+ /**
53
+ * Resolve platform branding for a front-end app.
54
+ * Pass `options` from Vite plugins (Node) where `import.meta.env` may be unavailable.
55
+ */
56
+ export function createBrand(app, options) {
57
+ const id = resolveBrandId(options?.brandId ?? readEnvBrandId());
58
+ const origin = options?.origin ?? readEnvOrigin();
59
+ const faviconBase = brandAssetBase(id, origin);
60
+ const suffix = APP_TITLE_SUFFIX[app];
61
+ const meta = id === 'corpushub'
62
+ ? {
63
+ name: 'CorpusHub',
64
+ completeName: 'CorpusHub',
65
+ documentTitle: `CorpusHub - ${suffix}`,
66
+ }
67
+ : {
68
+ name: 'Tycho',
69
+ completeName: 'Tycho Platform',
70
+ documentTitle: `Tycho Platform - ${suffix}`,
71
+ };
72
+ return {
73
+ id,
74
+ ...meta,
75
+ faviconBase,
76
+ logo: `${faviconBase}/logo.png`,
77
+ socialMedia: {
78
+ youtube: DEFAULT_YOUTUBE,
79
+ },
80
+ };
81
+ }
82
+ /** Shared helper for tests / tooling. */
83
+ export function brandFromEnv(raw) {
84
+ return resolveBrandId(raw);
85
+ }
@@ -35,5 +35,7 @@ export { useCorpusUtils } from "./useCorpusUtils";
35
35
  export { useLoggedUtils } from "./useLoggedUtils";
36
36
  export { useMessageUtils } from "./useMessageUtils";
37
37
  export { useTourUtils } from "./useTourUtils";
38
+ export { brandAssetBase, brandFromEnv, createBrand, } from "./brand";
39
+ export type { AppCode, BrandConfig, BrandId } from "./brand";
38
40
  export { useDriverTour, loadKnowledgeBase, defaultMatchPathnames, buildAnchorMap, flattenWorkflowSteps, getWorkflowsForPath, buildDriveSteps, createTourDriver, hasMatchingWorkflow, resolveAnchor, resolveAnchorById, waitForSelector, localizeKnowledgeBase, pickLocalizedString, resolveTourLocale, TOUR_FALLBACK_LOCALE, TOUR_LOCALES, } from "./tour";
39
41
  export type { AnchorSelector, Driver, KnowledgeBase, KnowledgeBaseRaw, LocalizedString, PopoverSide, ResolveKnowledgeBaseUrl, TourAnchor, TourButtonLabels, TourStep, TourStepCopy, TourStepStructural, TourWorkflow, TourWorkflowRaw, UseDriverTourOptions, } from "./tour";
@@ -19,4 +19,5 @@ export { useCorpusUtils } from "./useCorpusUtils";
19
19
  export { useLoggedUtils } from "./useLoggedUtils";
20
20
  export { useMessageUtils } from "./useMessageUtils";
21
21
  export { useTourUtils } from "./useTourUtils";
22
+ export { brandAssetBase, brandFromEnv, createBrand, } from "./brand";
22
23
  export { useDriverTour, loadKnowledgeBase, defaultMatchPathnames, buildAnchorMap, flattenWorkflowSteps, getWorkflowsForPath, buildDriveSteps, createTourDriver, hasMatchingWorkflow, resolveAnchor, resolveAnchorById, waitForSelector, localizeKnowledgeBase, pickLocalizedString, resolveTourLocale, TOUR_FALLBACK_LOCALE, TOUR_LOCALES, } from "./tour";
@@ -1,5 +1,7 @@
1
1
  import { HelpAction } from './HelpButton/HelpButton';
2
+ import type { HeaderBrand, HeaderSocialMedia } from './types/Brand';
2
3
  import './styles.scss';
4
+ export type { HeaderBrand, HeaderSocialMedia } from './types/Brand';
3
5
  type Props = {
4
6
  tool: string;
5
7
  redirect?: string;
@@ -22,12 +24,9 @@ type Props = {
22
24
  hideNotifications?: boolean;
23
25
  hideLanguageSelector?: boolean;
24
26
  hideHelpButton?: boolean;
25
- /** Overrides i18n `label.platform` (e.g. CorpusHub whitelabel). */
26
- platformName?: string;
27
- /** Overrides i18n `label.platform.complete` in the apps drawer. */
28
- platformCompleteName?: string;
29
- /** Overrides the apps-drawer platform tile image. */
30
- platformLogo?: string;
27
+ /** Overrides i18n platform labels and apps-drawer logo (e.g. CorpusHub whitelabel). */
28
+ brand?: HeaderBrand;
29
+ /** Optional social links shown in the apps drawer Quick section. */
30
+ socialMedia?: HeaderSocialMedia;
31
31
  };
32
- export default function Header({ tool, redirect, autoload, hideKeyboard, customHeader, notifications, keyboardLayout, navigateHome, navigateCorpora, navigateLogout, navigateNotLogged, helpActions, useOpenCorpora, hideCorpora, hideNotifications, hideLanguageSelector, hideHelpButton, platformName, platformCompleteName, platformLogo, }: Props): import("react").JSX.Element;
33
- export {};
32
+ export default function Header({ tool, redirect, autoload, hideKeyboard, customHeader, notifications, keyboardLayout, navigateHome, navigateCorpora, navigateLogout, navigateNotLogged, helpActions, useOpenCorpora, hideCorpora, hideNotifications, hideLanguageSelector, hideHelpButton, brand, socialMedia, }: Props): import("react").JSX.Element;
@@ -8,10 +8,10 @@ import HeaderUser from './HeaderUser';
8
8
  import './styles.scss';
9
9
  export default function Header({ tool, redirect, autoload, hideKeyboard, customHeader, notifications, keyboardLayout, navigateHome, navigateCorpora, navigateLogout, navigateNotLogged = () => {
10
10
  location.href = '/auth';
11
- }, helpActions, useOpenCorpora = true, hideCorpora = false, hideNotifications = false, hideLanguageSelector = false, hideHelpButton = false, platformName, platformCompleteName, platformLogo, }) {
11
+ }, helpActions, useOpenCorpora = true, hideCorpora = false, hideNotifications = false, hideLanguageSelector = false, hideHelpButton = false, brand, socialMedia, }) {
12
12
  const { t } = useTranslation('header');
13
13
  const homeTextsClass = cx('app-title', {
14
14
  pointer: navigateHome !== undefined,
15
15
  });
16
- return (_jsxs("div", { className: "ds-header", children: [_jsxs("div", { className: "header-left", children: [_jsx(HeaderApps, { navigateLogout: navigateLogout, navigateNotLogged: navigateNotLogged, hideKeyboard: hideKeyboard, notifications: notifications, keyboardLayout: keyboardLayout, helpActions: helpActions, platformCompleteName: platformCompleteName, platformLogo: platformLogo }), _jsxs("div", { className: homeTextsClass, onClick: () => navigateHome && navigateHome(), children: [_jsx("span", { className: "title", children: platformName ?? t('label.platform') }), _jsx("span", { className: "subtitle", children: tool })] }), _jsxs("div", { className: "header-corpus", children: [!hideCorpora && (_jsx(HeaderCorpora, { redirect: redirect, autoload: autoload, navigateCorpora: navigateCorpora, useOpenCorpora: useOpenCorpora })), customHeader && customHeader] })] }), _jsxs("div", { className: "header-right", children: [_jsx(HeaderButtons, { hideKeyboard: hideKeyboard, notifications: notifications, keyboardLayout: keyboardLayout, helpActions: helpActions, hideNotifications: hideNotifications, hideLanguageSelector: hideLanguageSelector, hideHelpButton: hideHelpButton }), _jsx(HeaderUser, { navigateLogout: navigateLogout, navigateNotLogged: navigateNotLogged })] })] }));
16
+ return (_jsxs("div", { className: "ds-header", children: [_jsxs("div", { className: "header-left", children: [_jsx(HeaderApps, { navigateLogout: navigateLogout, navigateNotLogged: navigateNotLogged, hideKeyboard: hideKeyboard, notifications: notifications, keyboardLayout: keyboardLayout, helpActions: helpActions, brand: brand, socialMedia: socialMedia }), _jsxs("div", { className: homeTextsClass, onClick: () => navigateHome && navigateHome(), children: [_jsx("span", { className: "title", children: brand?.name ?? t('label.platform') }), _jsx("span", { className: "subtitle", children: tool })] }), _jsxs("div", { className: "header-corpus", children: [!hideCorpora && (_jsx(HeaderCorpora, { redirect: redirect, autoload: autoload, navigateCorpora: navigateCorpora, useOpenCorpora: useOpenCorpora })), customHeader && customHeader] })] }), _jsxs("div", { className: "header-right", children: [_jsx(HeaderButtons, { hideKeyboard: hideKeyboard, notifications: notifications, keyboardLayout: keyboardLayout, helpActions: helpActions, hideNotifications: hideNotifications, hideLanguageSelector: hideLanguageSelector, hideHelpButton: hideHelpButton }), _jsx(HeaderUser, { navigateLogout: navigateLogout, navigateNotLogged: navigateNotLogged })] })] }));
17
17
  }
@@ -1,4 +1,5 @@
1
1
  import { HelpAction } from "../HelpButton/HelpButton";
2
+ import type { HeaderBrand, HeaderSocialMedia } from "../types/Brand";
2
3
  import "./style.scss";
3
4
  type Props = {
4
5
  navigateLogout?: () => void;
@@ -10,8 +11,8 @@ type Props = {
10
11
  };
11
12
  keyboardLayout?: string;
12
13
  helpActions?: HelpAction[];
13
- platformCompleteName?: string;
14
- platformLogo?: string;
14
+ brand?: HeaderBrand;
15
+ socialMedia?: HeaderSocialMedia;
15
16
  };
16
- export default function HeaderApps({ navigateLogout, navigateNotLogged, hideKeyboard, notifications, keyboardLayout, helpActions, platformCompleteName, platformLogo, }: Props): import("react").JSX.Element;
17
+ export default function HeaderApps({ navigateLogout, navigateNotLogged, hideKeyboard, notifications, keyboardLayout, helpActions, brand, socialMedia, }: Props): import("react").JSX.Element;
17
18
  export {};
@@ -11,7 +11,7 @@ import { AvailableApps } from "../types/App";
11
11
  import logo from "./logo.png";
12
12
  import youtube from "./youtube.png";
13
13
  import "./style.scss";
14
- export default function HeaderApps({ navigateLogout, navigateNotLogged, hideKeyboard, notifications, keyboardLayout, helpActions, platformCompleteName, platformLogo, }) {
14
+ export default function HeaderApps({ navigateLogout, navigateNotLogged, hideKeyboard, notifications, keyboardLayout, helpActions, brand, socialMedia, }) {
15
15
  const { t } = useTranslation("header-app");
16
16
  const { getCorpus, hasCorpus } = useCorpusUtils();
17
17
  const { isAdminOfAnyCorpus, isEditorOfAnyCorpus, isLogged } = useLoggedUtils();
@@ -20,22 +20,33 @@ export default function HeaderApps({ navigateLogout, navigateNotLogged, hideKeyb
20
20
  const resources = [
21
21
  {
22
22
  code: "platform",
23
- image: platformLogo || logo,
23
+ image: brand?.logo || logo,
24
24
  visibility: "public",
25
25
  external: platformOrigin || undefined,
26
26
  },
27
- {
28
- code: "youtube",
29
- image: youtube,
30
- visibility: "public",
31
- external: "https://www.youtube.com/@tychoplatform",
32
- },
27
+ ...(socialMedia?.youtube
28
+ ? [
29
+ {
30
+ code: "youtube",
31
+ image: youtube,
32
+ visibility: "public",
33
+ external: socialMedia.youtube,
34
+ },
35
+ ]
36
+ : []),
33
37
  ];
34
38
  const goto = (app, blank) => {
35
- const shouldAppendCorpusUid = app.appendCorpusUid !== false;
36
- const url = hasCorpus() && shouldAppendCorpusUid
37
- ? `/${app.code}/${getCorpus().uid}`
38
- : `/${app.code}`;
39
+ let url;
40
+ if (app.external) {
41
+ url = app.external;
42
+ }
43
+ else {
44
+ const shouldAppendCorpusUid = app.appendCorpusUid !== false;
45
+ url =
46
+ hasCorpus() && shouldAppendCorpusUid
47
+ ? `/${app.code}/${getCorpus().uid}`
48
+ : `/${app.code}`;
49
+ }
39
50
  if (blank) {
40
51
  window.open(url, "_blank");
41
52
  }
@@ -50,12 +61,12 @@ export default function HeaderApps({ navigateLogout, navigateNotLogged, hideKeyb
50
61
  return null;
51
62
  if (item.editor && !isEditorOfAnyCorpus())
52
63
  return null;
53
- return (_jsxs("div", { className: "item", title: t(`${item.code}.desc`), onClick: () => goto(item, item.external ? true : false), children: [item.icon && _jsx(Icon, { name: item.icon, size: "medium" }), item.image && _jsx("img", { src: item.image }), _jsx("span", { className: "title-app", children: item.code === "platform" && platformCompleteName
54
- ? platformCompleteName
64
+ return (_jsxs("div", { className: "item", title: t(`${item.code}.desc`), onClick: () => goto(item, item.external ? true : false), children: [item.icon && _jsx(Icon, { name: item.icon, size: "medium" }), item.image && _jsx("img", { src: item.image }), _jsx("span", { className: "title-app", children: item.code === "platform" && brand?.completeName
65
+ ? brand.completeName
55
66
  : t(`${item.code}.name`) }), _jsxs("div", { className: "options", children: [item.visibility && (_jsx(Tag, { text: t(`common:label.${item.visibility}`), size: "small", color: "green", className: "d-none" })), _jsx(IconButton, { name: "open_in_new", size: "x-small", className: "icon-open", mode: "ghost", onClick: (e) => {
56
67
  e.stopPropagation();
57
68
  goto(item, true);
58
69
  }, title: t("label.open.tab") })] })] }, idx.valueOf()));
59
70
  };
60
- return (_jsxs("div", { className: "header-apps-container", children: [_jsx(IconButton, { name: "apps", className: "icon-apps", size: "large", onClick: () => setOpen(!open), filledIcon: true, "data-tour": "header-apps" }), open && (_jsxs(Drawer, { anchor: "left", open: true, onClose: () => setOpen(false), className: "offcanvas-apps", children: [_jsxs("div", { className: "header", children: [_jsx(IconButton, { name: "close", size: "medium", mode: "ghost", iconSize: "medium", onClick: () => setOpen(false) }), _jsx("span", { className: "header-apps-title", children: platformCompleteName ?? t("header:label.platform.complete") }), _jsx("div", { className: "header-profile-apps", children: _jsx(HeaderUser, { navigateLogout: navigateLogout, navigateNotLogged: navigateNotLogged }) })] }), _jsxs("div", { className: "body", children: [_jsx("div", { className: "title", children: t("label.tools") }), AvailableApps.map((item, idx) => renderItem(item, idx)), _jsx("div", { className: "title", children: t("label.quick") }), resources.map((item, idx) => renderItem(item, idx))] }), _jsx("div", { className: "footer", children: _jsx("div", { className: "header-apps-buttons", children: _jsx(HeaderButtons, { hideKeyboard: hideKeyboard, notifications: notifications, keyboardLayout: keyboardLayout, helpActions: helpActions, mobile: true }) }) })] }))] }));
71
+ return (_jsxs("div", { className: "header-apps-container", children: [_jsx(IconButton, { name: "apps", className: "icon-apps", size: "large", onClick: () => setOpen(!open), filledIcon: true, "data-tour": "header-apps" }), open && (_jsxs(Drawer, { anchor: "left", open: true, onClose: () => setOpen(false), className: "offcanvas-apps", children: [_jsxs("div", { className: "header", children: [_jsx(IconButton, { name: "close", size: "medium", mode: "ghost", iconSize: "medium", onClick: () => setOpen(false) }), _jsx("span", { className: "header-apps-title", children: brand?.completeName ?? t("header:label.platform.complete") }), _jsx("div", { className: "header-profile-apps", children: _jsx(HeaderUser, { navigateLogout: navigateLogout, navigateNotLogged: navigateNotLogged }) })] }), _jsxs("div", { className: "body", children: [_jsx("div", { className: "title", children: t("label.tools") }), AvailableApps.map((item, idx) => renderItem(item, idx)), _jsx("div", { className: "title", children: t("label.quick") }), resources.map((item, idx) => renderItem(item, idx))] }), _jsx("div", { className: "footer", children: _jsx("div", { className: "header-apps-buttons", children: _jsx(HeaderButtons, { hideKeyboard: hideKeyboard, notifications: notifications, keyboardLayout: keyboardLayout, helpActions: helpActions, mobile: true }) }) })] }))] }));
61
72
  }
@@ -0,0 +1,8 @@
1
+ export type HeaderBrand = {
2
+ name?: string;
3
+ completeName?: string;
4
+ logo?: string;
5
+ };
6
+ export type HeaderSocialMedia = {
7
+ youtube?: string;
8
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -5,6 +5,7 @@ export { default as Logout } from './Base/Logout';
5
5
  export { default as NotFound } from './Base/NotFound';
6
6
  export { default as Unauthorized } from './Base/Unauthorized';
7
7
  export { default as Header } from './Header';
8
+ export type { HeaderBrand, HeaderSocialMedia } from './Header/types/Brand';
8
9
  export { default as HeaderCorpus } from './Header/HeaderCorpora/HeaderCorpus';
9
10
  export { default as HelpButton } from './Header/HelpButton';
10
11
  export type { HelpAction } from './Header/HelpButton/HelpButton';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tycho-components",
3
3
  "private": false,
4
- "version": "0.43.6",
4
+ "version": "0.44.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {