/// /** * Folksonomy Demo - Demonstration of the Folksonomy Tags System * * This pattern demonstrates the folksonomy tag system by showing multiple * tag instances with the same scope (to show local tag reuse) and * connection to the community aggregator. * * SETUP: * 1. Deploy folksonomy-aggregator.tsx first * 2. Favorite the aggregator with tag "folksonomy-aggregator" * 3. Deploy this demo pattern * 4. Add tags in one section, see them appear as suggestions in another * 5. Check the aggregator charm to see events flowing in * * FEATURES DEMONSTRATED: * - Two tag instances with the same scope share suggestions * - A third tag instance with a different scope is isolated * - Community suggestions show dimmed with usage counts * - Events are posted to the aggregator in real-time */ import { type Default, derive, NAME, pattern, UI, wish, Writable, } from "commontools"; // Import the FolksonomyTags sub-pattern import { FolksonomyTags } from "./folksonomy-tags.tsx"; interface Input { /** Tags for item A (scope: demo-shared) */ itemATags: Default; /** Tags for item B (scope: demo-shared - same as A) */ itemBTags: Default; /** Tags for item C (scope: demo-isolated - different scope) */ itemCTags: Default; /** Custom scope name */ customScope: Default; } interface Output { itemATags: string[]; itemBTags: string[]; itemCTags: string[]; } export default pattern( ({ itemATags, itemBTags, itemCTags, customScope }) => { // Discover aggregator via wish - searches favorites and current space const aggregatorWish = wish<{ events: unknown[]; suggestions: Record; postEvent: unknown; }>({ query: "#folksonomy-aggregator", scope: ["~", "."], }); const hasAggregator = derive( aggregatorWish.result, (agg: unknown) => agg != null, ); // Shared scope for A and B const sharedScope = derive( customScope, (cs: string) => `https://github.com/commontools/folksonomy-demo/${cs || "demo-shared"}`, ); // Isolated scope for C const isolatedScope = Writable.of( "https://github.com/commontools/folksonomy-demo/demo-isolated", ); // Create tag instances - aggregator discovered via wish() internally const tagsA = FolksonomyTags({ scope: sharedScope, tags: itemATags, }); const tagsB = FolksonomyTags({ scope: sharedScope, tags: itemBTags, }); const tagsC = FolksonomyTags({ scope: isolatedScope, tags: itemCTags, }); return { [NAME]: "Folksonomy Demo", [UI]: ( {/* Header */}

🏷️ Folksonomy Tags Demo

A community-enabled tag system with preferential attachment. Tags added here flow to the aggregator and become suggestions for others.

{/* Aggregator Status */}
{hasAggregator ? "✓" : "⚠"} {hasAggregator ? "Connected to folksonomy-aggregator - community features active!" : "Aggregator not found. Deploy and favorite folksonomy-aggregator with tag 'folksonomy-aggregator' for community features."}
{/* Shared Scope Section */}

Shared Scope: Recipe Items A & B

These two items share a scope. Tags added to one will appear as suggestions in the other (via community aggregator).

Scope: {sharedScope}
{/* Item A */} Item A: Pasta Recipe {/* Item B */} Item B: Pizza Recipe
{/* Isolated Scope Section */}

Isolated Scope: Project Tasks

This item has a different scope. Its tags are separate from the recipe items above, but still contribute to the aggregator.

Scope: demo-isolated
Item C: Task Tracker
{/* How It Works */}

How It Works

  1. Add tags to Item A (e.g., "italian", "quick", "vegetarian")
  2. Notice the green dot - events are being sent to the aggregator
  3. Now type in Item B - you'll see Item A's tags as suggestions (via aggregator)
  4. Select a suggestion to "use" it - this increases its popularity count
  5. Item C has a different scope, so it won't see recipe tags
  6. Check the aggregator charm to see all events and top tags
{/* Preferential Attachment Note */}
Preferential Attachment:{" "} The more a tag is used, the higher it appears in suggestions. This creates a natural convergence toward useful vocabulary without top-down control.
), itemATags, itemBTags, itemCTags, }; }, );