@3sln/deck 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/build.js CHANGED
@@ -6,6 +6,7 @@ import path from 'path';
6
6
  import {createRequire} from 'module';
7
7
  import { sha256, loadDeckConfig, getProjectFiles, getCardFiles, getHtmlTemplate } from '../src/config.js';
8
8
 
9
+ import {marked} from 'marked';
9
10
  const require = createRequire(import.meta.url);
10
11
 
11
12
  // --- Path Resolution ---
@@ -78,10 +79,25 @@ async function build() {
78
79
  const initialCardsData = await Promise.all(cardFiles.map(async (file) => {
79
80
  const content = await fs.readFile(path.resolve(outDir, file), 'utf-8');
80
81
  const hash = await sha256(content);
81
- return { path: `/${file}`, hash };
82
+
83
+ // Extract title for agents.md
84
+ const tokens = marked.lexer(content);
85
+ const heading = tokens.find(t => t.type === 'heading' && t.depth === 1);
86
+ const title = heading ? heading.text : path.basename(file, path.extname(file));
87
+
88
+ return { path: `/${file}`, hash, title };
82
89
  }));
83
90
  console.log(`Found and processed ${initialCardsData.length} cards.`);
84
91
 
92
+ // Generate agents.md
93
+ console.log('Generating agents.md...');
94
+ let agentsMd = "# Agents Index\n\nThis file is meant to help LLMs find documentation. Below is a list of available cards.\n\n";
95
+ for (const card of initialCardsData) {
96
+ agentsMd += `- [${card.title}](${card.path})\n`;
97
+ }
98
+ await fs.writeFile(path.resolve(outDir, 'agents.md'), agentsMd);
99
+ console.log('agents.md generated.');
100
+
85
101
  // Generate asset manifest for service worker
86
102
  console.log('Generating asset manifest...');
87
103
  const manifest = await fs.readJson(path.resolve(assetsDir, '.vite/manifest.json'));
@@ -111,6 +127,9 @@ async function build() {
111
127
  pinnedCardPaths: buildConfig.pinned,
112
128
  entryFile: `/assets/${entryFile}`,
113
129
  cssFiles,
130
+ favicon: buildConfig.favicon,
131
+ scripts: buildConfig.scripts,
132
+ stylesheets: buildConfig.stylesheets,
114
133
  });
115
134
 
116
135
  await fs.writeFile(path.resolve(outDir, 'index.html'), html);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3sln/deck",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "A Vite plugin for building scalable, zero-config, Markdown-based component playgrounds and documentation sites.",
5
5
  "type": "module",
6
6
  "author": "Ray Stubbs",
package/src/config.js CHANGED
@@ -17,6 +17,9 @@ export async function loadDeckConfig(root) {
17
17
 
18
18
  const defaultConfig = {
19
19
  title: 'Deck',
20
+ favicon: null,
21
+ scripts: [],
22
+ stylesheets: [],
20
23
  pinned: [],
21
24
  pick: {},
22
25
  include: ['**/*'],
@@ -58,7 +61,25 @@ export function getCardFiles(root, config) {
58
61
  }).filter(s => s.endsWith('.md') || s.endsWith('.html'));
59
62
  }
60
63
 
