본문으로 건너뛰기
cccskills
무료GitHub에 공개

push-based-widget-module-pattern

Build sidebars and widgets that receive data updates from their caller. This skill uses a single shared module instance, separate creation and update functions, and stored references to page elements for partial updates.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

원문원문 설명 보기

How to implement sidebar/widget UI components that receive pushed data updates from a caller, using a singleton module pattern with exported factory and update functions plus module-level DOM refs for efficient partial re-renders.

설치 방법 보기

이럴 때 유용합니다

  • Build a sidebar that updates frequently
  • Display data supplied by an external controller
  • Update selected page elements without rebuilding the whole widget
  • Implement a widget as a plain TypeScript module

포함된 파일(2)

  • SKILL.md6.7 KB
  • .skill_id46 B

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Push-Based Widget Module Pattern

Use this pattern when a UI sidebar or widget component needs to be updated by its caller (push-based) rather than fetching data itself. Instead of a class or framework component, implement the widget as a plain module with singleton state, a factory function to create the DOM, and an update function to efficiently patch it.


When to Use

  • A sidebar, panel, or widget is rendered once but updated frequently with new data.
  • The data source lives outside the component (e.g., a parent controller, event bus, or store).
  • You want lightweight partial re-renders without rebuilding the entire DOM tree.
  • You prefer a simple module interface over a class-based or framework-based component.

Core Structure

widgets/
  MyWidget.ts          # The module (singleton state + exports)
  MyWidget.types.ts    # (Optional) Exported data interface

Step-by-Step Instructions

1. Define the Data Interface

Export a typed interface so callers know exactly what data to push.

// MyWidget.types.ts  (or inline at top of MyWidget.ts)
export interface MyWidgetData {
  title: string;
  items: string[];
  status: "idle" | "active" | "error";
  // ... all fields the widget needs to render
}

2. Declare Module-Level Singleton Refs

At module scope, keep references to the container and any frequently updated child nodes. This avoids querying the DOM on every update.

// MyWidget.ts
import type { MyWidgetData } from "./MyWidget.types";

// Singleton DOM refs
let containerEl: HTMLElement | null = null;
let titleEl: HTMLElement | null = null;
let itemListEl: HTMLElement | null = null;
let statusEl: HTMLElement | null = null;

// Current state snapshot (optional — useful for diffing)
let currentData: MyWidgetData | null = null;

3. Export a Factory Function (createMyWidget)

The factory builds the full DOM tree once, stores refs to mutable nodes, and returns the root element to be mounted by the caller.

export function createMyWidget(): HTMLElement {
  // Build the container
  containerEl = document.createElement("aside");
  containerEl.className = "my-widget";

  // Build static structure; keep refs to dynamic parts
  titleEl = document.createElement("h2");
  titleEl.className = "my-widget__title";

  itemListEl = document.createElement("ul");
  itemListEl.className = "my-widget__list";

  statusEl = document.createElement("span");
  statusEl.className = "my-widget__status";

  // Compose the tree
  const header = document.createElement("header");
  header.appendChild(titleEl);
  header.appendChild(statusEl);

  containerEl.appendChild(header);
  containerEl.appendChild(itemListEl);

  return containerEl;
}

Rule: createMyWidget should be callable only once per page. If called again, consider returning the existing containerEl (idempotent factory).

4. Export an Update Function (updateMyWidget)

The update function receives the new data object and performs surgical DOM mutations on the stored refs. It does NOT rebuild the whole tree.

export function updateMyWidget(data: MyWidgetData): void {
  if (!containerEl) {
    console.warn("updateMyWidget called before createMyWidget");
    return;
  }

  currentData = data;

  // Update only what changed
  if (titleEl) titleEl.textContent = data.title;
  if (statusEl) {
    statusEl.textContent = data.status;
    statusEl.dataset.status = data.status; // for CSS hooks
  }

  // Rebuild only dynamic list items
  if (itemListEl) {
    itemListEl.innerHTML = ""; // clear previous items
    data.items.forEach((item) => {
      const li = document.createElement("li");
      li.textContent = item;
      itemListEl!.appendChild(li);
    });
  }
}

Optimization tip: For large lists, compare currentData to data before clearing and rebuilding, or use a keyed reconciliation approach.

5. Caller Usage Pattern

The caller mounts the widget once and pushes updates whenever its data changes.

import { createMyWidget, updateMyWidget } from "./widgets/MyWidget";
import type { MyWidgetData } from "./widgets/MyWidget.types";

