{"_id":"@aleyan/iceberg","_rev":"3-26ce20c2d70639489fcf69f6107c31c2","name":"@aleyan/iceberg","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@aleyan/iceberg","version":"0.1.0","keywords":["iceberg","three.js","visualization","webgl"],"author":{"name":"Alex Yankov"},"license":"MIT","_id":"@aleyan/iceberg@0.1.0","maintainers":[{"name":"aleyan","email":"yankov.alex@gmail.com"}],"homepage":"https://github.com/aleyan/iceberg#readme","bugs":{"url":"https://github.com/aleyan/iceberg/issues"},"dist":{"shasum":"6e1e33d1a0356ae3b6e7ff1b924c89a2b557c3e4","tarball":"https://registry.npmjs.org/@aleyan/iceberg/-/iceberg-0.1.0.tgz","fileCount":32,"integrity":"sha512-eQ7RFjtXZbcnc3bGgxN1iZFqZujKlv2+hqUXJDtgdpNPTb9ue1HoXV8nFsLTZntc22CjzsbN9rNhoBSghBxZRQ==","signatures":[{"sig":"MEQCIGBowD/2oLapGNoQe7EvwqMIjHE4ET3Lrx4YTlcOcGmcAiASszRA6ow+z6UwiV7Mkn50LNJg3cVNWiq+1kEsZ/gYGg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7984375},"type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./assets/*":"./assets/*","./styles.css":"./styles.css","./package.json":"./package.json"},"gitHead":"02718652c733b7398470c9e750fe94242d78a4a6","scripts":{"dev":"bun --hot demo/server.ts","test":"bun test","build":"tsc","check":"bun run typecheck && bun test && bun run check:demo","prepack":"bun run check && bun run build","typecheck":"tsc --noEmit","check:demo":"bun build demo/main.ts --target browser --outdir dist-demo"},"_npmUser":{"name":"aleyan","email":"yankov.alex@gmail.com"},"repository":{"url":"git+https://github.com/aleyan/iceberg.git","type":"git"},"_npmVersion":"11.6.2","description":"A reusable, interactive 3D iceberg with depth-aware item labels.","directories":{},"sideEffects":["./styles.css"],"_nodeVersion":"24.14.1","dependencies":{"smol-toml":"^1.8.0","three-mesh-bvh":"^0.9.14"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.8","devDependencies":{"three":"^0.185.1","@types/bun":"^1.3.8","typescript":"^5.9.3","@types/three":"^0.185.0"},"peerDependencies":{"three":">=0.180.0 <1"},"_npmOperationalInternal":{"tmp":"tmp/iceberg_0.1.0_1788623845166_0.3937856091364391","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aleyan/iceberg","version":"0.1.1","keywords":["iceberg","three.js","visualization","webgl"],"author":{"name":"Alex Yankov"},"license":"MIT","_id":"@aleyan/iceberg@0.1.1","maintainers":[{"name":"aleyan","email":"yankov.alex@gmail.com"}],"homepage":"https://github.com/aleyan/iceberg#readme","bugs":{"url":"https://github.com/aleyan/iceberg/issues"},"dist":{"shasum":"65801aba836f86be4540eca5e9804f01434e5ee5","tarball":"https://registry.npmjs.org/@aleyan/iceberg/-/iceberg-0.1.1.tgz","fileCount":40,"integrity":"sha512-Jxb9E8qZLj6dubnNtlRXSH7eVjveX/Lt9T5PkLKqsMk1KF8oiqZK8mdzC3HDK9SMPfDaSunGSBhj5VP6fAcmwA==","signatures":[{"sig":"MEUCIQCLHuY+o0v2Q7thra23ZE1IilXFWPmphInyl3gnCfT7xgIgJBua5IG6EAqRgNrhBvn9+2u3/IIOx4qYPxgnqH6icdY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aleyan%2ficeberg@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":688228},"type":"module","_from":"file:aleyan-iceberg-0.1.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./assets/*":"./assets/*","./styles.css":"./styles.css","./package.json":"./package.json"},"scripts":{"dev":"bun --hot demo/server.ts","test":"bun test","build":"tsc","check":"bun run typecheck && bun test && bun run check:demo","prepack":"bun run check && bun run build","typecheck":"tsc --noEmit","check:demo":"bun build demo/main.ts --target browser --outdir dist-demo","pack:release":"bun scripts/pack.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:e03e5930-7a44-45d8-b4f8-cdedf5bb348e"}},"_resolved":"/home/runner/work/iceberg/iceberg/aleyan-iceberg-0.1.1.tgz","_integrity":"sha512-Jxb9E8qZLj6dubnNtlRXSH7eVjveX/Lt9T5PkLKqsMk1KF8oiqZK8mdzC3HDK9SMPfDaSunGSBhj5VP6fAcmwA==","repository":{"url":"git+https://github.com/aleyan/iceberg.git","type":"git"},"_npmVersion":"11.19.0","description":"A reusable, interactive 3D iceberg with depth-aware item labels.","directories":{},"sideEffects":["./styles.css"],"_nodeVersion":"24.20.0","dependencies":{"smol-toml":"^1.8.0","three-mesh-bvh":"^0.9.14"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.8","devDependencies":{"three":"^0.185.1","@types/bun":"^1.3.8","typescript":"^5.9.3","@types/three":"^0.185.0"},"peerDependencies":{"three":">=0.180.0 <1"},"_npmOperationalInternal":{"tmp":"tmp/iceberg_0.1.1_1788668314738_0.673584304039835","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aleyan/iceberg","version":"0.2.0","description":"A reusable, interactive 3D iceberg with depth-aware item labels.","keywords":["iceberg","three.js","visualization","webgl"],"type":"module","packageManager":"bun@1.3.8","sideEffects":["./styles.css"],"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./styles.css":"./styles.css","./assets/*":"./assets/*","./package.json":"./package.json"},"types":"./dist/index.d.ts","module":"./dist/index.js","scripts":{"build":"tsc","check":"bun run typecheck && bun run typecheck:tests && bun run test:unit && bun run check:demo","check:demo":"bun build demo/main.ts --target browser --outdir dist-demo","dev":"bun --hot demo/server.ts","pack:release":"bun scripts/pack.ts","prepack":"bun run check && bun run build","test":"bun run test:unit","typecheck":"tsc --noEmit","test:unit":"bun test","test:unit:coverage":"bun test --coverage","typecheck:tests":"tsc -p tsconfig.tests.json","test:browser":"playwright test layout.spec.ts","test:screenshots":"bun scripts/browser-container.ts screenshots.spec.ts","test:screenshots:update":"bun scripts/browser-container.ts screenshots.spec.ts --update-snapshots","test:performance":"playwright test performance.spec.ts","test:all":"bun run check && bun run test:browser && bun run test:performance && bun run test:screenshots","test:report":"playwright show-report","test:install":"playwright install chromium firefox","test:container":"bun scripts/browser-container.ts"},"repository":{"type":"git","url":"git+https://github.com/aleyan/iceberg.git"},"bugs":{"url":"https://github.com/aleyan/iceberg/issues"},"homepage":"https://github.com/aleyan/iceberg#readme","author":{"name":"Alex Yankov"},"license":"MIT","publishConfig":{"access":"public"},"dependencies":{"smol-toml":"^1.8.0","three-mesh-bvh":"^0.9.14"},"peerDependencies":{"three":">=0.180.0 <1"},"devDependencies":{"@playwright/test":"1.63.0","@types/bun":"^1.3.8","@types/jsdom":"^30.0.0","@types/three":"^0.185.0","jsdom":"^30.0.1","three":"^0.185.1","typescript":"^5.9.3"},"_id":"@aleyan/iceberg@0.2.0","_integrity":"sha512-w6kCf3h/CV/rBjP6NMw0ha0WJFb6n4NkoNSRQUqDl9o8Jtr9JaSLS1rPWGgWbPxGXOupD74y7U2bNC2IAamDSw==","_resolved":"/home/runner/work/iceberg/iceberg/aleyan-iceberg-0.2.0.tgz","_from":"file:aleyan-iceberg-0.2.0.tgz","_nodeVersion":"24.20.0","_npmVersion":"11.19.0","dist":{"integrity":"sha512-w6kCf3h/CV/rBjP6NMw0ha0WJFb6n4NkoNSRQUqDl9o8Jtr9JaSLS1rPWGgWbPxGXOupD74y7U2bNC2IAamDSw==","shasum":"e6197812d65b080f61cbb6b26e6a177f5f1817b6","tarball":"https://registry.npmjs.org/@aleyan/iceberg/-/iceberg-0.2.0.tgz","fileCount":56,"unpackedSize":727181,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aleyan%2ficeberg@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCvg/sI8BEqLc1NIyg5+QEQTXAtIqxdNvDgOkfgsrbnLgIgdE6qaxy1OpfXVT9iLNywxMrDE8wO383erwGdCyo7J/A="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:e03e5930-7a44-45d8-b4f8-cdedf5bb348e"}},"directories":{},"maintainers":[{"name":"aleyan","email":"yankov.alex@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/iceberg_0.2.0_1788928730093_0.07453274420488532"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-05T15:57:25.055Z","modified":"2026-09-09T04:38:50.681Z","0.1.0":"2026-09-05T15:57:25.424Z","0.1.1":"2026-09-06T04:18:34.945Z","0.2.0":"2026-09-09T04:38:50.309Z"},"bugs":{"url":"https://github.com/aleyan/iceberg/issues"},"author":{"name":"Alex Yankov"},"license":"MIT","homepage":"https://github.com/aleyan/iceberg#readme","keywords":["iceberg","three.js","visualization","webgl"],"repository":{"type":"git","url":"git+https://github.com/aleyan/iceberg.git"},"description":"A reusable, interactive 3D iceberg with depth-aware item labels.","maintainers":[{"name":"aleyan","email":"yankov.alex@gmail.com"}],"readme":"# @aleyan/iceberg\n\nA reusable interactive 3D iceberg for the web. It renders the iceberg, split\nocean, atmosphere, and depth-aware item labels; your application supplies the\ncatalogue.\n\n\n## Affordances\n\nClick the iceberg or focus it with Tab to navigate without a mouse wheel.\nUp/Down moves a line, Page Up/Down (or Shift+Space/Space) moves a page, and\nHome/End jumps to the top/bottom. Left/Right rotates within the selected view's\nlimits. Holding an arrow key uses the keyboard's normal repeat behavior.\n\nAfter 15 seconds without interaction, water motion smoothly decelerates over\nfive seconds while rendering stays at full frame rate. At 20 seconds, motion\nreaches zero and rendering stops. Interacting with the viewer, changing views,\nor resizing resumes normal motion from the saved animation phase.\n\n## Install\n\n```sh\nbun add @aleyan/iceberg three\n```\n\n## Use\n\nImport the stylesheet once, give the host a height, then mount the scene in\nbrowser code. This example uses Vite's `?raw` import (also supported by Astro);\nwith other bundlers, load the TOML as text or pass an `IcebergItem[]` directly.\n\n```ts\nimport {\n  mountIceberg,\n  parseItems,\n  type IcebergItem,\n} from \"@aleyan/iceberg\";\nimport \"@aleyan/iceberg/styles.css\";\nimport itemsToml from \"./items.toml?raw\";\n\nconst host = document.querySelector<HTMLElement>(\"#iceberg\");\n\nif (host) {\n  try {\n    const items: IcebergItem[] = parseItems(itemsToml);\n    const iceberg = mountIceberg(host, { items, viewSelector: true });\n    // Register iceberg.dispose() with your component or route's cleanup hook.\n    await iceberg.ready;\n  } catch (error) {\n    if (!(error instanceof DOMException && error.name === \"AbortError\")) {\n      console.error(\"Unable to initialize the iceberg\", error);\n      host.textContent = \"The iceberg could not be loaded.\";\n    }\n  }\n}\n```\n\n```css\n#iceberg {\n  width: 100%;\n  height: 100svh;\n}\n```\n\nThe package uses system sans-serif and monospace stacks by default. The\noptional font custom properties inherit through every generated control and\nare read when the WebGL label atlas is built. Define them on the host only when\nthe containing application already provides its own fonts.\n\n```css\n#iceberg {\n  --iceberg-font-family: var(--app-sans-font);\n  --iceberg-mono-font-family: var(--app-mono-font);\n}\n```\n\n`mountIceberg` does not run during import, so the package is safe to import\nfrom server-rendered projects. Call it only in the browser (for example from an\nAstro `<script>` or a client-loaded component).\n\n`mountIceberg()` throws synchronously if initialization fails, including when\nWebGL is unavailable, and releases resources acquired before the failure.\nThe returned `ready` promise resolves once the model is loaded, framed, and\npopulated with labels. Model loading failures reject it and dispose the scene.\nOptional environment and texture loading is not part of `ready`.\nCalling `dispose()` before readiness rejects `ready` with a `DOMException`\nwhose name is `AbortError`; this is expected cancellation. Calling it after\nreadiness leaves the resolved promise unchanged. Disposal is idempotent.\n\n### Vite and Astro\n\nExclude the package from Vite's development dependency optimizer so asset URLs\nremain relative to the package instead of being moved into `.vite/deps`:\n\n```ts\n// vite.config.ts\nimport { defineConfig } from \"vite\";\n\nexport default defineConfig({\n  optimizeDeps: { exclude: [\"@aleyan/iceberg\"] },\n});\n```\n\nFor Astro, put the same option under `vite` in `astro.config.mjs`:\n\n```js\nimport { defineConfig } from \"astro/config\";\n\nexport default defineConfig({\n  vite: {\n    optimizeDeps: { exclude: [\"@aleyan/iceberg\"] },\n  },\n});\n```\n\nKeep this exclusion when installing from npm. A registry installation does not\nneed filesystem access to a sibling `iceberg` checkout.\n\nThe default asset URLs resolve from the installed package. Override any of them\nwhen a host needs to copy or CDN-serve assets:\n\n```ts\nmountIceberg(host, {\n  items,\n  assets: {\n    model: \"/iceberg/iceberg-web.glb\",\n  },\n});\n```\n\n## Item format\n\n`parseItems` reads a TOML document containing one or more `[[items]]`\nrecords:\n\n```toml\nschema_version = 1\nitem_count = 1\n\n[[items]]\nslug = \"mutable-default-arguments\"\nname = \"Mutable default arguments\"\nshort_description = \"Default values are evaluated once, when the function is defined.\"\nurl = \"https://docs.python.org/3/faq/programming.html\"\nobscurity_bucket = 1\nobscurity_rating = 2.2\ncursedness_rating = 6.7\n```\n\nSlugs must be unique kebab-case strings. Buckets are integers from 1 (above the\nwaterline) to 10 (the abyss); ratings are numbers from 0 to 10. Names and\ndescriptions support inline code surrounded by backticks, but not arbitrary\nHTML.\n\nBy default, selecting an item writes `?item=<slug>` and browser Back/Forward\nrestores it. Set `syncUrl: false` to keep the containing page URL untouched.\n\n## Views\n\nThe default `orbit` view keeps the original full rotation and labels around the\nwhole iceberg. `arc` limits rotation to 30 degrees total (15 degrees either side)\nand compresses the original irregular label arrangement onto the front arc.\nItems remain reachable by descending, without needing to turn to the back.\n`list` places names at equal intervals down the center, with horizontal rotation\nlocked. Labels stay at fixed world positions and move with the iceberg in every\nview; Arc retains the original depths, and List spaces names across the same\ndepth range. List uses the center column's width to reduce wrapping. All three\nviews share camera scrolling, label rendering, occlusion, and detail popups.\nWheel and touch navigation continue over open descriptions. Hover previews stay\nclosed while navigating; explicitly selected descriptions remain pinned.\n\nEnable the top-right dropdown with `viewSelector: true`. It shows the current\nchoice (Orbit, Arc, or List) and opens a vertical menu. Its font size matches the\nitem names. The selector\nis opt-in, so existing embeddings keep their current appearance. Selected items\nand their URL links survive view changes.\n\n```ts\nimport { icebergViews, mountIceberg, type IcebergView } from \"@aleyan/iceberg\";\n\nconst iceberg = mountIceberg(host, {\n  items,\n  view: \"arc\",\n  viewSelector: true,\n  onViewChange: (view: IcebergView) => console.log(view),\n});\n// For custom controls, omit viewSelector and use the controller:\niceberg.setView(\"list\");\nconsole.log(iceberg.view, iceberg.availableViews, icebergViews);\n// availableViews and icebergViews are the immutable [\"orbit\", \"arc\", \"list\"].\n```\n\n`setView()` also works before `ready` resolves. Unknown modes throw `TypeError`;\nvalid changes after disposal do nothing. The default interaction hint and canvas\naccessibility instructions update with the mode. Custom hints remain host-owned.\n\n## Options\n\n- `view`: initial `orbit`, `arc`, or `list` layout; defaults to `orbit`.\n- `viewSelector`: displays the built-in mode selector; defaults to `false`.\n- `onViewChange`: callback after a mode change, for custom controls or host state.\n\n- `aboveWaterLabelStretch`: expands the above-water label span; defaults to 1.\n- `assets`: overrides for the model, HDR environment, sky, or relief texture.\n- `hint`: custom interaction hint, or `false` to hide it.\n- `initialItem`: selected item when URL synchronization is disabled.\n- `overview`: frames the complete iceberg instead of the scrolling view.\n- `stillFrame`: freezes water motion for screenshots or visual tests.\n- `syncUrl`: enables item deep links; defaults to `true`.\n- `underwaterStretch`: stretches the submerged model from 1 to 4; defaults to 2.\n- `waterLevel`: changes the world-space waterline; defaults to -0.72.\n\n## Develop\n\n```sh\nbun install\nbun run dev\nbun run check\nbun run build\n```\n\nThe package publishes only `dist/`, `assets/`, `styles.css`, and its\ndocumentation/license. The small synthetic catalogue under `demo/items.toml`\nis for local development and tests and is deliberately not part of the npm\npackage. The real Python catalogue remains in the consuming site.\n\n## Tests\n\n`bun run check` runs source and test typechecks, the unit suite, and the demo\nbundle check. `bun run test:unit:coverage` writes an LCOV report.\n\nFor the real-browser suite, run `bun run test:install`, then\n`bun run test:browser` and `bun run test:performance`. Chrome and Firefox each\nrun desktop and mobile viewport profiles. `bun run test:screenshots` compares\nreviewed screenshots inside a pinned Linux container and requires Docker.\nCI runs every suite and retains failure artifacts and performance measurements.\n\nSee [the testing guide](tests/browser/README.md) for coverage, performance budgets,\nbaseline updates, mobile-emulation limits, and troubleshooting.\n\n## Prepare a release\n\n1) Update the version in package.json\n2) Run `bun run pack:release`\n3) Publish a release from github releases. This causes publish.yaml workflow to publish to npm.\n\n\n## Assets\n\nThe iceberg GLB, ocean HDR, sky texture, and glacial relief texture were created\nfor the original Python Iceberg project and are distributed under this\nrepository's MIT license. The texture sources were generated specifically for\nthe project; the runtime does not include the original meme reference or Blender\nworking files.\n","readmeFilename":"README.md"}