@leeoohoo/ui-apps-devkit 0.1.4 → 0.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leeoohoo/ui-apps-devkit",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "ChatOS UI Apps DevKit (CLI + templates + sandbox) for building installable ChatOS UI Apps plugins.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -628,6 +628,16 @@ function htmlPage() {
628
628
  }
629
629
  #container { flex: 1 1 auto; min-height:0; overflow:hidden; }
630
630
  #containerInner { height:100%; overflow:auto; }
631
+ body[data-surface='compact'] #headerSlot {
632
+ width: 50vw;
633
+ align-self: flex-end;
634
+ border-left: 1px solid var(--ds-panel-border);
635
+ }
636
+ body[data-surface='compact'] #container {
637
+ width: 50vw;
638
+ align-self: flex-end;
639
+ border-left: 1px solid var(--ds-panel-border);
640
+ }
631
641
  .muted { opacity: 0.7; font-size: 12px; }
632
642
  .bar { display:flex; gap:10px; align-items:center; justify-content:space-between; }
633
643
  .btn {
@@ -795,6 +805,7 @@ function htmlPage() {
795
805
  <div id="sandboxContext" class="muted"></div>
796
806
  <button id="btnLlmConfig" class="btn" type="button">AI Config</button>
797
807
  <button id="btnMcpTest" class="btn" type="button">MCP Test</button>
808
+ <button id="btnHalfApp" class="btn" type="button">半屏</button>
798
809
  <button id="btnInspectorToggle" class="btn" type="button">Inspect</button>
799
810
  <button id="btnReload" class="btn" type="button">Reload</button>
800
811
  </div>
@@ -950,6 +961,7 @@ const llmModelId = $('#llmModelId');
950
961
  const llmStatus = $('#llmStatus');
951
962
  const llmKeyStatus = $('#llmKeyStatus');
952
963
  const btnMcpTest = $('#btnMcpTest');
964
+ const btnHalfApp = $('#btnHalfApp');
953
965
  const mcpPanel = $('#mcpPanel');
954
966
  const btnMcpClose = $('#btnMcpClose');
955
967
  const btnMcpClear = $('#btnMcpClear');
@@ -1021,6 +1033,34 @@ const updateContextStatus = () => {
1021
1033
  sandboxContext.textContent = __SANDBOX__.pluginId + ':' + __SANDBOX__.appId;
1022
1034
  };
1023
1035
 
1036
+ const entryCompactUrl = __SANDBOX__.entryCompactUrl || '';
1037
+ const hasCompactEntry = Boolean(entryCompactUrl);
1038
+ let currentSurface = 'full';
1039
+
1040
+ const updateSurfaceState = (surface) => {
1041
+ currentSurface = surface === 'compact' ? 'compact' : 'full';
1042
+ document.body.dataset.surface = currentSurface;
1043
+ if (btnHalfApp) {
1044
+ if (!hasCompactEntry) {
1045
+ btnHalfApp.disabled = true;
1046
+ btnHalfApp.title = 'plugin.json 未配置 entry.compact.path';
1047
+ delete btnHalfApp.dataset.active;
1048
+ } else {
1049
+ btnHalfApp.disabled = false;
1050
+ btnHalfApp.title = '';
1051
+ btnHalfApp.dataset.active = currentSurface === 'compact' ? '1' : '0';
1052
+ }
1053
+ }
1054
+ if (host?.ui) host.ui.surface = currentSurface;
1055
+ };
1056
+
1057
+ const toggleCompactSurface = () => {
1058
+ if (!hasCompactEntry) return;
1059
+ updateSurfaceState(currentSurface === 'compact' ? 'full' : 'compact');
1060
+ loadAndMount().catch(renderError);
1061
+ updateInspectorIfOpen();
1062
+ };
1063
+
1024
1064
  const isInspectorOpen = () => sandboxInspector && sandboxInspector.style.display === 'flex';
1025
1065
  const isLlmPanelOpen = () => llmPanel && llmPanel.style.display === 'flex';
1026
1066
  const isMcpPanelOpen = () => mcpPanel && mcpPanel.style.display === 'flex';
@@ -1199,7 +1239,13 @@ const collectTokens = () => {
1199
1239
  const readHostContext = () => {
1200
1240
  if (!inspectorEnabled) return null;
1201
1241
  if (typeof host?.context?.get === 'function') return host.context.get();
1202
- return { pluginId: __SANDBOX__.pluginId, appId: __SANDBOX__.appId, theme: currentTheme, bridge: { enabled: true } };
1242
+ return {
1243
+ pluginId: __SANDBOX__.pluginId,
1244
+ appId: __SANDBOX__.appId,
1245
+ theme: currentTheme,
1246
+ surface: currentSurface,
1247
+ bridge: { enabled: true },
1248
+ };
1203
1249
  };
1204
1250
 
1205
1251
  const readThemeInfo = () => ({
@@ -1279,6 +1325,7 @@ if (btnLlmRefresh) btnLlmRefresh.addEventListener('click', () => refreshLlmConfi
1279
1325
  if (btnLlmSave) btnLlmSave.addEventListener('click', () => saveLlmConfig());
1280
1326
  if (btnLlmClear) btnLlmClear.addEventListener('click', () => saveLlmConfig({ clearKey: true }));
1281
1327
  if (btnMcpTest) btnMcpTest.addEventListener('click', () => setMcpPanelOpen(!isMcpPanelOpen()));
1328
+ if (btnHalfApp) btnHalfApp.addEventListener('click', () => toggleCompactSurface());
1282
1329
  if (btnMcpClose) btnMcpClose.addEventListener('click', () => setMcpPanelOpen(false));
1283
1330
  if (btnMcpClear)
1284
1331
  btnMcpClear.addEventListener('click', () => {
@@ -1471,7 +1518,15 @@ const getTheme = () => currentTheme || resolveTheme();
1471
1518
 
1472
1519
  const host = {
1473
1520
  bridge: { enabled: true },
1474
- context: { get: () => ({ pluginId: __SANDBOX__.pluginId, appId: __SANDBOX__.appId, theme: getTheme(), bridge: { enabled: true } }) },
1521
+ context: {
1522
+ get: () => ({
1523
+ pluginId: __SANDBOX__.pluginId,
1524
+ appId: __SANDBOX__.appId,
1525
+ theme: getTheme(),
1526
+ surface: currentSurface,
1527
+ bridge: { enabled: true },
1528
+ }),
1529
+ },
1475
1530
  theme: {
1476
1531
  get: getTheme,
1477
1532
  onChange: (listener) => {
@@ -1524,7 +1579,7 @@ const host = {
1524
1579
  close: () => (setPanelOpen(false), { ok: true }),
1525
1580
  toggle: () => (setPanelOpen(panel.style.display !== 'flex'), { ok: true }),
1526
1581
  },
1527
- ui: { navigate: (menu) => ({ ok: true, menu }) },
1582
+ ui: { navigate: (menu) => ({ ok: true, menu }), surface: 'full' },
1528
1583
  chat: (() => {
1529
1584
  const clone = (v) => JSON.parse(JSON.stringify(v));
1530
1585
 
@@ -1779,6 +1834,8 @@ const host = {
1779
1834
  })(),
1780
1835
  };
1781
1836
 
1837
+ updateSurfaceState('full');
1838
+
1782
1839
  inspectorEnabled = true;
1783
1840
  updateInspector();
1784
1841
 
@@ -1788,7 +1845,7 @@ async function loadAndMount() {
1788
1845
  if (typeof dispose === 'function') { try { await dispose(); } catch {} dispose = null; }
1789
1846
  container.textContent = '';
1790
1847
 
1791
- const entryUrl = __SANDBOX__.entryUrl;
1848
+ const entryUrl = currentSurface === 'compact' && entryCompactUrl ? entryCompactUrl : __SANDBOX__.entryUrl;
1792
1849
  const mod = await import(entryUrl + (entryUrl.includes('?') ? '&' : '?') + 't=' + Date.now());
1793
1850
  const mount = mod?.mount || mod?.default?.mount || (typeof mod?.default === 'function' ? mod.default : null);
1794
1851
  if (typeof mount !== 'function') throw new Error('module entry must export mount()');
@@ -1851,7 +1908,13 @@ export async function startSandboxServer({ pluginDir, port = 4399, appId = '' })
1851
1908
  const entryAbs = resolveInsideDir(pluginDir, entryRel);
1852
1909
  if (!isFile(entryAbs)) throw new Error(`module entry not found: ${entryRel}`);
1853
1910
 
1911
+ const entryCompactRel = String(app?.entry?.compact?.path || '').trim();
1854
1912
  const entryUrl = `/plugin/${encodeURIComponent(entryRel).replaceAll('%2F', '/')}`;
1913
+ const entryCompactAbs = entryCompactRel ? resolveInsideDir(pluginDir, entryCompactRel) : '';
1914
+ const entryCompactUrl =
1915
+ entryCompactAbs && isFile(entryCompactAbs)
1916
+ ? `/plugin/${encodeURIComponent(entryCompactRel).replaceAll('%2F', '/')}`
1917
+ : '';
1855
1918
 
1856
1919
  let backendInstance = null;
1857
1920
  let backendFactory = null;
@@ -2165,6 +2228,7 @@ export async function startSandboxServer({ pluginDir, port = 4399, appId = '' })
2165
2228
  .replaceAll('__SANDBOX__.pluginId', JSON.stringify(ctxBase.pluginId))
2166
2229
  .replaceAll('__SANDBOX__.appId', JSON.stringify(effectiveAppId))
2167
2230
  .replaceAll('__SANDBOX__.entryUrl', JSON.stringify(entryUrl))
2231
+ .replaceAll('__SANDBOX__.entryCompactUrl', JSON.stringify(entryCompactUrl))
2168
2232
  .replaceAll('__SANDBOX__.registryApp', JSON.stringify({ plugin: { id: ctxBase.pluginId }, id: effectiveAppId, entry: { type: 'module', url: entryUrl } }))
2169
2233
  .replaceAll('__SANDBOX__.tokenNames', JSON.stringify(tokenNames))
2170
2234
  .replaceAll('__SANDBOX__.paths', JSON.stringify(sandboxPaths));