dreamcontext 0.17.1 → 0.18.0
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 +321 -565
- package/agents/sleep-product.md +21 -2
- package/agents/sleep-state.md +24 -0
- package/agents/sleep-tasks.md +16 -0
- package/dist/agents/sleep-product.md +21 -2
- package/dist/agents/sleep-state.md +24 -0
- package/dist/agents/sleep-tasks.md +16 -0
- package/dist/dashboard/announcements/dashboard-highlights-0-17-0-18.excalidraw.md +1531 -0
- package/dist/dashboard/announcements/goal-skill-v2.excalidraw.md +1490 -0
- package/dist/dashboard/announcements/task-manager.excalidraw.md +1217 -0
- package/dist/dashboard/announcements/visual-announcements.excalidraw.md +1373 -0
- package/dist/dashboard/announcements.json +38 -0
- package/dist/dashboard/assets/{BrainCanvas3D-DyF_3yYD.js → BrainCanvas3D-D9G3dOLf.js} +1 -1
- package/dist/dashboard/assets/{_baseUniq-Do8VMBA0.js → _baseUniq-Cyshlz4Y.js} +1 -1
- package/dist/dashboard/assets/{ar-SA-G6X2FPQ2-DmbXDb2R.js → ar-SA-G6X2FPQ2-qj-2Cbgo.js} +1 -1
- package/dist/dashboard/assets/{arc-CwtUBQfU.js → arc-0OnX5WDs.js} +1 -1
- package/dist/dashboard/assets/{architectureDiagram-Q4EWVU46-O9bOog_f.js → architectureDiagram-Q4EWVU46-Cx3c0nUi.js} +1 -1
- package/dist/dashboard/assets/{az-AZ-76LH7QW2-c9Zk10vG.js → az-AZ-76LH7QW2-CBuoh6M3.js} +1 -1
- package/dist/dashboard/assets/{bg-BG-XCXSNQG7-uMTg84lX.js → bg-BG-XCXSNQG7-X5QYyZWx.js} +1 -1
- package/dist/dashboard/assets/{blockDiagram-DXYQGD6D-BTENXCSO.js → blockDiagram-DXYQGD6D-BwESAg1j.js} +1 -1
- package/dist/dashboard/assets/{bn-BD-2XOGV67Q-DUFKHcua.js → bn-BD-2XOGV67Q-DlodQLNR.js} +1 -1
- package/dist/dashboard/assets/{c4Diagram-AHTNJAMY-DMXPWRiQ.js → c4Diagram-AHTNJAMY-CopL0lbP.js} +1 -1
- package/dist/dashboard/assets/{ca-ES-6MX7JW3Y-BuYX1q89.js → ca-ES-6MX7JW3Y-DfqF6Yhy.js} +1 -1
- package/dist/dashboard/assets/channel-BLisRbYb.js +1 -0
- package/dist/dashboard/assets/{chunk-4BX2VUAB-DgefvnWS.js → chunk-4BX2VUAB-CRwifLK3.js} +1 -1
- package/dist/dashboard/assets/{chunk-4TB4RGXK-BnFO5G1t.js → chunk-4TB4RGXK-uxJYY_64.js} +1 -1
- package/dist/dashboard/assets/{chunk-55IACEB6-COm2TYEM.js → chunk-55IACEB6-DhYIp7Oc.js} +1 -1
- package/dist/dashboard/assets/{chunk-EDXVE4YY-CtsGCwZp.js → chunk-EDXVE4YY-CCxfcirt.js} +1 -1
- package/dist/dashboard/assets/{chunk-FMBD7UC4-cfSvx35j.js → chunk-FMBD7UC4-D7OC8QFC.js} +1 -1
- package/dist/dashboard/assets/{chunk-OYMX7WX6-DAhDKteL.js → chunk-OYMX7WX6-CwHgaUQo.js} +1 -1
- package/dist/dashboard/assets/{chunk-QZHKN3VN-B5h4qML3.js → chunk-QZHKN3VN-7Idpf6YJ.js} +1 -1
- package/dist/dashboard/assets/{chunk-YZCP3GAM-CGp_NiRg.js → chunk-YZCP3GAM-D4Lh0fgr.js} +1 -1
- package/dist/dashboard/assets/classDiagram-6PBFFD2Q-CgqeQiHi.js +1 -0
- package/dist/dashboard/assets/classDiagram-v2-HSJHXN6E-CgqeQiHi.js +1 -0
- package/dist/dashboard/assets/clone-CQXNMCV5.js +1 -0
- package/dist/dashboard/assets/{cose-bilkent-S5V4N54A-Dk7CMFA4.js → cose-bilkent-S5V4N54A-BYcMtJTF.js} +1 -1
- package/dist/dashboard/assets/{cs-CZ-2BRQDIVT-zfUTiS_d.js → cs-CZ-2BRQDIVT-rnrpKdSJ.js} +1 -1
- package/dist/dashboard/assets/{da-DK-5WZEPLOC-CU14f8NA.js → da-DK-5WZEPLOC-D5OGAvk7.js} +1 -1
- package/dist/dashboard/assets/{dagre-KV5264BT-BiMVxC4Q.js → dagre-KV5264BT-V0f1_b4R.js} +1 -1
- package/dist/dashboard/assets/{de-DE-XR44H4JA-CDOHAJGo.js → de-DE-XR44H4JA-CDyb8YfL.js} +1 -1
- package/dist/dashboard/assets/{diagram-5BDNPKRD-DfJEsVTA.js → diagram-5BDNPKRD-BmpizoaS.js} +1 -1
- package/dist/dashboard/assets/{diagram-G4DWMVQ6-CztLHIcY.js → diagram-G4DWMVQ6-ClQg-LTu.js} +1 -1
- package/dist/dashboard/assets/{diagram-MMDJMWI5-DqoQLlWK.js → diagram-MMDJMWI5-BkXcDMIV.js} +1 -1
- package/dist/dashboard/assets/{diagram-TYMM5635-DvyvQBCc.js → diagram-TYMM5635-BbFiiCM2.js} +1 -1
- package/dist/dashboard/assets/{el-GR-BZB4AONW-ChUphbcv.js → el-GR-BZB4AONW-Y1YnKZL0.js} +1 -1
- package/dist/dashboard/assets/{erDiagram-SMLLAGMA-D_uSs2_2.js → erDiagram-SMLLAGMA-Dzdv7_l5.js} +1 -1
- package/dist/dashboard/assets/{es-ES-U4NZUMDT-BNalBEhb.js → es-ES-U4NZUMDT-D0T536hs.js} +1 -1
- package/dist/dashboard/assets/{eu-ES-A7QVB2H4-BMiQyA2y.js → eu-ES-A7QVB2H4-Bfrya0et.js} +1 -1
- package/dist/dashboard/assets/{fa-IR-HGAKTJCU-Bd6ZnTSo.js → fa-IR-HGAKTJCU-pYE_hG-L.js} +1 -1
- package/dist/dashboard/assets/{fi-FI-Z5N7JZ37-BWAfJadC.js → fi-FI-Z5N7JZ37-D3C-fNyk.js} +1 -1
- package/dist/dashboard/assets/{flowDiagram-DWJPFMVM-Bmsp6kyU.js → flowDiagram-DWJPFMVM-CFhyjwKY.js} +1 -1
- package/dist/dashboard/assets/{fr-FR-RHASNOE6-CspbYzOW.js → fr-FR-RHASNOE6-CgwnG5JW.js} +1 -1
- package/dist/dashboard/assets/{ganttDiagram-T4ZO3ILL-wEdysXo_.js → ganttDiagram-T4ZO3ILL-BRKRiSzf.js} +1 -1
- package/dist/dashboard/assets/{gitGraphDiagram-UUTBAWPF-BEiGpPBj.js → gitGraphDiagram-UUTBAWPF-CTqCK49d.js} +1 -1
- package/dist/dashboard/assets/{gl-ES-HMX3MZ6V-CV76vsWr.js → gl-ES-HMX3MZ6V-DjJTqTm3.js} +1 -1
- package/dist/dashboard/assets/{graph-CZFIeNHZ.js → graph-D429kSbY.js} +1 -1
- package/dist/dashboard/assets/{he-IL-6SHJWFNN-Y1VoXJ_H.js → he-IL-6SHJWFNN-C84FlH6r.js} +1 -1
- package/dist/dashboard/assets/{hi-IN-IWLTKZ5I-Crh5S49o.js → hi-IN-IWLTKZ5I-Blhkfum2.js} +1 -1
- package/dist/dashboard/assets/{hu-HU-A5ZG7DT2-D4I3gCbp.js → hu-HU-A5ZG7DT2-DLC1JfN-.js} +1 -1
- package/dist/dashboard/assets/{id-ID-SAP4L64H-Chvzkuvi.js → id-ID-SAP4L64H-C3pB_IWB.js} +1 -1
- package/dist/dashboard/assets/image-FgR93jdt.js +1 -0
- package/dist/dashboard/assets/index-2m8ueOUn.js +1 -0
- package/dist/dashboard/assets/index-CWwjuihq.css +32 -0
- package/dist/dashboard/assets/{index-C7Bo5AQH.js → index-CbOXCDms.js} +1 -1
- package/dist/dashboard/assets/index-SJ64pf-4.js +551 -0
- package/dist/dashboard/assets/{infoDiagram-42DDH7IO-BzfuDHl4.js → infoDiagram-42DDH7IO-CIzDShDw.js} +1 -1
- package/dist/dashboard/assets/{ishikawaDiagram-UXIWVN3A-BVoNJ9VS.js → ishikawaDiagram-UXIWVN3A-CuasYzdX.js} +1 -1
- package/dist/dashboard/assets/{it-IT-JPQ66NNP-BH_gN5es.js → it-IT-JPQ66NNP-DGGEoNPu.js} +1 -1
- package/dist/dashboard/assets/{ja-JP-DBVTYXUO-DU_WmLVa.js → ja-JP-DBVTYXUO-CEqP9aps.js} +1 -1
- package/dist/dashboard/assets/{journeyDiagram-VCZTEJTY-DbwICa_a.js → journeyDiagram-VCZTEJTY-DHiq2OTQ.js} +1 -1
- package/dist/dashboard/assets/{kaa-6HZHGXH3-KYU9qowM.js → kaa-6HZHGXH3-GOFmTAXq.js} +1 -1
- package/dist/dashboard/assets/{kab-KAB-ZGHBKWFO-bjhyiUq5.js → kab-KAB-ZGHBKWFO-B865Wwzu.js} +1 -1
- package/dist/dashboard/assets/{kanban-definition-6JOO6SKY-D9XucLs4.js → kanban-definition-6JOO6SKY-BhlTKFbL.js} +1 -1
- package/dist/dashboard/assets/{kk-KZ-P5N5QNE5-CYTx_dAZ.js → kk-KZ-P5N5QNE5-CNfuE6b6.js} +1 -1
- package/dist/dashboard/assets/{km-KH-HSX4SM5Z-DqsuY4Lf.js → km-KH-HSX4SM5Z-CsmgRWxu.js} +1 -1
- package/dist/dashboard/assets/{ko-KR-MTYHY66A-BZe7NywF.js → ko-KR-MTYHY66A-B_02RgEz.js} +1 -1
- package/dist/dashboard/assets/{ku-TR-6OUDTVRD-F5d6HFWt.js → ku-TR-6OUDTVRD-JfQA5u05.js} +1 -1
- package/dist/dashboard/assets/{layout-CotqU0S9.js → layout-DvrQSvOI.js} +1 -1
- package/dist/dashboard/assets/{linear-Dqv87Scf.js → linear-6r0qttLs.js} +1 -1
- package/dist/dashboard/assets/{lt-LT-XHIRWOB4-BCCuaPbS.js → lt-LT-XHIRWOB4-DWFUXEdf.js} +1 -1
- package/dist/dashboard/assets/{lv-LV-5QDEKY6T-NLGi-vJ6.js → lv-LV-5QDEKY6T-C5yAd0n_.js} +1 -1
- package/dist/dashboard/assets/{min-BbJdnc3h.js → min-D_EP9dBf.js} +1 -1
- package/dist/dashboard/assets/{mindmap-definition-QFDTVHPH-Dk4QySu7.js → mindmap-definition-QFDTVHPH-Col3ao9B.js} +1 -1
- package/dist/dashboard/assets/{mr-IN-CRQNXWMA-BnGDIgVN.js → mr-IN-CRQNXWMA-Dwjz0Xdm.js} +1 -1
- package/dist/dashboard/assets/{my-MM-5M5IBNSE-CspgYE9_.js → my-MM-5M5IBNSE-EXJDNyjA.js} +1 -1
- package/dist/dashboard/assets/{nb-NO-T6EIAALU-BeRPw3hu.js → nb-NO-T6EIAALU-BWm2bgLd.js} +1 -1
- package/dist/dashboard/assets/{nl-NL-IS3SIHDZ-C-G-bljM.js → nl-NL-IS3SIHDZ-f8nP-yp-.js} +1 -1
- package/dist/dashboard/assets/{nn-NO-6E72VCQL-Ct7VZY8D.js → nn-NO-6E72VCQL-B0c_gCNR.js} +1 -1
- package/dist/dashboard/assets/{oc-FR-POXYY2M6-Cv5-C4Qq.js → oc-FR-POXYY2M6-C40PSwAh.js} +1 -1
- package/dist/dashboard/assets/{pa-IN-N4M65BXN-DAgkIThL.js → pa-IN-N4M65BXN-DDcgXFkB.js} +1 -1
- package/dist/dashboard/assets/{percentages-BXMCSKIN-vidRYabK.js → percentages-BXMCSKIN-MHBQP8nI.js} +7 -7
- package/dist/dashboard/assets/{pica-Cy-Xl-mi.js → pica-C5CFA09W.js} +1 -1
- package/dist/dashboard/assets/{pieDiagram-DEJITSTG-BnP4-zwd.js → pieDiagram-DEJITSTG-DFHmB0fL.js} +1 -1
- package/dist/dashboard/assets/{pl-PL-T2D74RX3-BuJQ3HD-.js → pl-PL-T2D74RX3-yFTBt0Xu.js} +1 -1
- package/dist/dashboard/assets/{pt-BR-5N22H2LF-BbAqsvD6.js → pt-BR-5N22H2LF-ilAW3Fdv.js} +1 -1
- package/dist/dashboard/assets/{pt-PT-UZXXM6DQ-DzFJedU3.js → pt-PT-UZXXM6DQ-CXHFN95K.js} +1 -1
- package/dist/dashboard/assets/{quadrantDiagram-34T5L4WZ-BXN88cHy.js → quadrantDiagram-34T5L4WZ-CR732_oP.js} +1 -1
- package/dist/dashboard/assets/{requirementDiagram-MS252O5E-D34hL42a.js → requirementDiagram-MS252O5E-mKoVG52Z.js} +1 -1
- package/dist/dashboard/assets/{ro-RO-JPDTUUEW-BV2-sOiv.js → ro-RO-JPDTUUEW-BKlWpyF4.js} +1 -1
- package/dist/dashboard/assets/{ru-RU-B4JR7IUQ-BCL4N9Ej.js → ru-RU-B4JR7IUQ-BGoxlhJm.js} +1 -1
- package/dist/dashboard/assets/{sankeyDiagram-XADWPNL6-0mFeok-3.js → sankeyDiagram-XADWPNL6-DXipts7M.js} +1 -1
- package/dist/dashboard/assets/{sequenceDiagram-FGHM5R23-C-5nW5j6.js → sequenceDiagram-FGHM5R23-DFQhU8q7.js} +1 -1
- package/dist/dashboard/assets/{si-LK-N5RQ5JYF-CNhaw4jx.js → si-LK-N5RQ5JYF-yDbJWFTH.js} +1 -1
- package/dist/dashboard/assets/{sk-SK-C5VTKIMK-mHCphOXu.js → sk-SK-C5VTKIMK-DglyhbNB.js} +1 -1
- package/dist/dashboard/assets/{sl-SI-NN7IZMDC--lzgHthQ.js → sl-SI-NN7IZMDC-DPfq0s4I.js} +1 -1
- package/dist/dashboard/assets/{stateDiagram-FHFEXIEX-C50iDFqe.js → stateDiagram-FHFEXIEX-Dm50jiEh.js} +1 -1
- package/dist/dashboard/assets/stateDiagram-v2-QKLJ7IA2-CoSm11sD.js +1 -0
- package/dist/dashboard/assets/{subset-shared.chunk-DNsjL37s.js → subset-shared.chunk-OHz6M6ct.js} +1 -1
- package/dist/dashboard/assets/{subset-worker.chunk-BaV3r-bl.js → subset-worker.chunk-BzwrOBtO.js} +1 -1
- package/dist/dashboard/assets/{sv-SE-XGPEYMSR-ckw8jfGN.js → sv-SE-XGPEYMSR-BLHNwr4k.js} +1 -1
- package/dist/dashboard/assets/{ta-IN-2NMHFXQM-CSeNmVhS.js → ta-IN-2NMHFXQM-Dw-6hUpN.js} +1 -1
- package/dist/dashboard/assets/{th-TH-HPSO5L25-Cvn1sg_3.js → th-TH-HPSO5L25-DPkcM_k9.js} +1 -1
- package/dist/dashboard/assets/{timeline-definition-GMOUNBTQ-DRYfuqHT.js → timeline-definition-GMOUNBTQ-BKFNQ_7e.js} +1 -1
- package/dist/dashboard/assets/{tr-TR-DEFEU3FU-CyhY22TB.js → tr-TR-DEFEU3FU-rBVsY5RS.js} +1 -1
- package/dist/dashboard/assets/{uk-UA-QMV73CPH-BvvAEO6U.js → uk-UA-QMV73CPH-CV6_yTZs.js} +1 -1
- package/dist/dashboard/assets/{vennDiagram-DHZGUBPP-Crril7Cj.js → vennDiagram-DHZGUBPP-BZb_r5tY.js} +1 -1
- package/dist/dashboard/assets/{vi-VN-M7AON7JQ-c7AwSUZ8.js → vi-VN-M7AON7JQ-CJMgSsLz.js} +1 -1
- package/dist/dashboard/assets/{wardley-RL74JXVD-Da5AhSWU.js → wardley-RL74JXVD-DgqjmWyN.js} +1 -1
- package/dist/dashboard/assets/{wardleyDiagram-NUSXRM2D-C4GeB_Df.js → wardleyDiagram-NUSXRM2D-BDpniS9m.js} +1 -1
- package/dist/dashboard/assets/webviewWindow-Bwv2421L.js +1 -0
- package/dist/dashboard/assets/window-BObcHN8e.js +1 -0
- package/dist/dashboard/assets/{xychartDiagram-5P7HB3ND-BPCFKXqR.js → xychartDiagram-5P7HB3ND-CCyPcx2A.js} +1 -1
- package/dist/dashboard/assets/{zh-CN-LNUGB5OW-BvNiZwKU.js → zh-CN-LNUGB5OW-9LJLF_AC.js} +1 -1
- package/dist/dashboard/assets/{zh-HK-E62DVLB3-CCFtoCWb.js → zh-HK-E62DVLB3-IefAiePp.js} +1 -1
- package/dist/dashboard/assets/{zh-TW-RAJ6MFWO-bYBr-stu.js → zh-TW-RAJ6MFWO-CHeY9rNZ.js} +1 -1
- package/dist/dashboard/index.html +2 -2
- package/dist/index.js +4099 -1538
- package/dist/skill-packs/agents/goal-implementer.md +42 -1
- package/dist/skill-packs/agents/goal-plan-reviewer.md +7 -0
- package/dist/skill-packs/agents/goal-planner.md +34 -0
- package/dist/skill-packs/catalog.json +31 -21
- package/dist/skill-packs/excalidraw/SKILL.md +244 -13
- package/dist/skill-packs/excalidraw/examples/Chart Kit.excalidraw.md +15740 -0
- package/dist/skill-packs/excalidraw/examples/Wireframe Kit.excalidraw.md +16860 -0
- package/dist/skill-packs/excalidraw/examples/atomic_check.js +99 -0
- package/dist/skill-packs/excalidraw/examples/chart.spec.json +122 -0
- package/dist/skill-packs/excalidraw/examples/chart_board.js +173 -0
- package/dist/skill-packs/excalidraw/examples/hello.spec.json +79 -9
- package/dist/skill-packs/excalidraw/examples/visual_board.js +82 -0
- package/dist/skill-packs/excalidraw/examples/wireframe_board.js +161 -0
- package/dist/skill-packs/excalidraw/scripts/build_excalidraw.js +292 -27
- package/dist/skill-packs/excalidraw/scripts/lib/charts.js +771 -0
- package/dist/skill-packs/excalidraw/scripts/lib/style.js +369 -35
- package/dist/skill-packs/excalidraw/scripts/lib/wireframe.js +406 -0
- package/dist/skill-packs/goal-skill/SKILL.md +369 -100
- package/dist/skill-packs/goal-skill/assets/goal-skill-demo.cjs +51 -0
- package/dist/skill-packs/goal-skill/assets/goal-skill-viewer.cjs +181 -0
- package/package.json +2 -1
- package/skill/references/integrations.md +54 -0
- package/skill/references/knowledge-and-recall.md +3 -1
- package/skill/references/sleep.md +4 -4
- package/skill-packs/agents/goal-implementer.md +42 -1
- package/skill-packs/agents/goal-plan-reviewer.md +7 -0
- package/skill-packs/agents/goal-planner.md +34 -0
- package/skill-packs/catalog.json +31 -21
- package/skill-packs/excalidraw/SKILL.md +244 -13
- package/skill-packs/excalidraw/examples/Chart Kit.excalidraw.md +15740 -0
- package/skill-packs/excalidraw/examples/Wireframe Kit.excalidraw.md +16860 -0
- package/skill-packs/excalidraw/examples/atomic_check.js +99 -0
- package/skill-packs/excalidraw/examples/chart.spec.json +122 -0
- package/skill-packs/excalidraw/examples/chart_board.js +173 -0
- package/skill-packs/excalidraw/examples/hello.spec.json +79 -9
- package/skill-packs/excalidraw/examples/visual_board.js +82 -0
- package/skill-packs/excalidraw/examples/wireframe_board.js +161 -0
- package/skill-packs/excalidraw/scripts/build_excalidraw.js +292 -27
- package/skill-packs/excalidraw/scripts/lib/charts.js +771 -0
- package/skill-packs/excalidraw/scripts/lib/style.js +369 -35
- package/skill-packs/excalidraw/scripts/lib/wireframe.js +406 -0
- package/skill-packs/goal-skill/SKILL.md +369 -100
- package/skill-packs/goal-skill/assets/goal-skill-demo.cjs +51 -0
- package/skill-packs/goal-skill/assets/goal-skill-viewer.cjs +181 -0
- package/skill-task-manager/SKILL.md +93 -0
- package/dist/dashboard/assets/channel-2YfRjala.js +0 -1
- package/dist/dashboard/assets/classDiagram-6PBFFD2Q-DB_UnN1h.js +0 -1
- package/dist/dashboard/assets/classDiagram-v2-HSJHXN6E-DB_UnN1h.js +0 -1
- package/dist/dashboard/assets/clone-DaDvB3N6.js +0 -1
- package/dist/dashboard/assets/index-BIfrbaFS.js +0 -516
- package/dist/dashboard/assets/index-BdZ3tppu.css +0 -32
- package/dist/dashboard/assets/stateDiagram-v2-QKLJ7IA2-eW9-s0Sm.js +0 -1
- package/dist/dashboard/assets/webviewWindow-BbG9Cr7f.js +0 -1
- package/dist/dashboard/assets/window-DmNpeJAL.js +0 -1
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
// Device + UI kit for Excalidraw wireframes. Same contract as style.js/charts.js: every builder
|
|
2
|
+
// returns an ElementSpec[] carrying `.x/.y/.w/.h/.nextX/.nextY`, so it composes inside stack()/row().
|
|
3
|
+
//
|
|
4
|
+
// Why a separate lib: style.js owns the *diagram* house style (cards, connectors, funnels) and a
|
|
5
|
+
// handful of generic wireframe bits. This file owns the *product UI* vocabulary — real device shells,
|
|
6
|
+
// nav chrome, form controls, icons. It re-exports style.js's wireframe primitives too, so one require
|
|
7
|
+
// gives you the whole kit.
|
|
8
|
+
//
|
|
9
|
+
// Rounded corners: build_excalidraw maps `roundness:true` to Excalidraw's ADAPTIVE_RADIUS (type 3),
|
|
10
|
+
// which caps at ~32px — far too tight for a phone silhouette (an iPhone's is ~14% of its width). So
|
|
11
|
+
// device shells are drawn as FILLED POLYGONS with exact arc corners, the same trick funnel()/donut()
|
|
12
|
+
// use for shapes Excalidraw has no primitive for. Everything inside a device stays a normal element.
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
INK, WIRE, pal, measureText, wrapToWidth, box, bbox, translate,
|
|
16
|
+
windowFrame, navbar, button, input, textRows, imagePlaceholder, avatar, chip, divider,
|
|
17
|
+
} = require('./style.js');
|
|
18
|
+
|
|
19
|
+
const LH = 1.25;
|
|
20
|
+
const FONT = 5;
|
|
21
|
+
const txt = (o) => Object.assign({ type: 'text', fontFamily: FONT, color: INK }, o);
|
|
22
|
+
let _gc = 0;
|
|
23
|
+
function G(els, hint) {
|
|
24
|
+
const g = `wf${++_gc}-${hint}`;
|
|
25
|
+
for (const e of els) if (e.group == null) e.group = g;
|
|
26
|
+
return els;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ── geometry ──────────────────────────────────────────────────────────────
|
|
30
|
+
// A closed rounded-rectangle polygon with an EXACT corner radius (Excalidraw's rect roundness can't
|
|
31
|
+
// go past ~32px). `seg` points per corner arc — 6 is smooth enough at board scale.
|
|
32
|
+
function roundRectPoints(x, y, w, h, r, seg = 6) {
|
|
33
|
+
const rr = Math.max(0, Math.min(r, Math.min(w, h) / 2));
|
|
34
|
+
const pts = [];
|
|
35
|
+
const arc = (cx, cy, a0, a1) => {
|
|
36
|
+
for (let i = 0; i <= seg; i++) {
|
|
37
|
+
const a = a0 + (a1 - a0) * (i / seg);
|
|
38
|
+
pts.push([cx + Math.cos(a) * rr, cy + Math.sin(a) * rr]);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const P = Math.PI;
|
|
42
|
+
arc(x + w - rr, y + rr, -P / 2, 0); // top-right
|
|
43
|
+
arc(x + w - rr, y + h - rr, 0, P / 2); // bottom-right
|
|
44
|
+
arc(x + rr, y + h - rr, P / 2, P); // bottom-left
|
|
45
|
+
arc(x + rr, y + rr, P, (3 * P) / 2); // top-left
|
|
46
|
+
pts.push(pts[0]);
|
|
47
|
+
return pts;
|
|
48
|
+
}
|
|
49
|
+
function roundRect(o) {
|
|
50
|
+
const { x, y, w, h, r = 12, fill = WIRE.paper, stroke = WIRE.edge, strokeWidth = 2, seg = 6 } = o;
|
|
51
|
+
return {
|
|
52
|
+
type: 'line', points: roundRectPoints(x, y, w, h, r, seg),
|
|
53
|
+
strokeColor: stroke, backgroundColor: fill, fillStyle: 'solid', strokeWidth,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ── icons ─────────────────────────────────────────────────────────────────
|
|
58
|
+
// Line-drawn glyphs on a size×size box centred at (cx,cy). Deliberately simple: a wireframe icon
|
|
59
|
+
// only has to READ as its concept, not be pixel-perfect. Every entry returns ElementSpec[].
|
|
60
|
+
const ICONS = {
|
|
61
|
+
menu: (c, s, k, w) => [0, 0.5, 1].map((t) => ln([[c[0] - s / 2, c[1] - s / 2 + t * s], [c[0] + s / 2, c[1] - s / 2 + t * s]], k, w)),
|
|
62
|
+
close: (c, s, k, w) => [ln([[c[0] - s / 2, c[1] - s / 2], [c[0] + s / 2, c[1] + s / 2]], k, w), ln([[c[0] + s / 2, c[1] - s / 2], [c[0] - s / 2, c[1] + s / 2]], k, w)],
|
|
63
|
+
plus: (c, s, k, w) => [ln([[c[0] - s / 2, c[1]], [c[0] + s / 2, c[1]]], k, w), ln([[c[0], c[1] - s / 2], [c[0], c[1] + s / 2]], k, w)],
|
|
64
|
+
minus: (c, s, k, w) => [ln([[c[0] - s / 2, c[1]], [c[0] + s / 2, c[1]]], k, w)],
|
|
65
|
+
check: (c, s, k, w) => [ln([[c[0] - s / 2, c[1]], [c[0] - s / 8, c[1] + s / 3], [c[0] + s / 2, c[1] - s / 3]], k, w)],
|
|
66
|
+
search: (c, s, k, w) => [el(c[0] - s / 2, c[1] - s / 2, s * 0.68, s * 0.68, k, w), ln([[c[0] + s * 0.14, c[1] + s * 0.14], [c[0] + s / 2, c[1] + s / 2]], k, w)],
|
|
67
|
+
'chevron-left': (c, s, k, w) => [ln([[c[0] + s / 4, c[1] - s / 2], [c[0] - s / 4, c[1]], [c[0] + s / 4, c[1] + s / 2]], k, w)],
|
|
68
|
+
'chevron-right': (c, s, k, w) => [ln([[c[0] - s / 4, c[1] - s / 2], [c[0] + s / 4, c[1]], [c[0] - s / 4, c[1] + s / 2]], k, w)],
|
|
69
|
+
'chevron-up': (c, s, k, w) => [ln([[c[0] - s / 2, c[1] + s / 4], [c[0], c[1] - s / 4], [c[0] + s / 2, c[1] + s / 4]], k, w)],
|
|
70
|
+
'chevron-down': (c, s, k, w) => [ln([[c[0] - s / 2, c[1] - s / 4], [c[0], c[1] + s / 4], [c[0] + s / 2, c[1] - s / 4]], k, w)],
|
|
71
|
+
'arrow-left': (c, s, k, w) => [ln([[c[0] + s / 2, c[1]], [c[0] - s / 2, c[1]]], k, w), ln([[c[0] - s / 6, c[1] - s / 3], [c[0] - s / 2, c[1]], [c[0] - s / 6, c[1] + s / 3]], k, w)],
|
|
72
|
+
'arrow-right': (c, s, k, w) => [ln([[c[0] - s / 2, c[1]], [c[0] + s / 2, c[1]]], k, w), ln([[c[0] + s / 6, c[1] - s / 3], [c[0] + s / 2, c[1]], [c[0] + s / 6, c[1] + s / 3]], k, w)],
|
|
73
|
+
more: (c, s, k) => [-1, 0, 1].map((t) => dot(c[0] + t * s * 0.34, c[1], s * 0.09, k)),
|
|
74
|
+
'more-v': (c, s, k) => [-1, 0, 1].map((t) => dot(c[0], c[1] + t * s * 0.34, s * 0.09, k)),
|
|
75
|
+
play: (c, s, k, w) => [poly([[c[0] - s / 3, c[1] - s / 2], [c[0] + s / 2, c[1]], [c[0] - s / 3, c[1] + s / 2]], k, w)],
|
|
76
|
+
heart: (c, s, k, w) => [poly(heartPts(c[0], c[1], s), k, w)],
|
|
77
|
+
star: (c, s, k, w) => [poly(starPts(c[0], c[1], s / 2, s / 4.6, 5), k, w)],
|
|
78
|
+
user: (c, s, k, w) => [el(c[0] - s * 0.22, c[1] - s / 2, s * 0.44, s * 0.44, k, w), ln([[c[0] - s * 0.38, c[1] + s / 2], [c[0] - s * 0.38, c[1] + s * 0.16], [c[0] + s * 0.38, c[1] + s * 0.16], [c[0] + s * 0.38, c[1] + s / 2]], k, w)],
|
|
79
|
+
home: (c, s, k, w) => [ln([[c[0] - s / 2, c[1]], [c[0], c[1] - s / 2], [c[0] + s / 2, c[1]]], k, w), ln([[c[0] - s / 3, c[1]], [c[0] - s / 3, c[1] + s / 2], [c[0] + s / 3, c[1] + s / 2], [c[0] + s / 3, c[1]]], k, w)],
|
|
80
|
+
bell: (c, s, k, w) => [ln([[c[0] - s * 0.36, c[1] + s * 0.2], [c[0] - s * 0.3, c[1] - s * 0.12], [c[0], c[1] - s * 0.44], [c[0] + s * 0.3, c[1] - s * 0.12], [c[0] + s * 0.36, c[1] + s * 0.2], [c[0] - s * 0.36, c[1] + s * 0.2]], k, w), ln([[c[0] - s * 0.1, c[1] + s * 0.34], [c[0] + s * 0.1, c[1] + s * 0.34]], k, w)],
|
|
81
|
+
trash: (c, s, k, w) => [ln([[c[0] - s * 0.4, c[1] - s * 0.26], [c[0] + s * 0.4, c[1] - s * 0.26]], k, w), ln([[c[0] - s * 0.28, c[1] - s * 0.26], [c[0] - s * 0.22, c[1] + s * 0.44], [c[0] + s * 0.22, c[1] + s * 0.44], [c[0] + s * 0.28, c[1] - s * 0.26]], k, w), ln([[c[0] - s * 0.12, c[1] - s * 0.26], [c[0] - s * 0.12, c[1] - s * 0.44], [c[0] + s * 0.12, c[1] - s * 0.44], [c[0] + s * 0.12, c[1] - s * 0.26]], k, w)],
|
|
82
|
+
settings: (c, s, k, w) => {
|
|
83
|
+
// flat-topped teeth — an alternating-radius star reads as a SUN, not a gear
|
|
84
|
+
const teeth = 7, R = s * 0.5, r = s * 0.36, pts = [];
|
|
85
|
+
for (let i = 0; i < teeth; i++) {
|
|
86
|
+
const a0 = (i / teeth) * Math.PI * 2, st = (Math.PI * 2) / teeth;
|
|
87
|
+
[[a0, R], [a0 + st * 0.42, R], [a0 + st * 0.58, r], [a0 + st, r]].forEach(([a, rr]) =>
|
|
88
|
+
pts.push([c[0] + Math.cos(a) * rr, c[1] + Math.sin(a) * rr]));
|
|
89
|
+
}
|
|
90
|
+
return [ln([...pts, pts[0]], k, w), el(c[0] - s * 0.16, c[1] - s * 0.16, s * 0.32, s * 0.32, k, w)];
|
|
91
|
+
},
|
|
92
|
+
share: (c, s, k, w) => [dot(c[0] + s * 0.32, c[1] - s * 0.36, s * 0.11, k), dot(c[0] - s * 0.34, c[1], s * 0.11, k), dot(c[0] + s * 0.32, c[1] + s * 0.36, s * 0.11, k), ln([[c[0] - s * 0.26, c[1] - s * 0.06], [c[0] + s * 0.24, c[1] - s * 0.3]], k, w), ln([[c[0] - s * 0.26, c[1] + s * 0.06], [c[0] + s * 0.24, c[1] + s * 0.3]], k, w)],
|
|
93
|
+
lock: (c, s, k, w) => [rc(c[0] - s * 0.32, c[1] - s * 0.04, s * 0.64, s * 0.5, k, w), ln([[c[0] - s * 0.18, c[1] - s * 0.04], [c[0] - s * 0.18, c[1] - s * 0.3], [c[0] + s * 0.18, c[1] - s * 0.3], [c[0] + s * 0.18, c[1] - s * 0.04]], k, w)],
|
|
94
|
+
mail: (c, s, k, w) => [rc(c[0] - s * 0.44, c[1] - s * 0.32, s * 0.88, s * 0.64, k, w), ln([[c[0] - s * 0.44, c[1] - s * 0.32], [c[0], c[1] + s * 0.06], [c[0] + s * 0.44, c[1] - s * 0.32]], k, w)],
|
|
95
|
+
camera: (c, s, k, w) => [rc(c[0] - s * 0.46, c[1] - s * 0.26, s * 0.92, s * 0.6, k, w), el(c[0] - s * 0.16, c[1] - s * 0.1, s * 0.32, s * 0.32, k, w), ln([[c[0] - s * 0.18, c[1] - s * 0.26], [c[0] - s * 0.1, c[1] - s * 0.4], [c[0] + s * 0.1, c[1] - s * 0.4], [c[0] + s * 0.18, c[1] - s * 0.26]], k, w)],
|
|
96
|
+
image: (c, s, k, w) => [rc(c[0] - s * 0.44, c[1] - s * 0.36, s * 0.88, s * 0.72, k, w), ln([[c[0] - s * 0.44, c[1] + s * 0.14], [c[0] - s * 0.12, c[1] - s * 0.14], [c[0] + s * 0.16, c[1] + s * 0.36]], k, w), dot(c[0] + s * 0.2, c[1] - s * 0.16, s * 0.07, k)],
|
|
97
|
+
edit: (c, s, k, w) => [ln([[c[0] - s * 0.44, c[1] + s * 0.44], [c[0] - s * 0.3, c[1] + s * 0.16], [c[0] + s * 0.3, c[1] - s * 0.44], [c[0] + s * 0.44, c[1] - s * 0.3], [c[0] - s * 0.16, c[1] + s * 0.3], [c[0] - s * 0.44, c[1] + s * 0.44]], k, w)],
|
|
98
|
+
filter: (c, s, k, w) => [ln([[c[0] - s * 0.46, c[1] - s * 0.34], [c[0] + s * 0.46, c[1] - s * 0.34], [c[0] + s * 0.1, c[1] + s * 0.06], [c[0] + s * 0.1, c[1] + s * 0.42], [c[0] - s * 0.1, c[1] + s * 0.28], [c[0] - s * 0.1, c[1] + s * 0.06], [c[0] - s * 0.46, c[1] - s * 0.34]], k, w)],
|
|
99
|
+
bookmark: (c, s, k, w) => [ln([[c[0] - s * 0.3, c[1] - s * 0.44], [c[0] + s * 0.3, c[1] - s * 0.44], [c[0] + s * 0.3, c[1] + s * 0.44], [c[0], c[1] + s * 0.14], [c[0] - s * 0.3, c[1] + s * 0.44], [c[0] - s * 0.3, c[1] - s * 0.44]], k, w)],
|
|
100
|
+
calendar: (c, s, k, w) => [rc(c[0] - s * 0.42, c[1] - s * 0.34, s * 0.84, s * 0.76, k, w), ln([[c[0] - s * 0.42, c[1] - s * 0.1], [c[0] + s * 0.42, c[1] - s * 0.1]], k, w), ln([[c[0] - s * 0.2, c[1] - s * 0.34], [c[0] - s * 0.2, c[1] - s * 0.48]], k, w), ln([[c[0] + s * 0.2, c[1] - s * 0.34], [c[0] + s * 0.2, c[1] - s * 0.48]], k, w)],
|
|
101
|
+
};
|
|
102
|
+
const ln = (points, k, w) => ({ type: 'line', points, strokeColor: k, strokeWidth: w, backgroundColor: 'transparent' });
|
|
103
|
+
const poly = (points, k, w) => ({ type: 'line', points: [...points, points[0]], strokeColor: k, backgroundColor: k, fillStyle: 'solid', strokeWidth: w });
|
|
104
|
+
const el = (x, y, w2, h2, k, w) => ({ type: 'ellipse', x, y, width: w2, height: h2, strokeColor: k, backgroundColor: 'transparent', strokeWidth: w });
|
|
105
|
+
const rc = (x, y, w2, h2, k, w) => ({ type: 'rectangle', x, y, width: w2, height: h2, strokeColor: k, backgroundColor: 'transparent', strokeWidth: w, roundness: true });
|
|
106
|
+
const dot = (cx, cy, r, k) => ({ type: 'ellipse', x: cx - r, y: cy - r, width: r * 2, height: r * 2, strokeColor: k, backgroundColor: k, fillStyle: 'solid', strokeWidth: 1 });
|
|
107
|
+
function starPts(cx, cy, R, r, n) {
|
|
108
|
+
const p = [];
|
|
109
|
+
for (let i = 0; i < n * 2; i++) {
|
|
110
|
+
const a = (i / (n * 2)) * Math.PI * 2 - Math.PI / 2;
|
|
111
|
+
const rr = i % 2 ? r : R;
|
|
112
|
+
p.push([cx + Math.cos(a) * rr, cy + Math.sin(a) * rr]);
|
|
113
|
+
}
|
|
114
|
+
return p;
|
|
115
|
+
}
|
|
116
|
+
function heartPts(cx, cy, s) {
|
|
117
|
+
const p = [];
|
|
118
|
+
for (let i = 0; i <= 24; i++) {
|
|
119
|
+
const t = (i / 24) * Math.PI * 2;
|
|
120
|
+
const x = 16 * Math.pow(Math.sin(t), 3);
|
|
121
|
+
const y = -(13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
|
|
122
|
+
p.push([cx + (x / 17) * (s / 2), cy + (y / 17) * (s / 2)]);
|
|
123
|
+
}
|
|
124
|
+
return p;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** A line-drawn glyph. `name` must be a key of ICONS; unknown names fall back to a box (so a typo is
|
|
128
|
+
* visible on the board instead of silently drawing nothing). */
|
|
129
|
+
function icon(o) {
|
|
130
|
+
const { name = 'menu', x = 0, y = 0, size = 20, color = WIRE.strong, strokeWidth = 1.6 } = o;
|
|
131
|
+
const c = [x + size / 2, y + size / 2];
|
|
132
|
+
const f = ICONS[name];
|
|
133
|
+
const els = f ? f(c, size, color, strokeWidth).flat() : [rc(x, y, size, size, color, strokeWidth)];
|
|
134
|
+
return box(els, x, y, size, size);
|
|
135
|
+
}
|
|
136
|
+
const iconNames = () => Object.keys(ICONS);
|
|
137
|
+
|
|
138
|
+
/** A tappable icon: glyph inside a circle/square/bare hit area. The workhorse of mobile chrome. */
|
|
139
|
+
function iconButton(o) {
|
|
140
|
+
const {
|
|
141
|
+
x = 0, y = 0, size = 40, icon: name = 'menu', shape = 'circle', color = null,
|
|
142
|
+
variant = 'ghost', iconSize = null, strokeWidth = 1.6,
|
|
143
|
+
} = o;
|
|
144
|
+
const p = color ? pal(color) : null;
|
|
145
|
+
const solid = variant === 'solid';
|
|
146
|
+
const fill = solid ? (p ? p.fill : WIRE.chrome) : (variant === 'ghost' ? 'transparent' : WIRE.paper);
|
|
147
|
+
const stroke = variant === 'ghost' ? 'transparent' : (p ? p.stroke : WIRE.edge);
|
|
148
|
+
const glyph = solid && p ? p.stroke : (p ? p.stroke : WIRE.strong);
|
|
149
|
+
const out = [];
|
|
150
|
+
if (variant !== 'ghost') {
|
|
151
|
+
if (shape === 'circle') out.push({ type: 'ellipse', x, y, width: size, height: size, strokeColor: stroke, backgroundColor: fill, fillStyle: 'solid', strokeWidth: 1.5 });
|
|
152
|
+
else out.push(roundRect({ x, y, w: size, h: size, r: shape === 'square' ? 8 : size / 2, fill, stroke, strokeWidth: 1.5 }));
|
|
153
|
+
}
|
|
154
|
+
const is = iconSize || size * 0.5;
|
|
155
|
+
out.push(...icon({ name, x: x + (size - is) / 2, y: y + (size - is) / 2, size: is, color: glyph, strokeWidth }));
|
|
156
|
+
return G(box(out, x, y, size, size), 'iconbtn');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── device shells ─────────────────────────────────────────────────────────
|
|
160
|
+
// Real proportions, so a phone mock reads as a phone and a layout drawn inside it is honest about
|
|
161
|
+
// how much room it actually has. aspect = h/w of the SCREEN (logical points).
|
|
162
|
+
const DEVICES = {
|
|
163
|
+
iphone: { aspect: 852 / 393, w: 380, bezel: 11, radius: 0.13, island: true, home: true, status: true, label: 'iPhone' },
|
|
164
|
+
ipad: { aspect: 1194 / 834, w: 560, bezel: 16, radius: 0.05, camera: true, home: true, status: true, label: 'iPad' },
|
|
165
|
+
mac: { aspect: 900 / 1440, w: 920, bezel: 9, radius: 0.014, menubar: true, notch: true, base: true, label: 'Mac' },
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/** A device shell with real proportions. Returns `.inner = {x,y,w,h}` — the SAFE content region
|
|
169
|
+
* (inside the bezel, below the status bar / notch, above the home indicator). Place children there
|
|
170
|
+
* (via stack/row) and they cannot hit the chrome. `screen` is the full screen rect if you need it. */
|
|
171
|
+
function device(o) {
|
|
172
|
+
const {
|
|
173
|
+
kind = 'iphone', x = 0, y = 0, w = null, h = null, label = null, labelSize = 14,
|
|
174
|
+
time = '9:41', status = null, wallpaper = WIRE.paper, bezelColor = '#343a40', dark = false,
|
|
175
|
+
} = o;
|
|
176
|
+
const d = DEVICES[kind] || DEVICES.iphone;
|
|
177
|
+
const bodyW = w || d.w;
|
|
178
|
+
const bezel = Math.max(4, Math.round(d.bezel * (bodyW / d.w)));
|
|
179
|
+
const screenW = bodyW - bezel * 2;
|
|
180
|
+
const screenH = h ? h - bezel * 2 : Math.round(screenW * d.aspect);
|
|
181
|
+
const bodyH = screenH + bezel * 2;
|
|
182
|
+
const r = Math.round(bodyW * d.radius) + bezel;
|
|
183
|
+
const out = [];
|
|
184
|
+
// caption above the device
|
|
185
|
+
let top = y;
|
|
186
|
+
if (label || d.label) {
|
|
187
|
+
const t = label || d.label;
|
|
188
|
+
out.push(txt({ x, y: top, text: t, fontSize: labelSize, color: WIRE.text, width: bodyW, align: 'center' }));
|
|
189
|
+
top += labelSize * LH + 8;
|
|
190
|
+
}
|
|
191
|
+
// body (filled polygon: exact corner radius, which a rect's adaptive roundness can't reach)
|
|
192
|
+
out.push(roundRect({ x, y: top, w: bodyW, h: bodyH, r, fill: bezelColor, stroke: '#212529', strokeWidth: 2, seg: 8 }));
|
|
193
|
+
const sx = x + bezel, sy = top + bezel;
|
|
194
|
+
out.push(roundRect({ x: sx, y: sy, w: screenW, h: screenH, r: Math.max(2, r - bezel), fill: dark ? '#212529' : wallpaper, stroke: dark ? '#343a40' : WIRE.line, strokeWidth: 1, seg: 8 }));
|
|
195
|
+
|
|
196
|
+
let innerTop = sy, innerBottom = sy + screenH;
|
|
197
|
+
const fg = dark ? '#f1f3f5' : INK;
|
|
198
|
+
const SB_H = 20;
|
|
199
|
+
|
|
200
|
+
// Status marks live in an explicit band [top, top+h] and the caller advances innerTop past it.
|
|
201
|
+
// (Deriving the band from innerTop and then rewinding innerTop is how the time ended up sitting on
|
|
202
|
+
// the first row of content — the audit caught it at 73%.)
|
|
203
|
+
const statusBand = (top, h) => {
|
|
204
|
+
out.push(txt({ x: sx + 14, y: top + (h - 12 * LH) / 2, text: time, fontSize: 12, color: fg, width: 60, align: 'left' }));
|
|
205
|
+
const bx = sx + screenW - 52, by = top + h / 2 - 5;
|
|
206
|
+
for (let i = 0; i < 3; i++) out.push({ type: 'rectangle', x: bx + i * 5, y: by + 8 - i * 2.5, width: 3, height: 3 + i * 2.5, strokeColor: fg, backgroundColor: fg, fillStyle: 'solid', strokeWidth: 1 });
|
|
207
|
+
out.push(rc(bx + 22, by + 1, 18, 9, fg, 1));
|
|
208
|
+
out.push({ type: 'rectangle', x: bx + 24, y: by + 3, width: 10, height: 5, strokeColor: fg, backgroundColor: fg, fillStyle: 'solid', strokeWidth: 1 });
|
|
209
|
+
};
|
|
210
|
+
const wantStatus = d.status && status !== false;
|
|
211
|
+
|
|
212
|
+
if (d.island) {
|
|
213
|
+
// dynamic island: a pill inset from the top; the status marks sit BESIDE it, in its own band
|
|
214
|
+
const iw = screenW * 0.34, ih = Math.max(18, screenW * 0.075), iy = sy + Math.max(8, screenW * 0.026);
|
|
215
|
+
out.push(roundRect({ x: sx + (screenW - iw) / 2, y: iy, w: iw, h: ih, r: ih / 2, fill: '#212529', stroke: '#212529', strokeWidth: 1 }));
|
|
216
|
+
if (wantStatus) statusBand(iy, ih);
|
|
217
|
+
innerTop = iy + ih + 8;
|
|
218
|
+
} else {
|
|
219
|
+
let bandTop = sy;
|
|
220
|
+
if (d.camera) { out.push(dot(sx + screenW / 2, sy + 11, 4, WIRE.hint)); bandTop = sy + 22; }
|
|
221
|
+
if (d.notch) { const nw = screenW * 0.16, nh = 12; out.push({ type: 'rectangle', x: sx + (screenW - nw) / 2, y: sy, width: nw, height: nh, strokeColor: '#212529', backgroundColor: '#212529', fillStyle: 'solid', strokeWidth: 1 }); bandTop = sy + nh + 2; }
|
|
222
|
+
if (wantStatus) { statusBand(bandTop, SB_H); bandTop += SB_H; }
|
|
223
|
+
innerTop = bandTop + 4;
|
|
224
|
+
}
|
|
225
|
+
if (d.menubar) {
|
|
226
|
+
const mbH = 22;
|
|
227
|
+
out.push({ type: 'rectangle', x: sx, y: innerTop, width: screenW, height: mbH, strokeColor: WIRE.line, backgroundColor: WIRE.chrome, fillStyle: 'solid', strokeWidth: 1 });
|
|
228
|
+
['#ff5f57', '#febc2e', '#28c840'].forEach((c, i) => out.push(dot(sx + 14 + i * 14, innerTop + mbH / 2, 4, c)));
|
|
229
|
+
out.push(txt({ x: sx + 60, y: innerTop + 4, text: label || 'Finder', fontSize: 11, color: WIRE.text, width: 120, align: 'left' }));
|
|
230
|
+
innerTop += mbH;
|
|
231
|
+
}
|
|
232
|
+
if (d.home) {
|
|
233
|
+
const hw = screenW * 0.36, hh = 5;
|
|
234
|
+
out.push(roundRect({ x: sx + (screenW - hw) / 2, y: sy + screenH - 12, w: hw, h: hh, r: hh / 2, fill: dark ? '#868e96' : '#adb5bd', stroke: 'transparent', strokeWidth: 1 }));
|
|
235
|
+
innerBottom = sy + screenH - 20;
|
|
236
|
+
}
|
|
237
|
+
if (d.base) {
|
|
238
|
+
// laptop foot — what makes a display read as a Mac
|
|
239
|
+
const baseH = 12, lipW = bodyW * 1.06;
|
|
240
|
+
out.push({ type: 'line', points: [[x - (lipW - bodyW) / 2, top + bodyH], [x + bodyW + (lipW - bodyW) / 2, top + bodyH], [x + bodyW + (lipW - bodyW) / 2 - 18, top + bodyH + baseH], [x + (lipW - bodyW) / 2 - 12, top + bodyH + baseH], [x - (lipW - bodyW) / 2, top + bodyH]], strokeColor: '#212529', backgroundColor: '#adb5bd', fillStyle: 'solid', strokeWidth: 2 });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const inner = { x: sx + 8, y: innerTop + 6, w: screenW - 16, h: Math.max(20, innerBottom - innerTop - 12) };
|
|
244
|
+
const screen = { x: sx, y: sy, w: screenW, h: screenH };
|
|
245
|
+
|
|
246
|
+
// `content` is the safe way to fill a screen: the callback receives `inner`, and anything that
|
|
247
|
+
// escapes the glass gets reported. The build-time audit CANNOT see this on its own — the shell is a
|
|
248
|
+
// filled polygon, and isOpaqueBox() only knows rect/ellipse/diamond/image — so a hardcoded child
|
|
249
|
+
// width that overflows the bezel would otherwise ship looking clean.
|
|
250
|
+
if (typeof o.content === 'function') {
|
|
251
|
+
const kids = [].concat(...[].concat(o.content(inner)));
|
|
252
|
+
if (kids.length) {
|
|
253
|
+
const b = bbox(kids);
|
|
254
|
+
const over = [
|
|
255
|
+
b.x < screen.x - 1 && 'left', b.y < screen.y - 1 && 'top',
|
|
256
|
+
b.x + b.w > screen.x + screen.w + 1 && 'right', b.y + b.h > screen.y + screen.h + 1 && 'bottom',
|
|
257
|
+
].filter(Boolean);
|
|
258
|
+
if (over.length) {
|
|
259
|
+
const by = Math.round(Math.max(b.x + b.w - (screen.x + screen.w), b.y + b.h - (screen.y + screen.h), screen.x - b.x, screen.y - b.y));
|
|
260
|
+
try {
|
|
261
|
+
console.warn(`[excalidraw] device(${kind}): content escapes the screen on the ${over.join('/')} by ~${by}px — it will render outside the bezel.\n Fix: size children from the \`inner\` you were handed (inner.w=${Math.round(inner.w)}), don't hardcode widths.`);
|
|
262
|
+
} catch (e) {}
|
|
263
|
+
}
|
|
264
|
+
out.push(...kids);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const els = box(out, x, y, bodyW, (top - y) + bodyH + (d.base ? 12 : 0));
|
|
269
|
+
els.inner = inner;
|
|
270
|
+
els.screen = screen;
|
|
271
|
+
return G(els, 'device');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ── app chrome ────────────────────────────────────────────────────────────
|
|
275
|
+
/** Mobile top bar: optional back chevron, centred title, trailing icon buttons. */
|
|
276
|
+
function appBar(o) {
|
|
277
|
+
const { x = 0, y = 0, w = 340, h = 44, title = '', back = false, actions = [], fontSize = 15, border = true, fill = WIRE.paper } = o;
|
|
278
|
+
const out = [{ type: 'rectangle', x, y, width: w, height: h, strokeColor: border ? WIRE.line : 'transparent', backgroundColor: fill, fillStyle: 'solid', strokeWidth: 1, roundness: false }];
|
|
279
|
+
if (back) out.push(...iconButton({ x: x + 4, y: y + (h - 32) / 2, size: 32, icon: 'chevron-left', variant: 'ghost' }));
|
|
280
|
+
if (title) out.push(txt({ x, y: y + (h - fontSize * LH) / 2, text: title, fontSize, width: w, align: 'center' }));
|
|
281
|
+
actions.slice(0, 3).forEach((a, i) => {
|
|
282
|
+
out.push(...iconButton({ x: x + w - 8 - (i + 1) * 34, y: y + (h - 32) / 2, size: 32, icon: typeof a === 'string' ? a : a.icon, variant: 'ghost' }));
|
|
283
|
+
});
|
|
284
|
+
return G(box(out, x, y, w, h), 'appbar');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Bottom tab bar. `items` are icon names or {icon,label}; `active` is the index that reads selected. */
|
|
288
|
+
function tabBar(o) {
|
|
289
|
+
const { x = 0, y = 0, w = 340, h = 52, items = [], active = 0, color = 'blue', fontSize = 10, fill = WIRE.paper } = o;
|
|
290
|
+
const p = pal(color);
|
|
291
|
+
const out = [{ type: 'rectangle', x, y, width: w, height: h, strokeColor: WIRE.line, backgroundColor: fill, fillStyle: 'solid', strokeWidth: 1, roundness: false }];
|
|
292
|
+
const n = Math.max(1, items.length);
|
|
293
|
+
const cw = w / n;
|
|
294
|
+
items.forEach((it, i) => {
|
|
295
|
+
const name = typeof it === 'string' ? it : it.icon;
|
|
296
|
+
const label = typeof it === 'string' ? null : it.label;
|
|
297
|
+
const on = i === active;
|
|
298
|
+
const k = on ? p.stroke : WIRE.hint;
|
|
299
|
+
const is = 20;
|
|
300
|
+
out.push(...icon({ name, x: x + i * cw + (cw - is) / 2, y: y + (label ? 7 : (h - is) / 2), size: is, color: k, strokeWidth: on ? 2 : 1.5 }));
|
|
301
|
+
if (label) out.push(txt({ x: x + i * cw, y: y + 31, text: label, fontSize, color: k, width: cw, align: 'center' }));
|
|
302
|
+
});
|
|
303
|
+
return G(box(out, x, y, w, h), 'tabbar');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ── form controls ─────────────────────────────────────────────────────────
|
|
307
|
+
// Truncate to a pixel width with an ellipsis. A row is ONE line by definition, so an over-long title
|
|
308
|
+
// has to be cut — but it must LOOK cut. Wrapping and keeping line 1 (what this used to do) turned
|
|
309
|
+
// "Yıldızlı" into "Yıldız" with no hint anything was lost.
|
|
310
|
+
function clip(s, fontSize, w) {
|
|
311
|
+
const str = String(s == null ? '' : s);
|
|
312
|
+
if (!str || measureText(str, fontSize) <= w) return str;
|
|
313
|
+
let acc = '';
|
|
314
|
+
for (const ch of str) {
|
|
315
|
+
if (measureText(acc + ch + '…', fontSize) > w) break;
|
|
316
|
+
acc += ch;
|
|
317
|
+
}
|
|
318
|
+
return acc.replace(/\s+$/, '') + '…';
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** A list/table row: leading icon or avatar, title + subtitle, trailing chevron/toggle/text. */
|
|
322
|
+
function listRow(o) {
|
|
323
|
+
const {
|
|
324
|
+
x = 0, y = 0, w = 340, h = 56, title = '', subtitle = null, leading = null, trailing = 'chevron-right',
|
|
325
|
+
fontSize = 14, subSize = 12, divider: rule = true,
|
|
326
|
+
} = o;
|
|
327
|
+
const out = [{ type: 'rectangle', x, y, width: w, height: h, strokeColor: 'transparent', backgroundColor: WIRE.paper, fillStyle: 'solid', strokeWidth: 1, roundness: false }];
|
|
328
|
+
let tx = x + 14;
|
|
329
|
+
if (leading) {
|
|
330
|
+
if (leading === 'avatar') { out.push(...avatar({ cx: x + 14 + 16, cy: y + h / 2, r: 16 })); tx = x + 14 + 40; }
|
|
331
|
+
else { out.push(...icon({ name: leading, x: x + 14, y: y + (h - 20) / 2, size: 20, color: WIRE.strong })); tx = x + 14 + 32; }
|
|
332
|
+
}
|
|
333
|
+
const tw = w - (tx - x) - 46;
|
|
334
|
+
if (subtitle) {
|
|
335
|
+
out.push(txt({ x: tx, y: y + h / 2 - fontSize * LH + 2, text: clip(title, fontSize, tw), fontSize, width: tw, align: 'left' }));
|
|
336
|
+
out.push(txt({ x: tx, y: y + h / 2 + 2, text: clip(subtitle, subSize, tw), fontSize: subSize, color: WIRE.text, width: tw, align: 'left' }));
|
|
337
|
+
} else {
|
|
338
|
+
out.push(txt({ x: tx, y: y + (h - fontSize * LH) / 2, text: clip(title, fontSize, tw), fontSize, width: tw, align: 'left' }));
|
|
339
|
+
}
|
|
340
|
+
if (trailing === 'toggle') out.push(...toggle({ x: x + w - 58, y: y + (h - 28) / 2, on: true }));
|
|
341
|
+
else if (typeof trailing === 'string' && ICONS[trailing]) out.push(...icon({ name: trailing, x: x + w - 30, y: y + (h - 16) / 2, size: 16, color: WIRE.hint }));
|
|
342
|
+
else if (trailing) out.push(txt({ x: x + w - 110, y: y + (h - subSize * LH) / 2, text: String(trailing), fontSize: subSize, color: WIRE.text, width: 96, align: 'right' }));
|
|
343
|
+
if (rule) out.push({ type: 'line', points: [[tx, y + h], [x + w, y + h]], strokeColor: WIRE.line, strokeWidth: 1 });
|
|
344
|
+
return G(box(out, x, y, w, h), 'listrow');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** iOS-style switch. */
|
|
348
|
+
function toggle(o) {
|
|
349
|
+
const { x = 0, y = 0, w = 48, h = 28, on = false, color = 'green' } = o;
|
|
350
|
+
const p = pal(color);
|
|
351
|
+
const out = [roundRect({ x, y, w, h, r: h / 2, fill: on ? p.fill : WIRE.line, stroke: on ? p.stroke : WIRE.edge, strokeWidth: 1.5 })];
|
|
352
|
+
const kr = h / 2 - 3;
|
|
353
|
+
out.push({ type: 'ellipse', x: on ? x + w - kr * 2 - 3 : x + 3, y: y + 3, width: kr * 2, height: kr * 2, strokeColor: WIRE.edge, backgroundColor: '#ffffff', fillStyle: 'solid', strokeWidth: 1 });
|
|
354
|
+
return G(box(out, x, y, w, h), 'toggle');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** Segmented control — the mobile tab picker. */
|
|
358
|
+
function segmented(o) {
|
|
359
|
+
const { x = 0, y = 0, w = 300, h = 34, items = [], active = 0, fontSize = 13, color = 'blue' } = o;
|
|
360
|
+
const p = pal(color);
|
|
361
|
+
const out = [roundRect({ x, y, w, h, r: 8, fill: WIRE.chrome, stroke: WIRE.edge, strokeWidth: 1.5 })];
|
|
362
|
+
const n = Math.max(1, items.length), cw = (w - 6) / n;
|
|
363
|
+
items.forEach((it, i) => {
|
|
364
|
+
const on = i === active;
|
|
365
|
+
if (on) out.push(roundRect({ x: x + 3 + i * cw, y: y + 3, w: cw, h: h - 6, r: 6, fill: p.fill, stroke: p.stroke, strokeWidth: 1 }));
|
|
366
|
+
out.push(txt({ x: x + 3 + i * cw, y: y + (h - fontSize * LH) / 2, text: String(it), fontSize, color: on ? p.stroke : WIRE.text, width: cw, align: 'center' }));
|
|
367
|
+
});
|
|
368
|
+
return G(box(out, x, y, w, h), 'segmented');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** A slider at `value` (0..1). */
|
|
372
|
+
function slider(o) {
|
|
373
|
+
const { x = 0, y = 0, w = 260, value = 0.5, color = 'blue', knob = 11 } = o;
|
|
374
|
+
const p = pal(color);
|
|
375
|
+
const cy = y + knob;
|
|
376
|
+
const v = Math.max(0, Math.min(1, value));
|
|
377
|
+
const out = [
|
|
378
|
+
roundRect({ x, y: cy - 2, w, h: 4, r: 2, fill: WIRE.line, stroke: 'transparent', strokeWidth: 1 }),
|
|
379
|
+
roundRect({ x, y: cy - 2, w: Math.max(2, w * v), h: 4, r: 2, fill: p.stroke, stroke: 'transparent', strokeWidth: 1 }),
|
|
380
|
+
{ type: 'ellipse', x: x + w * v - knob, y: cy - knob, width: knob * 2, height: knob * 2, strokeColor: WIRE.edge, backgroundColor: '#ffffff', fillStyle: 'solid', strokeWidth: 1.5 },
|
|
381
|
+
];
|
|
382
|
+
return G(box(out, x, y, w, knob * 2), 'slider');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/** A search field — an input with a magnifier, the most-drawn control in mobile wireframes. */
|
|
386
|
+
function searchField(o) {
|
|
387
|
+
const { x = 0, y = 0, w = 300, h = 36, placeholder = 'Search', fontSize = 13 } = o;
|
|
388
|
+
const out = [roundRect({ x, y, w, h, r: h / 2, fill: WIRE.chrome, stroke: WIRE.edge, strokeWidth: 1.5 })];
|
|
389
|
+
out.push(...icon({ name: 'search', x: x + 12, y: y + (h - 15) / 2, size: 15, color: WIRE.hint }));
|
|
390
|
+
out.push(txt({ x: x + 36, y: y + (h - fontSize * LH) / 2, text: placeholder, fontSize, color: WIRE.hint, width: w - 48, align: 'left' }));
|
|
391
|
+
return G(box(out, x, y, w, h), 'search');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
module.exports = {
|
|
395
|
+
// devices + chrome
|
|
396
|
+
device, DEVICES, appBar, tabBar,
|
|
397
|
+
// icons
|
|
398
|
+
icon, iconButton, ICONS, iconNames,
|
|
399
|
+
// controls
|
|
400
|
+
listRow, toggle, segmented, slider, searchField,
|
|
401
|
+
// geometry / text helpers
|
|
402
|
+
clip,
|
|
403
|
+
roundRect, roundRectPoints,
|
|
404
|
+
// re-exported from style.js so one require gives the whole UI kit
|
|
405
|
+
windowFrame, navbar, button, input, textRows, imagePlaceholder, avatar, chip, divider,
|
|
406
|
+
};
|