Communitygithub.com

OthmanAdi/bookshelf

Füge ein 3D-Bücherregal zu einem Webprojekt hinzu – eine ziehbare Reihe von Buchrücken als Lesezeichen-Manager, Leseliste oder „Später lesen“-Liste, in der gespeicherte Links wie Bücher aussehen und in eine Leseansicht aufgehen.

Was ist bookshelf?

Dieses Skill ermöglicht es, ein vollständig interaktives 3D-Bücherregal in jedem Webprojekt zu integrieren, ohne zusätzliche Laufzeitabhängigkeiten. Es besteht aus vier unabhängigen TypeScript-Modulen: `shelf` (CSS-3D-Regal ohne WebGL), `motion` (Federn, Ziehen, Wischen), `store` (versionierte Lesezeichenverwaltung mit Import/Export) und `reader` (sicherer Textleser, der die Leseposition merkt). Entwickler können entweder das gesamte Regal mit integrierter Animation und Datenhaltung nutzen oder einzelne Module wie die Ziehbewegung oder die Leseansicht isoliert in bestehende Projekte einbauen. Der Skill findet das Quellverzeichnis des Repositoriums, kopiert die gewünschten Dateien und berücksichtigt dabei Frameworks wie React, Vue oder Svelte, indem er auf die `mount`-Funktion der Module setzt. Besonders nützlich ist die Funktion, dass der Leser sauberes DOM erzeugt (kein `innerHTML`) und Links auf `http:` und `https:` beschränkt – ideal für sichere Lesezeichen-Apps. Der Skill unterstützt auch reduzierte Bewegung, drei verschiedene Integrationspfade und stellt sicher, dass Importe und Exporte die Privatsphäre der Nutzer schützen.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/OthmanAdi/bookmark-bookshelf/tree/HEAD/.agents/skills/bookshelf

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Bücherregal für Lesezeichen

Vier kleine TypeScript-Module ohne Laufzeitabhängigkeiten. Jedes funktioniert allein. Zusammen ergeben sie ein Regal mit gespeicherten Links, die man ziehen, öffnen und lesen kann. Dieser Skill beschreibt Version 0.2.0.

ModulOrdnerMachtWeiß nichts von
shelfsrc/shelfZeichnet Volumen für eine Pose, meldet Absichten (fokussieren, öffnen, schrittweise, springen)Zeit, Gesten, Speicher
motionsrc/motionFedern, Ziehen, Wischen, Einrasten, Öffnungs- und SchließfortschrittBücher, DOM-Struktur
storesrc/storeVersionierte Bibliothek im lokalen Speicher: Validierung, Migration, Import und Export, Suche, Sortierung, ein Regal als Datei oder LinkDOM, Rendering
readersrc/readerZeigt gespeicherten Text als Textknoten an, verfolgt und stellt die Position wieder her, Textgröße, private Notizen, „Als gelesen markieren“Regale, Speicher

Die Module befinden sich nicht in diesem Skill-Ordner. Sie liegen in einem Checkout des Bookmark-Bookshelf-Repositorys. Die erste Aufgabe ist es, dieses zu finden. Kopiere die Modul-Ordner daraus und tippe sie nicht neu ab.

0. Quellverzeichnis finden

