@hgdc/ui 0.41.0 → 0.43.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.
package/README.md CHANGED
@@ -44,22 +44,23 @@ Primitives déjà là : `avatar` `badge` `button` `dialog` `drawer` `dropdown-me
44
44
 
45
45
  ## Coque des consoles
46
46
 
47
- | Import | Exemple |
48
- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
49
- | `app-shell` | `<AppShell product="mail" organization={<OrgSwitcher …/>} nav={<SidebarNav label="Projet">…</SidebarNav>} links={{ documentation: "/docs", settings: { render: <Link to="/settings" /> } }} account={<AccountMenu …/>} onSearch={() => setOpen(true)} assistant={{ open, onOpenChange: setOpen, panel: <Conversation /> }}>` : Documentation, Réglages et État du service (la page de statut par défaut) juste au-dessus du compte ; bouton Assistant à côté de la recherche, panneau dans une colonne à droite (la page se rétrécit, vue dédiée sous 1280 px, Échap ferme, `shortcut: "j"` facultatif). `sidebarFooter` est déprécié |
50
- | | `<SidebarNavItem icon={Rocket01Icon} label="Déploiements" render={<Link to="/deployments" />} />` |
51
- | | Navigation imbriquée (l'espace d'une page, d'un projet) : `<SidebarNavBack label="Retour aux pages" render={<Link to="/pages" />} />` en tête, puis `<SidebarNavGroup label="Suivi">…</SidebarNavGroup>` (titre muet, trait en rail), ou `<SidebarNavGroup label="Réglages" collapsible icon={…} active={…}>` (entrée à flèche qui déroule ses rubriques, fermée par défaut, ouverte sur une rubrique active, mémorisée par navigateur) |
52
- | `org-switcher` | `<OrgSwitcher organizations={orgs} activeId={org.id} onSelect={go} createHref="/organisations/nouvelle" />` |
53
- | `project-switcher` | `<ProjectSwitcher projects={projects} activeId={project?.id} activeName={params.project} allHref="/o/acme" createHref="/o/acme/new" renderLink={(href) => <Link to={href} />} />` : projet actif ou « Tous les projets », marque du projet (`ProjectMark`), « Nouveau projet » |
54
- | `account-menu` | `<AccountMenu user={user} onProfile={…} onSecurity={…} onSignOut={signOut} product="Mail" />` : Account et les produits ouverts du catalogue, Mail coché. Dans l'ordre : identité, Profil, Sécurité, Thème (Système, Clair, Sombre, `ThemeSwitch`), entrées du produit, Se déconnecter. Ni « État du service » ni raccourcis : la coque pose le premier au-dessus du compte, l'aide garde sa touche `?` |
55
- | `command-menu` | `<CommandMenu groups={[{ label: "Aller à", items }, { label: "Créer", items }, { label: "Changer", items }]} />` (`Ctrl+K`), `<CommandMenuButton compact="mobile" …/>` |
56
- | `products` | `HGDC_PRODUCTS` (mot, nom, sous-domaine, phrase, pastille `badge`, icône, état, `entryHref`), `HGDC_ACCOUNT`, `productMenuEntries({ current: "Mail" })` : chaque produit du menu pointe vers son entrée `/auth/hgdc` (ADR 0010) |
57
- | `service-status` | Bandeau d'incident ou de maintenance posé par `AppShell` en tête du contenu (ADR 0018) : `useServiceStatus(pageUrl)`, `ServiceStatusBanner`. `statusPage={false}` pour ne rien lire |
58
- | `kbd` | `<KbdGroup><Kbd>Ctrl</Kbd><Kbd>K</Kbd></KbdGroup>` |
59
- | `page-header` | `<PageHeader title="Domaines" breadcrumbs={[{ label: "Acme", render: <Link to="/o/acme" /> }, { label: "Domaines" }]} actions={<Button>Ajouter</Button>} />` |
60
- | `page-header` | `<Page title="Réglages">…</Page>` (colonne de lecture de 768 px), `<Page title="Projets" wide actions={…}>…</Page>` (tableaux, listes, tableaux de bord) |
61
- | `member-item` | `<ResourceList><MemberItem name={m.name} email={m.email} orgRole={m.role} isMe={m.isMe} /></ResourceList>` : avatar à initiales, « (toi) », `RoleBadge` |
62
- | `initials` | `initials("Test vérif")` donne « TV » : la même règle pour une personne et une organisation (`OrgMark`, `AccountMenu`, `MemberItem`) ; `avatarTone(email)` donne la teinte stable (1 à 7) de l'avatar |
47
+ | Import | Exemple |
48
+ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
49
+ | `app-shell` | `<AppShell product="mail" organization={<OrgSwitcher …/>} nav={<SidebarNav label="Projet">…</SidebarNav>} links={{ documentation: "/docs", settings: { render: <Link to="/settings" /> } }} account={<AccountMenu …/>} onSearch={() => setOpen(true)} assistant={{ open, onOpenChange: setOpen, panel: <Conversation /> }}>` : Documentation, Réglages et État du service (la page de statut par défaut) juste au-dessus du compte ; bouton Assistant à côté de la recherche, panneau dans une colonne à droite (la page se rétrécit, vue dédiée sous 1280 px, Échap ferme, `shortcut: "j"` facultatif). `sidebarFooter` est déprécié |
50
+ | | `<SidebarNavItem icon={Rocket01Icon} label="Déploiements" render={<Link to="/deployments" />} />` |
51
+ | | Navigation imbriquée (l'espace d'une page, d'un projet) : `<SidebarNavBack label="Retour aux pages" render={<Link to="/pages" />} />` en tête, puis `<SidebarNavGroup label="Suivi">…</SidebarNavGroup>` (titre muet, trait en rail), ou `<SidebarNavGroup label="Réglages" collapsible icon={…} active={…}>` (entrée à flèche qui déroule ses rubriques, fermée par défaut, ouverte sur une rubrique active, mémorisée par navigateur) |
52
+ | `org-switcher` | `<OrgSwitcher organizations={orgs} activeId={org.id} onSelect={go} createHref="/organisations/nouvelle" />` |
53
+ | `project-switcher` | `<ProjectSwitcher projects={projects} activeId={project?.id} activeName={params.project} allHref="/o/acme" createHref="/o/acme/new" renderLink={(href) => <Link to={href} />} />` : projet actif ou « Tous les projets », marque du projet (`ProjectMark`), « Nouveau projet » |
54
+ | `account-menu` | `<AccountMenu user={user} onProfile={…} onSecurity={…} onSignOut={signOut} product="Mail" />` : Account et les produits ouverts du catalogue, Mail coché. Dans l'ordre : identité, Profil, Sécurité, Thème (Système, Clair, Sombre, `ThemeSwitch`), entrées du produit, Se déconnecter. Ni « État du service » ni raccourcis : la coque pose le premier au-dessus du compte, l'aide garde sa touche `?` |
55
+ | `command-menu` | `<CommandMenu groups={[{ label: "Aller à", items }, { label: "Créer", items }, { label: "Changer", items }]} />` (`Ctrl+K`), `<CommandMenuButton compact="mobile" …/>` |
56
+ | `products` | `HGDC_PRODUCTS` (mot, nom, sous-domaine, phrase, pastille `badge`, icône, état, `entryHref`), `HGDC_ACCOUNT`, `productMenuEntries({ current: "Mail" })` : chaque produit du menu pointe vers son entrée `/auth/hgdc` (ADR 0010) |
57
+ | `addresses` | Adresses d'un produit déduites de son mot, sans React (lisible par `vite.config.ts` et Convex) : `productAddresses("mail", import.meta.env)` rend `basePath`, `appOrigin`, `appUrl` (`https://app.hgdc.ch/mail`), `convexUrl` (`https://convex.mail.hgdc.ch`), `convexSiteUrl`, `withBase`. `productBasePath("mail", { override: process.env.HGDC_BASE_PATH, command })` donne la base du build : `/mail` pour `vite build`, la racine pour `vite dev` et Vitest. `APP_PRODUCTS` : produits servis sous `app.hgdc.ch` (d'où `PRODUCT_ORIGINS`). Surcharges : `HGDC_BASE_PATH` (`/` : racine), `VITE_CONVEX_URL`, `VITE_CONVEX_SITE_URL` |
58
+ | `service-status` | Bandeau d'incident ou de maintenance posé par `AppShell` en tête du contenu (ADR 0018) : `useServiceStatus(pageUrl)`, `ServiceStatusBanner`. `statusPage={false}` pour ne rien lire |
59
+ | `kbd` | `<KbdGroup><Kbd>Ctrl</Kbd><Kbd>K</Kbd></KbdGroup>` |
60
+ | `page-header` | `<PageHeader title="Domaines" breadcrumbs={[{ label: "Acme", render: <Link to="/o/acme" /> }, { label: "Domaines" }]} actions={<Button>Ajouter</Button>} />` |
61
+ | `page-header` | `<Page title="Réglages">…</Page>` (colonne de lecture de 768 px), `<Page title="Projets" wide actions={…}>…</Page>` (tableaux, listes, tableaux de bord) |
62
+ | `member-item` | `<ResourceList><MemberItem name={m.name} email={m.email} orgRole={m.role} isMe={m.isMe} /></ResourceList>` : avatar à initiales, « (toi) », `RoleBadge` |
63
+ | `initials` | `initials("Test vérif")` donne « TV » : la même règle pour une personne et une organisation (`OrgMark`, `AccountMenu`, `MemberItem`) ; `avatarTone(email)` donne la teinte stable (1 à 7) de l'avatar |
63
64
 
