labull-framework 1.0.1 → 1.0.3
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/README.md +6 -1
- package/dist/magic-block.js +2538 -1962
- package/dist/style.css +1 -1
- package/dist/types/magic-block-MagicBlockDialogs.vue.d.ts +2 -0
- package/dist/types/magic-block-bridge.d.ts +13 -1
- package/dist/types/magic-block-dialogs.d.ts +139 -0
- package/dist/types/magic-block-localfs.d.ts +23 -1
- package/dist/types/magic-block-preview.d.ts +18 -0
- package/dist/types/magic-block-real.d.ts +4 -2
- package/dist/types/magic-block-theme.d.ts +29 -0
- package/package.json +1 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
export default _default;
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
export declare const BLOCK_NO_SCROLL_CSS = "html, body { overflow: hidden !important; }";
|
|
13
13
|
/** 注入到块 iframe 里的 JS 源码(块没有构建步骤,它必须自己就是能跑的 JS)。
|
|
14
14
|
* 🔴 这是模板字符串:段内的注释出现反引号会提前截断它(实现也一律用字符串拼接)。 */
|
|
15
|
-
export declare const CLIENT_SOURCE = "\n(function () {\n var seq = 0, waiting = {}, queue = [], ready = false, last = -1;\n function call(method, args) {\n return new Promise(function (resolve, reject) {\n function run() {\n var id = ++seq;\n waiting[id] = { resolve: resolve, reject: reject };\n parent.postMessage({ '__mb': 'call', id: id, method: method, args: args }, '*');\n }\n if (ready) run(); else queue.push(run);\n });\n }\n window.addEventListener('message', function (event) {\n var data = event.data, pending;\n if (!data || data['__mb'] === undefined) return;\n if (data['__mb'] === 'host-ready') { ready = true; queue.splice(0).forEach(function (run) { run(); }); return; }\n if (data['__mb'] !== 'reply') return;\n if (!(pending = waiting[data.id])) return;\n delete waiting[data.id];\n if (data.ok) pending.resolve(data.value); else pending.reject(new Error(data.error || '\u8C03\u7528\u5931\u8D25'));\n });\n window.magicBlock = {\n getMe: function () { return call('getMe', []); },\n navigateTab: function (path) { return call('navigateTab', [path]); },\n context: function () { return call('context', []); },\n setTabTitle: function (title, icon) { return call('setTabTitle', [title, icon]); }\n };\n function make(prefix) {\n return new Proxy(function () {}, {\n get: function (_, name) { return make(prefix ? prefix + '.' + name : name); },\n apply: function (_, __, args) {\n var params = args[0] === undefined || args[0] === null ? {} : args[0];\n if (typeof params !== 'object' || Array.isArray(params)) return Promise.reject(new Error('magicBackendApi.' + prefix + '(params) \u7684\u53C2\u6570\u8981\u4E48\u662F\u4E00\u4E2A\u5BF9\u8C61\uFF08\u62FF\u4E0D\u51C6\u5C31\u4F20 {}\uFF09\uFF0C\u8981\u4E48\u662F\u4E00\u4E2A\u6587\u4EF6\uFF08File / Blob\uFF09'));\n return call('backend', [prefix, params]);\n }\n });\n }\n window.magicBackendApi = make('');\n if (typeof document === 'undefined') return;\n function report() {\n var body = document.body;\n if (!body) return;\n var max = -1, style = window.getComputedStyle(body), bodyTop = body.getBoundingClientRect().top;\n var gap = (parseFloat(style.marginTop) || 0) + (parseFloat(style.marginBottom) || 0);\n try {\n for (var i = 0; i < body.children.length; i += 1) {\n var el = body.children[i], cs = window.getComputedStyle(el), rect = el.getBoundingClientRect();\n if (cs.display === 'none' || cs.visibility === 'hidden' || cs.position === 'fixed' || rect.height <= 0) continue;\n if (rect.bottom > max) max = rect.bottom;\n }\n } catch (error) { return; }\n var height = max < 0 ? 0 : Math.ceil(max - bodyTop + gap); /* \u4E00\u6761\u6B63\u5E38\u6D41\u5185\u5BB9\u90FD\u6CA1\u6709 \u21D2 \u62A5 0\uFF08\u5BBF\u4E3B\u6309\u8FD8\u6CA1\u91CF\u5230\u5904\u7406\uFF09 */\n /* \uD83D\uDD34 +1 \u662F\"\u53D6\u6574\u7684\u4F59\u91CF\"\uFF1A\u51E0\u4F55\u4E0A\u7B97\u51FA\u6765\u7684\u5185\u5BB9\u9AD8\u5EA6\u5E38\u5E38\u5DEE\u90A3\u4E48\u96F6\u70B9\u51E0\u50CF\u7D20\uFF08\u5B50\u50CF\u7D20\u6392\u7248\uFF09\uFF0C\n * \u5BBF\u4E3B\u7167\u7740\u5B83\u8BBE\u9AD8\u5EA6\u65F6\u5C31\u4F1A**\u5DEE\u4E00\u50CF\u7D20**\u2014\u2014\u90A3\u4E00\u50CF\u7D20\u6B63\u662F\u4E00\u4E2A\u7AD6\u6EDA\u52A8\u6761\u7684\u5F00\u5173\u3002 */\n if (height > 0) height += 1;\n if (height === last) return; /* \u4E00\u6837\u9AD8\u5C31\u4E0D\u518D\u53D1\uFF1A\u5426\u5219\u300C\u5BBF\u4E3B\u6539\u9AD8\u5EA6 \u2192 \u89E6\u53D1\u89C2\u5BDF \u2192 \u53C8\u53D1\u4E00\u6B21\u300D\u4F1A\u8F6C\u4E2A\u4E0D\u505C */\n last = height;\n parent.postMessage({ '__mb': 'height', height: height }, '*');\n }\n function start() {\n report();\n if (window.ResizeObserver && document.body) new ResizeObserver(report).observe(document.body);\n }\n if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();\n setInterval(report, 500);\n})();\n";
|
|
15
|
+
export declare const CLIENT_SOURCE = "\n(function () {\n var seq = 0, waiting = {}, queue = [], ready = false, last = -1;\n function call(method, args) {\n return new Promise(function (resolve, reject) {\n function run() {\n var id = ++seq;\n waiting[id] = { resolve: resolve, reject: reject };\n parent.postMessage({ '__mb': 'call', id: id, method: method, args: args }, '*');\n }\n if (ready) run(); else queue.push(run);\n });\n }\n window.addEventListener('message', function (event) {\n var data = event.data, pending;\n if (!data || data['__mb'] === undefined) return;\n if (data['__mb'] === 'host-ready') { ready = true; queue.splice(0).forEach(function (run) { run(); }); return; }\n if (data['__mb'] !== 'reply') return;\n if (!(pending = waiting[data.id])) return;\n delete waiting[data.id];\n if (data.ok) pending.resolve(data.value); else pending.reject(new Error(data.error || '\u8C03\u7528\u5931\u8D25'));\n });\n window.magicBlock = {\n getMe: function () { return call('getMe', []); },\n navigateTab: function (path) { return call('navigateTab', [path]); },\n context: function () { return call('context', []); },\n setTabTitle: function (title, icon) { return call('setTabTitle', [title, icon]); },\n /* \u5F39\u7A97\uFF1A**\u7531\u5BBF\u4E3B\u9875\u9762\u753B**\uFF08\u5757\u81EA\u5DF1\u753B\u7684\u6D6E\u5C42\u57FA\u51C6\u662F\"\u6574\u5757\u9AD8\u5EA6\"\uFF0C\u89C1 MagicBlockHost \u9876\u4E0A\u90A3\u4E00\u6BB5\uFF09\u3002 */\n dialog: function (options) { return call('dialog', [options]); },\n /* \u26A0\uFE0F \u53EA\u5728 magicBlock.dialog({ html }) \u6253\u5F00\u7684**\u5F39\u7A97\u6587\u6863**\u91CC\u6709\u610F\u4E49\uFF08\u5757\u9875\u91CC\u8C03\u4F1A\u62FF\u5230\u4E00\u53E5\u660E\u786E\u62A5\u9519\uFF09\u3002\n \u8FD9\u6BB5\u662F\u6A21\u677F\u5B57\u7B26\u4E32\uFF1A\u91CC\u9762\u7684\u6CE8\u91CA**\u4E0D\u8BB8\u51FA\u73B0\u53CD\u5F15\u53F7**\uFF08\u4F1A\u63D0\u524D\u622A\u65AD\uFF0C\u89C1\u6587\u4EF6\u5934\u90A3\u4E00\u6761\uFF09\u3002 */\n done: function (payload) { return call('done', [payload]); }\n };\n function make(prefix) {\n return new Proxy(function () {}, {\n get: function (_, name) { return make(prefix ? prefix + '.' + name : name); },\n apply: function (_, __, args) {\n var params = args[0] === undefined || args[0] === null ? {} : args[0];\n if (typeof params !== 'object' || Array.isArray(params)) return Promise.reject(new Error('magicBackendApi.' + prefix + '(params) \u7684\u53C2\u6570\u8981\u4E48\u662F\u4E00\u4E2A\u5BF9\u8C61\uFF08\u62FF\u4E0D\u51C6\u5C31\u4F20 {}\uFF09\uFF0C\u8981\u4E48\u662F\u4E00\u4E2A\u6587\u4EF6\uFF08File / Blob\uFF09'));\n return call('backend', [prefix, params]);\n }\n });\n }\n window.magicBackendApi = make('');\n if (typeof document === 'undefined') return;\n function report() {\n var body = document.body;\n if (!body) return;\n var max = -1, style = window.getComputedStyle(body), bodyTop = body.getBoundingClientRect().top;\n /* \uD83D\uDD34 gap \u91CC\u4E3A\u4EC0\u4E48\u6709 paddingBottom\uFF1Amax - bodyTop \u91CF\u7684\u5C31\u662F\"body \u4E0A\u8FB9\u6846 \u2192 \u6700\u540E\u4E00\u4E2A\u5B69\u5B50\u7684\u5E95\u8FB9\"\uFF0C\n * \u5B83**\u5DF2\u7ECF\u542B\u7740 padding-top \u4E86**\uFF0C\u6240\u4EE5\u53EA\u5DEE\u5E95\u90E8\u90A3\u4E00\u622A\u3002\u5C11\u4E86\u5B83\uFF0C\u5BBF\u4E3B\u6309\u8FD9\u4E2A\u9AD8\u5EA6\u7ED9 iframe \u5B9A\u9AD8\u65F6\n * \u4F1A\u628A body \u7684 padding-bottom \u5403\u6389\uFF08\u5757\u81EA\u5DF1\u7684 body { padding: 12px } \u5C31\u662F\u8FD9\u4E48\u88AB\u54AC\u6389\u4E00\u622A\u7684\uFF09\u3002\n * \u26A0\uFE0F \u8FD9\u4E00\u6BB5\u5728\u6A21\u677F\u5B57\u7B26\u4E32\u91CC\uFF1A\u6CE8\u91CA\u91CC**\u4E0D\u8BB8\u51FA\u73B0\u53CD\u5F15\u53F7**\uFF08\u4F1A\u63D0\u524D\u622A\u65AD\uFF0C\u89C1\u6587\u4EF6\u5934\u90A3\u4E00\u6761\uFF09\u3002 */\n var gap = (parseFloat(style.marginTop) || 0) + (parseFloat(style.marginBottom) || 0) + (parseFloat(style.paddingBottom) || 0);\n try {\n for (var i = 0; i < body.children.length; i += 1) {\n var el = body.children[i], cs = window.getComputedStyle(el), rect = el.getBoundingClientRect();\n if (cs.display === 'none' || cs.visibility === 'hidden' || cs.position === 'fixed' || rect.height <= 0) continue;\n if (rect.bottom > max) max = rect.bottom;\n }\n } catch (error) { return; }\n var height = max < 0 ? 0 : Math.ceil(max - bodyTop + gap); /* \u4E00\u6761\u6B63\u5E38\u6D41\u5185\u5BB9\u90FD\u6CA1\u6709 \u21D2 \u62A5 0\uFF08\u5BBF\u4E3B\u6309\u8FD8\u6CA1\u91CF\u5230\u5904\u7406\uFF09 */\n /* \uD83D\uDD34 +1 \u662F\"\u53D6\u6574\u7684\u4F59\u91CF\"\uFF1A\u51E0\u4F55\u4E0A\u7B97\u51FA\u6765\u7684\u5185\u5BB9\u9AD8\u5EA6\u5E38\u5E38\u5DEE\u90A3\u4E48\u96F6\u70B9\u51E0\u50CF\u7D20\uFF08\u5B50\u50CF\u7D20\u6392\u7248\uFF09\uFF0C\n * \u5BBF\u4E3B\u7167\u7740\u5B83\u8BBE\u9AD8\u5EA6\u65F6\u5C31\u4F1A**\u5DEE\u4E00\u50CF\u7D20**\u2014\u2014\u90A3\u4E00\u50CF\u7D20\u6B63\u662F\u4E00\u4E2A\u7AD6\u6EDA\u52A8\u6761\u7684\u5F00\u5173\u3002 */\n if (height > 0) height += 1;\n if (height === last) return; /* \u4E00\u6837\u9AD8\u5C31\u4E0D\u518D\u53D1\uFF1A\u5426\u5219\u300C\u5BBF\u4E3B\u6539\u9AD8\u5EA6 \u2192 \u89E6\u53D1\u89C2\u5BDF \u2192 \u53C8\u53D1\u4E00\u6B21\u300D\u4F1A\u8F6C\u4E2A\u4E0D\u505C */\n last = height;\n parent.postMessage({ '__mb': 'height', height: height }, '*');\n }\n function start() {\n report();\n if (window.ResizeObserver && document.body) new ResizeObserver(report).observe(document.body);\n }\n if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();\n setInterval(report, 500);\n})();\n";
|
|
16
|
+
export declare const DIALOG_BOOTSTRAP = "\n(function () {\n var seq = 0, waiting = {}, queue = [], ready = false, last = -1;\n function call(method, args) {\n return new Promise(function (resolve, reject) {\n function run() {\n var id = ++seq;\n waiting[id] = { resolve: resolve, reject: reject };\n parent.postMessage({ '__mb': 'call', id: id, method: method, args: args }, '*');\n }\n if (ready) run(); else queue.push(run);\n });\n }\n window.addEventListener('message', function (event) {\n var data = event.data, pending;\n if (!data || data['__mb'] === undefined) return;\n if (data['__mb'] === 'host-ready') { ready = true; queue.splice(0).forEach(function (run) { run(); }); return; }\n if (data['__mb'] !== 'reply') return;\n if (!(pending = waiting[data.id])) return;\n delete waiting[data.id];\n if (data.ok) pending.resolve(data.value); else pending.reject(new Error(data.error || '\u8C03\u7528\u5931\u8D25'));\n });\n window.magicBlock = {\n getMe: function () { return call('getMe', []); },\n navigateTab: function (path) { return call('navigateTab', [path]); },\n context: function () { return call('context', []); },\n setTabTitle: function (title, icon) { return call('setTabTitle', [title, icon]); },\n /* \u5F39\u7A97\uFF1A**\u7531\u5BBF\u4E3B\u9875\u9762\u753B**\uFF08\u5757\u81EA\u5DF1\u753B\u7684\u6D6E\u5C42\u57FA\u51C6\u662F\"\u6574\u5757\u9AD8\u5EA6\"\uFF0C\u89C1 MagicBlockHost \u9876\u4E0A\u90A3\u4E00\u6BB5\uFF09\u3002 */\n dialog: function (options) { return call('dialog', [options]); },\n /* \u26A0\uFE0F \u53EA\u5728 magicBlock.dialog({ html }) \u6253\u5F00\u7684**\u5F39\u7A97\u6587\u6863**\u91CC\u6709\u610F\u4E49\uFF08\u5757\u9875\u91CC\u8C03\u4F1A\u62FF\u5230\u4E00\u53E5\u660E\u786E\u62A5\u9519\uFF09\u3002\n \u8FD9\u6BB5\u662F\u6A21\u677F\u5B57\u7B26\u4E32\uFF1A\u91CC\u9762\u7684\u6CE8\u91CA**\u4E0D\u8BB8\u51FA\u73B0\u53CD\u5F15\u53F7**\uFF08\u4F1A\u63D0\u524D\u622A\u65AD\uFF0C\u89C1\u6587\u4EF6\u5934\u90A3\u4E00\u6761\uFF09\u3002 */\n done: function (payload) { return call('done', [payload]); }\n };\n function make(prefix) {\n return new Proxy(function () {}, {\n get: function (_, name) { return make(prefix ? prefix + '.' + name : name); },\n apply: function (_, __, args) {\n var params = args[0] === undefined || args[0] === null ? {} : args[0];\n if (typeof params !== 'object' || Array.isArray(params)) return Promise.reject(new Error('magicBackendApi.' + prefix + '(params) \u7684\u53C2\u6570\u8981\u4E48\u662F\u4E00\u4E2A\u5BF9\u8C61\uFF08\u62FF\u4E0D\u51C6\u5C31\u4F20 {}\uFF09\uFF0C\u8981\u4E48\u662F\u4E00\u4E2A\u6587\u4EF6\uFF08File / Blob\uFF09'));\n return call('backend', [prefix, params]);\n }\n });\n }\n window.magicBackendApi = make('');\n if (typeof document === 'undefined') return;\n function report() {\n var body = document.body;\n if (!body) return;\n var max = -1, style = window.getComputedStyle(body), bodyTop = body.getBoundingClientRect().top;\n /* \uD83D\uDD34 gap \u91CC\u4E3A\u4EC0\u4E48\u6709 paddingBottom\uFF1Amax - bodyTop \u91CF\u7684\u5C31\u662F\"body \u4E0A\u8FB9\u6846 \u2192 \u6700\u540E\u4E00\u4E2A\u5B69\u5B50\u7684\u5E95\u8FB9\"\uFF0C\n * \u5B83**\u5DF2\u7ECF\u542B\u7740 padding-top \u4E86**\uFF0C\u6240\u4EE5\u53EA\u5DEE\u5E95\u90E8\u90A3\u4E00\u622A\u3002\u5C11\u4E86\u5B83\uFF0C\u5BBF\u4E3B\u6309\u8FD9\u4E2A\u9AD8\u5EA6\u7ED9 iframe \u5B9A\u9AD8\u65F6\n * \u4F1A\u628A body \u7684 padding-bottom \u5403\u6389\uFF08\u5757\u81EA\u5DF1\u7684 body { padding: 12px } \u5C31\u662F\u8FD9\u4E48\u88AB\u54AC\u6389\u4E00\u622A\u7684\uFF09\u3002\n * \u26A0\uFE0F \u8FD9\u4E00\u6BB5\u5728\u6A21\u677F\u5B57\u7B26\u4E32\u91CC\uFF1A\u6CE8\u91CA\u91CC**\u4E0D\u8BB8\u51FA\u73B0\u53CD\u5F15\u53F7**\uFF08\u4F1A\u63D0\u524D\u622A\u65AD\uFF0C\u89C1\u6587\u4EF6\u5934\u90A3\u4E00\u6761\uFF09\u3002 */\n var gap = (parseFloat(style.marginTop) || 0) + (parseFloat(style.marginBottom) || 0) + (parseFloat(style.paddingBottom) || 0);\n try {\n for (var i = 0; i < body.children.length; i += 1) {\n var el = body.children[i], cs = window.getComputedStyle(el), rect = el.getBoundingClientRect();\n if (cs.display === 'none' || cs.visibility === 'hidden' || cs.position === 'fixed' || rect.height <= 0) continue;\n if (rect.bottom > max) max = rect.bottom;\n }\n } catch (error) { return; }\n var height = max < 0 ? 0 : Math.ceil(max - bodyTop + gap); /* \u4E00\u6761\u6B63\u5E38\u6D41\u5185\u5BB9\u90FD\u6CA1\u6709 \u21D2 \u62A5 0\uFF08\u5BBF\u4E3B\u6309\u8FD8\u6CA1\u91CF\u5230\u5904\u7406\uFF09 */\n /* \uD83D\uDD34 +1 \u662F\"\u53D6\u6574\u7684\u4F59\u91CF\"\uFF1A\u51E0\u4F55\u4E0A\u7B97\u51FA\u6765\u7684\u5185\u5BB9\u9AD8\u5EA6\u5E38\u5E38\u5DEE\u90A3\u4E48\u96F6\u70B9\u51E0\u50CF\u7D20\uFF08\u5B50\u50CF\u7D20\u6392\u7248\uFF09\uFF0C\n * \u5BBF\u4E3B\u7167\u7740\u5B83\u8BBE\u9AD8\u5EA6\u65F6\u5C31\u4F1A**\u5DEE\u4E00\u50CF\u7D20**\u2014\u2014\u90A3\u4E00\u50CF\u7D20\u6B63\u662F\u4E00\u4E2A\u7AD6\u6EDA\u52A8\u6761\u7684\u5F00\u5173\u3002 */\n if (height > 0) height += 1;\n if (height === last) return; /* \u4E00\u6837\u9AD8\u5C31\u4E0D\u518D\u53D1\uFF1A\u5426\u5219\u300C\u5BBF\u4E3B\u6539\u9AD8\u5EA6 \u2192 \u89E6\u53D1\u89C2\u5BDF \u2192 \u53C8\u53D1\u4E00\u6B21\u300D\u4F1A\u8F6C\u4E2A\u4E0D\u505C */\n last = height;\n parent.postMessage({ '__mb': 'height', height: height }, '*');\n }\n function start() {\n report();\n if (window.ResizeObserver && document.body) new ResizeObserver(report).observe(document.body);\n }\n if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();\n setInterval(report, 500);\n})();\n\n(function () {\n window.addEventListener('keydown', function (event) {\n if (event.key !== 'Escape') return;\n try { var sent = window.magicBlock.done({ closed: 'escape' }); if (sent && sent.catch) sent.catch(function () {}); } catch (error) {}\n });\n})();\n";
|
|
16
17
|
export interface BridgeDeps {
|
|
17
18
|
uuid: string;
|
|
18
19
|
onHeight?: (height: number) => void;
|
|
@@ -22,5 +23,16 @@ export interface BridgeDeps {
|
|
|
22
23
|
context?: () => Record<string, unknown>;
|
|
23
24
|
/** 给**所属那条标签**起名 / 换图标("订单 SO-42"这种名字路由表算不出来,只有块自己知道)。 */
|
|
24
25
|
onTabTitle?: (title: string, icon?: string) => void;
|
|
26
|
+
/**
|
|
27
|
+
* 块要弹一个窗(`magicBlock.dialog(options)`):**由宿主页面渲染**,`Promise` 的结果就是用户的选择。
|
|
28
|
+
*
|
|
29
|
+
* 🔴 这件事必须落在宿主这一侧:块里的 `position: fixed` / `100vh` 基准是**整块高度**(不是用户那一屏),
|
|
30
|
+
* 所以块自己画的浮层会跑到几屏之外;宿主这一层才是用户的视口。判据与用法见 `dialogs.ts`。
|
|
31
|
+
* 🔴 收到的 `options` 是**块说了算的数据**:形状由 `dialogs.ts` 的 `normalizeDialogOptions()` 现校验,
|
|
32
|
+
* 绝不把块给的字符串当 HTML 塞进宿主文档(要放也是放进**另一个沙箱 iframe**)。
|
|
33
|
+
*/
|
|
34
|
+
onDialog?: (options: unknown) => Promise<unknown> | unknown;
|
|
35
|
+
/** 只在"宿主用沙箱 iframe 装块自己的弹窗 HTML"那一层有值:弹窗文档用 `magicBlock.done()` 交回结果。 */
|
|
36
|
+
onDone?: (payload: unknown) => unknown;
|
|
25
37
|
}
|
|
26
38
|
export declare function installBridge(iframe: HTMLIFrameElement, deps: BridgeDeps): () => void;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/** 字段能有的几种类型(宿主自己画:只有 CSS,没有第三方组件)。 */
|
|
2
|
+
declare const FIELD_TYPES: readonly ["text", "textarea", "number", "checkbox", "select"];
|
|
3
|
+
export type DialogFieldType = (typeof FIELD_TYPES)[number];
|
|
4
|
+
/** 能过桥的"值":只认基本类型与它们的一层数组(块与宿主是两个文档,函数 / DOM 都过不去)。 */
|
|
5
|
+
export type DialogValue = string | number | boolean | Array<string | number | boolean>;
|
|
6
|
+
export interface DialogSelectOption {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DialogField {
|
|
11
|
+
name: string;
|
|
12
|
+
type: DialogFieldType;
|
|
13
|
+
label: string;
|
|
14
|
+
value: string | boolean;
|
|
15
|
+
placeholder: string;
|
|
16
|
+
hint: string;
|
|
17
|
+
required: boolean;
|
|
18
|
+
options: DialogSelectOption[];
|
|
19
|
+
}
|
|
20
|
+
export interface DialogButton {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
primary: boolean;
|
|
24
|
+
danger: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** 块发过来的那一份(已校验、已补默认值)。 */
|
|
27
|
+
export interface DialogOptions {
|
|
28
|
+
mode: 'data' | 'html';
|
|
29
|
+
title: string;
|
|
30
|
+
body: string;
|
|
31
|
+
fields: DialogField[];
|
|
32
|
+
buttons: DialogButton[];
|
|
33
|
+
html: string;
|
|
34
|
+
size: {
|
|
35
|
+
width: string;
|
|
36
|
+
height: string;
|
|
37
|
+
};
|
|
38
|
+
dismissible: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** 交回给块的那一份。 */
|
|
41
|
+
export interface DialogResult {
|
|
42
|
+
/** 用户按了哪个按钮的 `id`;`null` = 没按按钮就关了(Esc / 点遮罩 / 宿主收掉)。 */
|
|
43
|
+
button: string | null;
|
|
44
|
+
values: Record<string, DialogValue>;
|
|
45
|
+
closed: 'button' | 'escape' | 'backdrop' | 'host';
|
|
46
|
+
}
|
|
47
|
+
/** 弹窗文档那一格 iframe 要交给宿主的两件事(由浮层组件提供:它才知道面板该多大、选择该交给谁)。 */
|
|
48
|
+
export interface DialogFrameHandlers {
|
|
49
|
+
/** 用户在这一篇里做了选择(`magicBlock.done(payload)`)。 */
|
|
50
|
+
onDone: (payload: unknown) => void;
|
|
51
|
+
/** 这一篇**自己报上来的高度**:面板按它自适应(见 `MagicBlockDialogs.vue` 的 `boxStyle`)。 */
|
|
52
|
+
onHeight: (height: number) => void;
|
|
53
|
+
}
|
|
54
|
+
/** 排队中的一个弹窗(宿主侧)。 */
|
|
55
|
+
export interface HostDialog {
|
|
56
|
+
/** 自增号:组件用它判"换了一个弹窗"(重量、清表单、重新聚焦都靠它)。 */
|
|
57
|
+
id: number;
|
|
58
|
+
/** 🔴 哪个块发起的:宿主自己记的(**不让块在参数里自报身份**,那等于给了冒名的口子)。 */
|
|
59
|
+
uuid: string;
|
|
60
|
+
options: DialogOptions;
|
|
61
|
+
/** 自带 HTML 那一型的那份 `srcdoc`(数据型是空串)。 */
|
|
62
|
+
doc: string;
|
|
63
|
+
/** 给那一格 iframe 挂桥(摘除函数还回去):由发起方给,见 `HostDialogContext.mountBridge`。 */
|
|
64
|
+
mountBridge?: (frame: HTMLIFrameElement, handlers: DialogFrameHandlers) => () => void;
|
|
65
|
+
/** 已经结算过没有(幂等那一手)。 */
|
|
66
|
+
answered: boolean;
|
|
67
|
+
resolve: (result: DialogResult) => void;
|
|
68
|
+
}
|
|
69
|
+
/** 发起一个弹窗时要宿主补的上下文(块包与主题都在宿主手上)。 */
|
|
70
|
+
export interface HostDialogContext {
|
|
71
|
+
uuid: string;
|
|
72
|
+
/**
|
|
73
|
+
* "块自己那一篇 HTML" → 内层沙箱 iframe 的 `srcdoc`。
|
|
74
|
+
*
|
|
75
|
+
* 🔴 由调用方给(`MagicBlockHost` 把它接成 `preview.ts` 的 `buildDialogPreview()`):这个文件不 import 本包
|
|
76
|
+
* 其它文件,见文件头上那一段。**html 型弹窗必须给** —— 少了它就是一块空白(那种毛病不该靠人盯出来)。
|
|
77
|
+
*/
|
|
78
|
+
buildDoc?: (html: string) => string;
|
|
79
|
+
/**
|
|
80
|
+
* 给"弹窗文档"那一格 iframe 挂桥(返回摘除函数)。
|
|
81
|
+
*
|
|
82
|
+
* 🔴 也由发起方给(`MagicBlockHost` 把它接成 `installBridge(frame, {...})`):只有它知道这一块属于哪条标签、
|
|
83
|
+
* 怎么调本块后端。
|
|
84
|
+
* 🔴 弹窗文档拿到的桥与块页**同一套**(`getMe` / `backend` / `context` / `dialog` / `done`):
|
|
85
|
+
* 它就是这个块的一部分,作者写的表单要能存,就得能调自己的后端。
|
|
86
|
+
* ⚠️ 与块页那一路的区别只有一处:**这一层要接 `onHeight`**(面板按弹窗文档报的高度自适应)。
|
|
87
|
+
* 块页那一路绝不能接(那一层报的是"块有多高",接了就把块页撑坏)—— 见 `MagicBlockHost.vue`。
|
|
88
|
+
*/
|
|
89
|
+
mountBridge?: (frame: HTMLIFrameElement, handlers: DialogFrameHandlers) => () => void;
|
|
90
|
+
}
|
|
91
|
+
/** 一路最多摞几个(防"块里写了个循环"把宿主页面糊死)。 */
|
|
92
|
+
export declare const MAX_DIALOGS = 4;
|
|
93
|
+
/**
|
|
94
|
+
* 一个 CSS 长度能不能用(用在**宿主的内联样式**上,所以只放行白名单里的形状)。
|
|
95
|
+
*
|
|
96
|
+
* 🔴 放行 `min()` / `max()` / `calc()`(默认尺寸就是 `min(46rem, 90vw)`),但里面**只许有**数字、单位、逗号、
|
|
97
|
+
* 空格与四则符号 —— `;` `}` `url(` 这类一旦进来,就是往宿主页面的样式里注入。
|
|
98
|
+
*/
|
|
99
|
+
export declare function validCssLength(value: unknown): value is string;
|
|
100
|
+
/** 块发来的 `options` → 宿主认的形状。**不认识的东西一律报错**(宁可当场说清,也不要"写了没反应")。 */
|
|
101
|
+
export declare function normalizeDialogOptions(raw: unknown): DialogOptions;
|
|
102
|
+
/** 只收基本类型(一层数组):块与宿主是两个文档,能给的就是这些。 */
|
|
103
|
+
export declare function normalizeValues(raw: unknown): Record<string, DialogValue>;
|
|
104
|
+
/** 弹窗文档 `magicBlock.done(payload)` 交回来的那一份 → 结果。 */
|
|
105
|
+
export declare function doneToResult(raw: unknown): DialogResult;
|
|
106
|
+
/** 组件读它(`MagicBlockDialogs.vue`)。 */
|
|
107
|
+
export declare function useHostDialogs(): {
|
|
108
|
+
current: import("vue").ComputedRef<HostDialog | null>;
|
|
109
|
+
ready: import("vue").Ref<boolean, boolean>;
|
|
110
|
+
};
|
|
111
|
+
/** 浮层挂上 / 卸下时由组件调:没挂就等于"这个应用没有弹窗宿主",块调用时要拿到一句人话而不是干等。 */
|
|
112
|
+
export declare function setDialogHostReady(on: boolean): void;
|
|
113
|
+
/** 这个弹窗还在那一摞里吗(组件用它清"草稿"缓存:只留活着的那些)。 */
|
|
114
|
+
export declare function isOpenDialog(id: number): boolean;
|
|
115
|
+
/** 用户按了某个按钮(`values` 是宿主从自己画的表单里收上来的)。 */
|
|
116
|
+
export declare function respondDialog(entry: HostDialog, button: string, values?: Record<string, DialogValue>): void;
|
|
117
|
+
/** 用户 Esc / 点了遮罩:不给关的弹窗(`dismissible: false`)在这里被挡回去。 */
|
|
118
|
+
export declare function dismissDialog(entry: HostDialog, closed: 'escape' | 'backdrop'): void;
|
|
119
|
+
/**
|
|
120
|
+
* 弹窗文档自己报回来的那一发(`magicBlock.done(payload)`,含注入进去的 Esc 那一手)。
|
|
121
|
+
*
|
|
122
|
+
* 🔴 交回来的东西和"用户点了按钮"是同一份结果形状,所以走同一个 `finish()`:**只结算一次**。
|
|
123
|
+
* 🔴 `dismissible: false` 在这里也认:弹窗文档并不知道自己那一条规则(它只管报"用户想关")。
|
|
124
|
+
* 返回"认没认"(认了 = 这个弹窗已经结算)。
|
|
125
|
+
*/
|
|
126
|
+
export declare function reportFromDialogDoc(entry: HostDialog, payload: unknown): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* 发起这一块的那些弹窗全部收掉(标签关了 / 换了块 / 块页卸载)。
|
|
129
|
+
* 🔴 结果是 `closed: 'host'` 而不是 reject:这不是块写错了,是它已经不在了 —— 让 `await` 抛错只会多一堆无谓的 catch。
|
|
130
|
+
*/
|
|
131
|
+
export declare function cancelDialogsOf(uuid: string): void;
|
|
132
|
+
/**
|
|
133
|
+
* 块要弹一个窗(`MagicBlockHost` 的 `onDialog` 调它)。
|
|
134
|
+
*
|
|
135
|
+
* 🔴 `Promise` 拖多久都行:它就是"用户什么时候点"那件事(桥那一侧的回条等它 resolve 才发)。
|
|
136
|
+
* 🔴 校验失败 / 浮层没挂 / 摞太深 ⇒ **reject 一句人话**(块那边 `await` 直接抛,比"弹不出来又不说"强)。
|
|
137
|
+
*/
|
|
138
|
+
export declare function openHostDialog(options: unknown, ctx: HostDialogContext): Promise<DialogResult>;
|
|
139
|
+
export {};
|
|
@@ -189,11 +189,19 @@ export declare function clearChildren(dir: LocalDir): Promise<number>;
|
|
|
189
189
|
* "这一包要的文件 / 目录"集合里,于是被删掉,紧接着由写那一步按正确的形状建出来。
|
|
190
190
|
* (这是"全删再铺"顺带保证的一件事,这里必须显式保住:否则同名目录会让那一次写直接失败。)
|
|
191
191
|
*
|
|
192
|
+
* :param keep: **顶层那几段名字整棵不动**(`['workspace']`)—— 不进去、不数、更不删。
|
|
193
|
+
* 🔴 判据是"**只认顶层那一段**",理由是要保的那一层就在顶层(块代码全在
|
|
194
|
+
* `<开发目录>/workspace/`);底下同名的一律照常规走(`workspace/workspace/` 这种不该
|
|
195
|
+
* 因为名字撞上就被整棵放行)。
|
|
196
|
+
* 🔴 谁在用它:「只铺框架文件」那一档(那一包里**没有** `workspace/…`,照常规判据
|
|
197
|
+
* "这一包没有的一律删"就会把作者改过的块代码整棵删光 —— 所以那条保证写在这里,
|
|
198
|
+
* 不靠调用方记得别传 `clean`)。
|
|
199
|
+
*
|
|
192
200
|
* :return: `{ removed, existing }` —— 删掉的**条目数**(每层各算一个;被整棵删掉的目录算它自己
|
|
193
201
|
* 那一个,与 `clearChildren` 报的"几项"同一个口径)+ 这一包里**盘上真有**的那些路径
|
|
194
202
|
* (顺手数出来的,`WriteOptions.existing` 直接用,省掉再多走一遍目录树)。
|
|
195
203
|
*/
|
|
196
|
-
export declare function pruneTo(dir: LocalDir, files: Record<string, string
|
|
204
|
+
export declare function pruneTo(dir: LocalDir, files: Record<string, string>, keep?: string[]): Promise<PruneResult>;
|
|
197
205
|
/** `pruneTo()` 的结果。 */
|
|
198
206
|
export interface PruneResult {
|
|
199
207
|
/** 删掉的条目数(每层各算一个)。 */
|
|
@@ -219,6 +227,20 @@ export declare function listExisting(dir: LocalDir, files: Record<string, string
|
|
|
219
227
|
* 于是这个错会一直躺着,直到有人去看目录结构。
|
|
220
228
|
*/
|
|
221
229
|
export declare function assertNotBlocksDir(dir: LocalDir, rules: ScanRules): void;
|
|
230
|
+
/**
|
|
231
|
+
* 「只铺框架文件」那一档的**回话判据**:这一包里**不许**出现块代码(`<blocksDir>/…`)。
|
|
232
|
+
*
|
|
233
|
+
* 🔴 为什么要有它:铺哪一半是**后端筛的**(它才认得哪些是块代码),而"老后端不认识 `framework`
|
|
234
|
+
* 这个参数"时回话里照样是整库 —— 照它写盘就等于把作者改过、还没读回库的块代码覆盖一遍,
|
|
235
|
+
* 而这一档的**全部意义**就是"别碰块代码"。宁可这一趟停下报一句,也不能铺下去。
|
|
236
|
+
* (另一个方向是安全的:新前端 + 老后端只会走到这一步,不会悄悄铺错;老前端 + 新后端不传
|
|
237
|
+
* `framework`,走的是它原来那一档。)
|
|
238
|
+
* 🔴 只读回话、不碰盘(与 `assertNotBlocksDir` 同一类:**写盘前**那条判据)。
|
|
239
|
+
*
|
|
240
|
+
* :param files: 导出报文里的那一份(相对路径 → 正文)。
|
|
241
|
+
* :param rules: 同一条报文里的 `rules`(`blocksDir` 的名字从它来 —— 前端不抄字面量)。
|
|
242
|
+
*/
|
|
243
|
+
export declare function assertFrameworkPackage(files: Record<string, string>, rules: ScanRules): void;
|
|
222
244
|
/**
|
|
223
245
|
* 扫一遍用户选中的目录,攒出递给 `POST /magic-blocks/read` 的那一份事实。
|
|
224
246
|
*
|
|
@@ -10,3 +10,21 @@ export interface PreviewExtras {
|
|
|
10
10
|
themeCss?: string;
|
|
11
11
|
}
|
|
12
12
|
export declare function buildPreview(files: Record<string, string>, extras?: PreviewExtras): string;
|
|
13
|
+
/**
|
|
14
|
+
* "块自己写的那一篇弹窗" → 内层沙箱 iframe 的 `srcdoc`(`magicBlock.dialog({ html })` 那一路)。
|
|
15
|
+
*
|
|
16
|
+
* 🔴 **这一篇绝不进宿主文档**:它只是一串**字符串**,被放进一个**新建的 `srcdoc` iframe**(不透明源、
|
|
17
|
+
* 没有 `allow-same-origin`)—— 于是块的东西照样出不了它那一格,宿主页面一个字节都不被它碰。
|
|
18
|
+
* 谁要是图省事把这段 HTML `v-html` 进宿主,等于把沙箱拆掉半边(与 `MagicBlockHost.vue` 顶上那条禁令同一条道理)。
|
|
19
|
+
* 🔴 **块自己的样式层要跟着过去**(`theme.ts` 的 `dialogStyleCarry()`):入口里那几段 `<style>` + 它引的样式表。
|
|
20
|
+
* 不搬的话弹窗里是"裸"的 —— 块自己的类名、`body { padding }`、`button { … }` 全都不在,
|
|
21
|
+
* 而主题那一层只认得 `p-*` 这类现成类名,补不了块自己的选择器(真踩过:弹窗里的按钮是浏览器默认长相)。
|
|
22
|
+
* ⚠️ 只搬**样式**,不搬入口里的那些 `<script>`:那是块页的逻辑(它会去找块页才有的元素)。
|
|
23
|
+
* 🔴 拼装**整篇复用块页那一套**(`buildPreview`):包内的 `<script src>` / `<link rel=stylesheet>` 照样内联,
|
|
24
|
+
* 主题照样注入在块自己样式之前,引导脚本照样插在 `</head>` 前。只有"入口正文"换成了弹窗这一篇。
|
|
25
|
+
* ⚠️ 主题与块页同源(宿主那份 `collectThemeCss()` 的缓存),所以弹窗里的控件类名与块页长得一样。
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildDialogPreview(files: Record<string, string>, html: string, extras?: {
|
|
28
|
+
bootstrap?: string;
|
|
29
|
+
themeCss?: string;
|
|
30
|
+
}): string;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import MagicBlockPage from './magic-block-MagicBlockPage.vue';
|
|
2
2
|
export { MagicBlockPage };
|
|
3
3
|
export { default as MagicBlockConsole } from './magic-block-MagicBlockConsole.vue';
|
|
4
|
+
export { default as MagicBlockDialogs } from './magic-block-MagicBlockDialogs.vue';
|
|
4
5
|
export { loadBlockRender, onBlockRenderRefresh, refreshBlockRender, type BlockRender } from './magic-block-render';
|
|
5
|
-
export { BLOCK_BACKEND_FILE, BLOCK_ENTRY, buildPreview } from './magic-block-preview';
|
|
6
|
-
export { CLIENT_SOURCE, installBridge, type BridgeDeps } from './magic-block-bridge';
|
|
6
|
+
export { BLOCK_BACKEND_FILE, BLOCK_ENTRY, buildDialogPreview, buildPreview } from './magic-block-preview';
|
|
7
|
+
export { CLIENT_SOURCE, DIALOG_BOOTSTRAP, installBridge, type BridgeDeps } from './magic-block-bridge';
|
|
8
|
+
export { MAX_DIALOGS, cancelDialogsOf, dismissDialog, doneToResult, isOpenDialog, normalizeDialogOptions, openHostDialog, reportFromDialogDoc, respondDialog, setDialogHostReady, useHostDialogs, validCssLength, type DialogButton, type DialogField, type DialogOptions, type DialogResult, type DialogValue, type HostDialog, type HostDialogContext, } from './magic-block-dialogs';
|
|
7
9
|
export { BLOCK_MANIFEST, MANIFEST_KEYS, MANIFEST_REQUIRED, MANIFEST_MOVED, NAME_MAX, README_OPEN, REQUIRES_KEYS, REQUIRES_OPEN, checkSelection, parseManifest, validFileName, validName, validateBlock, type BlockValidation, type Manifest, type ManifestKey, type Requires, type SelectionCheck, } from './magic-block-manifest';
|
|
8
10
|
export { API_BASE, setApiBase } from './auth-real';
|
|
9
11
|
export { BLOCK_PAGE_MARK, blockPlacementProblems, declaredMissing, declaredUuidOf, declaredUuidsOf, isBlockPage, requiresRouteProblems, sameRouteShape, type BlockPlacementProblem, type DeclaredEntry, type DeclaredRoute, type DeclaredRouteProblem, } from './magic-block-declared';
|
|
@@ -22,6 +22,35 @@ export interface ThemeBundle {
|
|
|
22
22
|
* 🔴 纯函数:只看文本,不看 DOM —— 所以能直接用 node 跑用例。
|
|
23
23
|
*/
|
|
24
24
|
export declare function keepCssRule(selector: string, cssText: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* 块**自己入口里**那几段 `<style>` 的正文。
|
|
27
|
+
*
|
|
28
|
+
* 🔴 为什么需要它:块自己的样式通常就写在 `index.open.html` 的 `<head>` 里,而"块自己写的弹窗"
|
|
29
|
+
* (`magicBlock.dialog({ html })`)是**另一篇文档** —— 不把这几段带过去,弹窗就是"没穿衣服"的:
|
|
30
|
+
* 裸 `<button>`、默认字号,跟块页长得完全不一样。宿主给的那一层主题救不了它 ——
|
|
31
|
+
* 主题是"现成的类名"(`p-button` 那一套),而块自己的类名与元素选择器只活在块自己的样式里。
|
|
32
|
+
* ⚠️ 判据是**文本扫描**(不是 DOM):`<script>` 里写着 `'<style>'` 这种字面量会被误收。
|
|
33
|
+
* 块入口里极少这么写,而误收的后果只是多一段无效 CSS(不影响块的逻辑)。
|
|
34
|
+
*/
|
|
35
|
+
export declare function entryStyles(html: string): string[];
|
|
36
|
+
/**
|
|
37
|
+
* 块入口里那几条 `<link rel="stylesheet" …>` 的**整条标签**(原样搬走)。
|
|
38
|
+
*
|
|
39
|
+
* 🔴 搬整条标签而不是只搬 `href`:包内的那几条由 `preview.ts` 的占位符机制**内联**成 `<style>`,
|
|
40
|
+
* 而外链(CDN)那几条保持原样 —— 与块页的处理**同一套**,不另立一份判据。
|
|
41
|
+
*/
|
|
42
|
+
export declare function entryStyleLinks(html: string): string[];
|
|
43
|
+
/**
|
|
44
|
+
* "块自己的样式层"整段备好(把 `<style>` 正文包回标签 + 原来那几条样式表标签**原样搬走**)。
|
|
45
|
+
*
|
|
46
|
+
* 🔴 搬进弹窗文档的就是这一段:包内的样式表接下来由 `preview.ts` 的占位符机制内联,外链保持原样 ——
|
|
47
|
+
* 与块页**同一套**处理,不另立一份判据。
|
|
48
|
+
*/
|
|
49
|
+
export declare function dialogStyleCarry(entryHtml: string): string;
|
|
50
|
+
/** 把上面那一段塞进弹窗文档**靠前**的位置(`<body>` 一开就放:`<style>` / `<link>` 在 body 里一样生效)。 */
|
|
51
|
+
export declare function carryIntoBody(doc: string, carry: string): string;
|
|
52
|
+
/** 片段补成整篇文档(弹窗那一篇可以只写片段;补整篇才有 `</head>` 可插、中文也不至于撞上编码)。 */
|
|
53
|
+
export declare function asDialogDocument(html: string): string;
|
|
25
54
|
/** 收一份自足的 CSS(规则从**当前页面已加载的样式表**里来)。 */
|
|
26
55
|
export declare function collectThemeCss(): ThemeBundle;
|
|
27
56
|
/**
|