Skip to content

Upgrading to 0.17.0

This guide covers migrating from Pika 0.16.x to 0.17.0. Version 0.17.0 introduces new rendering contexts, companion mode for application-first UX, and a comprehensive event system.

Pika 0.17.0 focuses on enabling "application-first with chat assistance" experiences:

  • Hero Context - New singleton widget area for dominant UI elements
  • Companion Mode - Compact chat pane when canvas widgets are primary
  • Event System - Subscribe to framework state changes
  • Canvas Options - Full control mode, close configuration, companion mode
  • Chat Pane Minimize - User can minimize chat to a strip

This release is fully backward compatible. All existing widgets and configurations continue to work without modification.

A new rendering context for singleton dominant widgets displayed below the spotlight:

// Render a widget in hero context
await chatAppState.renderTag('acme.dashboard', 'hero', { userId: '123' });
// Control hero visibility
chatAppState.showHero();
chatAppState.hideHero();
chatAppState.closeHero(); // Destroys the widget

Hero vs Spotlight:

FeatureHeroSpotlight
InstancesSingleton (one at a time)Multiple widgets
LifecycleCan be destroyedPersistent during session
PurposeDominant display widgetQuick access carousel
VisibilityAPI-controlled show/hideAlways visible

Enables canvas widgets to become the primary focus while chat becomes a compact assistant:

// Render canvas in companion mode
await chatAppState.renderTag('acme.ide', 'canvas', {
companionMode: true,
chatPaneMinimized: false // Optional: start minimized
});
// Check companion mode state
if (chatAppState.isCompanionMode) {
// Adjust for compact chat UI
}
// Minimize/expand chat pane programmatically
chatAppState.setChatPaneMinimized(true);

Companion Mode Behavior:

  • Chat pane becomes compact with reduced padding
  • Spotlight widgets are hidden
  • Hero widget is hidden
  • Chat history panel is minimized
  • User can minimize chat to ~20px strip

Subscribe to framework state changes for reactive widgets:

// Available events
type ChatAppEvents = {
widgetOpen: { tagId: string; instanceId: string; renderingContext: WidgetRenderingContextType };
widgetClose: { tagId: string; instanceId: string; renderingContext: WidgetRenderingContextType };
canvasOpen: { tagId: string; instanceId: string };
canvasClose: { tagId: string; instanceId: string };
chatPaneMinimized: undefined;
chatPaneExpanded: undefined;
companionModeEnter: undefined;
companionModeExit: undefined;
heroShow: { tagId: string; instanceId: string };
heroHide: { tagId: string; instanceId: string };
};
// Subscribe with automatic cleanup (recommended)
const unsubscribe = chatAppState.addEventListener('canvasOpen', (data) => {
console.log(`Canvas opened: ${data.tagId}`);
}, instanceId); // Pass instanceId for auto-cleanup
// Or manage subscription manually
const unsubscribe = chatAppState.addEventListener('companionModeEnter', () => {
this.classList.add('expanded');
});
// Later: unsubscribe();

Widgets can now provide their own chrome (title bar, close button):

// Render with full control
await chatAppState.renderTag('acme.editor', 'canvas', {
fullControl: true,
closeConfig: {
confirmOnClose: true,
confirmTitle: 'Unsaved Changes',
confirmMessage: 'You have unsaved work. Close anyway?'
}
});
// In your widget's close handler
async function handleClose() {
const shouldClose = await context.chatAppState.requestCanvasClose();
// Framework handles confirmation and closes if user confirms
}

Configure confirmation dialogs for canvas close:

interface CanvasCloseConfig {
confirmOnClose?: boolean;
confirmTitle?: string;
confirmMessage?: string;
confirmButtonLabel?: string;
cancelButtonLabel?: string;
}
interface IChatAppState {
// Show the hero widget (if one is loaded)
showHero(): void;
// Hide the hero widget (keeps in memory)
hideHero(): void;
// Destroy the hero widget
closeHero(): void;
}
interface IChatAppState {
// Check if companion mode is active
readonly isCompanionMode: boolean;
// Check if chat pane is minimized
readonly isChatPaneMinimized: boolean;
// Minimize or expand chat pane
setChatPaneMinimized(minimized: boolean): void;
}
interface IChatAppState {
// Subscribe to an event
addEventListener<K extends keyof ChatAppEvents>(
event: K,
handler: ChatAppEventHandler<K>,
instanceId?: string // Pass for auto-cleanup
): () => void; // Returns unsubscribe function
// Unsubscribe from an event
removeEventListener<K extends keyof ChatAppEvents>(
event: K,
handler: ChatAppEventHandler<K>
): void;
}
interface IChatAppState {
// Request canvas close (for fullControl widgets)
// Returns true if close proceeded, false if cancelled
requestCanvasClose(): Promise<boolean>;
}

The renderTag method now accepts 'hero' as a context:

// Before (still works)
renderTag(tagId: string, context: 'spotlight' | 'inline' | 'dialog' | 'canvas' | 'static', data?, metadata?): Promise<void>;
// After (with hero)
renderTag(tagId: string, context: 'spotlight' | 'inline' | 'dialog' | 'canvas' | 'static' | 'hero', data?, metadata?): Promise<void>;
interface CanvasWidgetOptions {
companionMode?: boolean;
chatPaneMinimized?: boolean;
fullControl?: boolean;
closeConfig?: CanvasCloseConfig;
}
interface HeroContextConfig {
enabled: boolean;
sizing?: {
minHeight?: number;
maxHeight?: number;
preferredHeight?: number | 'auto';
};
}
interface ChatAppEvents {
widgetOpen: { tagId: string; instanceId: string; renderingContext: WidgetRenderingContextType };
widgetClose: { tagId: string; instanceId: string; renderingContext: WidgetRenderingContextType };
canvasOpen: { tagId: string; instanceId: string };
canvasClose: { tagId: string; instanceId: string };
chatPaneMinimized: undefined;
chatPaneExpanded: undefined;
companionModeEnter: undefined;
companionModeExit: undefined;
heroShow: { tagId: string; instanceId: string };
heroHide: { tagId: string; instanceId: string };
}

New CSS variables are available when companion mode is active:

/* Applied when data-companion-mode="true" */
--companion-padding: 0.5rem;
--companion-font-size: 0.875rem;
--companion-spacing: 0.25rem;

If you're not using the new features, your existing code continues to work unchanged.

  1. Identify widgets that should be dominant display elements
  2. Update your orchestrator/static widget to call renderTag with 'hero' context
  3. Optionally configure hero sizing in tag definition
  1. Identify canvas widgets that should be "primary application" experiences
  2. Pass companionMode: true in your renderTag data
  3. Optionally implement event listeners for mode changes
  4. Test UI in both normal and companion modes
  1. Identify places where widgets need to react to framework state
  2. Add event listeners with instanceId for automatic cleanup
  3. Remove any manual state polling
// GOOD: Pass instanceId for automatic cleanup
chatAppState.addEventListener('canvasClose', handler, instanceId);
// ACCEPTABLE: Manual cleanup when widget lifecycle is managed
const unsubscribe = chatAppState.addEventListener('canvasClose', handler);
// Later in disconnectedCallback:
unsubscribe();
// BAD: No cleanup leads to memory leaks
chatAppState.addEventListener('canvasClose', handler);

When using fullControl: true:

  1. Provide clear visual affordances for closing
  2. Use requestCanvasClose() to respect closeConfig
  3. Handle the case where close is cancelled
  4. Consider adding a visible title/header

When designing for companion mode:

  1. Ensure your canvas widget works at various widths
  2. Consider the minimized chat strip state
  3. Provide ways for users to interact with chat when needed
  4. Test responsive behavior as users resize panes
  1. Verify you're calling renderTag with 'hero' context
  2. Check that companion mode isn't active (hero is hidden in companion mode)
  3. Ensure widget loaded successfully (check console for errors)
  1. Verify event name matches exactly (case-sensitive)
  2. Ensure handler isn't being removed elsewhere
  3. Check that the triggering action actually occurred
  1. Always pass instanceId when subscribing to events
  2. Call unsubscribe functions in disconnectedCallback
  3. Use browser DevTools to monitor event listener counts