Standalone Web Interface
The standalone web interface for ppt-craft is a client-side application designed to facilitate an agentic chat flow for generating PowerPoint presentations. It consists of a single HTML entry point (index.html) that defines a two-column layout: a chat panel on the left for user interaction and a workspace on the right for displaying the generated storyline and slides. The visual presentation is managed by styles.css, which implements a responsive grid layout, theme support (including dark mode), and specific styling for chat messages and slide previews. The application logic is encapsulated in app.js, which handles the communication with the backend and updates the DOM based on the state of the deck generation process.
Application Shell and Layout
Section titled “Application Shell and Layout”The application is bootstrapped via src/ppt_craft/static/standalone/index.html 1. The HTML document sets the language to en-GB and includes a meta tag to prevent indexing by search engines. The visual structure is defined by a top bar and a main layout area.
The top bar contains the application branding, a theme selector, and action buttons. The branding includes a mark, the name “ppt-craft”, and a tag indicating the local model version “local Qwen3.6 · GB10”. The theme selector allows users to choose between “corporate”, “dark-tech”, and “minimal” themes. Action buttons include “New deck” and “Download .pptx”, with the latter being disabled by default.
The main layout uses a CSS grid to split the screen into a chat panel and a workspace. The chat panel contains a status indicator, a transcript area for messages, and a form for user input. The workspace contains several card components: “storyline”, “slides”, “preview”, and an “empty-state”. These cards are hidden by default and are revealed as the generation process progresses.
Styling and Theming
Section titled “Styling and Theming”The visual design is handled by src/ppt_craft/static/standalone/styles.css 2. The stylesheet defines CSS variables for colors, fonts, and spacing, allowing for easy theming. The default theme uses a light background with warm tones.
The stylesheet supports dark mode by detecting the system preference via prefers-color-scheme and applying different variable values when the data-theme attribute is set to “auto”. The dark theme uses darker backgrounds and lighter text colors.
The layout is responsive, using a grid with a minimum width for the chat panel and a flexible width for the workspace. On screens smaller than 880px, the layout switches to a single column with the chat panel on top.
Chat messages are styled differently based on their role: user messages are aligned to the right with an accent background, assistant messages are aligned to the left with a neutral background, and system messages are styled with a monospace font. Error messages have a distinct red border and background.
The workspace cards have a consistent style with a border, shadow, and rounded corners. The storyline list uses a grid layout to display slide titles and layout tags. The slide grid displays thumbnails in a responsive grid, with hover and selection states. Building slides are indicated by an animated overlay.
Agentic Chat Flow
Section titled “Agentic Chat Flow”The agentic chat flow is initiated by the user submitting a prompt in the chat form 1. The form includes a textarea for input, a “Send” button, and a “Stop” button. The “Stop” button is hidden by default and is shown when a generation is in progress.
The status indicator shows the connection state with a colored dot and text. The dot changes color based on the state: pending (yellow, pulsing), ok (green), or err (red) 2.
The transcript area displays the conversation history 1. Messages are appended as they are received.
The workspace updates based on the conversation. The storyline card shows the proposed outline and provides buttons to approve, refine, or discard it. The slides card shows the generated slide thumbnails. The preview card shows a zoomed-in view of a selected slide.
The empty state is displayed when no deck is being generated. It provides instructions to the user.
<!doctype html>
<html lang="en-GB" data-theme="auto">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ppt-craft</title>
<meta name="robots" content="noindex, nofollow">
<link rel="stylesheet" href="/static/styles.css">
</head>
<body>
<header class="topbar">
<div class="brand">
<span class="brand-mark">C</span>
<span class="brand-name">ppt-craft</span>
<span class="brand-tag">local Qwen3.6 · GB10</span>
</div>
<div class="topbar-right">
<label class="theme-picker">
<span class="picker-label">theme</span>
<select id="theme-select">
<option value="corporate" selected>corporate</option>
<option value="dark-tech">dark-tech</option>
<option value="minimal">minimal</option>
</select>
</label>
<button id="btn-new" class="ghost">New deck</button>
<button id="btn-download" class="primary" disabled>Download .pptx</button>
</div>
</header>
<main class="layout">
<!-- Left: agentic chat panel -->
<section class="chat-panel">
<div id="status" class="status">
<span id="status-dot" class="dot pending"></span>
<span id="status-text">connecting…</span>
</div>
<div id="transcript" class="transcript" aria-live="polite"></div>
<form id="chat-form" class="chat-input">
<textarea
/* ppt-craft standalone - Claude-ish, neutral, demo-grade. */
* { box-sizing: border-box; }
:root {
--bg: #fafaf6;
--bg-2: #f1ede1;
--bg-3: #e7e2d3;
--fg: #1c1c1a;
--fg-2: #5e5d57;
--fg-3: #9f9b8e;
--accent: #c4763a;
--accent-fg: #fff;
--ok: #1d863a;
--err: #b8302e;
--pending: #c2a13a;
--border: #d6d2c4;
--shadow: 0 1px 2px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.04);
--radius: 12px;
--mono: ui-monospace, SFMono-Regular, "JetBrains Mono", Menlo, monospace;
--sans: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", system-ui, sans-serif;
}
@media (prefers-color-scheme: dark) {
:root[data-theme="auto"] {
--bg: #18181a;
--bg-2: #232227;
--bg-3: #2f2e34;
--fg: #f3f0e6;
--fg-2: #b3afa1;
--fg-3: #76736b;
--border: #3a383f;
--shadow: 0 1px 2px rgba(0,0,0,0.3), 0 12px 32px rgba(0,0,0,0.32);
}
}
html, body { height: 100%; margin: 0; }
body {
font: 14px/1.45 var(--sans);
background: var(--bg);
color: var(--fg);
display: flex;
flex-direction: column;
}