diff --git a/frontend/package.json b/frontend/package.json index f4dfc7c09e..2cdd847955 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,8 +9,10 @@ "scripts": { "start": "cross-env NODE_ENV=development webpack serve --config webpack.config.js", "start-sky": "cross-env NODE_ENV=development UI_VERSION=sky webpack serve --config webpack.config.js", + "start-factory": "cross-env NODE_ENV=development UI_VERSION=factory webpack serve --config webpack.config.js", "build": "cross-env NODE_ENV=production webpack build --config webpack.config.js", "build-sky": "cross-env NODE_ENV=production UI_VERSION=sky webpack build --config webpack.config.js", + "build-factory": "cross-env NODE_ENV=production UI_VERSION=factory webpack build --config webpack.config.js", "eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx", "eslint-fix": "eslint ./src --ext .js,.jsx,.ts,.tsx --fix", "test": "jest", diff --git a/frontend/src/App/Login/LoginByGithub/index.tsx b/frontend/src/App/Login/LoginByGithub/index.tsx index 61ce317cc8..2b89f3fa21 100644 --- a/frontend/src/App/Login/LoginByGithub/index.tsx +++ b/frontend/src/App/Login/LoginByGithub/index.tsx @@ -382,11 +382,11 @@ function OtherVersions() { -
  • +
  • - dstack Enterprise + dstack Factory Self-hosted Single sign-on, advanced governance controls, and dedicated support. diff --git a/frontend/src/App/Login/EnterpriseLogin/index.tsx b/frontend/src/App/Login/SelfHostedLogin/index.tsx similarity index 94% rename from frontend/src/App/Login/EnterpriseLogin/index.tsx rename to frontend/src/App/Login/SelfHostedLogin/index.tsx index 7cb9b530af..6cb247a4e5 100644 --- a/frontend/src/App/Login/EnterpriseLogin/index.tsx +++ b/frontend/src/App/Login/SelfHostedLogin/index.tsx @@ -15,7 +15,7 @@ import { LoginByTokenForm } from '../LoginByTokenForm'; import styles from './styles.module.scss'; -export const EnterpriseLogin: React.FC = () => { +export const SelfHostedLogin: React.FC = () => { const { t } = useTranslation(); const { data: oktaData, isLoading: isLoadingOkta } = useGetOktaInfoQuery(); const { data: entraData, isLoading: isLoadingEntra } = useGetEntraInfoQuery(); @@ -33,7 +33,7 @@ export const EnterpriseLogin: React.FC = () => {
    - {t('auth.sign_in_to_dstack_enterprise')} + {t('auth.sign_in_to_dstack')} {!isLoading && isShowTokenForm && } diff --git a/frontend/src/App/Login/EnterpriseLogin/styles.module.scss b/frontend/src/App/Login/SelfHostedLogin/styles.module.scss similarity index 100% rename from frontend/src/App/Login/EnterpriseLogin/styles.module.scss rename to frontend/src/App/Login/SelfHostedLogin/styles.module.scss diff --git a/frontend/src/App/Login/TokenLogin/index.tsx b/frontend/src/App/Login/TokenLogin/index.tsx index b41393ba39..96c369424d 100644 --- a/frontend/src/App/Login/TokenLogin/index.tsx +++ b/frontend/src/App/Login/TokenLogin/index.tsx @@ -19,7 +19,7 @@ export const TokenLogin: React.FC = () => {
    - {t('auth.sign_in_to_dstack_enterprise')} + {t('auth.sign_in_to_dstack')} diff --git a/frontend/src/App/index.tsx b/frontend/src/App/index.tsx index de0f919079..618257a453 100644 --- a/frontend/src/App/index.tsx +++ b/frontend/src/App/index.tsx @@ -7,8 +7,8 @@ import AppLayout from 'layouts/AppLayout'; import { useAppDispatch, useAppSelector } from 'hooks'; import { useGetUserDataQuery } from 'services/user'; -import { EnterpriseLogin } from './Login/EnterpriseLogin'; import { LoginByGithub } from './Login/LoginByGithub'; +import { SelfHostedLogin } from './Login/SelfHostedLogin'; import { ROUTES } from '../routes'; import { AuthErrorMessage } from './AuthErrorMessage'; import { selectAuthToken, setUserData } from './slice'; @@ -23,7 +23,7 @@ const IGNORED_AUTH_PATHS = [ ROUTES.AUTH.TOKEN, ]; -const LoginFormComponent = process.env.UI_VERSION === 'enterprise' ? EnterpriseLogin : LoginByGithub; +const LoginFormComponent = process.env.UI_VERSION === 'sky' ? LoginByGithub : SelfHostedLogin; const App: React.FC = () => { const { t } = useTranslation(); diff --git a/frontend/src/layouts/AppLayout/hooks.ts b/frontend/src/layouts/AppLayout/hooks.ts index 52e31db286..34d633d313 100644 --- a/frontend/src/layouts/AppLayout/hooks.ts +++ b/frontend/src/layouts/AppLayout/hooks.ts @@ -30,7 +30,11 @@ export const useSideNavigation = () => { { type: 'link', text: t('navigation.volumes'), href: ROUTES.VOLUMES.LIST }, { type: 'link', text: t('navigation.models'), href: ROUTES.MODELS.LIST }, process.env.UI_VERSION === 'sky' && { type: 'link', text: t('navigation.presets'), href: ROUTES.PRESETS.LIST }, - { type: 'link', text: t('navigation.events'), href: ROUTES.EVENTS.LIST }, + (process.env.UI_VERSION === 'factory' || process.env.UI_VERSION === 'sky') && { + type: 'link', + text: t('navigation.events'), + href: ROUTES.EVENTS.LIST, + }, { type: 'link', text: t('navigation.project_other'), href: ROUTES.PROJECT.LIST }, isGlobalAdmin && { diff --git a/frontend/src/layouts/AppLayout/index.tsx b/frontend/src/layouts/AppLayout/index.tsx index 3dd4db470b..e55ffc239b 100644 --- a/frontend/src/layouts/AppLayout/index.tsx +++ b/frontend/src/layouts/AppLayout/index.tsx @@ -124,7 +124,7 @@ const AppLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => { const isVisibleInfoTab = helpPanelContent.header || helpPanelContent.footer || helpPanelContent.body; - const avatarProps = process.env.UI_VERSION === 'enterprise' ? { name: userName } : { githubHandle: userName }; + const avatarProps = process.env.UI_VERSION === 'sky' ? { githubHandle: userName } : { name: userName }; const onChangeSystemModeToggle: SideNavigationProps['onFollow'] = (event) => { event.preventDefault(); diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json index a06f08c4de..c1115ad421 100644 --- a/frontend/src/locale/en.json +++ b/frontend/src/locale/en.json @@ -62,8 +62,8 @@ "invalid_token": "Invalid token", "you_are_not_logged_in": "You are not logged in", "contact_to_administrator": "For getting the authorization token, contact to the administrator", - "sign_in_to_dstack": "Welcome to dstack Sky", - "sign_in_to_dstack_enterprise": "Welcome to dstack", + "sign_in_to_dstack_sky": "Welcome to dstack Sky", + "sign_in_to_dstack": "Welcome to dstack", "authorization_failed": "Authorization is failed", "try_again": "Please try again", "login_by_token": "Sign in via a token", diff --git a/frontend/src/pages/Fleets/Details/Events/index.tsx b/frontend/src/pages/Fleets/Details/Events/index.tsx index 9a81c7dec3..eaecfb184f 100644 --- a/frontend/src/pages/Fleets/Details/Events/index.tsx +++ b/frontend/src/pages/Fleets/Details/Events/index.tsx @@ -46,7 +46,13 @@ export const EventsList = () => { loading={isLoading} loadingText={t('common.loading')} header={ -
    {t('common.full_view')}}> +
    {t('common.full_view')} + ) + } + > {t('navigation.events')}
    } diff --git a/frontend/src/pages/Instances/Details/Events/index.tsx b/frontend/src/pages/Instances/Details/Events/index.tsx index 53a07f1cdc..52294acbc9 100644 --- a/frontend/src/pages/Instances/Details/Events/index.tsx +++ b/frontend/src/pages/Instances/Details/Events/index.tsx @@ -46,7 +46,13 @@ export const EventsList = () => { loading={isLoading} loadingText={t('common.loading')} header={ -
    {t('common.full_view')}}> +
    {t('common.full_view')} + ) + } + > {t('navigation.events')}
    } diff --git a/frontend/src/pages/Project/Backends/Table/constants.tsx b/frontend/src/pages/Project/Backends/Table/constants.tsx index 0d9fb99128..67e22a0f79 100644 --- a/frontend/src/pages/Project/Backends/Table/constants.tsx +++ b/frontend/src/pages/Project/Backends/Table/constants.tsx @@ -28,7 +28,7 @@ export const BACKENDS_HELP_SKY = { ), }; -export const BACKENDS_HELP_ENTERPRISE = { +export const BACKENDS_HELP_SELF_HOSTED = { header:

    Backends

    , body: ( <> diff --git a/frontend/src/pages/Project/Backends/Table/index.tsx b/frontend/src/pages/Project/Backends/Table/index.tsx index 3ccfa9d9c5..fcac9b446a 100644 --- a/frontend/src/pages/Project/Backends/Table/index.tsx +++ b/frontend/src/pages/Project/Backends/Table/index.tsx @@ -5,11 +5,11 @@ import { Button, ButtonWithConfirmation, Header, InfoLink, ListEmptyMessage, Spa import { useCollection, useHelpPanel } from 'hooks'; -import { BACKENDS_HELP_ENTERPRISE, BACKENDS_HELP_SKY } from './constants'; +import { BACKENDS_HELP_SELF_HOSTED, BACKENDS_HELP_SKY } from './constants'; import { useColumnsDefinitions } from './hooks'; import { IProps } from './types'; -const INFO = process.env.UI_VERSION === 'enterprise' ? BACKENDS_HELP_ENTERPRISE : BACKENDS_HELP_SKY; +const INFO = process.env.UI_VERSION === 'sky' ? BACKENDS_HELP_SKY : BACKENDS_HELP_SELF_HOSTED; export const BackendsTable: React.FC = ({ backends, diff --git a/frontend/src/pages/Project/Backends/YAMLForm/constants.tsx b/frontend/src/pages/Project/Backends/YAMLForm/constants.tsx index a139f137d5..f8686b246e 100644 --- a/frontend/src/pages/Project/Backends/YAMLForm/constants.tsx +++ b/frontend/src/pages/Project/Backends/YAMLForm/constants.tsx @@ -48,7 +48,7 @@ export const CONFIG_YAML_HELP_SKY = { ), }; -export const CONFIG_YAML_HELP_ENTERPRISE = { +export const CONFIG_YAML_HELP_SELF_HOSTED = { header:

    Backend config

    , body: ( <> diff --git a/frontend/src/pages/Project/Backends/YAMLForm/index.tsx b/frontend/src/pages/Project/Backends/YAMLForm/index.tsx index e5ebc10a70..f14e8b1f3a 100644 --- a/frontend/src/pages/Project/Backends/YAMLForm/index.tsx +++ b/frontend/src/pages/Project/Backends/YAMLForm/index.tsx @@ -7,11 +7,11 @@ import { Button, FormCodeEditor, FormUI, InfoLink, SpaceBetween } from 'componen import { useHelpPanel, useNotifications } from 'hooks'; import { isResponseServerError, isResponseServerFormFieldError } from 'libs'; -import { CONFIG_YAML_HELP_ENTERPRISE, CONFIG_YAML_HELP_SKY } from './constants'; +import { CONFIG_YAML_HELP_SELF_HOSTED, CONFIG_YAML_HELP_SKY } from './constants'; import { FieldPath } from 'react-hook-form/dist/types/path'; -const INFO = process.env.UI_VERSION === 'enterprise' ? CONFIG_YAML_HELP_ENTERPRISE : CONFIG_YAML_HELP_SKY; +const INFO = process.env.UI_VERSION === 'sky' ? CONFIG_YAML_HELP_SKY : CONFIG_YAML_HELP_SELF_HOSTED; export interface IProps { initialValues?: IBackendConfigYaml; diff --git a/frontend/src/pages/Project/Details/index.tsx b/frontend/src/pages/Project/Details/index.tsx index f667319eb2..74bd1bcf85 100644 --- a/frontend/src/pages/Project/Details/index.tsx +++ b/frontend/src/pages/Project/Details/index.tsx @@ -16,22 +16,22 @@ export const ProjectDetails: React.FC = () => { const matchSettings = useMatch(ROUTES.PROJECT.DETAILS.SETTINGS.FORMAT(paramProjectName)); const matchEvents = useMatch(ROUTES.PROJECT.DETAILS.EVENTS.FORMAT(paramProjectName)); - const tabs: { - label: string; - id: string; - href: string; - }[] = [ + const tabs = [ { label: t('projects.settings'), id: 'settings', href: ROUTES.PROJECT.DETAILS.SETTINGS.FORMAT(paramProjectName), }, - { + (process.env.UI_VERSION === 'factory' || process.env.UI_VERSION === 'sky') && { label: t('projects.events'), id: 'events', href: ROUTES.PROJECT.DETAILS.EVENTS.FORMAT(paramProjectName), }, - ].filter(Boolean); + ].filter(Boolean) as { + label: string; + id: string; + href: string; + }[]; const showTabs = useMemo(() => { return Boolean(matchSettings) || Boolean(matchEvents); diff --git a/frontend/src/pages/Runs/Details/Events/List/index.tsx b/frontend/src/pages/Runs/Details/Events/List/index.tsx index fb6610033e..178ad717c7 100644 --- a/frontend/src/pages/Runs/Details/Events/List/index.tsx +++ b/frontend/src/pages/Runs/Details/Events/List/index.tsx @@ -51,7 +51,13 @@ export const EventsList = () => { loading={isLoading} loadingText={t('common.loading')} header={ -
    {t('common.full_view')}}> +
    {t('common.full_view')} + ) + } + > {t('navigation.events')}
    } diff --git a/frontend/src/pages/Runs/Details/Jobs/Events/index.tsx b/frontend/src/pages/Runs/Details/Jobs/Events/index.tsx index 48adc56364..3693f067d5 100644 --- a/frontend/src/pages/Runs/Details/Jobs/Events/index.tsx +++ b/frontend/src/pages/Runs/Details/Jobs/Events/index.tsx @@ -64,9 +64,11 @@ export const EventsList = () => { header={
    - {t('common.full_view')} - + (process.env.UI_VERSION === 'factory' || process.env.UI_VERSION === 'sky') && ( + + ) } > {t('navigation.events')} diff --git a/frontend/src/pages/User/Details/index.tsx b/frontend/src/pages/User/Details/index.tsx index 3236d9acad..62cff381e4 100644 --- a/frontend/src/pages/User/Details/index.tsx +++ b/frontend/src/pages/User/Details/index.tsx @@ -72,7 +72,7 @@ export const UserDetails: React.FC = () => { id: UserDetailsTabTypeEnum.PROJECTS, href: ROUTES.USER.PROJECTS.FORMAT(paramUserName), }, - { + (process.env.UI_VERSION === 'factory' || process.env.UI_VERSION === 'sky') && { label: t('users.events'), id: UserDetailsTabTypeEnum.EVENTS, href: ROUTES.USER.EVENTS.FORMAT(paramUserName), diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 693d6c7d27..5a94dc6269 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -82,7 +82,7 @@ export const router = createBrowserRouter([ index: true, element: , }, - { + (process.env.UI_VERSION === 'factory' || process.env.UI_VERSION === 'sky') && { path: ROUTES.PROJECT.DETAILS.EVENTS.TEMPLATE, element: , }, @@ -102,7 +102,7 @@ export const router = createBrowserRouter([ path: ROUTES.PROJECT.GATEWAY.EDIT.TEMPLATE, element: , }, - ], + ].filter(Boolean) as RouteObject[], }, { path: ROUTES.PROJECT.DETAILS.RUNS.DETAILS.TEMPLATE, @@ -191,11 +191,13 @@ export const router = createBrowserRouter([ element: , }, - // Events - { - path: ROUTES.EVENTS.LIST, - element: , - }, + // Events, Factory and Sky only + ...([ + (process.env.UI_VERSION === 'factory' || process.env.UI_VERSION === 'sky') && { + path: ROUTES.EVENTS.LIST, + element: , + }, + ].filter(Boolean) as RouteObject[]), // Fleets { @@ -302,7 +304,7 @@ export const router = createBrowserRouter([ path: ROUTES.USER.PROJECTS.TEMPLATE, element: , }, - { + (process.env.UI_VERSION === 'factory' || process.env.UI_VERSION === 'sky') && { path: ROUTES.USER.EVENTS.TEMPLATE, element: , }, diff --git a/frontend/src/types/global.d.ts b/frontend/src/types/global.d.ts index e690164739..5a999b0124 100644 --- a/frontend/src/types/global.d.ts +++ b/frontend/src/types/global.d.ts @@ -23,7 +23,7 @@ declare interface HashMap { declare namespace NodeJS { interface ProcessEnv { readonly NODE_ENV: 'development' | 'production' | 'test'; - readonly UI_VERSION: 'sky' | 'enterprise'; + readonly UI_VERSION: 'sky' | 'factory' | 'oss'; readonly PUBLIC_URL: string; readonly API_URL: string; } diff --git a/frontend/webpack/env.js b/frontend/webpack/env.js index 6b6e854d4b..02940787b9 100644 --- a/frontend/webpack/env.js +++ b/frontend/webpack/env.js @@ -15,9 +15,9 @@ const buildDir = join(__dirname, '../build'); const publicDir = join(__dirname, '../public'); const apiUrl = process.env.API_URL || apiURLs; const publicUrl = process.env.PUBLIC_URL || publicURLs; -const uiVersion = process.env.UI_VERSION === 'sky' ? 'sky' : 'enterprise'; +const uiVersion = ['sky', 'factory'].includes(process.env.UI_VERSION) ? process.env.UI_VERSION : 'oss'; -const title = uiVersion === 'enterprise' ? 'dstack' : 'dstack Sky'; +const title = uiVersion === 'sky' ? 'dstack Sky' : 'dstack'; const description = 'Get GPUs at the best prices and availability from a wide range of providers. No cloud ' + 'account of your own is required.\n'; diff --git a/mkdocs/assets/stylesheets/cloudscape-docs.css b/mkdocs/assets/stylesheets/cloudscape-docs.css index 298a0f0ee7..b8cb27ad8d 100644 --- a/mkdocs/assets/stylesheets/cloudscape-docs.css +++ b/mkdocs/assets/stylesheets/cloudscape-docs.css @@ -275,7 +275,7 @@ } /* "Get started" header dropdown — mirrors the landing's Products popup (featured open-source + - dstack Sky + Enterprise). Pure-CSS hover/focus-within (the docs are static); right-aligned so the + dstack Sky + Factory). Pure-CSS hover/focus-within (the docs are static); right-aligned so the panel never overflows the header's right edge. Reuses the --cs-* tokens (defined in extra.css). */ .cs-gs-menu { position: relative; @@ -303,10 +303,10 @@ z-index: 1000; width: 360px; padding: 0; - background: var(--cs-bg); - border: 1px solid var(--cs-text); - border-radius: 12px; - box-shadow: 0 12px 34px rgba(0, 0, 0, 0.14); + /* A floating popup can't show a see-through hole between the groups (arbitrary page content + sits behind it) — back the rail with the unselected-row color so the gap reads as ground. */ + background: var(--cs-panel); + border-radius: 8px; opacity: 0; visibility: hidden; transform: translateY(-4px); @@ -317,88 +317,109 @@ visibility: visible; transform: translateY(0); } -/* Text inside the popup must wrap — Material forces white-space:nowrap on header links/buttons. */ +/* No hole at all in the popup: the groups sit flush, like one continuous menu. */ +.cs-gs-menu__popup .gs-opt + .gs-rail__group { + margin-top: 0; +} +/* Text inside the popup must wrap — Material forces white-space:nowrap on header links/buttons + (the group bars keep their own nowrap). */ .cs-gs-menu__popup, -.cs-gs-menu__feat, -.cs-gs-menu__row, -.cs-gs-menu__feat *, -.cs-gs-menu__row * { +.cs-gs-menu__popup .gs-opt, +.cs-gs-menu__popup .gs-opt * { white-space: normal !important; } -.cs-gs-menu__feat { - position: relative; + +/* The switcher rail — copied pixel-for-pixel from the landing (website/src/styles.css, .gs-rail + block): grouped rows under solid bars, with a white indicator card that slides to the hovered + row. The docs and the landing are separate builds, so the styles can't be shared — any change + to the landing's rail must be mirrored here (and vice versa). */ +.gs-rail { display: flex; - align-items: flex-start; - gap: 12px; - padding: 11px 10px; - border-radius: 10px; - background: var(--cs-text); - color: var(--cs-bg); - text-decoration: none; + flex-direction: column; + padding: 0; + position: relative; /* anchors the sliding selection indicator */ + border-radius: 8px; + overflow: hidden; /* clip the full-width group bars to the rounded corners */ + background: transparent; /* the rows carry the panel ground, so the gap between groups is a true hole */ } -.cs-gs-menu__feat-body { - flex: 1 1 auto; - min-width: 0; +.gs-rail__indicator { + position: absolute; + left: 0; + right: 0; + z-index: 2; /* above the rows' ground, beneath their content (3) and the bars (4) */ + background: var(--cs-bg); + border: 1px solid var(--cs-text); + border-radius: 8px; + pointer-events: none; } -/* Left column: icon tile with the live star count centered beneath it (mirrors the landing). */ -.cs-gs-menu__feat-iccol { +/* No z-index on the rows: a row with its own stacking context would trap its content beneath + the indicator card, so the content is lifted above the card explicitly instead. */ +.gs-opt__icwrap, +.gs-opt__ic, +.gs-opt__body { + position: relative; + z-index: 3; +} +.gs-rail a.gs-opt { + color: var(--cs-text); + text-decoration: none; +} +.gs-opt { + position: relative; display: flex; - flex-direction: column; - align-items: center; - gap: 6px; - flex: 0 0 auto; + align-items: flex-start; + width: 100%; + border-radius: 8px; + background: var(--cs-panel); /* each row paints its own ground (the rail is transparent) */ + font: inherit; + line-height: 1.15; /* pinned (not inherited) so the rail matches the landing exactly */ + text-align: left; + cursor: pointer; } -.cs-gs-menu__gh { - color: color-mix(in srgb, var(--cs-bg) 88%, transparent); - font-size: 12px; - font-weight: 300; - font-variant-numeric: tabular-nums; +/* Adjacent rows inside a group square off their shared edge, so the group's ground reads as one + block (rounded corners there would open see-through notches between the rows). */ +.gs-opt + .gs-opt { + border-top-left-radius: 0; + border-top-right-radius: 0; } -.cs-gs-menu__feat-ic { - display: inline-flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; - width: 34px; - height: 34px; - border-radius: 9px; - border: 0.5px solid color-mix(in srgb, var(--cs-bg) 50%, transparent); - color: var(--cs-bg); +.gs-opt:has(+ .gs-opt) { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } -.cs-gs-menu__feat-ic svg { - width: 18px; - height: 18px; +.gs-opt--feat { + padding: 13px 14px; + gap: 14px; } -.cs-gs-menu__feat-name { +.gs-opt--row { + padding: 13px 14px; + gap: 12px; +} +.gs-opt__name { display: block; - color: var(--cs-bg); font-size: 16px; - font-weight: 600; + font-weight: 700; } -.cs-gs-menu__feat-desc { +.gs-opt__desc { display: block; - margin-top: 8px; - color: color-mix(in srgb, var(--cs-bg) 88%, transparent); + margin-top: 4px; font-size: 13px; line-height: 1.5; + color: color-mix(in srgb, var(--cs-text) 68%, var(--cs-muted)); } -.cs-gs-menu__list { +.gs-opt__icwrap { display: flex; flex-direction: column; + align-items: center; + gap: 6px; + flex: 0 0 auto; } -.cs-gs-menu__row { - display: flex; - align-items: flex-start; - gap: 12px; - padding: 11px 10px; - border-radius: 9px; - color: var(--cs-text); - text-decoration: none; -} -.cs-gs-menu__row:hover { - background: var(--cs-hover); +.gs-opt__stars { + font-size: 12px; + font-weight: 300; + font-variant-numeric: tabular-nums; + color: inherit; } -.cs-gs-menu__ic { +.gs-opt__ic { display: inline-flex; align-items: center; justify-content: center; @@ -406,40 +427,61 @@ width: 34px; height: 34px; border-radius: 9px; - border: 0.5px solid var(--cs-text); - color: var(--cs-text); + border: 1px solid var(--cs-border); + color: inherit; } -.cs-gs-menu__ic svg { +.gs-opt__ic svg { width: 20px; height: 20px; } -.cs-gs-menu__rbody { +.gs-opt__body { flex: 1 1 auto; min-width: 0; } -.cs-gs-menu__name { - display: inline-flex; - align-items: center; - gap: 5px; - color: var(--cs-text); - font-size: 15px; - font-weight: 600; +/* Group bars ("Self-hosted" / "Hosted by us"): thin solid segments that hug the row beneath. */ +.gs-rail__group { + position: relative; + z-index: 4; + background: var(--cs-text); + color: var(--cs-bg); + font-size: 11px; + font-weight: 400; + line-height: 1; + padding: 5px 16px 5px 60.5px; /* left-aligns the label with the row titles */ + border-radius: 8px 8px 0 0; + white-space: nowrap; + margin-bottom: -2px; /* tuck under the next row past any fractional-pixel rounding */ } -.cs-gs-menu__ext { - flex: 0 0 auto; - /* Shared Cloudscape external-link icon (same as footer / GitHub / dstack Sky buttons). */ - width: 0.9em; - height: 0.9em; - background-color: currentColor; - -webkit-mask: var(--cs-ext-icon) center / contain no-repeat; - mask: var(--cs-ext-icon) center / contain no-repeat; +/* A group bar that follows a row stands off from it, so the groups read as separate + blocks inside the rail (keyed on adjacency, not :first-child — the indicator div + is the rail's first DOM child). */ +.gs-opt + .gs-rail__group { + margin-top: 10px; } -.cs-gs-menu__desc { - display: block; - margin-top: 3px; - color: var(--cs-muted); - font-size: 13px; - line-height: 1.5; +.gs-rail__group:first-child { + border-radius: 8px 8px 0 0; /* flush with the borderless rail's 8px clip */ +} +/* A black top border bridges the bar/row boundary in the middle span; inverted-radius notches + fill the space outside the row's top corners so the bar hugs them without hairlines. */ +.gs-rail__group + .gs-opt { + border-top: 1px solid var(--cs-text); +} +.gs-rail__group + .gs-opt::before, +.gs-rail__group + .gs-opt::after { + content: ''; + position: absolute; + top: -1.5px; + width: 9px; + height: 9.5px; + pointer-events: none; +} +.gs-rail__group + .gs-opt::before { + left: -1px; + background: radial-gradient(circle at 100% 100%, transparent 8px, var(--cs-text) 8.7px); +} +.gs-rail__group + .gs-opt::after { + right: -1px; + background: radial-gradient(circle at 0 100%, transparent 8px, var(--cs-text) 8.7px); } /* On tablet/mobile the header buttons collapse; keep the dropdown from overflowing tiny screens. */ @media (max-width: 76.1875em) { diff --git a/mkdocs/docs/concepts/events.md b/mkdocs/docs/concepts/events.md index d4057715a2..d70133fcc1 100644 --- a/mkdocs/docs/concepts/events.md +++ b/mkdocs/docs/concepts/events.md @@ -22,25 +22,13 @@ Events are accessible through the UI, CLI, and API. ## UI -The UI allows you to query events either globally on the dedicated `Events` page or within a specific group on the page of a run, job, fleet, and other resources. - -### Global page - -The global page shows events from all projects that the user has access to and allows filtering by many fields. - -![](https://dstack.ai/static-assets/static-assets/images/dstack-ui-events-global.png){ width=800 } - -This page allows you to query events targeting a specific resource or within a particular group. - -### Resource page - -The resource page shows events within that specific group. For example, if you open a run and switch to the `Events` tab, you will see all events about that run and its jobs. +In the UI, the `Events` tab is included on the run, job, fleet, and instance pages. It shows events within that specific group. For example, if you open a run and switch to the `Events` tab, you will see all events about that run and its jobs. ![](https://dstack.ai/static-assets/static-assets/images/dstack-ui-events-run.png){ width=800 } ## CLI -To query events via the CLI, use the `dstack event` command. This command provides several arguments that allow filtering by target and within scopes. +To query events via the CLI, use the `dstack event` command. Here is an example of querying all events within a particular run: @@ -66,9 +54,17 @@ $ dstack event --within-run cursor
    -To see all supported arguments, check the [reference](../reference/cli/dstack/event.md). +If you invoke the command without arguments, it shows the last events targeting resources in the current project. + +The command supports arguments for narrowing down the output: -If you invoke the command without arguments, it will include all events targeting resources in the project. +* `--target-fleet`, `--target-run`, `--target-volume`, `--target-gateway`, and `--target-secret` only show events that target the specified resource itself. +* `--within-fleet`, `--within-run`, and `--within-gateway` also include events about the resources it contains: instances within a fleet, jobs within a run, and replicas within a gateway. +* `--include-target-type` only shows events that target entities of the specified types, e.g. `job` or `instance`. +* `--since` only shows events newer than the specified duration (e.g. `10s`, `5m`, `1d`) or RFC 3339 timestamp. If not specified, the last 100 events are shown. +* `-w` (`--watch`) streams new events in realtime as they are recorded. + +To see all supported arguments, check the [reference](../reference/cli/dstack/event.md). ## TTL diff --git a/mkdocs/docs/concepts/presets.md b/mkdocs/docs/concepts/presets.md index b48b7d0641..db77a3048f 100644 --- a/mkdocs/docs/concepts/presets.md +++ b/mkdocs/docs/concepts/presets.md @@ -242,7 +242,7 @@ Pushing the same name again moves the name to the new preset. The previous one s Presets are pushed to and pulled from the registry hosted at [dstack Sky](https://sky.dstack.ai). To share a preset, create a project there, add the people you want to share it with, and push the preset to that project. To push or pull a preset from a project, you have to be its member. -A self-hosted registry is part of [dstack Enterprise](https://calendly.com/dstackai/discovery-call){ target="_blank" }. +A self-hosted registry is part of [dstack Factory](https://calendly.com/dstackai/discovery-call){ target="_blank" }. ## Export a preset diff --git a/mkdocs/overrides/header-2.html b/mkdocs/overrides/header-2.html index f1e6bbd5ff..ab688ef45e 100644 --- a/mkdocs/overrides/header-2.html +++ b/mkdocs/overrides/header-2.html @@ -1,6 +1,3 @@ -{#- - This file was automatically generated - do not edit --#} {% set class = "md-header" %} {% if "navigation.tabs.sticky" in features %} {% set class = class ~ " md-header--shadow md-header--lifted" %} @@ -101,8 +98,9 @@ GitHub - {# "Get started" dropdown — mirrors the landing's Products popup (featured open-source + - dstack Sky + Enterprise). Pure-CSS hover/focus-within (the site is static), opens below, + {# "Get started" dropdown — the landing's product switcher rail as a popup. The product + descriptions are duplicated in website/src/components/SiteNavigation.tsx (products) and + website/src/pages/Home/GetStartedSection.tsx — keep all three in sync. Opens below, right-aligned so it never overflows the header's right edge. #}