@leeoohoo/ui-apps-devkit 0.1.5 → 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.5",
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 {
@@ -739,16 +749,6 @@ function htmlPage() {
739
749
  box-shadow: 0 14px 40px rgba(0,0,0,0.16);
740
750
  z-index: 12;
741
751
  }
742
- #mcpPanel[data-mode='half'] {
743
- right: 0;
744
- top: 72px;
745
- width: 50vw;
746
- height: calc(100vh - 84px);
747
- max-height: none;
748
- border-right: none;
749
- border-top-right-radius: 0;
750
- border-bottom-right-radius: 0;
751
- }
752
752
  #mcpPanelHeader {
753
753
  padding: 10px 12px;
754
754
  display:flex;
@@ -805,7 +805,7 @@ function htmlPage() {
805
805
  <div id="sandboxContext" class="muted"></div>
806
806
  <button id="btnLlmConfig" class="btn" type="button">AI Config</button>
807
807
  <button id="btnMcpTest" class="btn" type="button">MCP Test</button>
808
- <button id="btnMcpHalf" class="btn" type="button">Half Test</button>
808
+ <button id="btnHalfApp" class="btn" type="button">半屏</button>
809
809
  <button id="btnInspectorToggle" class="btn" type="button">Inspect</button>
810
810
  <button id="btnReload" class="btn" type="button">Reload</button>
811
811
  </div>
@@ -961,7 +961,7 @@ const llmModelId = $('#llmModelId');
961
961
  const llmStatus = $('#llmStatus');
962
962
  const llmKeyStatus = $('#llmKeyStatus');
963
963
  const btnMcpTest = $('#btnMcpTest');
964
- const btnMcpHalf = $('#btnMcpHalf');
964
+ const btnHalfApp = $('#btnHalfApp');
965
965
  const mcpPanel = $('#mcpPanel');
966
966
  const btnMcpClose = $('#btnMcpClose');
967
967
  const btnMcpClear = $('#btnMcpClear');
@@ -1033,6 +1033,34 @@ const updateContextStatus = () => {
1033
1033
  sandboxContext.textContent = __SANDBOX__.pluginId + ':' + __SANDBOX__.appId;
1034
1034
  };
1035
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
+
1036
1064
  const isInspectorOpen = () => sandboxInspector && sandboxInspector.style.display === 'flex';
1037
1065
  const isLlmPanelOpen = () => llmPanel && llmPanel.style.display === 'flex';
1038
1066
  const isMcpPanelOpen = () => mcpPanel && mcpPanel.style.display === 'flex';
@@ -1140,13 +1168,8 @@ const refreshMcpConfigHint = async () => {
1140
1168
  }
1141
1169
  };
1142
1170
 