// Mount once
const sidebar = document.getElementById("sidebar")!;
sidebar.appendChild(createMyWidget());

// Push updates (e.g., from a store subscription, WebSocket, timer)
function onDataChange(newData: MyWidgetData) {
  updateMyWidget(newData);
}

// Example: push an initial state immediately
onDataChange({
  title: "Today's Focus",
  items: ["Task A", "Task B"],
  status: "active",
});

Full Module Template

// widgets/MyWidget.ts

export interface MyWidgetData {
  // Define all fields here
}

// --- Singleton state ---
let containerEl: HTMLElement | null = null;
// Add more refs as needed per section of the widget

// --- Factory ---
export function createMyWidget(): HTMLElement {
  if (containerEl) return containerEl; // idempotent

  containerEl = document.createElement("aside");
  // ... build DOM, store refs ...
  return containerEl;
}

// --- Update ---
export function updateMyWidget(data: MyWidgetData): void {
  if (!containerEl) return;
  // ... patch DOM refs with new data ...
}

Guidelines & Best Practices

ConcernRecommendation
Idempotent factoryReturn existing containerEl if called twice to avoid duplicate mounts.
Null guardsAlways check refs before mutating — the update fn may be called before mount in some flows.
Granular refsStore refs for every section that updates independently. Avoid querySelector in the update path.
Interface exportsAlways export the data interface so callers can type their push calls.
No internal fetchingThe module must never fetch or subscribe to external data — that is the caller's responsibility.
CSS hooksUse data-* attributes or CSS classes on refs to let stylesheets react to state changes.
CleanupOptionally export a destroyMyWidget() that nulls all refs for SPA teardown scenarios.

Anti-Patterns to Avoid

  • ❌ Class with constructor — adds instantiation overhead and prevents the clean factory/update split.
  • ❌ Querying the DOM in the update function (document.querySelector(...)) — slow and fragile.
  • ❌ Rebuilding the entire tree on every update — defeats the purpose of the module-level refs.
  • ❌ Mixing data-fetching into the widget module — breaks the push-based contract.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Prepare audio stems, the separate parts of a track, for mixing by matching their durations with silence, loops, or crossfades. Verify each generated and processed file before moving to the next production step.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Match audio parts with different durations
  • Extend drums and melodic parts using suitable methods
  • Check audio specifications before mixing
HKUDS/OpenSpace7.8천2026년 8월 13일 업데이트

Produce audio in separate stems, meaning individual instrument tracks, and verify each output as you go. Align track lengths with silence, loops, or crossfades, then apply effects, mix a master, and package the files.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Align instrument tracks to a reference audio file
  • Choose how to extend tracks that are too short
  • Verify audio properties after generation and effects
HKUDS/OpenSpace7.8천2026년 8월 13일 업데이트

Build serverless endpoints that relay external API requests for a dashboard while keeping API keys on the server. Includes Vercel examples for input checks, caching, cross-origin access, and consistent JSON responses.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Connect a dashboard to external APIs without exposing API keys
  • Create endpoints for stock quotes and news
  • Standardize API responses and error handling
HKUDS/OpenSpace7.8천2026년 8월 13일 업데이트

Produce a master track and separate audio parts through a structured Python workflow. Apply filters and reverb, export WAV files with consistent settings, package them in a ZIP archive, and check that the deliverables meet the requested specifications.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Export a master track with separate audio parts
  • Apply filters and reverb to audio
  • Check duration and format across deliverables
HKUDS/OpenSpace7.8천2026년 8월 13일 업데이트

Keep document creation moving when web searches and page retrieval repeatedly fail. The skill preserves the original task, uses the model’s existing knowledge, and clearly identifies information that could not be checked against external sources.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Continue drafting after repeated web retrieval failures
  • Preserve task requirements during network errors
  • Mark information that could not be externally verified
HKUDS/OpenSpace7.8천2026년 8월 13일 업데이트

When Python execution repeatedly fails in execute_code_sandbox, use a file-based alternative: save the script, run it through the shell, and capture results or errors. The guidance covers paths, imports, timeouts, structured output, and optional cleanup.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Retry Python work through the shell after repeated sandbox failures
  • Run longer tasks with an adjusted timeout
  • Execute Python work that needs multiple files or system utilities
HKUDS/OpenSpace7.8천2026년 8월 13일 업데이트

HKUDS의 스킬 모두 보기

이 스킬의 문제 신고