Skip to content

Commit

Permalink
Add <FileTree> component (#1308)
Browse files Browse the repository at this point in the history
* feat: add basic file tree rehype processor

* feat: add basic file tree component

* feat: add basic file icon support

* feat: folder icon

* feat: file icon

* feat: port old icons and add new ones

* refactor: simplify partial definitions

* chore: add demo page

* feat: add localized directory label

* feat: validation

* test: add file tree tests

* docs: update demo

* refactor: tweak error messsages

* docs: add file tree component documentation

* docs: update i18n ui strings

* docs: replace file tree component

* refactor: component name

* chore: add changesets

* feat: add file icons generator package

* docs: revert docs file tree component replacements

* chore: update icons changeset

* chore: clean file icons generator `package.json` file

* chore: ignore file icons generator in changeset configuration

* fix: add `fileTree.directory` ui string for `zh-TW`

* feat: prefix all Seti icons with `seti:`

* feat: make Seti repo branch configurable

* fix: improve file tree component errors wording

* fix: udpate astro icon

* docs: move components icon section to the bottom of the page

* fix: add any point wrapping to icons list in the docs

An icon name like `seti:crystal_embedded` would overflow.

* chore: update changesets

* fix: add prefix support to aliases

* chore: slim down changeset

Co-authored-by: Chris Swithinbank <357379+delucis@users.noreply.github.com>

* docs: apply suggested improvements

Co-authored-by: Chris Swithinbank <357379+delucis@users.noreply.github.com>

* fix: update error snapshots

* feat: add `mdx` icon and mapping

* feat: add Markdown backticks in error messages

Co-authored-by: Chris Swithinbank <357379+delucis@users.noreply.github.com>

* fix: update file tree validation error documentation anchor

* chore: remove file tree demo page

* refactor: use rehype in fragment mode

Co-authored-by: Chris Swithinbank <357379+delucis@users.noreply.github.com>

---------

Co-authored-by: Chris Swithinbank <357379+delucis@users.noreply.github.com>
Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
  • Loading branch information
3 people authored Mar 1, 2024
1 parent 5f99a71 commit 9a918a5
Show file tree
Hide file tree
Showing 51 changed files with 2,238 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["starlight-docs", "@example/*"],
"ignore": ["starlight-docs", "@example/*", "starlight-file-icons-generator"],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
}
Expand Down
5 changes: 5 additions & 0 deletions .changeset/hot-paws-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/starlight': minor
---

Adds `<FileTree>` component to display the structure of a directory.
5 changes: 5 additions & 0 deletions .changeset/mighty-adults-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/starlight': minor
---