1143
- const setMcpPanelOpen = (open, mode) => {
1171
+ const setMcpPanelOpen = (open) => {
1144
1172
  if (!mcpPanel) return;
1145
- if (mode) {
1146
- mcpPanel.dataset.mode = mode;
1147
- } else {
1148
- delete mcpPanel.dataset.mode;
1149
- }
1150
1173
  mcpPanel.style.display = open ? 'flex' : 'none';
1151
1174
  mcpPanel.setAttribute('aria-hidden', open ? 'false' : 'true');
1152
1175
  if (open) {
@@ -1155,16 +1178,6 @@ const setMcpPanelOpen = (open, mode) => {
1155
1178
  }
1156
1179
  };
1157
1180
 
1158
- const toggleMcpPanel = (mode) => {
1159
- if (!mcpPanel) return;
1160
- const currentMode = mcpPanel.dataset.mode || 'panel';
1161
- if (isMcpPanelOpen() && currentMode === mode) {
1162
- setMcpPanelOpen(false);
1163
- return;
1164
- }
1165
- setMcpPanelOpen(true, mode);
1166
- };
1167
-
1168
1181
  const runMcpTest = async () => {
1169
1182
  const sendBtn = btnMcpSend;
1170
1183
  try {
@@ -1226,7 +1239,13 @@ const collectTokens = () => {
1226
1239
  const readHostContext = () => {
1227
1240
  if (!inspectorEnabled) return null;
1228
1241
  if (typeof host?.context?.get === 'function') return host.context.get();
1229
- 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
+ };
1230
1249
  };
1231
1250
 
1232
1251
  const readThemeInfo = () => ({
@@ -1305,8 +1324,8 @@ if (btnLlmClose) btnLlmClose.addEventListener('click', () => setLlmPanelOpen(fal
1305
1324
  if (btnLlmRefresh) btnLlmRefresh.addEventListener('click', () => refreshLlmConfig());
1306
1325
  if (btnLlmSave) btnLlmSave.addEventListener('click', () => saveLlmConfig());
1307
1326
  if (btnLlmClear) btnLlmClear.addEventListener('click', () => saveLlmConfig({ clearKey: true }));
1308
- if (btnMcpTest) btnMcpTest.addEventListener('click', () => toggleMcpPanel('panel'));
1309
- if (btnMcpHalf) btnMcpHalf.addEventListener('click', () => toggleMcpPanel('half'));
1327
+ if (btnMcpTest) btnMcpTest.addEventListener('click', () => setMcpPanelOpen(!isMcpPanelOpen()));
1328
+ if (btnHalfApp) btnHalfApp.addEventListener('click', () => toggleCompactSurface());
1310
1329
  if (btnMcpClose) btnMcpClose.addEventListener('click', () => setMcpPanelOpen(false));
1311
1330
  if (btnMcpClear)
1312
1331
  btnMcpClear.addEventListener('click', () => {
@@ -1499,7 +1518,15 @@ const getTheme = () => currentTheme || resolveTheme();
1499
1518
 
1500
1519
  const host = {
1501
1520
  bridge: { enabled: true },
1502
- 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
+ },
1503
1530
  theme: {
1504
1531
  get: getTheme,
1505
1532
  onChange: (listener) => {
@@ -1552,7 +1579,7 @@ const host = {
1552
1579
  close: () => (setPanelOpen(false), { ok: true }),
1553
1580
  toggle: () => (setPanelOpen(panel.style.display !== 'flex'), { ok: true }),
1554
1581
  },
1555
- ui: { navigate: (menu) => ({ ok: true, menu }) },
1582
+ ui: { navigate: (menu) => ({ ok: true, menu }), surface: 'full' },
1556
1583
  chat: (() => {
1557
1584
  const clone = (v) => JSON.parse(JSON.stringify(v));
1558
1585
 
@@ -1807,6 +1834,8 @@ const host = {
1807
1834
  })(),
1808
1835
  };
1809
1836
 
1837
+ updateSurfaceState('full');
1838
+
1810
1839
  inspectorEnabled = true;
1811
1840
  updateInspector();
1812
1841
 
@@ -1816,7 +1845,7 @@ async function loadAndMount() {
1816
1845
  if (typeof dispose === 'function') { try { await dispose(); } catch {} dispose = null; }
1817
1846
  container.textContent = '';
1818
1847
 
1819
- const entryUrl = __SANDBOX__.entryUrl;
1848
+ const entryUrl = currentSurface === 'compact' && entryCompactUrl ? entryCompactUrl : __SANDBOX__.entryUrl;
1820
1849
  const mod = await import(entryUrl + (entryUrl.includes('?') ? '&' : '?') + 't=' + Date.now());
1821
1850
  const mount = mod?.mount || mod?.default?.mount || (typeof mod?.default === 'function' ? mod.default : null);
1822
1851
  if (typeof mount !== 'function') throw new Error('module entry must export mount()');
@@ -1879,7 +1908,13 @@ export async function startSandboxServer({ pluginDir, port = 4399, appId = '' })
1879
1908
  const entryAbs = resolveInsideDir(pluginDir, entryRel);
1880
1909
  if (!isFile(entryAbs)) throw new Error(`module entry not found: ${entryRel}`);
1881
1910
 
1911
+ const entryCompactRel = String(app?.entry?.compact?.path || '').trim();
1882
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
+ : '';
1883
1918
 
1884
1919
  let backendInstance = null;
1885
1920
  let backendFactory = null;
@@ -2193,6 +2228,7 @@ export async function startSandboxServer({ pluginDir, port = 4399, appId = '' })
2193
2228
  .replaceAll('__SANDBOX__.pluginId', JSON.stringify(ctxBase.pluginId))
2194
2229
  .replaceAll('__SANDBOX__.appId', JSON.stringify(effectiveAppId))
2195
2230
  .replaceAll('__SANDBOX__.entryUrl', JSON.stringify(entryUrl))
2231
+ .replaceAll('__SANDBOX__.entryCompactUrl', JSON.stringify(entryCompactUrl))
2196
2232
  .replaceAll('__SANDBOX__.registryApp', JSON.stringify({ plugin: { id: ctxBase.pluginId }, id: effectiveAppId, entry: { type: 'module', url: entryUrl } }))
2197
2233
  .replaceAll('__SANDBOX__.tokenNames', JSON.stringify(tokenNames))
2198
2234
  .replaceAll('__SANDBOX__.paths', JSON.stringify(sandboxPaths));