@master/internal-site 0.1.0 → 0.1.2

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
@@ -23,6 +23,8 @@ export default defineConfig({ integrations: [masterSite(site)] })
23
23
 
24
24
  `site.config.ts` exports `defineSite(...)` from `@master/internal-site/config`. It declares the site's origin, content directory, app module, locale policy and section module URLs. Optional document layouts, category sources, styles and a server-only highlighter are also URLs. Keep route content lazy: do not import a complete page or specimen catalog into the configuration.
25
25
 
26
+ Optional `header.communityNavs` selects the links shown beside search without removing community links from the compact menu or footer. Set `header.searchOnHome` to show search in the desktop homepage header. When available, the language switch follows the community links and a divider.
27
+
26
28
  The app module exports its logo component, navigation, version links and source/feedback URLs using the `SiteApp` contract. Consumers retain their own content and homepage composition. Ordinary documents use `content/<section>/<slug>/content.mdx` and `metadata.ts`; a local `page.astro` can override the generated entry. Dynamic routes enumerate params from lightweight `params.ts` files. The integration owns generated routes, page asset collection and the Astro/Svelte renderer setup.
27
29
 
28
30
  ```css
@@ -65,3 +67,14 @@ The CSS-specific subpaths require the matching optional compiler, tooling, schem
65
67
  From the Master repository, run `bun run build:packages`, `bun run --cwd packages/internal-site type-check` and `bun run --cwd packages/internal-site test`. Astro checks `.astro` implementations and Svelte checks interactive components separately. The root `build` builds the Node entrypoints before the site.
66
68
 
67
69
  Before publishing, inspect the tarball, install it in the Master CSS consumer and validate both sites, including local CSS imports, themes, mobile navigation and lazy page styles. Publish an immutable version, update exact consumer pins and lockfiles, then deploy preview builds. Roll back by restoring the previous dependency version and deployment; do not unpublish a release in use.
70
+
71
+
72
+ ## Publishing
73
+
74
+ Master's `publish.yml` workflow validates the site and packages, retains the
75
+ packed archives, then publishes those exact bytes through npm Trusted Publishing.
76
+ The trusted publisher is GitHub repository `master-co/master`, workflow
77
+ `publish.yml`, with no GitHub environment. Versions are committed before dispatch;
78
+ consumers upgrade exact pins after registry integrity verification. No npm token
79
+ is stored in GitHub. Master remains private, so these releases use OIDC
80
+ without public provenance attestations.
package/dist/config.d.mts CHANGED
@@ -13,6 +13,11 @@ export interface SiteApp {
13
13
  Logotype: Component<any>;
14
14
  navs: AppNavItem[];
15
15
  communityNavs?: AppNavItem[];
16
+ /** Header controls can be quieter than the full community menu and footer. */
17
+ header?: {
18
+ communityNavs?: AppNavItem[];
19
+ searchOnHome?: boolean;
20
+ };
16
21
  sourceBaseURL?: string;
17
22
  feedbackURL?: string;
18
23
  versions: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@master/internal-site",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "First-party Astro and Svelte website framework for Master sites.",
6
6
  "engines": {
package/src/config.ts CHANGED
@@ -15,6 +15,8 @@ export interface SiteApp {
15
15
  Logotype: Component<any>
16
16
  navs: AppNavItem[]
17
17
  communityNavs?: AppNavItem[]
18
+ /** Header controls can be quieter than the full community menu and footer. */
19
+ header?: { communityNavs?: AppNavItem[], searchOnHome?: boolean }
18
20
  sourceBaseURL?: string
19
21
  feedbackURL?: string
20
22
  versions: { name: string, href: string }[]
@@ -21,6 +21,7 @@
21
21
  import HeaderNav from './HeaderNav.svelte'
22
22
  import LanguageButton from './LanguageButton.svelte'
23
23
  import IconChevronDown from '@tabler/icons-svelte-runes/icons/chevron-down'
24
+ import IconArrowUpRight from '@tabler/icons-svelte-runes/icons/arrow-up-right'
24
25
  import IconSearch from '@tabler/icons-svelte-runes/icons/search'
25
26
  import DocVersionSelect from './DocVersionSelect.svelte'
26
27
  import Link from './Link.svelte'
@@ -31,9 +32,11 @@
31
32
  import { getSite } from '../site'
32
33
 
33
34
  let { class: className, stickable, transitionAtTop, documentNavigation, documentNavigationTitle }: DocHeaderProps = $props()
34
- const { t, locale, pathname } = getSite()
35
+ const { t, locale, pathname, i18n } = getSite()
35
36
  const tw = locale === 'tw'
36
37
  const home = pathname === (tw ? '/tw' : '/')
38
+ const headerCommunityNavs = app.header?.communityNavs ?? app.communityNavs ?? []
39
+ const showLanguage = i18n.locales.length > 1
37
40
  const version = import.meta.env.PUBLIC_VERSION ? `v${import.meta.env.PUBLIC_VERSION}` : ''
38
41
  </script>
39
42
 
@@ -60,13 +63,13 @@
60
63
  {#if eachLink.date && isDateWithinSevenDays(eachLink.date)}<small>{t('New')}</small>{/if}
61
64
  </HeaderNav>{/each}
62
65
  </nav>
63
- <div class="ui-header-end">
64
- <!-- Search leads the end group as a quiet pill: the glass and its shortcut. The homepage leaves it to the compact header. -->
65
- <SearchButton class="ui-header-search" data-compact={home ? '' : undefined}>
66
+ <div class="ui-header-end" data-home-controls={app.header?.searchOnHome ? 'full' : undefined}>
67
+ <!-- Search leads the end group as a quiet pill: the glass and its shortcut. Homepages may opt into the same desktop search control. -->
68
+ <SearchButton class="ui-header-search" data-compact={home && !app.header?.searchOnHome ? '' : undefined}>
66
69
  <IconSearch size={16} stroke={1.5} aria-hidden="true" />
67
70
  <kbd class="ui-kbd" aria-hidden="true">⌘K</kbd>
68
71
  </SearchButton>
69
- {#each app.communityNavs ?? [] as { Icon, disabled, fullName, ...eachLink } (eachLink.name)}
72
+ {#each headerCommunityNavs as { Icon, disabled, fullName, ...eachLink } (eachLink.name)}
70
73
  <Link
71
74
  {...eachLink}
72
75
  aria-label={fullName || eachLink.name}
@@ -78,10 +81,13 @@
78
81
  {disabled}
79
82
  >
80
83
  {#if Icon}<Icon size={18} stroke={1.5} aria-hidden="true" />{/if}
84
+ {#if eachLink.href?.startsWith('http')}<IconArrowUpRight class="ui-header-external" size={10} stroke={1.5} aria-hidden="true" />{/if}
81
85
  <span class="ui-tooltip" data-side="bottom" aria-hidden="true">{fullName || eachLink.name}</span>
82
86
  </Link>
83
87
  {/each}
84
- {#if app.communityNavs?.length}<span class="ui-header-divider" aria-hidden="true"></span>{/if}
85
- <LanguageButton />
88
+ {#if showLanguage}
89
+ {#if headerCommunityNavs.length}<span class="ui-header-divider" aria-hidden="true"></span>{/if}
90
+ <LanguageButton />
91
+ {/if}
86
92
  </div>
87
93
  </Header>
@@ -72,6 +72,7 @@
72
72
  <nav class="ui-nav-group" aria-label={tw ? '主要導覽' : 'Main navigation'}>
73
73
  {#each app.navs as { Icon, disabled, fullName, ...link } (link.name)}<Link class="ui-nav-link" current="true" ambiguous {...link} {disabled} onclick={close}>
74
74
  {#if Icon}<Icon size={18} stroke={1.5} aria-hidden="true" />{/if}{t(fullName || link.name)}
75
+ {#if link.href?.startsWith('http')}<IconArrowUpRight size={14} stroke={1.5} aria-hidden="true" />{/if}
75
76
  </Link>{/each}
76
77
  </nav>
77
78
  {#if documentNavigation?.length}<details class="ui-disclosure" data-variant="rule" open>
@@ -23,7 +23,7 @@
23
23
  <script lang="ts">
24
24
  import '../../styles/docs-shell/footer.css'
25
25
  import clsx from 'clsx'
26
- import Link from './Link.svelte'
26
+ import FooterNavLink from './FooterLink.svelte'
27
27
  import LanguageSelect from './LanguageSelect.svelte'
28
28
  import ThemeSelect from './ThemeSelect.svelte'
29
29
  import { useThemeMode } from '../contexts/theme-mode.svelte'
@@ -53,7 +53,7 @@
53
53
  <ul>
54
54
  {#each group.links as link (link.href || link.name)}
55
55
  <li>
56
- <Link href={link.href} disabled={link.disabled}>{t(link.name)}</Link>
56
+ <FooterNavLink href={link.href} disabled={link.disabled}>{t(link.name)}</FooterNavLink>
57
57
  </li>
58
58
  {/each}
59
59
  </ul>
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+ import IconArrowUpRight from '@tabler/icons-svelte-runes/icons/arrow-up-right'
4
+ import Link from './Link.svelte'
5
+ import '../../styles/docs-shell/footer-link.css'
6
+
7
+ let { href, disabled, class: className, children }: {
8
+ href?: string
9
+ disabled?: boolean
10
+ class?: string
11
+ children?: Snippet
12
+ } = $props()
13
+ </script>
14
+
15
+ <Link {href} {disabled} class={className}>
16
+ <span>{@render children?.()}{#if href?.startsWith('http') && !disabled}<span class="ui-footer-link-mark" aria-hidden="true"><IconArrowUpRight size={10} stroke={1.5} /></span>{/if}</span>
17
+ </Link>
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte'
3
+ import IconArrowUpRight from '@tabler/icons-svelte-runes/icons/arrow-up-right'
3
4
  import Link from './Link.svelte'
4
5
 
5
6
  interface Props {
@@ -15,4 +16,5 @@
15
16
 
16
17
  <Link {...props} current="true" ambiguous>
17
18
  {@render children?.()}
19
+ {#if props.href?.startsWith('http')}<IconArrowUpRight class="ui-header-external" size={10} stroke={1.5} aria-hidden="true" />{/if}
18
20
  </Link>
@@ -6,7 +6,7 @@
6
6
  import IconMoon from '@tabler/icons-svelte-runes/icons/moon'
7
7
  import IconSun from '@tabler/icons-svelte-runes/icons/sun'
8
8
  import LanguageSelect from '@master/internal-site/components/LanguageSelect.svelte'
9
- import Link from '@master/internal-site/components/Link.svelte'
9
+ import FooterNavLink from '../docs-shell/components/FooterLink.svelte'
10
10
  import ThemeSelect from '@master/internal-site/components/ThemeSelect.svelte'
11
11
  import { useThemeMode } from '@master/internal-site/contexts/theme-mode.svelte'
12
12
  import { getSite } from '@master/internal-site/site'
@@ -32,7 +32,7 @@
32
32
  <ul class="home-footer-links">
33
33
  {#each group.links as link (link.href)}
34
34
  <li>
35
- <Link class="home-footer-link" href={link.href}>{t(link.name)}</Link>
35
+ <FooterNavLink class="home-footer-link" href={link.href} disabled={link.disabled}>{t(link.name)}</FooterNavLink>
36
36
  </li>
37
37
  {/each}
38
38
  </ul>
package/src/home/home.css CHANGED
@@ -81,10 +81,14 @@
81
81
  max-width: 95rem;
82
82
  }
83
83
 
84
- .home-header[data-background="transparent"] :is(.ui-header-nav, .ui-header-end > :not(a[aria-label="GitHub"])) {
84
+ .home-header[data-background="transparent"] :is(.ui-header-nav, .ui-header-end:not([data-home-controls="full"]) > :not(a[aria-label="GitHub"])) {
85
85
  display: none;
86
86
  }
87
87
 
88
+ @media (width < 64rem) {
89
+ .home-header[data-background="transparent"] .ui-header-end > :not(a[aria-label="GitHub"]) { display: none; }
90
+ }
91
+
88
92
  /* The compact header keeps the brand centred: GitHub takes the search button's end, as wide as the menu button. */
89
93
  .home-header[data-background="transparent"] .ui-header-end {
90
94
  justify-content: flex-end;
@@ -62,12 +62,12 @@
62
62
  .ui-pager > a { min-width: 0; overflow-wrap: anywhere; }
63
63
 
64
64
  /* The contents rail stands still beside the article; a long list scrolls inside it. */
65
- :is(.ui-docs-body > .ui-toc, .ui-docs-body > site-island > .ui-toc) {
65
+ :is(.ui-docs-body > .ui-toc, .ui-docs-body > astro-island > .ui-toc) {
66
66
  position: sticky; top: var(--docs-header); display: flex; flex: none; flex-direction: column; align-self: start; width: var(--docs-rail); max-height: calc(100dvh - var(--docs-header));
67
67
  padding: 3.5rem var(--ui-space-lg) var(--ui-space-xl) 0;
68
68
  }
69
- :is(.ui-docs-body > .ui-toc, .ui-docs-body > site-island > .ui-toc) > ol { flex: 0 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; }
70
- @media (width < 80rem) { :is(.ui-docs-body > .ui-toc, .ui-docs-body > site-island > .ui-toc) { display: none; } }
69
+ :is(.ui-docs-body > .ui-toc, .ui-docs-body > astro-island > .ui-toc) > ol { flex: 0 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; }
70
+ @media (width < 80rem) { :is(.ui-docs-body > .ui-toc, .ui-docs-body > astro-island > .ui-toc) { display: none; } }
71
71
  @container docs-main (width < 48rem) { .ui-toc[data-rail] { display: none; } }
72
72
 
73
73
  @media print {
@@ -0,0 +1,5 @@
1
+ @layer components {
2
+ /* Keep the raised mark with its text inside both inline and flex footer links. */
3
+ .ui-footer-link-mark { display: inline-block; width: .625rem; height: .625rem; margin-inline-start: .1875rem; vertical-align: .25em; line-height: 0; }
4
+ .ui-footer-link-mark > svg { display: block; width: 100%; height: 100%; }
5
+ }
@@ -22,6 +22,10 @@
22
22
  .ui-header .ui-version > svg { width: 12px; height: 12px; color: var(--ui-text-muted); }
23
23
  .ui-header .ui-version:hover { color: var(--ui-text-strong); }
24
24
  .ui-header-nav > a > .ui-badge { margin-left: var(--ui-space-2xs); }
25
+ /* External destinations carry a small raised arrow beside the label or icon. */
26
+ .ui-header-nav > a:has(.ui-header-external) { position: relative; padding-right: 1.125rem; }
27
+ .ui-header-nav > a > .ui-header-external, .ui-header-end > .ui-button > .ui-header-external { position: absolute; top: .375rem; right: .125rem; width: .625rem; height: .625rem; pointer-events: none; }
28
+ .ui-header-end > .ui-button > .ui-header-external { top: .125rem; right: 0; }
25
29
  .ui-header-end > .ui-button[data-disabled] { color: var(--ui-text-disabled); pointer-events: none; }
26
30
  /* The community and preference icons are words in the bar, not buttons: ink under the pointer, no material. */
27
31
  .ui-header-end > .ui-button[data-shape="icon"] { width: 2.25rem; min-height: 2.25rem; }