Adds 144 new file-type icons from the [Seti UI icon set](https://github.com/jesseweed/seti-ui#current-icons), available with the `seti:` prefix, e.g. `seti:javascript`.
5 changes: 5 additions & 0 deletions .changeset/smart-planets-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/starlight': minor
---

Adds 5 new icons: `astro`, `biome`, `bun`, `mdx`, and `pnpm`.
1 change: 1 addition & 0 deletions docs/src/components/icons-list.astro
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const icons = Object.keys(Icons) as (keyof typeof Icons)[];
font-size: var(--sl-text-sm);
gap: 0.25rem;
margin: 0;
overflow-wrap: anywhere;
padding: 0.75rem;
background: none;
}
Expand Down
103 changes: 76 additions & 27 deletions docs/src/content/docs/guides/components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -233,33 +233,6 @@ Other content is also supported in asides.
Starlight also provides a custom syntax for rendering asides in Markdown and MDX as an alternative to the `<Aside>` component.
See the [“Authoring Content in Markdown”](/guides/authoring-content/#asides) guide for details of the custom syntax.

### Icon

import { Icon } from '@astrojs/starlight/components';
import IconsList from '~/components/icons-list.astro';

Starlight provides a set of common icons that you can display in your content using the `<Icon>` component.

Each `<Icon>` requires a [`name`](#all-icons) and can optionally include a `label`, `size`, and `color` attribute.

```mdx
# src/content/docs/example.mdx

import { Icon } from '@astrojs/starlight/components';

<Icon name="star" color="goldenrod" size="2rem" />
```

The code above generates the following on the page:

<Icon name="star" color="goldenrod" size="2rem" />

#### All icons

A list of all available icons is shown below with their associated names. Click an icon to copy the component code for it.

<IconsList />

### Code

Use the `<Code>` component to render syntax highlighted code when using a [Markdown code block](/guides/authoring-content/#code-blocks) is not possible, for example, to render data coming from external sources like files, databases, or APIs.
Expand Down Expand Up @@ -305,3 +278,79 @@ The code above generates the following on the page:
import importedCode from '/src/env.d.ts?raw';

<Code code={importedCode} lang="ts" title="src/env.d.ts" />

### File Tree

Use the `<FileTree>` component to display the structure of a directory with file icons and collapsible sub-directories.

Specify the structure of your files and directories with an [unordered Markdown list](https://www.markdownguide.org/basic-syntax/#unordered-lists) inside `<FileTree>`.
Create a sub-directory using a nested list or add a `/` to the end of a list item to render it as a directory without specific content.

The following syntax can be used to customize the appearance of the file tree:

- Highlight a file or directory by making its name bold, e.g. `**README.md**`.
- Add a comment to a file or directory by adding more text after the name.
- Add placeholder files and directories by using either `...` or `` as the name.

```mdx
# src/content/docs/example.mdx

import { FileTree } from '@astrojs/starlight/components';

<FileTree>

- astro.config.mjs an **important** file
- package.json
- README.md
- src
- components
- **Header.astro**
-
- pages/

</FileTree>
```

The above code generates the following on the page:

import { FileTree } from '@astrojs/starlight/components';

<FileTree>

- astro.config.mjs an **important** file
- package.json
- README.md
- src
- components
- **Header.astro**
-
- pages/

</FileTree>

### Icon

import { Icon } from '@astrojs/starlight/components';
import IconsList from '~/components/icons-list.astro';

Starlight provides a set of common icons that you can display in your content using the `<Icon>` component.

Each `<Icon>` requires a [`name`](#all-icons) and can optionally include a `label`, `size`, and `color` attribute.

```mdx
# src/content/docs/example.mdx

import { Icon } from '@astrojs/starlight/components';

<Icon name="star" color="goldenrod" size="2rem" />
```

The code above generates the following on the page:

<Icon name="star" color="goldenrod" size="2rem" />

#### All icons

A list of all available icons is shown below with their associated names. Click an icon to copy the component code for it.

<IconsList />
3 changes: 2 additions & 1 deletion docs/src/content/docs/guides/i18n.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ You can provide translations for additional languages you support — or overrid
"aside.note": "Note",
"aside.tip": "Tip",
"aside.caution": "Caution",
"aside.danger": "Danger"
"aside.danger": "Danger",
"fileTree.directory": "Directory"
}
```
Expand Down
93 changes: 93 additions & 0 deletions packages/file-icons-generator/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type { Definitions } from '../starlight/user-components/rehype-file-tree';

export const seti = {
/** The GitHub repository containing the Seti UI theme using the `username/repo` format. */
repo: 'jesseweed/seti-ui',
/** The repository branch to use. */
branch: 'master',
/** The path to the icon mapping file in the repository. */
mapping: 'styles/components/icons/mapping.less',
/** The path to the icon font file in the repository. */
font: 'styles/_fonts/seti/seti.woff',
/**
* Some Seti UI icons share identical SVG for multiple icons. When converted to a font, identical
* SVGs will be saved as one unique glyph with its name being the first icon encountered. The
* other names are lost in the process and only their associated unicode character will be kept.
* As we want to access icons by their name, we need to manually map the names of identical SVGs
* to the icon name that is embedded in the font glyph.
* Note that when this happens, an error will be thrown indicating which icon is affected.
*/
aliases: {
coffee: 'cjsx',
html_erb: 'html',
less: 'json',
},
/**
* Seti UI icons can be overriden to use another Seti UI icon.
* The key is the icon name to override and the value is the Seti icon name to use instead.
* The reason to support aliasing to another Seti UI icon is that some Seti UI icons can be
* almost identical. For example, the `npm_ignored` icon used for `npm-debug.log` files is
* identical to the `npm` icon except the npm logo is shifted 1px to the right. There is no need
* to provide a separate icon for this case and we can just use the `npm` icon instead.
*/
overrides: {
ejs: 'html',
go: 'go2',
npm_ignored: 'npm',
},
/**
* Allows for renaming Seti UI icons to another name.
* For example, the Visual Studio Code icon used for Go is named `go2` and this is not the name
* we want to expose to the user.
* Note that renaming an icon happens after overrides are applied.
*/
renames: {
go2: 'go',
},
/**
* A list of Seti UI icons to ignore.
* Each entry should be commented with the reason why the icon is ignored.
*/
ignores: [
// The ReasonML SVG icon contains a path issue that makes it render a plain square instead of a
// square with the "RE" letters when converted to a font.
// This is also an issue in Visual Studio Code but considering that in Starlight, all the icons
// are also available with the `<Icon>` component, it's better to ignore it for now instead of
// providing an icon with no meaning.
'reasonml',
],
};

export const starlight = {
/** The path of the generated file in the Starlight package directory. */
output: 'user-components/file-tree-icons.ts',
/** A prefix to add to the Seti icon names. */
prefix: 'seti:',
/**
* The Starlight `<Icon>` component viewBox size.
* @see {@link file://../starlight/user-components/Icon.astro}
*/
iconViewBoxSize: 24,
/**
* Extra definitions for the `<FileTree>` component that add mappings using built-in Starlight
* icons.
*/
definitions: {
files: {
'astro.config.js': 'astro',
'astro.config.mjs': 'astro',
'astro.config.cjs': 'astro',
'astro.config.ts': 'astro',
'pnpm-debug.log': 'pnpm',
'pnpm-lock.yaml': 'pnpm',
'pnpm-workspace.yaml': 'pnpm',
'biome.json': 'biome',
'bun.lockb': 'bun',
},
extensions: {
'.astro': 'astro',
'.mdx': 'mdx',
},
partials: {},
} satisfies Definitions,
};
24 changes: 24 additions & 0 deletions packages/file-icons-generator/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { writeDefinitionsAndSVGs } from './utils/file';
import { getIconSvgPaths } from './utils/font';
import { fetchFont, fetchMapping, parseMapping } from './utils/seti';

/**
* Script generating definitions used by the Starlight `<FileTree>` component and associated SVGs.
*
* To do so, it fetches the Seti UI icon mapping file and font from GitHub, parses the mapping to
* generate the definitions and a list of icons to extract as SVGs, and finally extracts the SVGs
* from the font and writes the definitions and SVGs to the Starlight package in a file ready to be
* consumed by Starlight.
*
* @see {@link file://./config.ts} for the configuration used by this script.
* @see {@link file://../starlight/user-components/file-tree-icons.ts} for the generated file.
* @see {@link https://opentype.js.org/glyph-inspector.html} for a font glyph inspector.
*/

const mapping = await fetchMapping();
const { definitions, icons } = parseMapping(mapping);

const font = await fetchFont();
const svgPaths = getIconSvgPaths(icons, definitions, font);

await writeDefinitionsAndSVGs(definitions, svgPaths);
18 changes: 18 additions & 0 deletions packages/file-icons-generator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "starlight-file-icons-generator",
"version": "0.1.0",
"description": "Generates Starlight file icons based on the Seti UI theme",
"private": true,
"scripts": {
"build": "tsx ."
},
"license": "MIT",
"dependencies": {
"opentype.js": "^1.3.4",
"tsx": "^4.7.1"
},
"devDependencies": {
"@types/opentype.js": "^1.3.8"
},
"type": "module"
}
81 changes: 81 additions & 0 deletions packages/file-icons-generator/utils/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { spawn } from 'node:child_process';
import fs from 'node:fs/promises';
import path from 'node:path';
import { starlight } from '../config';
import type { Definitions } from '../../starlight/user-components/rehype-file-tree';

const copyrightNotice = `/**
* Based on https://github.com/elviswolcott/seti-icons which
* is derived from https://github.com/jesseweed/seti-ui/
*
* Copyright (c) 2014 Jesse Weed
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/`;

const generatedFileHeader = `/**
* This file was generated by the \`file-icons-generator\` package.
* Do not edit this file directly as it will be overwritten.
*/`;

/** Write the generated definitions and SVGs to the Starlight package. */
export async function writeDefinitionsAndSVGs(
definitions: Definitions,
svgPaths: Record<string, string>
) {
const content = `${generatedFileHeader}
import type { Definitions } from './rehype-file-tree.ts';
${copyrightNotice}
export const definitions: Definitions = ${JSON.stringify(definitions)};
export const FileIcons = ${JSON.stringify(svgPaths)};
`;

const filePath = path.join('..', 'starlight', starlight.output);

await fs.writeFile(filePath, content);

await prettifyFile(path.resolve(filePath));
}

/** Run Prettier on a generated file. */
function prettifyFile(filePath: string) {
return new Promise<void>((resolve, reject) => {
const child = spawn('pnpm', ['prettier', '-w', filePath], {
cwd: '../..',
stdio: [],
});

const error = new Error('Failed to run Prettier on the generated file.');

child.on('error', () => reject(error));
child.on('close', (code) => {
if (code !== 0) {
reject(error);

return;
}

resolve();
});
});
}
Loading

0 comments on commit 9a918a5

Please sign in to comment.