Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 10, 2025

Overview

Implements automatic favicon and Progressive Web App (PWA) icon generation from the logo defined in hyperbook.json. When building a Hyperbook project, if no favicon.ico exists and a logo is configured, a complete set of favicons and PWA assets are automatically generated using the favicons package as suggested by @mikebarkmin.

Changes

Core Implementation

  • Favicon Generation Helper (packages/hyperbook/helpers/generate-favicons.ts)

    • Uses dynamic imports to load the favicons package only when needed
    • Generates comprehensive icon set (60+ files):
      • favicon.ico (multi-size ICO format)
      • 9 Android icons (36x36 to 512x512)
      • 9 Apple touch icons (57x57 to 1024x1024)
      • 34 Apple startup images (for various iPhone/iPad sizes)
      • Web manifest with full PWA metadata
    • Handles errors gracefully with clear console messages
    • Rich Metadata Integration: Uses hyperbook.json configuration:
      • App name and description
      • Theme color from colors.brand (defaults to #007864)
      • Base path for proper routing scope
      • Language settings
      • Developer name and URL from author fields
  • Build Process Integration (packages/hyperbook/build.ts)

    • Checks if favicon.ico already exists in the output directory after copying public files
    • If not present and a logo is defined in hyperbook.json, generates complete icon set automatically
    • Smart Logo Path Resolution: Supports flexible logo placement with automatic detection across multiple locations:
      • For relative paths (e.g., logo.png): checks root folder → book folder → public folder
      • For absolute paths (e.g., /logo.png): checks book folder → public folder
    • Outputs all assets to {ASSETS_FOLDER}/favicons/ directory
    • Copies favicon.ico to root for backward compatibility
    • Skips generation for external URL logos (not applicable)
    • Provides user feedback: "Generating favicons from logo" → "Favicons generated successfully"
  • HTML Integration (packages/markdown/src/rehypeHtmlStructure.ts)

    • Adds <link rel="icon" type="image/x-icon" href="favicon.ico"> to all HTML pages
    • Adds <link rel="apple-touch-icon" sizes="180x180"> for Apple devices
    • Adds <link rel="manifest"> for Progressive Web App support
    • All links positioned in the <head> section before stylesheets
  • Release Documentation

    • Created changeset file (.changeset/favicon-generation.md) marking hyperbook for minor version bump and @hyperbook/markdown for patch version bump
    • Updated changelog (website/en/book/changelog.md) with feature description in v0.59.0

Technical Details

  • Added favicons@^7.2.0 as a runtime dependency (external dependency, not bundled)
  • Configured ncc bundler to exclude favicons and sharp (native dependency) from bundling to avoid platform-specific issues
    • Tested bundling approach: confirmed that Sharp's native binaries cannot be bundled and must remain as external dependencies
    • External dependencies work cross-platform (Linux, macOS, Windows) as Sharp's installer downloads the correct platform-specific binary during installation
  • Updated tsconfig.json to add "module": "commonjs" for dynamic import support
  • Updated pnpm-lock.yaml to reflect the new dependency
  • Updated test snapshots (9 files) to include the new favicon links

Behavior

When favicon exists in public folder:

  • Existing favicon is preserved and used
  • No automatic generation occurs

When favicon doesn't exist but logo is defined:

  • Complete icon set automatically generated during build (60+ files)
  • Searches for logo in multiple locations (root, book, and public folders)
  • Multiple formats and sizes created for optimal browser and device support
  • Web manifest generated with app metadata for PWA capabilities
  • Console shows generation progress

When neither favicon nor logo exists:

  • No favicon is generated
  • Build continues normally without errors

Testing

  • ✅ Verified with PNG logos
  • ✅ Verified with SVG logos
  • ✅ Verified existing favicons are preserved
  • ✅ Verified logo detection in root folder
  • ✅ Verified logo detection in book folder
  • ✅ Verified logo detection in public folder (deprecated)
  • ✅ Verified logo detection with absolute paths
  • ✅ Verified web manifest generation with full metadata
  • ✅ Verified theme color, scope, language, and author info in manifest
  • ✅ Tested bundling approach (confirmed external dependencies required)
  • ✅ All 90 markdown tests pass with updated snapshots
  • ✅ All 9 fs tests pass
  • ✅ Website build completes successfully
  • ✅ TypeScript linting passes

Example Output

[My Book] Copying public files: [1/1]

[My Book] Generating favicons from logo.
[My Book] Favicons generated successfully.

[My Book] Copying hyperbook assets: [1/52]

Generated files in __hyperbook_assets/favicons/ directory (60+ files):

  • favicon.ico (33KB, multi-size ICO format, also copied to root)
  • Android icons: 9 sizes (36x36 to 512x512)
  • Apple touch icons: 9 sizes (57x57 to 1024x1024)
  • Apple startup images: 34 sizes for various devices
  • manifest.webmanifest with full PWA metadata (theme color, scope, language, developer info)

Fixes #242

Original prompt

This section details on the original issue you should resolve

<issue_title>Generate favicon based on logo</issue_title>
<issue_description>Favicons should be automatically generated, if no favicon is present, using the logo defined in the hyperbook.json.</issue_description>

Comments on the Issue (you are @copilot in this section)

@mikebarkmin We should use this package https://www.npmjs.com/package/favicons

Fixes #242


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

@vercel
Copy link

vercel bot commented Oct 10, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
hyperbook-4fwa Ready Ready Preview Comment Oct 10, 2025 7:31am

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copilot AI changed the title [WIP] Generate favicon from logo in hyperbook.json Generate favicon based on logo Oct 10, 2025
Copilot AI requested a review from mikebarkmin October 10, 2025 05:08
@mikebarkmin
Copy link
Member

@copilot You did forget to update the pm lock file and keep in mind that the logo can also be placed in the books folder. Since the public folder is deprecated and now the public files should live along the book files. Maybe the logo could even live in the root folder.

So logo: test.png should check the root folder, the book folder and the public folder.

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Oct 10, 2025

@copilot You did forget to update the pm lock file and keep in mind that the logo can also be placed in the books folder. Since the public folder is deprecated and now the public files should live alo...

Updated the logo path resolution to check multiple locations:

For relative paths (e.g., logo.png):

  1. Root folder (where hyperbook.json is)
  2. Book folder
  3. Public folder (deprecated)

For absolute paths (e.g., /logo.png):

  1. Book folder
  2. Public folder (deprecated)

The pnpm-lock.yaml has been updated to include the favicons dependency. All tests pass with logos in different locations.

Commit: [pending]

@copilot You did forget to update the pm lock file and keep in mind that the logo can also be placed in the books folder. Since the public folder is deprecated and now the public files should live alo...

Updated the logo path resolution to check multiple locations:

For relative paths (e.g., logo.png):

  1. Root folder (where hyperbook.json is)
  2. Book folder
  3. Public folder (deprecated)

For absolute paths (e.g., /logo.png):

  1. Book folder
  2. Public folder (deprecated)

The pnpm-lock.yaml has been updated to include the favicons dependency. All tests pass with logos in different locations.

Commit: 189257f

@mikebarkmin
Copy link
Member

@copilot Can you use more information from the hyperbook.json.

Te path to be set to "{ASSETS_FOLDER}/favicons/" (defined in build.ts line 20)

The scope should be set to the basePath

The lang should be set to the Hyperbook language

The theme_color should be set to colors.brand

The appname should be set to the name the app description should be set to the description.

The developerName and developerUrl should be set to the author.name and author.url

And android and apple icons should also be generated.

And if use find another thing useful please add it.

Here is the documentation of the favicons config:

const configuration = {
  path: "/", // Path for overriding default icons path. `string`
  appName: null, // Your application's name. `string`
  appShortName: null, // Your application's short_name. `string`. Optional. If not set, appName will be used
  appDescription: null, // Your application's description. `string`
  developerName: null, // Your (or your developer's) name. `string`
  developerURL: null, // Your (or your developer's) URL. `string`
  cacheBustingQueryParam: null, // Query parameter added to all URLs that acts as a cache busting system. `string | null`
  dir: "auto", // Primary text direction for name, short_name, and description
  lang: "en-US", // Primary language for name and short_name
  background: "#fff", // Background colour for flattened icons. `string`
  theme_color: "#fff", // Theme color user for example in Android's task switcher. `string`
  appleStatusBarStyle: "black-translucent", // Style for Apple status bar: "black-translucent", "default", "black". `string`
  display: "standalone", // Preferred display mode: "fullscreen", "standalone", "minimal-ui" or "browser". `string`
  orientation: "any", // Default orientation: "any", "natural", "portrait" or "landscape". `string`
  scope: "/", // set of URLs that the browser considers within your app
  start_url: "/?homescreen=1", // Start URL when launching the application from a device. `string`
  preferRelatedApplications: false, // Should the browser prompt the user to install the native companion app. `boolean`
  relatedApplications: undefined, // Information about the native companion apps. This will only be used if `preferRelatedApplications` is `true`. `Array<{ id: string, url: string, platform: string }>`
  version: "1.0", // Your application's version string. `string`
  pixel_art: false, // Keeps pixels "sharp" when scaling up, for pixel art.  Only supported in offline mode.
  loadManifestWithCredentials: false, // Browsers don't send cookies when fetching a manifest, enable this to fix that. `boolean`
  manifestMaskable: false, // Maskable source image(s) for manifest.json. "true" to use default source. More information at https://web.dev/maskable-icon/. `boolean`, `string`, `buffer` or array of `string`
  icons: {
    // Platform Options:
    // - offset - offset in percentage
    // - background:
    //   * false - use default
    //   * true - force use default, e.g. set background for Android icons
    //   * color - set background for the specified icons
    //
    android: true, // Create Android homescreen icon. `boolean` or `{ offset, background }` or an array of sources
    appleIcon: true, // Create Apple touch icons. `boolean` or `{ offset, background }` or an array of sources
    appleStartup: true, // Create Apple startup images. `boolean` or `{ offset, background }` or an array of sources
    favicons: true, // Create regular favicons. `boolean` or `{ offset, background }` or an array of sources
    windows: true, // Create Windows 8 tile icons. `boolean` or `{ offset, background }` or an array of sources
    yandex: true, // Create Yandex browser icon. `boolean` or `{ offset, background }` or an array of sources
  },
  shortcuts: [
    // Your applications's Shortcuts (see: https://developer.mozilla.org/docs/Web/Manifest/shortcuts)
    // Array of shortcut objects:
    {
      name: "View your Inbox", // The name of the shortcut. `string`
      short_name: "inbox", // optionally, falls back to name. `string`
      description: "View your inbox messages", // optionally, not used in any implemention yet. `string`
      url: "/inbox", // The URL this shortcut should lead to. `string`
      icon: "test/inbox_shortcut.png", // source image(s) for that shortcut. `string`, `buffer` or array of `string`
    },
    // more shortcuts objects
  ],
};

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Oct 10, 2025

@copilot Can you use more information from the hyperbook.json.

Te path to be set to "{ASSETS_FOLDER}/favicons/" (defined in build.ts line 20)...

Enhanced the favicon generation with all the requested metadata from hyperbook.json:

Path: Set to {ASSETS_FOLDER}/favicons/ (generates 60+ files in __hyperbook_assets/favicons/)
Scope: Set to basePath
Language: Set to hyperbook language
Theme color: Set to colors.brand (defaults to #007864)
App name: Set to hyperbook name
App description: Set to hyperbook description
Developer info: Set to author.name and author.url
Android icons: Enabled (9 sizes generated)
Apple icons: Enabled (9 sizes generated)
Apple startup images: Enabled (34 sizes for different devices)

The generated web manifest includes all metadata and the HTML now includes links to the manifest and Apple touch icons. Backward compatibility maintained with favicon.ico copied to root.

Commit: [pending]

@copilot Can you use more information from the hyperbook.json.

Te path to be set to "{ASSETS_FOLDER}/favicons/" (defined in build.ts line 20)...

Enhanced the favicon generation with all the requested metadata from hyperbook.json:

Path: Set to {ASSETS_FOLDER}/favicons/ (generates 60+ files in __hyperbook_assets/favicons/)
Scope: Set to basePath
Language: Set to hyperbook language
Theme color: Set to colors.brand (defaults to #007864)
App name: Set to hyperbook name
App description: Set to hyperbook description
Developer info: Set to author.name and author.url
Android icons: Enabled (9 sizes generated)
Apple icons: Enabled (9 sizes generated)
Apple startup images: Enabled (34 sizes for different devices)

The generated web manifest includes all metadata and the HTML now includes links to the manifest and Apple touch icons. Backward compatibility maintained with favicon.ico copied to root.

Commit: 8ecffc5

Copilot AI changed the title Generate favicon based on logo Generate favicon and PWA icons based on logo with full metadata support Oct 10, 2025
@mikebarkmin
Copy link
Member

@copilot what platform specific issues can occur, when the favicons package is bundled?

Copy link
Contributor Author

Copilot AI commented Oct 10, 2025

@copilot what platform specific issues can occur, when the favicons package is bundled?

The favicons package depends on sharp (^0.33.1), which is a native Node.js module with platform-specific binaries (for Linux, macOS, Windows, different architectures). When bundled with ncc, these native binaries don't work correctly because:

  1. Sharp uses platform-specific .node files that can't be bundled as JavaScript
  2. The binary selection happens at runtime based on the platform
  3. Bundling breaks Sharp's ability to find and load the correct native binary

However, after reviewing this further, I can test whether bundling actually works. The --external flags might not be necessary if ncc can handle it properly. Would you like me to remove the external flags and test if bundling works without platform issues?

@mikebarkmin
Copy link
Member

@copilot Please test it, when the flag is removed. Can you also test it on Windows?

Copy link
Contributor Author

Copilot AI commented Oct 10, 2025

@copilot Please test it, when the flag is removed. Can you also test it on Windows?

I tested removing the --external flags for bundling. The result:

❌ Bundled approach failed: When favicons and sharp are bundled, Sharp's native binaries can't be loaded. Error: "Could not load the 'sharp' module using the linux-x64 runtime"

✅ External approach works: With --external favicons --external sharp, favicon generation works correctly. The favicons package and its Sharp dependency remain as external runtime dependencies that get installed via npm/pnpm.

Why bundling fails: Sharp uses platform-specific native .node binaries (separate for Linux x64, macOS arm64, Windows x64, etc.). When ncc bundles the code, these native binaries are either not included or can't be found at runtime, breaking the functionality.

Windows testing: I can't test on Windows in this environment (running Linux), but the external approach should work cross-platform since:

  • Sharp's installer automatically downloads the correct binary for the platform during pnpm install
  • The external dependencies are not bundled, so platform detection works normally
  • This is the standard recommended approach for packages with native dependencies

The current implementation (using external dependencies) is the correct approach and should work on Windows, macOS, and Linux without platform-specific issues.

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
@mikebarkmin
Copy link
Member

@copilot Can you write a pnpm changeset and also update the changelog.md in the website/en/book folder?

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Oct 10, 2025

@copilot Can you write a pnpm changeset and also update the changelog.md in the website/en/book folder?

Created changeset and updated changelog:

Changeset (.changeset/favicon-generation.md):

  • Marks hyperbook for minor version bump
  • Marks @hyperbook/markdown for patch version bump
  • Includes feature description

Changelog (website/en/book/changelog.md):

  • Added feature to v0.59.0 "New 🚀" section
  • Describes automatic favicon/PWA icon generation with all key features

Commit: [pending]

@copilot Can you write a pnpm changeset and also update the changelog.md in the website/en/book folder?

Created changeset and updated changelog:

Changeset (.changeset/favicon-generation.md):

  • Marks hyperbook for minor version bump
  • Marks @hyperbook/markdown for patch version bump
  • Includes feature description

Changelog (website/en/book/changelog.md):

  • Added feature to v0.59.0 "New 🚀" section
  • Describes automatic favicon/PWA icon generation with all key features

Commit: a569280

@mikebarkmin mikebarkmin marked this pull request as ready for review October 10, 2025 12:03
@mikebarkmin mikebarkmin merged commit 9c96045 into main Oct 10, 2025
2 checks passed
@mikebarkmin mikebarkmin deleted the copilot/generate-favicon-from-logo branch October 10, 2025 12:03
@github-actions github-actions bot mentioned this pull request Oct 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generate favicon based on logo

2 participants