Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ ICHAVA_VITE_DEV=true
# Lives in core's config (`ichava.prefix`) — set ICHAVA_BROWSER_PREFIX in core
# .env or here; both read the same env var.
ICHAVA_BROWSER_PREFIX=ichava
# Serve the browser page elsewhere, e.g. `app` for /app. The API stays under the
# prefix, and /{prefix}/icons redirects (301) to the new URL. Unset: /{prefix}/icons.
# ICHAVA_BROWSER_PATH=
ICHAVA_BROWSER_PER_PAGE=60
ICHAVA_BROWSER_CACHE=3600

Expand All @@ -43,7 +46,7 @@ ICHAVA_DOMAINS=
# ----------------------------------------------------------------------------
ICHAVA_API_PRETTY_PRINT=true
ICHAVA_API_CORS_ENABLED=true
ICHAVA_API_CORS_ORIGINS=*
ICHAVA_API_CORS_ORIGINS="*"

# ----------------------------------------------------------------------------
# HTTP request size guard (bytes)
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to `ichava/icon-browser` follow [Keep a Changelog](https://k

### Fixed

- **Re-vendored `ichava/react-icon-browser` at `41b0a4b`** (react-icon-browser#214): one caret for every dropdown and popover, drawn in the panel's own colours over its edge and centred on the trigger (the header menus drew none), and the "Cookies & storage" notice moves by its grip or the arrow keys, inside the window, for the session. `inertia-app.*` rebuilt with `npm run build:prod` after a fresh `npm install --no-package-lock`, as `committed-build.yml` does. `zustand` is held at `>=5.0.15 <5.1.0`, the vendored library's own range: 5.1.0 (2026-11-06) changes the type react-icon-browser publishes for `useStore`, and a fresh build that took it would no longer match the committed bundle.

- **The browser page works under a host's nonce-based CSP.** A host that sends its own policy with a per-request nonce (`script-src 'self' 'nonce-…'`, as ichava.com does) hands that nonce to Vite; the page now puts it on the loader's `<style>` and `<script>`, the frame `<style>`, the stylesheet, the module scripts and the dev-server scripts, and writes it to a `csp-nonce` meta tag for the styles React Aria injects at runtime. Before, those blocks carried no nonce and the policy blocked them. Without a host nonce, the package's `nonce` CSP mode supplies core's, as before. `Support\PageNonce` resolves which; `HostCspNonceTest` and `PrepaintTest` guard it.

### Removed
Expand All @@ -17,6 +19,8 @@ All notable changes to `ichava/icon-browser` follow [Keep a Changelog](https://k

### Added

- **The browser page can live at a path of the host's choosing**: `browser.path` (`ICHAVA_BROWSER_PATH`, e.g. `app` for `/app`), read through `Helpers::browserPath()`. The page and its icon pages (`/{path}/{id}`) move under the same route names; the REST API and the other pages stay under `/{prefix}`. When it moves, `/{prefix}/icons` and `/{prefix}/icons/{id}` answer **301** to the new URLs with the query kept (`MovedBrowserRedirectController`; `Route::redirect()` would drop `?icon=` deep links), and `/{prefix}` redirects to the new path. `ValidateIchavaRoute` accepts the moved path. The pages share it as `ichava.browserPath`, and the Inertia pages build icon links from it (`iconPageBase`), with a guard that fails any page building them from the prefix. The browser and its icon pages carry `<link rel="canonical">` to their URL without the filter query. Unset, empty, not a plain path, the bare prefix or under `{prefix}/api`: `/{prefix}/icons`, unchanged. Tests: `tests/Feature/BrowserPathTest.php`, the `MovedBrowser` suite.

- **Re-vendored `ichava/react-icon-browser` at `648f92a`** (react-icon-browser#212), built from a fresh clone. The Twemoji pack is `ichava/icon-sets-twemoji`, renamed from the emoji pack forward only (ichava/icon-sets-twemoji#103): the demo fixture names it so, and the copy snippets import its components from `@ichava/js-icon-components/<flavour>/twemoji` (ichava/js-icon-components#15). `vendored.json` and the bundle (`npm run build:prod`) follow.
- **Re-vendored `ichava/react-icon-browser` at `c970756`** (react-icon-browser#209). Its demo fixture names OpenMoji icons by `ichava/icon-sets-openmoji`, now that `icon-sets-emoji` is Twemoji only, and `defaults.packages` selects that pack. `vendored.json` names the commit, and the committed bundle under `public/assets` is rebuilt from it.
- **Re-vendored `ichava/react-icon-browser` at `a02bcd3`** (react-icon-browser#208). It brings the About credits to 38 rows from the public packs' recipe credits with no private pack named, the Qwik, Ember and Elm snippets as js-icon-components documents them, zero lint warnings, and the removal of its appearance-default migration and localStorage preference fallback. `vendored.json` names the commit, and the committed bundle under `public/assets` is rebuilt from it.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ php artisan ichava::ichava-core.database seed --package=ichava/icon-sets-tabler

### Usage

Open the browser at `/{prefix}/icons` (`/ichava/icons` by default). To expose the JSON API as well, turn it on:
Open the browser at `/{prefix}/icons` (`/ichava/icons` by default, or wherever `ICHAVA_BROWSER_PATH` puts it). To expose the JSON API as well, turn it on:

```dotenv
ICHAVA_API_ENABLED=true
Expand Down
14 changes: 11 additions & 3 deletions config/icon-browser.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,19 @@
|--------------------------------------------------------------------------
| Browser UI
|--------------------------------------------------------------------------
| Mounted at /{prefix}/icons by default ({prefix} is the value below).
| The same prefix is used by core's API routes (/{prefix}/api/v1/...) so
| the SPA and the REST API share a unified URL space.
| Mounted at /{prefix}/icons by default ({prefix} is core's
| ICHAVA_BROWSER_PREFIX). The same prefix is used by the API routes
| (/{prefix}/api/v1/...) so the SPA and the REST API share a URL space.
|
| `path` moves the browser page (and its /{path}/{id} icon pages) to a URL
| of the host's choosing, e.g. ICHAVA_BROWSER_PATH=app serves it at /app.
| The API and the other pages stay under the prefix, and /{prefix}/icons
| (with its query) and /{prefix}/icons/{id} answer with a permanent
| redirect to the new URLs, so old links keep working. Unset, empty or not
| a plain path: /{prefix}/icons, as before.
*/
'browser' => [
'path' => env('ICHAVA_BROWSER_PATH'),
'per_page' => env('ICHAVA_BROWSER_PER_PAGE', IchavaConstants::BROWSER_PER_PAGE),
'cache_duration' => env('ICHAVA_BROWSER_CACHE', IchavaConstants::DEFAULT_CACHE_TTL),
// Whether the browser shows its theme control (`features.themeToggle`).
Expand Down
2 changes: 2 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ The third case is the one worth stating plainly: in a stateless host the API is

The `/{prefix}/icons` path comes from `ICHAVA_BROWSER_PREFIX` in **core's** configuration, and it is shared between core's API and the browser pages. Changing it in one place moves both, which is why it lives upstream of the package that serves the routes.

The browser page alone can move off the prefix with this package's `browser.path` (`ICHAVA_BROWSER_PATH`), for a host that wants it at a short URL such as `/app`. It is this package's setting, not core's, because only the page moves: the API stays at `/{prefix}/api/v1/...`, and the old page URLs redirect permanently to the new ones, so links already shared keep working.

## See also

- [Installation](installation.md)
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ The `ichava` Inertia prop and `GET {prefix}/api/v1/bootstrap` carry three docume
## Notes

- The route prefix (`/{prefix}/icons`, `/{prefix}/api/v1/...`) is set by `ICHAVA_BROWSER_PREFIX` and lives in **core's** config (`config('ichava.ichava-core.prefix')`). Both packages read it; icon-browser reads it through `Helpers::routePrefix()`.
- `browser.path` (`ICHAVA_BROWSER_PATH`) moves only the browser page and its icon pages, e.g. to `/app`; read through `Helpers::browserPath()`. The route names stay `ichava.inertia.browser` and `ichava.inertia.icons.show`, the page gets a `<link rel="canonical">` to the new URL, the Inertia pages link icons through the shared `ichava.browserPath`, and the old `/{prefix}/icons` URLs answer 301 with their query kept (the browser's `?icon=` deep links live there). `/{prefix}` redirects to the new path. A value that is not a plain path is ignored. If your host sends a CSP, the moved page is the same page, so nothing in it changes; if you prerender or cache pages by URL, add the new path.
- `rate_limiting.enabled = false` (default) makes per-route limits no-ops. The `api_floor` global throttle still applies regardless.
- With `rate_limiting.enabled = true`, each REST route carries the limit it declares (for example 60 a minute for `icons/filters`, 10 for the cache writes), counted per route and per caller. `rate_limiting.api` can only **lower** that figure for every API route; it never raises it. The figure is read per request, so changing it needs no route cache clear, but `enabled` is read when routes load.
- `api.cors.allowed_origins` takes one origin, a list, or the comma-separated string `ICHAVA_API_CORS_ORIGINS` holds. With one origin it is sent as is; with several, `Access-Control-Allow-Origin` echoes the request's `Origin` when it is listed (and the response varies on `Origin`), and is omitted otherwise. No credentials header is ever sent.
Expand Down
1 change: 1 addition & 0 deletions docs/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Copy what you need from [`.env.example`](https://github.com/ichava/icon-browser/
| Key | Default | Effect | Config |
|---|---|---|---|
| `ICHAVA_BROWSER_PREFIX` | `ichava` | URL prefix segment. Browser mounts at `/{prefix}/icons` and core's API at `/{prefix}/api/v1/...`. Lives in core's config (`ichava.prefix`); listed here because env-var ownership is conventional | `ichava.prefix` *(in core)* |
| `ICHAVA_BROWSER_PATH` | *(none)* | Serve the browser page somewhere else, e.g. `app` for `/app`, with its icon pages at `/{path}/{id}`. The REST API and the other pages stay under the prefix, and `/{prefix}/icons` and `/{prefix}/icons/{id}` answer 301 to the new URLs with their query kept. Unset, empty, not a plain path, the bare prefix or under `{prefix}/api`: `/{prefix}/icons` | `ichava.icon-browser.browser.path` |
| `ICHAVA_BROWSER_PER_PAGE` | `60` | Default page size in the icon-browser grid | `ichava.icon-browser.browser.per_page` |
| `ICHAVA_BROWSER_CACHE` | `3600` | Discovery cache TTL (seconds) | `ichava.icon-browser.browser.cache_duration` |
| `ICHAVA_BROWSER_THEME_TOGGLE` | `true` | Show the theme control (`ui.themeToggle`, `features.themeToggle`) | `ichava.icon-browser.browser.theme_toggle` |
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ With the config and assets published, the SPA is at:
http://example.com/ichava/icons
```

The `ichava` segment is the **prefix**, and it comes from `ICHAVA_BROWSER_PREFIX` in core's configuration rather than this package's — it is shared between core's API and this SPA, so changing it moves both. See [Configuration](configuration.md).
The `ichava` segment is the **prefix**, and it comes from `ICHAVA_BROWSER_PREFIX` in core's configuration rather than this package's — it is shared between core's API and this SPA, so changing it moves both. To serve the page somewhere else, such as `/app`, set `ICHAVA_BROWSER_PATH=app`; the old URL then redirects there. See [Configuration](configuration.md).

## Nothing appears until a pack is seeded

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"react-aria": "^3.53.1",
"react-aria-components": "^1.22.1",
"react-dom": "^19.3.1",
"zustand": "^5.0.15"
"zustand": ">=5.0.15 <5.1.0"
},
"devDependencies": {
"@axe-core/playwright": "^4.14.0",
Expand Down
3 changes: 3 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
<testsuite name="Gated">
<directory>tests/Gated</directory>
</testsuite>
<testsuite name="MovedBrowser">
<directory>tests/MovedBrowser</directory>
</testsuite>
<testsuite name="Prefixed">
<directory>tests/Prefixed</directory>
</testsuite>
Expand Down
4 changes: 2 additions & 2 deletions public/assets/css/inertia-app.css

Large diffs are not rendered by default.

Loading
Loading