diff --git a/apps/x/apps/main/src/main.ts b/apps/x/apps/main/src/main.ts index 97704225..aef29945 100644 --- a/apps/x/apps/main/src/main.ts +++ b/apps/x/apps/main/src/main.ts @@ -25,6 +25,7 @@ import { init as initAgentRunner } from "@x/core/dist/agent-schedule/runner.js"; import { init as initAgentNotes } from "@x/core/dist/knowledge/agent_notes.js"; import { init as initTrackScheduler } from "@x/core/dist/knowledge/track/scheduler.js"; import { init as initTrackEventProcessor } from "@x/core/dist/knowledge/track/events.js"; +import { init as initLocalSites } from "@x/core/dist/local-sites/server.js"; import { initConfigs } from "@x/core/dist/config/initConfigs.js"; import started from "electron-squirrel-startup"; @@ -291,6 +292,11 @@ app.whenReady().then(async () => { // start chrome extension sync server initChromeSync(); + // start local sites server for iframe dashboards and other mini apps + initLocalSites().catch((error) => { + console.error('[LocalSites] Failed to start:', error); + }); + app.on("activate", () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); diff --git a/apps/x/apps/renderer/src/components/markdown-editor.tsx b/apps/x/apps/renderer/src/components/markdown-editor.tsx index 3d22c646..49915dc0 100644 --- a/apps/x/apps/renderer/src/components/markdown-editor.tsx +++ b/apps/x/apps/renderer/src/components/markdown-editor.tsx @@ -13,6 +13,7 @@ import { TrackBlockExtension } from '@/extensions/track-block' import { TrackTargetOpenExtension, TrackTargetCloseExtension } from '@/extensions/track-target' import { ImageBlockExtension } from '@/extensions/image-block' import { EmbedBlockExtension } from '@/extensions/embed-block' +import { IframeBlockExtension } from '@/extensions/iframe-block' import { ChartBlockExtension } from '@/extensions/chart-block' import { TableBlockExtension } from '@/extensions/table-block' import { CalendarBlockExtension } from '@/extensions/calendar-block' @@ -177,6 +178,8 @@ function blockToMarkdown(node: JsonNode): string { return '```image\n' + (node.attrs?.data as string || '{}') + '\n```' case 'embedBlock': return '```embed\n' + (node.attrs?.data as string || '{}') + '\n```' + case 'iframeBlock': + return '```iframe\n' + (node.attrs?.data as string || '{}') + '\n```' case 'chartBlock': return '```chart\n' + (node.attrs?.data as string || '{}') + '\n```' case 'tableBlock': @@ -676,6 +679,7 @@ export const MarkdownEditor = forwardRef }; deleteNode: () => void }) { + const raw = node.attrs.data as string + let config: blocks.IframeBlock | null = null + + try { + config = blocks.IframeBlockSchema.parse(JSON.parse(raw)) + } catch { + // fallback below + } + + if (!config) { + return ( + +
+ + Invalid iframe block +
+
+ ) + } + + const meta = getIframeMeta(config.url) + const title = config.title || meta?.host || 'Embedded page' + const allow = config.allow || DEFAULT_IFRAME_ALLOW + const height = config.height ?? DEFAULT_IFRAME_HEIGHT + + return ( + +
+ +
+
+
+ + Iframe +
+
+
{title}
+ {meta && ( +
+ {meta.host} + {meta.path} +
+ )} +
+
+ + + Open + +
+
+