61
- export function getHtmlTemplate({ title, importMap, initialCardsData, pinnedCardPaths, entryFile, cssFiles = [] }) {
64
+ export function getHtmlTemplate({ title, importMap, initialCardsData, pinnedCardPaths, entryFile, cssFiles = [], favicon, scripts = [], stylesheets = [] }) {
65
+ const scriptTags = scripts.map(s => {
66
+ if (typeof s === 'string') return `<script src="${s}"></script>`;
67
+ const attrs = Object.entries(s).map(([k, v]) => {
68
+ if (v === true) return k;
69
+ return `${k}="${v}"`;
70
+ }).join(' ');
71
+ return `<script ${attrs}></script>`;
72
+ }).join('\n');
73
+
74
+ const styleTags = stylesheets.map(s => {
75
+ if (typeof s === 'string') return `<link rel="stylesheet" href="${s}">`;
76
+ const attrs = Object.entries(s).map(([k, v]) => {
77
+ if (v === true) return k;
78
+ return `${k}="${v}"`;
79
+ }).join(' ');
80
+ return `<link rel="stylesheet" ${attrs}>`;
81
+ }).join('\n');
82
+
62
83
  return `
63
84
  <!doctype html>
64
85
  <html lang="en">
@@ -66,6 +87,8 @@ export function getHtmlTemplate({ title, importMap, initialCardsData, pinnedCard
66
87
  <meta charset="utf-8">
67
88
  <meta name="viewport" content="width=device-width, initial-scale=1">
68
89
  <title>${title}</title>
90
+ ${favicon ? `<link rel="icon" href="${favicon}">` : ''}
91
+ ${styleTags}
69
92
  ${importMap ? `<script type="importmap">${JSON.stringify(importMap)}</script>` : ''}
70
93
  <script>
71
94
  window.__INITIAL_CARDS_DATA__ = ${JSON.stringify(initialCardsData)};
@@ -88,9 +111,13 @@ export function getHtmlTemplate({ title, importMap, initialCardsData, pinnedCard
88
111
  }
89
112
  </style>
90
113
  ${cssFiles.map(file => `<link rel="stylesheet" href="/assets/${file}">`).join('\n')}
114
+ ${scriptTags}
91
115
  </head>
92
116
  <body>
93
117
  <div id="root"></div>
118
+ <div style="display: none;" aria-hidden="true">
119
+ This is a Single Page Application. Agents should look at the <a href="/agents.md">agents.md</a> file for documentation they can read easily.
120
+ </div>
94
121
  <script type="module">
95
122
  import { renderDeck } from '${entryFile}';
96
123
  renderDeck({
package/src/deck-demo.js CHANGED
@@ -222,12 +222,14 @@ function createEngine(src, canonicalSrc) {
222
222
  };
223
223
 
224
224
  const driver = {
225
- panel: (name, render, {pane = 'left', order = undefined} = {}) => {
225
+ panel: (name, render, {pane = 'left', order = undefined, mode = 'shadow', colorScheme = undefined} = {}) => {
226
226
  const panel = {
227
227
  name,
228
228
  pane,
229
229
  render,
230
230
  order,
231
+ mode,
232
+ colorScheme,
231
233
  };
232
234
  engine.dispatch(new CreateOrUpdatePanel(panel));
233
235
  },
@@ -737,7 +739,7 @@ const demoStyle = css`
737
739
  .panel-content.active {
738
740
  pointer-events: auto;
739
741
  overflow: auto;
740
- width: initial;
742
+ width: 100%;
741
743
  }
742
744
  pre > code {
743
745
  padding: 1em;
@@ -820,16 +822,32 @@ class DeckDemo extends HTMLElement {
820
822
  }
821
823
 
822
824
  el._aborter?.abort();
823
-
824
- const div = document.createElement('div');
825
- const shadow = div.attachShadow({mode: 'open'});
826
825
  const aborter = new AbortController();
827
-
828
- el.replaceChildren(div);
829
826
  el._aborter = aborter;
830
827
  el._renderer = p.render;
831
828
 
832
- p.render(shadow, aborter.signal);
829
+ if (p.mode === 'iframe') {
830
+ const iframe = document.createElement('iframe');
831
+ iframe.style.cssText = 'border: none; width: 100%; height: 100%; display: block;';
832
+
833
+ iframe.onload = () => {
834
+ const doc = iframe.contentDocument;
835
+ if (!doc) return;
836
+
837
+ if (p.colorScheme) {
838
+ doc.documentElement.style.colorScheme = p.colorScheme;
839
+ }
840
+ doc.body.style.margin = '0';
841
+
842
+ p.render(doc.body, aborter.signal);
843
+ };
844
+ el.replaceChildren(iframe);
845
+ } else {
846
+ const div = document.createElement('div');
847
+ const shadow = div.attachShadow({mode: 'open'});
848
+ el.replaceChildren(div);
849
+ p.render(shadow, aborter.signal);
850
+ }
833
851
  },
834
852
  $detach: el => {
835
853
  el._aborter?.abort();
package/vite-plugin.js CHANGED
@@ -151,7 +151,10 @@ export default function deckPlugin() {
151
151
  importMap: devConfig.importMap,
152
152
  initialCardsData,
153
153
  pinnedCardPaths: devConfig.pinned,
154
- entryFile: '@3sln/deck'
154
+ entryFile: '@3sln/deck',
155
+ favicon: devConfig.favicon,
156
+ scripts: devConfig.scripts,
157
+ stylesheets: devConfig.stylesheets,
155
158
  });
156
159
  const html = await server.transformIndexHtml(req.url, template);
157
160
  res.end(html);