Probiere die Routen in dieser Reihenfolge und stoppe bei der ersten, die funktioniert.

  1. Neben dem Skill. Wenn sich diese Datei unter <root>/.agents/skills/bookshelf/SKILL.md und <root>/src/shelf/index.ts existiert, dann ist <root> das Quellverzeichnis. Dies ist der Fall, wenn der Skill direkt genutzt wird oder als Plugin installiert wurde, das das gesamte Repository behält.

  2. Ein Pfad, den die Person angibt. Wenn der Skill woanders hin kopiert wurde (z. B. in den Skills-Ordner eines Agenten), hat die Person möglicherweise bereits ein Checkout. Wenn die Anfrage einen Pfad enthält, verwende ihn als Quellverzeichnis. Wenn nicht, frage danach und biete Route 3 in derselben Frage an.

  3. Ein flaches Klon, nur nach Zustimmung der Person. Frage zuerst und sage klar, was passieren wird: Das öffentliche Repository https://github.com/OthmanAdi/bookmark-bookshelf wird unter dem Release-Tag v0.2.0 in einen neuen temporären Ordner geklont, und nichts weiter wird heruntergeladen. Lade nichts herunter, bevor die Person nicht zugestimmt hat. Nach einem Ja, führe aus:

    git clone --depth 1 --branch v0.2.0 https://github.com/OthmanAdi/bookmark-bookshelf <temporärer Ordner>
    

    Der temporäre Ordner ist ein neues, leeres Verzeichnis außerhalb des Projekts (das System-Temp-Verzeichnis ist in Ordnung). Sage der Person, wo es ist. Es ist das Quellverzeichnis. Kopiere daraus, bearbeite niemals darin, und sage am Ende, dass es gelöscht werden kann. Wenn das Klonen fehlschlägt (kein Netzwerk, kein Git, fehlendes Tag), sage das und gehe zurück zu Route 2.

  4. Überprüfe vor dem Kopieren. Im Quellverzeichnis:

    • package.json hat "name": "bookmark-bookshelf". Notiere dessen version.
    • Diese Marker existieren: src/shelf/index.ts, src/motion/index.ts, src/store/index.ts, src/reader/index.ts, src/app/bookshelf.ts, src/app/bookshelf.css, src/app/flight.ts, src/styles/tokens.css.
    • Wenn es ein Git-Checkout ist, notiere git -C <root> rev-parse --short HEAD.

    Wenn ein Marker fehlt, stoppe und sage, welcher fehlt. Dieser Skill beschreibt Version 0.2.x. Für eine andere Version lies die types.ts jedes Moduls, bevor du den Referenzdateien hier vertraust: 0.1.0 hat kein flight.ts, und seine shelf-, store- und reader-Verträge unterscheiden sich (siehe Ende von references/modules.md).

  5. Melde den Quellpfad, die Version und den Commit, von dem du kopiert hast, und ob es ein von dir erstellter Klon war, damit ein späteres Update damit verglichen werden kann.

Jeder Pfad, der unten mit src/, examples/ oder public/ beginnt, ist relativ zu diesem Quellverzeichnis.

1. Zuerst den Umfang festlegen

Frage, was die Person braucht, und nimm dann nur das.