64
65
  Le menu « Produits » et la palette (`badge` d'une commande) montrent la pastille de chaque produit.
65
66
 
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Adresses d'un produit de la H Collection, déduites de son mot (ADR 0001 et 0005 de la famille) :
3
+ * console sous `https://app.hgdc.ch/<mot>`, Convex sur `https://convex.<mot>.hgdc.ch` et
4
+ * `https://actions.<mot>.hgdc.ch`. Écrites une fois ici, au lieu de variables posées sur Host et
5
+ * Vercel puis recopiées dans chaque produit. Une surcharge reste possible : développement local
6
+ * (Convex de dev, app à la racine), bacs à sable, Status et ses deux builds.
7
+ *
8
+ * Sans dépendance ni React : lisible par `vite.config.ts`, le serveur, le navigateur et Convex.
9
+ */
10
+ /** Hôte commun des produits relayés par la passerelle. */
11
+ export declare const APP_ORIGIN = "https://app.hgdc.ch";
12
+ /**
13
+ * Produits servis sous `https://app.hgdc.ch/<mot>` (consoles relayées par la passerelle, ADR 0005).
14
+ * Account (fournisseur d'identité) et Portal (pas encore relayé) gardent leur hôte. Status : la
15
+ * console seule, `status.hgdc.ch` garde les pages publiques et la console de secours. Vérifiée contre
16
+ * le manifeste privé de la famille (`famille check`) : un produit relayé s'ajoute ici et là.
17
+ */
18
+ export declare const APP_PRODUCTS: readonly ["analytics", "mail", "host", "docs", "status", "iassistant"];
19
+ /** Vrai si le produit est servi sous `app.hgdc.ch`. */
20
+ export declare function isAppProduct(product: string): boolean;
21
+ /**
22
+ * Base de chemin normalisée : `""` (racine) ou `/segment`, sans barre finale. Même règle que
23
+ * `normalizeBasePath` de `@hgdc/auth` : lève sur tout ce qui n'est pas un chemin de segments simples.
24
+ */
25
+ export declare function normalizeProductBasePath(value: string | null | undefined): string;
26
+ /**
27
+ * Base de chemin du build, pour `vite.config.ts` : `/<mot>` pour `vite build` d'un produit servi
28
+ * sous app.hgdc.ch, la racine pour `vite dev` et Vitest (`command` = `serve`) et pour les autres
29
+ * produits. `override` (la variable `HGDC_BASE_PATH`, si elle est posée) l'emporte : `/` force la
30
+ * racine (build de `status.hgdc.ch`).
31
+ */
32
+ export declare function productBasePath(product: string, options?: {
33
+ override?: string | undefined;
34
+ command?: "build" | "serve" | undefined;
35
+ }): string;
36
+ /** Surcharges lues dans `import.meta.env` de Vite : absentes ou vides, la convention s'applique. */
37
+ export type AddressOverrides = {
38
+ /** Base du build telle que Vite la pose (`/mail/`, `/` à la racine). */
39
+ BASE_URL?: string | undefined;
40
+ /** Convex de développement (écrit par `convex dev` dans `.env.local`) ou d'un bac à sable. */
41
+ VITE_CONVEX_URL?: string | undefined;
42
+ VITE_CONVEX_SITE_URL?: string | undefined;
43
+ };
44
+ export type ProductAddresses = {
45
+ /** Base de chemin : `/mail`, ou `""` à la racine. */
46
+ basePath: string;
47
+ /** Origine publique de l'app : `https://app.hgdc.ch`, ou `https://<mot>.hgdc.ch` à la racine. */
48
+ appOrigin: string;
49
+ /** Adresse publique de l'app, sans barre finale : `https://app.hgdc.ch/mail`. */
50
+ appUrl: string;
51
+ /** Client Convex : `https://convex.<mot>.hgdc.ch`. */
52
+ convexUrl: string;
53
+ /** Actions HTTP de Convex : `https://actions.<mot>.hgdc.ch`. */
54
+ convexSiteUrl: string;
55
+ /** `withBase("/o/acme")` : `/mail/o/acme` sous la base, `/o/acme` à la racine. */
56
+ withBase: (path: string) => string;
57
+ };
58
+ /**
59
+ * Adresses d'un produit, d'après son mot. Dans l'app : `productAddresses("mail", import.meta.env)`
60
+ * (la base vient alors du build, `BASE_URL`, et un Convex de dev de `.env.local`). Sans `env` : la
61
+ * production. Les adresses publiques sont des constantes, jamais tirées d'une requête (`Host`,
62
+ * `x-forwarded-host` valent l'origine relayée).
63
+ */
64
+ export declare function productAddresses(product: string, env?: AddressOverrides): ProductAddresses;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Adresses d'un produit de la H Collection, déduites de son mot (ADR 0001 et 0005 de la famille) :
3
+ * console sous `https://app.hgdc.ch/<mot>`, Convex sur `https://convex.<mot>.hgdc.ch` et
4
+ * `https://actions.<mot>.hgdc.ch`. Écrites une fois ici, au lieu de variables posées sur Host et
5
+ * Vercel puis recopiées dans chaque produit. Une surcharge reste possible : développement local
6
+ * (Convex de dev, app à la racine), bacs à sable, Status et ses deux builds.
7
+ *
8
+ * Sans dépendance ni React : lisible par `vite.config.ts`, le serveur, le navigateur et Convex.
9
+ */
10
+ /** Hôte commun des produits relayés par la passerelle. */
11
+ export const APP_ORIGIN = "https://app.hgdc.ch";
12
+ /**
13
+ * Produits servis sous `https://app.hgdc.ch/<mot>` (consoles relayées par la passerelle, ADR 0005).
14
+ * Account (fournisseur d'identité) et Portal (pas encore relayé) gardent leur hôte. Status : la
15
+ * console seule, `status.hgdc.ch` garde les pages publiques et la console de secours. Vérifiée contre
16
+ * le manifeste privé de la famille (`famille check`) : un produit relayé s'ajoute ici et là.
17
+ */
18
+ export const APP_PRODUCTS = ["analytics", "mail", "host", "docs", "status", "iassistant"];
19
+ /** Vrai si le produit est servi sous `app.hgdc.ch`. */
20
+ export function isAppProduct(product) {
21
+ return APP_PRODUCTS.includes(product.toLowerCase());
22
+ }
23
+ /**
24
+ * Base de chemin normalisée : `""` (racine) ou `/segment`, sans barre finale. Même règle que
25
+ * `normalizeBasePath` de `@hgdc/auth` : lève sur tout ce qui n'est pas un chemin de segments simples.
26
+ */
27
+ export function normalizeProductBasePath(value) {
28
+ if (value === undefined || value === null || value === "" || value === "/")
29
+ return "";
30
+ const trimmed = value.endsWith("/") ? value.slice(0, -1) : value;
31
+ const segments = trimmed.split("/");
32
+ const valid = segments[0] === "" &&
33
+ segments.length > 1 &&
34
+ segments.slice(1).every((segment) => /^[A-Za-z0-9._~-]+$/.test(segment) && segment !== "." && segment !== "..");
35
+ if (!valid)
36
+ throw new Error(`@hgdc/ui/addresses : base invalide (${JSON.stringify(value)}), attendu « /produit ».`);
37
+ return trimmed;
38
+ }
39
+ /**
40
+ * Base de chemin du build, pour `vite.config.ts` : `/<mot>` pour `vite build` d'un produit servi
41
+ * sous app.hgdc.ch, la racine pour `vite dev` et Vitest (`command` = `serve`) et pour les autres
42
+ * produits. `override` (la variable `HGDC_BASE_PATH`, si elle est posée) l'emporte : `/` force la
43
+ * racine (build de `status.hgdc.ch`).
44
+ */
45
+ export function productBasePath(product, options = {}) {
46
+ if (options.override !== undefined && options.override !== "")
47
+ return normalizeProductBasePath(options.override);
48
+ if (options.command === "serve" || !isAppProduct(product))
49
+ return "";
50
+ return `/${product.toLowerCase()}`;
51
+ }
52
+ const nonEmpty = (value) => (value ? value : undefined);
53
+ /**
54
+ * Adresses d'un produit, d'après son mot. Dans l'app : `productAddresses("mail", import.meta.env)`
55
+ * (la base vient alors du build, `BASE_URL`, et un Convex de dev de `.env.local`). Sans `env` : la
56
+ * production. Les adresses publiques sont des constantes, jamais tirées d'une requête (`Host`,
57
+ * `x-forwarded-host` valent l'origine relayée).
58
+ */
59
+ export function productAddresses(product, env = {}) {
60
+ const word = product.toLowerCase();
61
+ if (!/^[a-z]+$/.test(word))
62
+ throw new Error(`@hgdc/ui/addresses : produit invalide « ${product} »`);
63
+ const basePath = env.BASE_URL !== undefined ? normalizeProductBasePath(env.BASE_URL) : productBasePath(word);
64
+ const appOrigin = basePath ? APP_ORIGIN : `https://${word}.hgdc.ch`;
65
+ return {
66
+ basePath,
67
+ appOrigin,
68
+ appUrl: `${appOrigin}${basePath}`,
69
+ convexUrl: nonEmpty(env.VITE_CONVEX_URL) ?? `https://convex.${word}.hgdc.ch`,
70
+ convexSiteUrl: nonEmpty(env.VITE_CONVEX_SITE_URL) ?? `https://actions.${word}.hgdc.ch`,
71
+ withBase: (path) => `${basePath}${path}`,
72
+ };
73
+ }
@@ -1,5 +1,5 @@
1
1
  /** Les produits de la H Collection qui ont une icône, par leur mot en minuscules. */
2
- export declare const FAMILY_PRODUCTS: readonly ["account", "host", "mail", "portal", "status", "analytics", "docs"];
2
+ export declare const FAMILY_PRODUCTS: readonly ["account", "host", "mail", "portal", "status", "analytics", "docs", "iassistant"];
3
3
  export type FamilyProduct = (typeof FAMILY_PRODUCTS)[number];
4
4
  /** Vrai si le mot en minuscules (« host ») désigne un produit qui a une icône. */
5
5
  export declare function isFamilyProduct(word: string): word is FamilyProduct;
@@ -1,6 +1,15 @@
1
1
  // Généré par `bun scripts/family.ts` depuis `packages/ui/family/`. Ne pas modifier à la main.
2
2
  /** Les produits de la H Collection qui ont une icône, par leur mot en minuscules. */
3
- export const FAMILY_PRODUCTS = ["account", "host", "mail", "portal", "status", "analytics", "docs"];
3
+ export const FAMILY_PRODUCTS = [
4
+ "account",
5
+ "host",
6
+ "mail",
7
+ "portal",
8
+ "status",
9
+ "analytics",
10
+ "docs",
11
+ "iassistant",
12
+ ];
4
13
  /** Vrai si le mot en minuscules (« host ») désigne un produit qui a une icône. */
5
14
  export function isFamilyProduct(word) {
6
15
  return FAMILY_PRODUCTS.includes(word);
@@ -17,6 +26,7 @@ export const PRODUCT_BADGE_SVG = {
17
26
  status: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="14 14 36 36"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#11ce8b"/><stop offset="1" stop-color="#09b077"/></linearGradient></defs><path d="M14 32A18 18 0 1 0 50 32A18 18 0 1 0 14 32Z" fill="url(#g)" fill-opacity="0.22" fill-rule="evenodd"/><path d="M19.5 32A12.5 12.5 0 1 0 44.5 32A12.5 12.5 0 1 0 19.5 32Z" fill="url(#g)" fill-opacity="0.45" fill-rule="evenodd"/><path d="M25 32A7 7 0 1 0 39 32A7 7 0 1 0 25 32Z" fill="url(#g)" fill-rule="evenodd"/></svg></svg>',
18
27
  analytics: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="15 15 34 34"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ffb020"/><stop offset="1" stop-color="#e58309"/></linearGradient></defs><path d="M18.3 34H21.7A2.8 2.8 0 0 1 24.5 36.8V46.2A2.8 2.8 0 0 1 21.7 49H18.3A2.8 2.8 0 0 1 15.5 46.2V36.8A2.8 2.8 0 0 1 18.3 34Z" fill="url(#g)" fill-opacity="0.4" fill-rule="evenodd"/><path d="M30.3 25H33.7A2.8 2.8 0 0 1 36.5 27.8V46.2A2.8 2.8 0 0 1 33.7 49H30.3A2.8 2.8 0 0 1 27.5 46.2V27.8A2.8 2.8 0 0 1 30.3 25Z" fill="url(#g)" fill-opacity="0.7" fill-rule="evenodd"/><path d="M42.3 15H45.7A2.8 2.8 0 0 1 48.5 17.8V46.2A2.8 2.8 0 0 1 45.7 49H42.3A2.8 2.8 0 0 1 39.5 46.2V17.8A2.8 2.8 0 0 1 42.3 15Z" fill="url(#g)" fill-rule="evenodd"/></svg></svg>',
19
28
  docs: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="14 13 36 36"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0ec1dc"/><stop offset="1" stop-color="#0aa3c1"/></linearGradient></defs><path d="M26 13H46A3 3 0 0 1 49 16V43A3 3 0 0 1 46 46H26A3 3 0 0 1 23 43V16A3 3 0 0 1 26 13Z" fill="url(#g)" fill-opacity="0.22" fill-rule="evenodd"/><path d="M18 18H32V24.5A2.5 2.5 0 0 0 34.5 27H41V46A3 3 0 0 1 38 49H18A3 3 0 0 1 15 46V21A3 3 0 0 1 18 18ZM21.3 33H34.7A1.3 1.3 0 0 1 36 34.3V34.300000000000004A1.3 1.3 0 0 1 34.7 35.6H21.3A1.3 1.3 0 0 1 20 34.300000000000004V34.3A1.3 1.3 0 0 1 21.3 33ZM21.3 39H29.7A1.3 1.3 0 0 1 31 40.3V40.300000000000004A1.3 1.3 0 0 1 29.7 41.6H21.3A1.3 1.3 0 0 1 20 40.300000000000004V40.3A1.3 1.3 0 0 1 21.3 39Z" fill="url(#g)" fill-rule="evenodd"/><path d="M32 18L41 27H34.5A2.5 2.5 0 0 1 32 24.5Z" fill="url(#g)" fill-opacity="0.45" fill-rule="evenodd"/></svg></svg>',
29
+ iassistant: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="16 18 34 34"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#d946ef"/><stop offset="1" stop-color="#c026d3"/></linearGradient></defs><path d="M24 18H38A6 6 0 0 1 44 24V32A6 6 0 0 1 38 38H27L21 43V37.2A6 6 0 0 1 18 32V24A6 6 0 0 1 24 18Z" fill="url(#g)" fill-opacity="0.4" fill-rule="evenodd"/><path d="M29 27H43A6 6 0 0 1 49 33V41A6 6 0 0 1 43 47V51.5L37.5 47H29A6 6 0 0 1 23 41V33A6 6 0 0 1 29 27ZM28.1 37A1.9 1.9 0 1 0 31.9 37A1.9 1.9 0 1 0 28.1 37ZM34.1 37A1.9 1.9 0 1 0 37.9 37A1.9 1.9 0 1 0 34.1 37ZM40.1 37A1.9 1.9 0 1 0 43.9 37A1.9 1.9 0 1 0 40.1 37Z" fill="url(#g)" fill-rule="evenodd"/></svg></svg>',
20
30
  };
21
31
  /**
22
32
  * Empreinte de chaque icône d'e-mail (`email-icons/<mot>.png`), ajoutée à son adresse : un client
@@ -30,6 +40,7 @@ export const EMAIL_ICON_VERSION = {
30
40
  status: "90f70c05",
31
41
  analytics: "83add9fb",
32
42
  docs: "77f11f0b",
43
+ iassistant: "2b9ed0e0",
33
44
  };
34
45
  /** SVG du H « Double », la marque de la H Collection, sans fond (`family/hcollection-icon(-dark).svg`). */
35
46
  export const FAMILY_MARK_SVG = {
@@ -14,6 +14,7 @@ export const PRODUCT_GRADIENT = {
14
14
  status: { from: "#11ce8b", to: "#09b077" },
15
15
  analytics: { from: "#ffb020", to: "#e58309" },
16
16
  docs: { from: "#0ec1dc", to: "#0aa3c1" },
17
+ iassistant: { from: "#d946ef", to: "#c026d3" },
17
18
  };
18
19
  // En image plutôt qu'en SVG en ligne : chaque icône garde ses identifiants de dégradé pour elle,
19
20
  // plusieurs icônes dans une même page ne se marchent pas dessus.
@@ -47,13 +47,9 @@ export type ProductOrigin = {
47
47
  basePath: string;
48
48
  };
49
49
  /**
50
- * Adresse de l'app de chaque produit, par mot en minuscules. Défaut : `https://<mot>.hgdc.ch` sans
51
- * base. Un produit servi sous `https://app.hgdc.ch/<mot>` (`@hgdc/auth`, `appBasePath`) migre en
52
- * ajoutant sa ligne ici, une à la fois, quand son déploiement est prêt :
53
- *
54
- * ```ts
55
- * analytics: { origin: "https://app.hgdc.ch", basePath: "/analytics" },
56
- * ```
50
+ * Adresse de l'app de chaque produit servi sous `https://app.hgdc.ch/<mot>`, par mot en minuscules,
51
+ * déduite d'`APP_PRODUCTS` (`@hgdc/ui/addresses`). Un autre produit reste à `https://<mot>.hgdc.ch`
52
+ * sans base.
57
53
  *
58
54
  * Ne change que l'entrée dans l'app (`entryHref`, menus et barre) : la page d'accueil publique
59
55
  * (`href`) reste `https://<mot>.hgdc.ch`.
package/dist/products.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import Activity01Icon from "@hugeicons/core-free-icons/Activity01Icon";
2
2
  import Analytics01Icon from "@hugeicons/core-free-icons/Analytics01Icon";
3
3
  import BookOpen01Icon from "@hugeicons/core-free-icons/BookOpen01Icon";
4
+ import BubbleChatIcon from "@hugeicons/core-free-icons/BubbleChatIcon";
4
5
  import Briefcase01Icon from "@hugeicons/core-free-icons/Briefcase01Icon";
5
6
  import Mail01Icon from "@hugeicons/core-free-icons/Mail01Icon";
6
7
  import Rocket01Icon from "@hugeicons/core-free-icons/Rocket01Icon";
7
8
  import UserCircleIcon from "@hugeicons/core-free-icons/UserCircleIcon";
9
+ import { APP_ORIGIN, APP_PRODUCTS } from "./addresses.js";
8
10
  import { HGDC_EMAIL_ACCENT } from "./email-palette.js";
9
11
  import { isFamilyProduct } from "./family-svg.js";
10
12
  export const PRODUCT_STAGE_LABEL = {
@@ -19,25 +21,14 @@ export const PRODUCT_STAGE_LABEL = {
19
21
  */
20
22
  export const PRODUCT_ENTRY_PATH = "/auth/hgdc";
21
23
  /**
22
- * Adresse de l'app de chaque produit, par mot en minuscules. Défaut : `https://<mot>.hgdc.ch` sans
23
- * base. Un produit servi sous `https://app.hgdc.ch/<mot>` (`@hgdc/auth`, `appBasePath`) migre en
24
- * ajoutant sa ligne ici, une à la fois, quand son déploiement est prêt :
25
- *
26
- * ```ts
27
- * analytics: { origin: "https://app.hgdc.ch", basePath: "/analytics" },
28
- * ```
24
+ * Adresse de l'app de chaque produit servi sous `https://app.hgdc.ch/<mot>`, par mot en minuscules,
25
+ * déduite d'`APP_PRODUCTS` (`@hgdc/ui/addresses`). Un autre produit reste à `https://<mot>.hgdc.ch`
26
+ * sans base.
29
27
  *
30
28
  * Ne change que l'entrée dans l'app (`entryHref`, menus et barre) : la page d'accueil publique
31
29
  * (`href`) reste `https://<mot>.hgdc.ch`.
32
30
  */
33
- export const PRODUCT_ORIGINS = {
34
- analytics: { origin: "https://app.hgdc.ch", basePath: "/analytics" },
35
- mail: { origin: "https://app.hgdc.ch", basePath: "/mail" },
36
- host: { origin: "https://app.hgdc.ch", basePath: "/host" },
37
- docs: { origin: "https://app.hgdc.ch", basePath: "/docs" },
38
- // Console seule : status.hgdc.ch reste l'adresse des pages publiques et la console de secours.
39
- status: { origin: "https://app.hgdc.ch", basePath: "/status" },
40
- };
31
+ export const PRODUCT_ORIGINS = Object.fromEntries(APP_PRODUCTS.map((word) => [word, { origin: APP_ORIGIN, basePath: `/${word}` }]));
41
32
  /** Adresse de l'app d'un produit : celle de `PRODUCT_ORIGINS`, sinon `https://<mot>.hgdc.ch`. */
42
33
  export function productOrigin(word, origins = PRODUCT_ORIGINS) {
43
34
  const key = word.toLowerCase();
@@ -83,6 +74,7 @@ export const HGDC_PRODUCTS = [
83
74
  product("Status", "La surveillance de tes sites et ta page de statut.", Activity01Icon, "live"),
84
75
  product("Analytics", "Tes visiteurs mesurés, sans cookies ni bannière.", Analytics01Icon, "live"),
85
76
  product("Docs", "Ta documentation en ligne, avec un assistant qui y répond.", BookOpen01Icon, "live"),
77
+ product("IAssistant", "Une bulle de chat IA sur ton site, qui répond à tes visiteurs.", BubbleChatIcon, "soon"),
86
78
  ];
87
79
  /** Vrai si le produit répond déjà (en service ou en préversion). */
88
80
  export function isProductOpen(item) {
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><style>.d{display:none}@media (prefers-color-scheme:dark){.l{display:none}.d{display:inline}}</style><g class="l"><svg x="2.56" y="2.56" width="58.88" height="58.88" viewBox="16 18 34 34"><defs><linearGradient id="lg" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#d946ef"/><stop offset="1" stop-color="#c026d3"/></linearGradient></defs><path d="M24 18H38A6 6 0 0 1 44 24V32A6 6 0 0 1 38 38H27L21 43V37.2A6 6 0 0 1 18 32V24A6 6 0 0 1 24 18Z" fill="url(#lg)" fill-opacity="0.4" fill-rule="evenodd"/><path d="M29 27H43A6 6 0 0 1 49 33V41A6 6 0 0 1 43 47V51.5L37.5 47H29A6 6 0 0 1 23 41V33A6 6 0 0 1 29 27ZM28.1 37A1.9 1.9 0 1 0 31.9 37A1.9 1.9 0 1 0 28.1 37ZM34.1 37A1.9 1.9 0 1 0 37.9 37A1.9 1.9 0 1 0 34.1 37ZM40.1 37A1.9 1.9 0 1 0 43.9 37A1.9 1.9 0 1 0 40.1 37Z" fill="url(#lg)" fill-rule="evenodd"/></svg></g><g class="d"><svg x="2.56" y="2.56" width="58.88" height="58.88" viewBox="16 18 34 34"><defs><linearGradient id="dg" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ef8cfb"/><stop offset="1" stop-color="#d946ef"/></linearGradient></defs><path d="M24 18H38A6 6 0 0 1 44 24V32A6 6 0 0 1 38 38H27L21 43V37.2A6 6 0 0 1 18 32V24A6 6 0 0 1 24 18Z" fill="url(#dg)" fill-opacity="0.61" fill-rule="evenodd"/><path d="M29 27H43A6 6 0 0 1 49 33V41A6 6 0 0 1 43 47V51.5L37.5 47H29A6 6 0 0 1 23 41V33A6 6 0 0 1 29 27ZM28.1 37A1.9 1.9 0 1 0 31.9 37A1.9 1.9 0 1 0 28.1 37ZM34.1 37A1.9 1.9 0 1 0 37.9 37A1.9 1.9 0 1 0 34.1 37ZM40.1 37A1.9 1.9 0 1 0 43.9 37A1.9 1.9 0 1 0 40.1 37Z" fill="url(#dg)" fill-rule="evenodd"/></svg></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="12.5 14 42 42"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ef8cfb"/><stop offset="1" stop-color="#d946ef"/></linearGradient></defs><path d="M24 18H38A6 6 0 0 1 44 24V32A6 6 0 0 1 38 38H27L21 43V37.2A6 6 0 0 1 18 32V24A6 6 0 0 1 24 18Z" fill="url(#g)" fill-opacity="0.61" fill-rule="evenodd"/><path d="M29 27H43A6 6 0 0 1 49 33V41A6 6 0 0 1 43 47V51.5L37.5 47H29A6 6 0 0 1 23 41V33A6 6 0 0 1 29 27ZM28.1 37A1.9 1.9 0 1 0 31.9 37A1.9 1.9 0 1 0 28.1 37ZM34.1 37A1.9 1.9 0 1 0 37.9 37A1.9 1.9 0 1 0 34.1 37ZM40.1 37A1.9 1.9 0 1 0 43.9 37A1.9 1.9 0 1 0 40.1 37Z" fill="url(#g)" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="12.5 14 42 42"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#d946ef"/><stop offset="1" stop-color="#c026d3"/></linearGradient></defs><path d="M24 18H38A6 6 0 0 1 44 24V32A6 6 0 0 1 38 38H27L21 43V37.2A6 6 0 0 1 18 32V24A6 6 0 0 1 24 18Z" fill="url(#g)" fill-opacity="0.4" fill-rule="evenodd"/><path d="M29 27H43A6 6 0 0 1 49 33V41A6 6 0 0 1 43 47V51.5L37.5 47H29A6 6 0 0 1 23 41V33A6 6 0 0 1 29 27ZM28.1 37A1.9 1.9 0 1 0 31.9 37A1.9 1.9 0 1 0 28.1 37ZM34.1 37A1.9 1.9 0 1 0 37.9 37A1.9 1.9 0 1 0 34.1 37ZM40.1 37A1.9 1.9 0 1 0 43.9 37A1.9 1.9 0 1 0 40.1 37Z" fill="url(#g)" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="16 18 34 34"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#d946ef"/><stop offset="1" stop-color="#c026d3"/></linearGradient></defs><path d="M24 18H38A6 6 0 0 1 44 24V32A6 6 0 0 1 38 38H27L21 43V37.2A6 6 0 0 1 18 32V24A6 6 0 0 1 24 18Z" fill="url(#g)" fill-opacity="0.4" fill-rule="evenodd"/><path d="M29 27H43A6 6 0 0 1 49 33V41A6 6 0 0 1 43 47V51.5L37.5 47H29A6 6 0 0 1 23 41V33A6 6 0 0 1 29 27ZM28.1 37A1.9 1.9 0 1 0 31.9 37A1.9 1.9 0 1 0 28.1 37ZM34.1 37A1.9 1.9 0 1 0 37.9 37A1.9 1.9 0 1 0 34.1 37ZM40.1 37A1.9 1.9 0 1 0 43.9 37A1.9 1.9 0 1 0 40.1 37Z" fill="url(#g)" fill-rule="evenodd"/></svg></svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hgdc/ui",
3
- "version": "0.41.0",
3
+ "version": "0.43.0",
4
4
  "description": "Interface de la H Collection : jetons de la DA, thèmes, polices et composants.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://github.com/hugogebel-boop/hgdc-kit/tree/main/packages/ui#readme",
package/styles.css CHANGED
@@ -295,6 +295,8 @@
295
295
  --product-analytics-to: #e58309;
296
296
  --product-docs-from: #0ec1dc;
297
297
  --product-docs-to: #0aa3c1;
298
+ --product-iassistant-from: #d946ef;
299
+ --product-iassistant-to: #c026d3;
298
300
  --glow-blue: #2c78fd;
299
301
  --glow-violet: #7343fe;
300
302
  --glow-pink: #fe5292;
@@ -859,7 +861,8 @@
859
861
  :root[data-hgdc-product="portal"] [data-slot="shell-frame"] [data-product="portal"],
860
862
  :root[data-hgdc-product="status"] [data-slot="shell-frame"] [data-product="status"],
861
863
  :root[data-hgdc-product="analytics"] [data-slot="shell-frame"] [data-product="analytics"],
862
- :root[data-hgdc-product="docs"] [data-slot="shell-frame"] [data-product="docs"] {
864
+ :root[data-hgdc-product="docs"] [data-slot="shell-frame"] [data-product="docs"],
865
+ :root[data-hgdc-product="iassistant"] [data-slot="shell-frame"] [data-product="iassistant"] {
863
866
  display: contents;
864
867
  }
865
868
  :root[data-hgdc-product="account"] [data-slot="shell-frame-rail"] [data-product="account"],
@@ -868,7 +871,8 @@
868
871
  :root[data-hgdc-product="portal"] [data-slot="shell-frame-rail"] [data-product="portal"],
869
872
  :root[data-hgdc-product="status"] [data-slot="shell-frame-rail"] [data-product="status"],
870
873
  :root[data-hgdc-product="analytics"] [data-slot="shell-frame-rail"] [data-product="analytics"],
871
- :root[data-hgdc-product="docs"] [data-slot="shell-frame-rail"] [data-product="docs"] {
874
+ :root[data-hgdc-product="docs"] [data-slot="shell-frame-rail"] [data-product="docs"],
875
+ :root[data-hgdc-product="iassistant"] [data-slot="shell-frame-rail"] [data-product="iassistant"] {
872
876
  display: flex;
873
877
  background: var(--shell-active);
874
878
  color: var(--shell-foreground);