Sie wollenNimmNimm auch
Eine 3D-Reihe von Buchrücken, die man ziehen kannshelf, motionsrc/styles/tokens.css
Ein Regal mit eigenem Klick-Handling, ohne Ziehenshelftokens.css
Eine Leseansicht mit Wiederaufnahmereadertokens.css, die Regeln .bb-button, .bb-label und .bb-visually-hidden aus src/styles/base.css
Eine Lesezeichen-Bibliothek ohne UI: Import, Export, Suche, Teilen eines Regalsstorenichts
Ziehen, Wischen und Einrasten für eine andere Reihemotionnichts
Ein Regal, das in einen Reader aufgeht, mit eigenen Datenshelf, motion, readertokens.css, dieselben drei Regeln aus base.css
Die gesamte Kompositionalle vier, src/app/bookshelf.ts, src/app/bookshelf.css, src/app/flight.tssrc/styles/*, src/data/* falls die Beispiel-Links gewünscht sind

Module importieren einander nie. Wenn ein Import zwischen zwei Modul-Ordnern auftaucht, ist die Integration falsch: Der Treffpunkt gehört in die eigene Kompositionsdatei des Hosts (siehe src/app/bookshelf.ts).

Die gesamte Komposition besteht aus sieben Dingen, und es ist leicht, eines zu vergessen: die vier Modul-Ordner, src/app/bookshelf.ts, sein Stylesheet src/app/bookshelf.css und src/app/flight.ts, das den Buchdeckel zwischen Regal und Seite transportiert. Das Skript importiert die anderen beiden, daher schlägt ein Build ohne sie mit einem Fehler fehl. Kopiere die drei Dateien zusammen in einen Ordner, der neben den Modul-Ordnern liegt.

Die App rund um die Komposition (die Leiste und ihre Menüs, die Dialoge zum Hinzufügen von Links, zum Importieren vieler Links und zum Teilen eines Regals, die Listenansicht) ist der Rest von src/app/ zusammen mit app.html. Diese Dateien hängen voneinander und von Element-IDs in app.html ab. Nimm sie als Ganzes und passe sie an, oder baue deine eigene Oberfläche auf den Funktionen des Stores, die references/composition.md zeigt.

2. Dateien einbringen

  1. Kopiere jeden ausgewählten Ordner aus src/ unverändert, einschließlich der Tests (*.test.ts), in den Quellbaum des Hosts.
  2. Kopiere src/styles/tokens.css und lade es einmal. Die Module lesen --bb-* Custom Properties für Farbe, Typografie, Abstände und Timing. Zum Umstyling überschreibe die Tokens und bearbeite kein Modul-CSS. Tokens definieren nur Werte: Der Hintergrund, die Schriftart und die Resets leben in src/styles/base.css. In einem leeren Projekt kopiere diese Datei ebenfalls (sie importiert tokens.css und fonts.css, also entferne den Fonts-Import, wenn du Schritt 3 überspringst) oder setze background: var(--bb-paper); color: var(--bb-ink); font-family: var(--bb-font-serif) selbst auf body.
  3. Schriftarten sind optional. src/styles/fonts.css bindet Newsreader und IBM Plex Mono (SIL OFL 1.1) und benötigt zwei Dev-Abhängigkeiten: @fontsource-variable/newsreader und @fontsource/ibm-plex-mono. Wenn du sie auslieferst, liefere auch die Lizenztexte in public/licenses/ mit. Ohne sie entferne die Zeile @import './fonts.css' aus base.css, und die Token-Stapel fallen auf System-Schriftarten zurück.
  4. shelf.ts und reader.ts importieren ihr CSS (import './shelf.css'). Das benötigt einen Bundler, der CSS-Imports verarbeitet (Vite, webpack, Parcel, esbuild mit einem CSS-Loader). Ohne einen solchen lösche diese Importzeilen und verlinke die CSS-Dateien von der Seite aus: shelf.css, cover.css und variants.css für das Regal, reader.css und reader-text.css für den Reader.
  5. TypeScript: Der Code ist geschrieben für strict, noUncheckedIndexedAccess und verbatimModuleSyntax, mit moduleResolution: "bundler". Er kompiliert auch unter weniger strengen Einstellungen.
  6. Werkzeuge für ein leeres Projekt: npm i -D vite typescript vitest happy-dom @playwright/test, Node ^22.12.0 || ^24.0.0 || >=26.0.0. references/checks.md enthält die minimale Vitest- und Playwright-Konfiguration.

Framework-Hosts (React, Vue, Svelte): Jedes Modul ist mount(element, options) und gibt ein Handle mit destroy() zurück. Mounte im „nach dem ersten Rendern“-Hook des Frameworks und zerstöre es in dessen Aufräumfunktion. references/composition.md enthält eine einfache Funktion zum Einwickeln.

3. Verdrahten

Lies references/modules.md für die Verträge (Eingaben, Ausgaben, Ereignisse) und references/composition.md für kopierfertige Verdrahtung jeder Kombination. Die kürzeste nützliche, Regal plus Bewegung:

import { createShelfMotion, prefersReducedMotion } from './motion';
import { mountShelf } from './shelf';

const motion = createShelfMotion({ count: items.length, reducedMotion: prefersReducedMotion });
const shelf = mountShelf(host, {
  items,
  label: 'Mein Regal',
  onLoopChange: (loop) => motion.setLoop(loop), // das Regal entscheidet, ob die Reihe schleift
});
motion.setLoop(shelf.loop);
motion.setTiming(shelf.variant.motion);

motion.subscribe((state) => {
  shelf.setPose({ position: state.position, open: 0, reduced: state.reduced });
  shelf.element.dataset.settled = state.settled ? '1' : '0'; // ermöglicht Tests zu warten, bis Ruhe einkehrt
});
motion.attach(shelf.surface, {
  pxPerItem: () => shelf.pitch(),
  extent: () => shelf.surface.clientWidth,
  enabled: () => !shelf.fixed, // eine kurze Reihe steht still, also darf kein Zug sie bewegen
});

shelf.on((intent) => {
  if (intent.type === 'focus') motion.goTo(intent.slot);
  else if (intent.type === 'step') motion.step(intent.direction);
  else if (intent.type === 'jump') motion.goTo(intent.to === 'first' ? 0 : items.length - 1);
  else openSomething(intent.id);
});

items ist ein Array von { id, title, source?, author?, cloth?, pattern?, seed?, progress?, thickness?, stature?, outline?, note?, done? }. Ausführbare Versionen der Kombinationen befinden sich in examples/.

Das Schleifen wird vom Regal entschieden. Eine Reihe schleift nur, wenn ihre Volumen mindestens vier Fünftel der Szene ausfüllen. Eine kürzere Reihe steht still in der Mitte zwischen zwei Buchstützen (shelf.fixed): Die Pfeile, die Pfeiltasten und ein Klick auf einen Buchrücken wählen das präsentierte Volumen aus, und ein Zug tut nichts. Sage der Person, welche der beiden sie mit ihrer Anzahl an Elementen sehen wird, denn acht Titel auf einem Desktop-Bildschirm stehen still.

4. Regeln, die dafür sorgen, dass es funktioniert

Dies sind die Dinge, die kaputt gehen, wenn sie geändert werden. Jedes hat einen Grund.

  • Nichts zwischen .bb-shelf__row und einer Buchseite darf overflow, filter, clip-path, mask, contain: paint oder opacity unter 1 setzen. Jedes von ihnen flacht den 3D-Kontext ab, und die Bücher fallen zu einer Ebene zusammen. Blende stattdessen die Szene (das Perspektivelement) aus.
  • Das Regal zeichnet die Pose, die ihm gegeben wird. Bewegung entlang der Reihe und das Öffnen kommen von setPose in einer Frameschleife, die dem Bewegungsmodul gehört. Das Hinzufügen von CSS-Transitionen zu .bb-book kämpft gegen diese Schleife. Hover-Effekte gehören auf .bb-book__body.
  • Das Regal fliegt den Buchdeckel nicht. Während pose.open über 0 ist, versteckt es das zentrierte Volumen und teilt seine Nachbarn. Ein reisender Buchdeckel ist die Aufgabe des Hosts (src/app/flight.ts): Eine Kopie auf einer fixierten Ebene, nur über transform bewegt, von shelf.coverPlacement() zu reader.flightTarget(). Das Volumen des Regals, die Kopie und der eigene Deckel des Readers sind nie gleichzeitig sichtbar.
  • Ein nativer Button pro Element, nicht pro gerendertem Buch. Eine schleifende Reihe rendert Kopien. Nur die echte Kopie trägt button.bb-book__control, und Kopien sind aria-hidden. Die Buchseiten sind einfache Spans, und der Button ist ein separates, unsichtbares Element davor. Mache Buchseiten nicht fokussierbar und wickele die Buchseiten nicht in einen <button>.
  • Behalte touch-action: pan-y auf der Ziehfläche. Mit none kann ein Finger, der auf dem Regal landet, die Seite nicht mehr scrollen.
  • Ignoriere während eines Touch-Zugs lostpointercapture von Nachkommen. Das Element unter dem Finger fängt den Zeiger implizit ein, und wenn die Fläche die Aufnahme übernimmt, steigt das Verlustereignis dieses Nachkommens auf. gesture.ts prüft event.target === element. Behalte die Prüfung.
  • Touch-Taps auf Volumen werden von Zeigerereignissen erkannt, nicht von click. Nach einem schnell losgelassenen Wisch können Browser den Klick des nächsten Taps verschlucken. shelf.ts aktiviert daher ein Volumen bei pointerup, wenn die Berührung einen Radius von 10 Pixeln nie verlassen hat und unter 700 ms dauerte, und überspringt den Klick, den der Browser für denselben Tap sendet (Tastaturklicks mit detail 0 zählen immer). gesture.ts verbraucht auch touchmove, sobald ein Zug gesperrt ist, damit kein Browser-Fling startet.
  • Der Reader baut DOM nur mit createElement und textContent. Es gibt kein innerHTML irgendwo in src/reader, und ein Test auf Quellcodeebene erzwingt dies. Links werden nur für http: und https: behalten.
  • Der Store akzeptiert nur http:- und https:-Adressen und ruft niemals eine ab. Die App zeigt einen Link und den Text an, den die Person eingefügt hat. Füge kein Remote-Fetching hinzu, um den Reader „intelligenter“ zu machen: Browser blockieren dies für die meisten Websites, und ein Proxy ändert das Sicherheitsmodell.
  • Ein Import überschreibt niemals, und nichts, das von außen kommt, wird ohne Vorschau angewendet. Eine Shelf-Datei oder ein Shelf-Link kommt von einer fremden Person: Lies sie mit readShelfFile oder decodeShelfLink, zeige summariseShelfFile, und rufe importShelf nur nach Zustimmung der Person auf. Lesepositionen, Lesezeichen und private Notizen gehen niemals in eine Shelf-Datei.
  • Reduzierte Bewegung ist ein eigener Pfad, bei dem nichts reist. reducedMotion im Bewegungsmodul macht Bewegungen sofort und verwandelt Öffnen und Schließen in ein 150 ms Einblenden. Übergib die aktuelle Präferenz (prefersReducedMotion), nicht einen einmal gelesenen Wert.
  • Gib immer die Reader-Grenze an, wo die Person sie sehen kann. Ein Link ohne Text muss „Original öffnen“ zeigen und einen einstufigen Weg, Text hinzuzufügen.

5. Ergebnis prüfen

Führe aus, was zutrifft. references/checks.md enthält die Befehle, die DOM-Hooks, auf die geprüft werden soll, und Playwright-Schnipsel für echte Maus- und Touch-Züge.

GenommenMinimale Überprüfung
jedes Modulnpx tsc --noEmit sauber, die eigenen *.test.ts bestehen unter Vitest mit happy-dom
shelfBücher werden gerendert, data-focus ändert sich bei setPose, genau ein .bb-book__control pro Element, Pfeiltasten emittieren step
shelf + motionBei einer lang genug schleifenden Reihe ändert ein echter Mauszug data-focus-id und endet mit data-settled="1". Bei einer stehenden Reihe ändern die Pfeiltasten es, und ein Zug tut nichts. Ein vertikaler Touch-Zug scrollt die Seite
readerEscape ruft onClose auf, ein Neuladen stellt scrollTop wieder her, ein <script> im Text wird als Text angezeigt
storeEine javascript:-Adresse wird abgelehnt, Export und dann Import ergeben eine Rundreise, ein zweites createStore auf demselben Speicher sieht die Daten, ein Shelf überlebt encodeShelfLink und dann decodeShelfLink
alle vierDer Setup-Smoke-Check in references/checks.md besteht. Öffne ein Volumen, scrolle, lade neu: Dasselbe Volumen ist an derselben Stelle geöffnet. Schließen: Der Fokus ist zurück auf diesem Volumen

Berichte, was du ausgeführt hast und was nicht. Ein Build, der die Typüberprüfung besteht, hat noch nicht gezeigt, dass er gezogen werden kann.

Aussehen

paper (Standard)

Verwandte Skills