{"context":{"domain":"playground"},"layers":{"ApplicationLayer":{"reference":"ApplicationLayer","availableInstances":[{"reference":"ComponentsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@DataSet","reference":"SystemsDataSet","params":{"filter":{"domain":"data2"},"pagination":{"size":1},"datasource":{"component":"data2-core-types@ProcessReference","params":{"reference":"SystemsDataSource"}}}},{"reference":"ExternalComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"SystemsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"5e3b796d6228227ca637d0f4","name":"data2","domain":"data2","organization":"62292184611f480b3191c37c","visibility":"public","updatedAt":"2026-07-27T14:19:36.810Z"}]}},{"reference":"OrganizationsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"DataComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@DataSet","reference":"OneFileComponentInstancesDataSet","params":{"pagination":{"size":1},"master":{"dataset":{"component":"data2-core-types@ProcessReference","params":{"reference":"SystemsDataSet"}},"localField":"system","masterField":"_id"},"datasource":{"component":"data2-core-types@ProcessReference","params":{"reference":"FileComponentInstancesDataSource"}}}},{"reference":"ApplicationsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"ComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"ServiceComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@Data2TableDataSource","reference":"FileComponentInstancesDataSource","params":{"table":"fileComponentInstances","schema":[{"component":"data2-core-types@Field","params":{"key":"component","title":"Component","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"system","title":"System","component":"data2-core-primitives@ObjectId"}},{"component":"data2-core-types@Field","params":{"key":"displayName","title":"Display Name","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"params","title":"Params","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"reference","title":"Reference","component":"data2-core-primitives@String"}}]}},{"component":"data2-core@MemoryDataSource","reference":"EmptyDataSource","params":{"initialData":[]}},{"reference":"ComponentsDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"UserComponentsDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"DataComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ServiceComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ExternalComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ApplicationsDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}}],"processesParams":{}},"HostApplicationLayer":{"reference":"HostApplicationLayer","availableInstances":[{"_id":"5f15ff190528b92601890f71","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"SystemsTableDataSource"}},"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"domain"}}},"pk":"_id","fields":[{"component":"data2-core-types@FieldReference","params":{"key":"_id"}},{"component":"data2-core-types@FieldReference","params":{"key":"name"}},{"component":"data2-core-types@FieldReference","params":{"key":"domain"}},{"component":"data2-core-types@FieldReference","params":{"key":"visibility"}},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"customDomains"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization._id"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization.name"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization.billingStatus"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization.publicTrial.active"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"settings.defaultAccessPolicy"}}},"break":true}],"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}},"staticPagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}},"policies":[{"component":"data2-core-auth@Policy","params":{"allow":{"params":{"actions":[{"params":{"components":[{"params":{"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$params.query"}}}},"field":{"component":"data2-core-primitives@String","params":{"content":"_id"}},"fieldType":"string"},"component":"data2@7KkQq0vK4a7D2gg6IVJT7"},{"params":{"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$params.query"}}}},"field":{"component":"data2-core-primitives@String","params":{"content":"domain"}},"fieldType":"string"},"component":"data2@7KkQq0vK4a7D2gg6IVJT7"},{"params":{"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$params.query"}}}},"field":{"component":"data2-core-primitives@String","params":{"content":"customDomains"}},"fieldType":"string"},"component":"data2@7KkQq0vK4a7D2gg6IVJT7"}]},"component":"data2-core-logical-operators@Or"}]},"component":"data2-core-flow@Series"}}}]},"reference":"SystemsDataSet","updatedAt":"2026-08-07T21:51:24.037Z","createdAt":"2020-07-20T20:31:21.838Z"},{"_id":"5f1601ce0528b92601890f7c","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":1000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentsTableDataSource"}},"sort":{"params.package":1,"params.component":1},"pk":"_id","autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"staticFilter":{"system":{"exists":{"params":{"content":false},"component":"data2-core-primitives@Boolean"}}},"filter":{"system":{"exists":{"params":{"content":false},"component":"data2-core-primitives@Boolean"}}}},"reference":"ComponentsDataSet","updatedAt":"2025-11-05T19:37:01.935Z","createdAt":"2020-07-20T20:42:54.898Z"},{"_id":"5f9041dbc8da12be8b94aea4","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":2000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentsTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"UserComponentsDataSet","updatedAt":"2025-11-06T21:06:58.095Z","createdAt":"2020-07-20T20:44:09.350Z"},{"_id":"65d8c4fabc9dbb8f94de8d53","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ExternalComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ExternalComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:23.742Z","createdAt":"2024-02-23T16:17:01.816Z"},{"reference":"SystemsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"5e72ab6c7ff98e774545a642","name":"playground","domain":"playground","visibility":"public","settings":{"defaultAccessPolicy":"private"},"updatedAt":"2026-08-14T19:26:29.927Z"}]}},{"reference":"ComponentsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"5f52a278a575399a45d239b4","component":"data2-core-types@Component","params":{"package":"data2-core-primitives","component":"Router","interactions":{"component":"data2-core-types@FieldSet","params":{"fields":{"goBack":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-types@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"component":"data2-core-types@This","params":{}},"prop":"history.goBack","args":[]}}}},"icon":{"component":"data2-core-primitives@String","params":{"content":"arrow-left"}},"visibility":"public"}},"goTo":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"component":"data2-core-types@This","params":{}},"prop":"history.push","args":[{"component":"data2-core-types@Reference","params":{"reference":"url"}},{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"preserveQuery"}}}}]}},"parameters":[{"component":"data2-core-primitives@String","params":{"content":"url"}},{"component":"data2-core-primitives@String","params":{"content":"preserveQuery"}}]},"break":true},"icon":{"component":"data2-core-primitives@String","params":{"content":"share"}},"visibility":"public","arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Path to route"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Path to redirect user"}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"},{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Boolean"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Preserve Query"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Preserve query string parameters  preserve query parameters on navigating."}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"}]}},"open":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-types@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"component":"data2-core-types@This","params":{}},"prop":"history.push","args":[{"pathname":{"component":"data2-core-types@Reference","params":{"reference":"value"}}}]}},"parameters":["value"]}},"icon":{"component":"data2-core-primitives@String","params":{"content":"share"}},"visibility":"public","arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Link to open"}},"description":{"component":"data2-core-primitives@String","params":{"content":""}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"},{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Target"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Where to open the link"}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"oneOf":[{"params":{"content":"_blank"},"component":"data2-core-primitives@String"},{"params":{"content":"_self"},"component":"data2-core-primitives@String"},{"params":{"content":"_parent"},"component":"data2-core-primitives@String"},{"params":{"content":"_top"},"component":"data2-core-primitives@String"}]},"component":"data2-core-types@ComponentParameter"}]}},"close":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-types@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"params":{},"component":"data2-core-web@Window"},"prop":"close"}},"parameters":["value"]}},"icon":{"component":"data2-core-primitives@String","params":{"content":"times"}},"visibility":"public","arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Link to open"}},"description":{"component":"data2-core-primitives@String","params":{"content":""}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"},{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Target"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Where to open the link"}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"oneOf":[{"params":{"content":"_blank"},"component":"data2-core-primitives@String"},{"params":{"content":"_self"},"component":"data2-core-primitives@String"},{"params":{"content":"_parent"},"component":"data2-core-primitives@String"},{"params":{"content":"_top"},"component":"data2-core-primitives@String"}]},"component":"data2-core-types@ComponentParameter"}],"description":{"component":"data2-core-primitives@String","params":{"content":"Closes a window that was opened via router.open."}}}}}}}}},{"_id":"620d28658271aa4129919d89","component":"data2-core-types@Component","params":{"package":"data2-interface-services","component":"DeviceController","interactions":{"params":{"fields":{"postMessage":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":"data2-core-primitives@String","title":{"component":"data2-core-primitives@String","params":{"content":"data"}}}}]},"component":"data2-core-types@ComponentParameter"},"print":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Object"}},"flat":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"parameters":{"zoom":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Number"}},"title":{"component":"data2-core-primitives@String","params":{"content":"zoom"}},"visibility":"public"}}},"visibility":"public"},"component":"data2-core-types@ComponentParameter"}],"visibility":"public"},"component":"data2-core-types@ComponentParameter"},"share":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}}},"component":"data2-core-types@ComponentParameter"},"clipboard.copy":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}}}}]},"component":"data2-core-types@ComponentParameter"},"clipboard.paste":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}}}}]},"component":"data2-core-types@ComponentParameter"},"download":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Filename"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Content"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Object"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Options"}},"parameters":{"params":{"fields":{"type":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Mimetype"}}}}}},"component":"data2-core-types@FieldSet"}}}]},"component":"data2-core-types@ComponentParameter"},"downloadUrl":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Filename"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Url"}}}}]},"component":"data2-core-types@ComponentParameter"},"promptFiles":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":"data2-core-primitives@Object","flat":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"parameters":{"params":{"fields":{"multiple":{"component":"data2-core-types@ComponentParameter","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Multiple"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Whether or not to allow selecting multiple files"}},"component":"data2-core-primitives@Boolean"}},"acceptedFormats":{"component":"data2-core-types@ComponentParameter","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Accepted Formats"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Accepted file formats"}},"component":"data2-core-primitives@String","list":{"component":"data2-core-primitives@Boolean","params":{"content":true}}}},"timeout":{"component":"data2-core-types@ComponentParameter","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Timeout"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Max milliseconds to wait for file upload"}},"component":"data2-core-primitives@Number"}}}},"component":"data2-core-types@FieldSet"}}}],"visibility":"public"},"component":"data2-core-types@ComponentParameter"},"meetingRecord":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","definition":{"params":{"actions":[{"params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"meeting"}},"script":{"component":"data2-core-primitives@String","params":{"content":"(function() {\n  let mediaRecorder;\n  let audioChunks = [];\n  let combinedStream;\n  let audioContext;\n  \n  window.meetingCapture = async function() {\n    try {\n      // Solicitar acesso ao áudio da guia (e vídeo, que será ignorado depois)\n      const tabStream = await navigator.mediaDevices.getDisplayMedia({ \n        video: true, // Necessário para alguns navegadores\n        audio: true\n      });\n      \n      // Remover a trilha de vídeo\n      const tabAudioTrack = tabStream.getAudioTracks()[0];\n      \n      // Solicitar acesso ao microfone\n      const micStream = await navigator.mediaDevices.getUserMedia({ audio: true });\n      const micAudioTrack = micStream.getAudioTracks()[0];\n      \n      // Criar um novo MediaStream com ambas as trilhas de áudio\n      combinedStream = new MediaStream([tabAudioTrack, micAudioTrack]);\n      \n      // Criar um contexto de áudio para misturar os streams\n      audioContext = new AudioContext();\n      const destination = audioContext.createMediaStreamDestination();\n      \n      // Conectar a trilha de áudio da guia\n      const tabSource = audioContext.createMediaStreamSource(new MediaStream([tabAudioTrack]));\n      tabSource.connect(destination);\n      \n      // Conectar a trilha de áudio do microfone\n      const micSource = audioContext.createMediaStreamSource(new MediaStream([micAudioTrack]));\n      micSource.connect(destination);\n      \n      // Iniciar gravação com o stream misturado\n      mediaRecorder = new MediaRecorder(destination.stream);\n      audioChunks = [];\n      mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data);\n      mediaRecorder.start();\n      \n      console.log('Gravação iniciada. Chame meetingCaptureStop() para finalizar.');\n    } catch (error) {\n      console.error('Erro ao iniciar a captura:', error);\n    }\n  };\n  \n  window.meetingCaptureStop = function() {\n    return new Promise((resolve, reject) => {\n      if (!mediaRecorder) {\n        reject('Nenhuma gravação em andamento.');\n        return;\n      }\n      \n      mediaRecorder.onstop = () => {\n        const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });\n        combinedStream.getTracks().forEach(track => track.stop());\n        audioContext.close();\n        \n        resolve(audioBlob);\n        console.log('Gravação finalizada. O arquivo de áudio está pronto para upload.');\n      };\n      \n      mediaRecorder.stop();\n    });\n  };\n})();"}}},"component":"data2-core-web@Script"},{"component":"data2-core-types@Getter","params":{"module":{"params":{},"component":"data2-core-web@Window"},"prop":{"component":"data2-core-primitives@String","params":{"content":"meetingCapture"}}}}]},"component":"data2-core-flow@Series"}},"component":"data2-core-types@ComponentParameter"},"meetingRecordStop":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Filename"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Content"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Object"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Options"}},"parameters":{"params":{"fields":{"type":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Mimetype"}}}}}},"component":"data2-core-types@FieldSet"}}}],"definition":{"params":{"actions":[{"component":"data2-core-types@Getter","params":{"module":{"params":{},"component":"data2-core-web@Window"},"prop":{"component":"data2-core-primitives@String","params":{"content":"meetingCaptureStop"}}}}]},"component":"data2-core-flow@Series"}},"component":"data2-core-types@ComponentParameter"},"notify":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"private","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":"data2-core-primitives@Object","parameters":{"title":{"params":{"component":"data2-core-primitives@String","index":{"component":"data2-core-primitives@Number","params":{"content":0}},"title":{"component":"data2-core-primitives@String","params":{"content":"title"}}},"component":"data2-core-types@ComponentParameter"},"sound":{"params":{"component":"data2-core-types@FileComponentUrlReference","index":{"component":"data2-core-primitives@Number","params":{"content":1}},"title":{"component":"data2-core-primitives@String","params":{"content":"sound"}}},"component":"data2-core-types@ComponentParameter"}},"flat":{"component":"data2-core-primitives@Boolean","params":{"content":true}}}}]},"component":"data2-core-types@ComponentParameter"}}},"component":"data2-core-types@FieldSet"}}},{"_id":"6a3197264d3a3f03ffbe1a21","component":"data2-core-types@Component","system":"5e72ab6c7ff98e774545a642","params":{"extends":"data2-core-primitives@Block","component":"acervo-stat-card","package":"playground","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `.kpi{display:flex;flex-direction:column;justify-content:center;gap:6px;height:100%;box-sizing:border-box;\n        padding:14px 16px;border-radius:14px;background:#fff;border:1px solid #eceaf3;\n        box-shadow:0 1px 2px rgba(20,16,41,.04);font-family:'Inter',system-ui,-apple-system,sans-serif}\n        .top{display:flex;align-items:center;gap:10px}\n        .g{width:34px;height:34px;flex:none;display:flex;align-items:center;justify-content:center;border-radius:10px;font-size:18px}\n        .l{font-size:11px;color:#8a86a3;text-transform:uppercase;letter-spacing:.06em;font-weight:600}\n        .v{font-size:26px;font-weight:800;color:#1b1830;line-height:1}`,\n  render(d2){\n    const accent = d2.param('accent','#6b4ef0')\n    const label = (d2.param('label','')+'').replace(/</g,'&lt;')\n    const glyph = d2.param('glyph','📦')\n    const metric = d2.param('metric','count')\n    const ds = d2.data()\n    let value = '—'\n    if (ds && !ds.loading){\n      const rows = ds.records()\n      if (metric==='tipos') value = String(new Set(rows.map(r=>r.tipo).filter(Boolean)).size)\n      else if (metric==='oldest'){ const anos = rows.map(r=>Number(r.ano)).filter(n=>n>0); value = anos.length ? String(Math.min(...anos)) : '—' }\n      else value = String(rows.length)\n    }\n    return `<div class=\"kpi\">\n      <div class=\"top\">\n        <span class=\"g\" style=\"background:${accent}1a;color:${accent}\">${glyph}</span>\n        <span class=\"l\">${label}</span>\n      </div>\n      <div class=\"v\">${value}</div>\n    </div>`\n  },\n}"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}},"metric":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"metric"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"glyph":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"glyph"}}}},"accent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"accent"}}}}}}}}}},{"_id":"6a31975d4d3a3f03ffbe1b57","component":"data2-core-types@Component","system":"5e72ab6c7ff98e774545a642","params":{"extends":"data2-core-primitives@Block","component":"acervo-cadastro","package":"playground","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"const STAT_CARD = 'playground@acervo-stat-card'\nconst FORM = 'playground@acervo-form'\nconst esc = (s)=> (s==null?'':String(s)).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;')\n// crossfade: fade out current ficha, swap selection, fade in the new one\nconst goTo = (d2, next)=>{\n  if (d2.state.__t) clearTimeout(d2.state.__t)\n  d2.state.fading = true\n  d2.state.__t = setTimeout(()=>{ d2.state.selected = next; d2.state.fading = false; d2.state.__t = null }, 180)\n}\n\nexport default {\n  css: `.root{display:flex;flex-direction:column;height:100%;box-sizing:border-box;padding:18px;gap:14px;\n        background:#f6f5fb;font-family:'Inter',system-ui,-apple-system,sans-serif;color:#1b1830;overflow:hidden}\n        .topbar{display:flex;align-items:center;justify-content:space-between}\n        .brand{display:flex;align-items:center;gap:12px}\n        .logo{width:40px;height:40px;border-radius:11px;display:flex;align-items:center;justify-content:center;\n          background:linear-gradient(135deg,#6b4ef0,#9b6bff);color:#fff;font-weight:800;font-size:20px}\n        .bt{font-size:18px;font-weight:800;line-height:1.1}\n        .bs{font-size:12px;color:#8a86a3}\n        .stats{display:grid;grid-template-columns:repeat(3,1fr);grid-auto-rows:92px;gap:14px}\n        .body{flex:1;min-height:0;display:grid;grid-template-columns:340px 1fr;gap:14px}\n        .col-list{display:flex;flex-direction:column;min-height:0;background:#fff;border:1px solid #eceaf3;border-radius:16px;overflow:hidden}\n        .search{padding:12px;border-bottom:1px solid #f0eef7}\n        .search-in{width:100%;box-sizing:border-box;padding:9px 12px;border:1px solid #e3e0ee;border-radius:10px;font-size:13px;outline:none;background:#faf9fe}\n        .search-in:focus{border-color:#6b4ef0;background:#fff}\n        .list{flex:1;overflow:auto;padding:8px}\n        .item{padding:11px 12px;border-radius:11px;cursor:pointer;border:1px solid transparent;margin-bottom:4px;transition:background .15s ease,border-color .15s ease}\n        .item:hover{background:#f7f5fe}\n        .item[data-active]{background:#efeaff;border-color:#d9cdff}\n        .it-top{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:3px}\n        .it-cod{font-size:11px;font-weight:700;color:#6b4ef0;font-family:ui-monospace,monospace}\n        .it-tipo{font-size:10px;color:#8a86a3;background:#f0eef7;padding:2px 7px;border-radius:20px;text-transform:uppercase;letter-spacing:.04em}\n        .it-tit{font-size:14px;font-weight:700;line-height:1.25}\n        .it-aut{font-size:12px;color:#8a86a3;margin-top:2px}\n        .empty{padding:40px 16px;text-align:center;color:#a8a4bd;font-size:13px}\n        .col-form{min-height:0;display:flex}\n        .form-wrap{flex:1;display:flex;min-height:0;opacity:1;transform:none;transition:opacity .2s ease,transform .2s ease;will-change:opacity,transform}\n        .form-wrap.fading{opacity:0;transform:translateY(6px)}\n        .empty-panel{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;text-align:center;padding:40px;\n          background:#fff;border:1px solid #eceaf3;border-radius:16px}\n        .ep-glyph{font-size:46px}\n        .ep-title{font-size:18px;font-weight:800}\n        .ep-sub{font-size:13px;color:#8a86a3;max-width:300px;line-height:1.5;margin-bottom:8px}\n        .btn-primary{padding:10px 18px;border:0;border-radius:10px;background:#6b4ef0;color:#fff;font-weight:700;font-size:13px;cursor:pointer;font-family:inherit}\n        .btn-primary:hover{background:#5a3fe0}\n        .loading{padding:40px;text-align:center;color:#8a86a3}`,\n  render(d2){\n    const ds = d2.data()\n    if (ds.loading) return `<div class=\"root\"><div class=\"loading\">Carregando acervo…</div></div>`\n    const all = ds.records()\n    const q = (d2.state.search||'').toLowerCase().trim()\n    const rows = q ? all.filter(r => (`${r.titulo||''} ${r.autor||''} ${r.codigo||''} ${r.assuntos||''} ${r.tipo||''} ${r.colecao||''}`).toLowerCase().includes(q)) : all\n    const sel = d2.state.selected || null\n    const selId = sel && sel._id ? sel._id : null\n\n    const stats = `<div class=\"stats\">\n      ${d2.render(STAT_CARD,{dataset:d2.params.dataset,metric:'count',label:'Itens catalogados',glyph:'📚',accent:'#6b4ef0'},{key:'s1'})}\n      ${d2.render(STAT_CARD,{dataset:d2.params.dataset,metric:'tipos',label:'Tipos de material',glyph:'🗂️',accent:'#1aa37a'},{key:'s2'})}\n      ${d2.render(STAT_CARD,{dataset:d2.params.dataset,metric:'oldest',label:'Item mais antigo',glyph:'🕰️',accent:'#d9822b'},{key:'s3'})}\n    </div>`\n\n    const list = rows.length ? rows.map(r=>{\n      const active = r._id===selId ? ' data-active' : ''\n      return `<div class=\"item\" data-key=\"${r._id}\" data-id=\"${r._id}\"${active}>\n        <div class=\"it-top\"><span class=\"it-cod\">${esc(r.codigo)||'—'}</span><span class=\"it-tipo\">${esc(r.tipo)||'—'}</span></div>\n        <div class=\"it-tit\">${esc(r.titulo)||'(sem título)'}</div>\n        <div class=\"it-aut\">${esc(r.autor)||'Autor não informado'}${r.ano?(' · '+esc(r.ano)):''}</div>\n      </div>`\n    }).join('') : `<div class=\"empty\">Nenhum item encontrado${q?' para \"'+esc(q)+'\"':''}.</div>`\n\n    const formSlot = sel\n      ? d2.render(FORM, { dataset: d2.params.dataset, record: sel }, {\n          key: selId || 'new',\n          onSaved: (rec)=>{ d2.state.selected = (rec && rec._id) ? {...rec} : null },\n          onCancel: ()=>{ goTo(d2, null) },\n          onDeleted: ()=>{ goTo(d2, null) },\n        })\n      : `<div class=\"empty-panel\"><div class=\"ep-glyph\">🗃️</div><div class=\"ep-title\">Catalogação de acervo</div><div class=\"ep-sub\">Selecione um item à esquerda para editar a ficha, ou cadastre um novo registro no acervo digital.</div><button class=\"btn-primary\" data-act=\"new\">+ Novo item</button></div>`\n\n    return `<div class=\"root\">\n      <div class=\"topbar\">\n        <div class=\"brand\"><span class=\"logo\">S</span><div><div class=\"bt\">Acervo Digital</div><div class=\"bs\">Catalogação · inspirado no Sophia</div></div></div>\n        <button class=\"btn-primary\" data-act=\"new\">+ Novo item</button>\n      </div>\n      ${stats}\n      <div class=\"body\">\n        <div class=\"col-list\">\n          <div class=\"search\"><input class=\"search-in\" placeholder=\"Buscar por título, autor, código, assunto…\" value=\"${esc(d2.state.search)}\"/></div>\n          <div class=\"list\">${list}</div>\n        </div>\n        <div class=\"col-form\"><div class=\"form-wrap${d2.state.fading?' fading':''}\">${formSlot}</div></div>\n      </div>\n    </div>`\n  },\n  mounted(d2){\n    d2.on('.search-in','input',(e,el)=>{ d2.state.search = el.value })\n    d2.on('.item','click',(e,el)=>{ const rec = d2.data().records().find(r=>r._id===el.dataset.id); if(rec) goTo(d2, {...rec}) })\n    d2.on('[data-act=\"new\"]','click',()=>{ if (d2.param('onNewItem')) { d2.emit('onNewItem', { dataset: d2.params.dataset }); return } goTo(d2, {}) })\n    d2.onUnmount(()=>{ if (d2.state.__t) clearTimeout(d2.state.__t) })\n  },\n}"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}},"onNewItem":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNewItem"}}}}}}}}}},{"_id":"6a319cd77fc78eca6d2dbf78","component":"data2-core-types@Component","system":"5e72ab6c7ff98e774545a642","params":{"extends":"data2-core-primitives@Block","component":"acervo-form","package":"playground","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"const TIPOS = ['Livro','Periódico','Documento','Manuscrito','Fotografia','Mapa','Áudio','Vídeo','Objeto','Outro']\nconst ESTADOS = ['Ótimo','Bom','Regular','Ruim']\nconst FIELDS = [\n  {k:'codigo', l:'Código / Nº de Tombo', ph:'BIB-0001'},\n  {k:'titulo', l:'Título', ph:'Título do item', req:true, wide:true},\n  {k:'autor', l:'Autor / Responsável', ph:'Nome do autor'},\n  {k:'tipo', l:'Tipo de Material', sel:TIPOS},\n  {k:'colecao', l:'Coleção / Fundo', ph:'Nome da coleção'},\n  {k:'ano', l:'Ano', ph:'1881', num:true},\n  {k:'idioma', l:'Idioma', ph:'Português'},\n  {k:'classificacao', l:'Classificação (CDD/CDU)', ph:'869.3'},\n  {k:'assuntos', l:'Assuntos / Descritores', ph:'separados por vírgula', wide:true},\n  {k:'localizacao', l:'Localização', ph:'Estante / Prateleira'},\n  {k:'estadoConservacao', l:'Estado de Conservação', sel:ESTADOS},\n  {k:'urlDigital', l:'Objeto Digital (URL)', ph:'https://...', wide:true},\n  {k:'descricao', l:'Descrição / Resumo', ph:'Resumo do item', area:true, wide:true},\n]\nconst esc = (s)=> (s==null?'':String(s)).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;')\nconst today = ()=>{ const d=new Date(); return d.toISOString().slice(0,10) }\nconst recKey = (d2)=>{ const r=d2.param('record'); return (r && r._id) ? r._id : '__new__' }\nconst seedFrom = (d2)=>{ const r=d2.param('record'); d2.state.draft = (r && Object.keys(r).length) ? {...r} : { tipo:'Livro', estadoConservacao:'Bom', idioma:'Português', dataCadastro: today() }; d2.state.seededKey = recKey(d2) }\n\nexport default {\n  css: `.panel{display:flex;flex-direction:column;height:100%;box-sizing:border-box;min-height:0;background:#fff;border:1px solid #eceaf3;border-radius:16px;overflow:hidden;font-family:'Inter',system-ui,-apple-system,sans-serif;color:#1b1830}\n        .ph-head{display:flex;align-items:center;justify-content:space-between;padding:16px 18px;border-bottom:1px solid #f0eef7}\n        .ph-title{font-size:16px;font-weight:800}\n        .form{flex:1;overflow:auto;padding:18px;display:grid;grid-template-columns:1fr 1fr;gap:14px 16px;align-content:start}\n        .fld{display:flex;flex-direction:column;gap:5px}\n        .fld.wide{grid-column:1 / -1}\n        .flbl{font-size:11px;font-weight:600;color:#6e6a86;text-transform:uppercase;letter-spacing:.04em}\n        .flbl i{color:#e0567a;font-style:normal}\n        .ctl{box-sizing:border-box;padding:9px 11px;border:1px solid #e3e0ee;border-radius:10px;font-size:13px;outline:none;background:#faf9fe;font-family:inherit;color:#1b1830}\n        .ctl:focus{border-color:#6b4ef0;background:#fff}\n        .ta{resize:vertical;min-height:64px}\n        .ph-foot{display:flex;align-items:center;justify-content:flex-end;gap:10px;padding:14px 18px;border-top:1px solid #f0eef7}\n        .btn-primary{padding:10px 18px;border:0;border-radius:10px;background:#6b4ef0;color:#fff;font-weight:700;font-size:13px;cursor:pointer;font-family:inherit}\n        .btn-primary:hover{background:#5a3fe0}\n        .btn-primary[disabled]{opacity:.55;cursor:default}\n        .btn-ghost{padding:10px 16px;border:1px solid #e3e0ee;border-radius:10px;background:#fff;color:#6e6a86;font-weight:600;font-size:13px;cursor:pointer;font-family:inherit}\n        .btn-del{padding:7px 14px;border:1px solid #f3c9d6;border-radius:9px;background:#fff;color:#d6436a;font-weight:600;font-size:12px;cursor:pointer;font-family:inherit}\n        .btn-del:hover{background:#fdf0f4}`,\n  render(d2){\n    const ds = d2.data()\n    const draft = d2.state.draft || {}\n    const isNew = !draft._id\n    const fieldHtml = FIELDS.map(f=>{\n      const v = draft[f.k]\n      let control\n      if (f.sel){\n        control = `<select class=\"ctl\" data-field=\"${f.k}\"><option value=\"\">—</option>${f.sel.map(o=>`<option value=\"${esc(o)}\"${String(v||'')===o?' selected':''}>${esc(o)}</option>`).join('')}</select>`\n      } else if (f.area){\n        control = `<textarea class=\"ctl ta\" data-field=\"${f.k}\" placeholder=\"${esc(f.ph)}\">${esc(v)}</textarea>`\n      } else {\n        control = `<input class=\"ctl\" data-field=\"${f.k}\" type=\"${f.num?'number':'text'}\" value=\"${esc(v)}\" placeholder=\"${esc(f.ph)}\"/>`\n      }\n      return `<label class=\"fld${f.wide?' wide':''}\"><span class=\"flbl\">${esc(f.l)}${f.req?' <i>*</i>':''}</span>${control}</label>`\n    }).join('')\n    return `<div class=\"panel\">\n      <div class=\"ph-head\"><div class=\"ph-title\">${isNew?'Novo item do acervo':'Editar ficha catalográfica'}</div>${isNew?'':`<button class=\"btn-del\" data-act=\"del\">Excluir</button>`}</div>\n      <div class=\"form\">${fieldHtml}</div>\n      <div class=\"ph-foot\"><button class=\"btn-ghost\" data-act=\"cancel\">Cancelar</button><button class=\"btn-primary\" data-act=\"save\" ${ds.mutating?'disabled':''}>${ds.mutating?'Salvando…':'Salvar ficha'}</button></div>\n    </div>`\n  },\n  mounted(d2){\n    if (!d2.state.draft) seedFrom(d2)\n    const setField = (el)=>{ if(!d2.state.draft) return; const val = el.type==='number' ? (el.value===''?null:Number(el.value)) : el.value; d2.state.draft[el.dataset.field] = val }\n    d2.on('.ctl','input',(e,el)=> setField(el))\n    d2.on('.ctl','change',(e,el)=> setField(el))\n    d2.on('[data-act=\"cancel\"]','click',()=> d2.emit('onCancel'))\n    d2.on('[data-act=\"save\"]','click',async ()=>{\n      const draft = d2.state.draft; if(!draft) return\n      if(!draft.titulo || !String(draft.titulo).trim()){ if(d2.dialog&&d2.dialog.alert) d2.dialog.alert('Informe o título do item.'); return }\n      const ds = d2.data()\n      try{\n        let saved\n        if(!draft._id){ const { _id, ...payload } = draft; saved = await ds.create(payload) }\n        else { saved = await ds.update(draft) }\n        if(ds.refresh) await ds.refresh()\n        d2.emit('onSaved', (saved && saved._id) ? saved : draft)\n      }catch(err){ if(d2.dialog&&d2.dialog.alert) d2.dialog.alert('Erro ao salvar: '+((err&&err.message)||err)) }\n    })\n    d2.on('[data-act=\"del\"]','click',async ()=>{\n      const draft = d2.state.draft; if(!draft||!draft._id) return\n      const ok = (d2.dialog&&d2.dialog.confirm) ? await d2.dialog.confirm('Excluir este item do acervo?') : true\n      if(!ok) return\n      await d2.data().remove(draft._id)\n      if(d2.data().refresh) await d2.data().refresh()\n      d2.emit('onDeleted', draft._id)\n    })\n  },\n  updated(d2){ if (d2.state.seededKey !== recKey(d2)) seedFrom(d2) },\n}"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}},"record":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"record"}}}},"onSaved":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSaved"}}}},"onCancel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCancel"}}}},"onDeleted":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onDeleted"}}}}}}}}}},{"_id":"6a59bd1a78be641096fc740f","component":"data2-core-types@Component","system":"5e72ab6c7ff98e774545a642","params":{"extends":"data2-core-primitives@Block","component":"china-shell","package":"playground","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// China v2 — a super-app of mini-apps.\n// ONE code component: app shell (home/create) + a multi-screen interpreter with\n// entities, relationships, full CRUD lifecycle, kanban/chat/custom screens,\n// a per-app WIDGET SYSTEM (apps register their own micro-interfaces), QR, and\n// live-data actions. Backward compatible with v1 flat \"blocks\" specs.\n\n// ============ pure helpers ============\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\nconst attr = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\nconst slug = (s) => String(s || 'x').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 28) || 'x';\nconst rid = () => Math.random().toString(36).slice(2, 6);\nconst isArr = Array.isArray;\nconst j = (o) => attr(JSON.stringify(o == null ? {} : o));\nconst fill = (tpl, bag) => String(tpl == null ? '' : tpl).replace(/\\{\\{\\s*([\\w.-]+)\\s*\\}\\}/g, (_, k) => {\n  const v = bag[k]; return v == null ? '' : (typeof v === 'object' ? JSON.stringify(v) : String(v));\n});\nconst NADJ = ['sleepy', 'fluffy', 'sunny', 'peppy', 'mochi', 'cozy', 'zippy', 'dreamy', 'giggly', 'bouncy'];\nconst NANI = ['panda', 'bun', 'kitten', 'duckling', 'otter', 'penguin', 'fox', 'seal', 'hammy', 'corgi'];\nfunction nick() {\n  try {\n    let n = window.localStorage.getItem('china_nick');\n    if (!n) { n = NADJ[(Math.random() * NADJ.length) | 0] + '-' + NANI[(Math.random() * NANI.length) | 0] + '-' + ((Math.random() * 90 + 10) | 0); window.localStorage.setItem('china_nick', n); }\n    return n;\n  } catch (_) { return 'anon-bun'; }\n}\nconst nowISO = () => new Date().toISOString();\nconst relTime = (d2, iso) => { try { return d2.fmt.relative(new Date(iso)); } catch (_) { return ''; } };\n\nasync function hostDataUri(d2, uri) {\n  try {\n    const [meta, b64] = uri.split(','); const bin = atob(b64); const bytes = new Uint8Array(bin.length);\n    for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n    const type = (meta.match(/data:(.*?);/) || [])[1] || 'image/png';\n    const file = new File([bytes], 'china-' + rid() + '.png', { type });\n    const out = await d2.drive('china-drive').uploadFromFiles([file]);\n    return (out && out[0] && out[0].fileUrl) || uri;\n  } catch (_) { return uri; }\n}\n\n// per-render registry for custom widget action closures (single shell instance)\nlet WREG = {};\nlet WSEQ = 0;\n\n// normalize any spec into v2 { entities, screens, nav, home, widgets }\nfunction normalizeSpec(feat) {\n  const spec = feat.uiSpec || {};\n  if (spec.version === 2 || spec.screens) {\n    return {\n      entities: spec.entities || [],\n      screens: spec.screens || [],\n      nav: spec.nav || [],\n      home: spec.home || (spec.screens && spec.screens[0] && spec.screens[0].id),\n      widgets: spec.widgets || [],\n    };\n  }\n  // v1 fallback: a single scrollable page from flat blocks\n  const blocks = isArr(spec) ? spec : (spec.blocks || []);\n  return { entities: [], widgets: [], nav: [], home: 'main', screens: [{ id: 'main', type: 'page', title: feat.name, blocks }] };\n}\n\n// ============ context builder (read-side) ============\nfunction buildCtx(d2, feat) {\n  const app = normalizeSpec(feat);\n  const ds = d2.data('records');\n  const all = (ds ? ds.records() : []).filter((r) => r.featureKey === feat.key);\n  const stack = d2.state.stack || [];\n  const frame = stack[stack.length - 1] || { screenId: app.home };\n  const screen = (app.screens || []).find((s) => s.id === frame.screenId) || app.screens[0] || { type: 'page', blocks: [] };\n  const rec = frame.recordId ? all.find((r) => r._id === frame.recordId) : null;\n  return {\n    d2, feat, app, ds, all, stack, frame, screen, rec,\n    form: d2.state.form || {}, refs: d2.state.refs || {}, results: d2.state.results || {},\n    fetching: ds ? ds.fetching : false,\n    ofCollection: (c) => all.filter((r) => r.collection === c),\n    byId: (id) => all.find((r) => r._id === id),\n    relatedTo: (c, via, id) => all.filter((r) => r.collection === c && r.refs && String(r.refs[via]) === String(id)),\n    entity: (c) => (app.entities || []).find((e) => e.collection === c),\n    widget: (n) => (app.widgets || []).find((w) => w.name === n),\n    bag: () => Object.assign({}, (rec && rec.data) || {}, d2.state.form || {}, d2.state.results || {}),\n  };\n}\n\n// ============ form field control ============\nfunction fieldControl(f, ctx) {\n  const val = ctx.form[f.key] ?? (ctx.rec && ctx.rec.data ? ctx.rec.data[f.key] : undefined) ?? f.default ?? '';\n  const t = f.type || 'text';\n  const lbl = `<span class=\"c-lbl\">${esc(f.label || f.key)}</span>`;\n  if (t === 'textarea') return `<label class=\"c-block\">${lbl}<textarea class=\"c-field j-input\" data-name=\"${attr(f.key)}\" data-kind=\"text\" rows=\"3\" placeholder=\"${attr(f.placeholder || '')}\">${esc(val)}</textarea></label>`;\n  if (t === 'number') return `<label class=\"c-block\">${lbl}<input class=\"c-field j-input\" data-name=\"${attr(f.key)}\" data-kind=\"number\" type=\"number\" placeholder=\"${attr(f.placeholder || '')}\" value=\"${attr(val)}\"/></label>`;\n  if (t === 'date') return `<label class=\"c-block\">${lbl}<input class=\"c-field j-input\" data-name=\"${attr(f.key)}\" data-kind=\"text\" type=\"date\" value=\"${attr(val)}\"/></label>`;\n  if (t === 'toggle') { const on = !!(ctx.form[f.key] ?? (ctx.rec && ctx.rec.data && ctx.rec.data[f.key]) ?? f.default); return `<div class=\"c-block c-toggle-row j-toggle\" data-name=\"${attr(f.key)}\">${lbl}<span class=\"c-switch ${on ? 'on' : ''}\"><span class=\"c-knob\"></span></span></div>`; }\n  if (t === 'select' || t === 'chips') {\n    const multi = t === 'chips' && !!f.multi; const cur = val;\n    const opts = (f.options || []).map((o) => {\n      const v = o.value ?? o.label; const sel = multi ? (isArr(cur) && cur.some((x) => String(x) === String(v))) : String(cur) === String(v);\n      return `<button class=\"c-opt j-opt ${sel ? 'sel' : ''}\" data-name=\"${attr(f.key)}\" data-value=\"${attr(v)}\" data-multi=\"${multi ? 1 : 0}\">${o.emoji ? `<span class=\"c-opt-e\">${esc(o.emoji)}</span>` : ''}<span>${esc(o.label ?? v)}</span></button>`;\n    }).join('');\n    return `<div class=\"c-block\">${lbl}<div class=\"c-opts\">${opts}</div></div>`;\n  }\n  if (t === 'rating') {\n    const max = f.max || 5; const n = Number(val) || 0; let stars = '';\n    for (let i = 1; i <= max; i++) stars += `<button class=\"c-star j-rating ${i <= n ? 'on' : ''}\" data-name=\"${attr(f.key)}\" data-val=\"${i}\">★</button>`;\n    return `<div class=\"c-block\">${lbl}<div class=\"c-stars\">${stars}</div></div>`;\n  }\n  if (t === 'photo') { const v = ctx.form[f.key] ?? (ctx.rec && ctx.rec.data && ctx.rec.data[f.key]); const prev = v ? `<div class=\"c-imgwrap\"><img class=\"c-img\" src=\"${attr(v)}\"/></div>` : ''; return `<div class=\"c-block\">${lbl}<label class=\"c-photo\"><input type=\"file\" accept=\"image/*\" class=\"j-photo\" data-name=\"${attr(f.key)}\" hidden/><span class=\"c-photo-cta\">${v ? 'Change 🔁' : 'Choose 🌸'}</span></label>${prev}</div>`; }\n  if (t === 'qr') { const v = ctx.form[f.key] ?? ''; return `<div class=\"c-block\">${lbl}<div class=\"c-qrrow\"><input class=\"c-field j-input\" data-name=\"${attr(f.key)}\" data-kind=\"text\" placeholder=\"scan or type…\" value=\"${attr(v)}\"/><button class=\"c-qrbtn j-act\" data-action=\"${j({ type: 'qrscan', field: f.key })}\">📷</button></div></div>`; }\n  if (t === 'relation') {\n    const targets = ctx.ofCollection(f.relatesTo); const ent = ctx.entity(f.relatesTo); const lf = (ent && ent.labelField) || 'name';\n    const cur = ctx.refs[f.key] ?? (ctx.rec && ctx.rec.refs ? ctx.rec.refs[f.key] : undefined);\n    const opts = targets.length ? targets.map((r) => `<button class=\"c-opt j-relopt ${String(cur) === String(r._id) ? 'sel' : ''}\" data-name=\"${attr(f.key)}\" data-id=\"${attr(r._id)}\">${esc((ent && ent.emoji) || '🔗')} ${esc((r.data && r.data[lf]) || 'Untitled')}</button>`).join('')\n      : `<span class=\"c-hint\">No ${esc(f.relatesTo)} yet.</span>`;\n    return `<div class=\"c-block\">${lbl}<div class=\"c-opts\">${opts}</div></div>`;\n  }\n  // text default\n  return `<label class=\"c-block\">${lbl}<input class=\"c-field j-input\" data-name=\"${attr(f.key)}\" data-kind=\"text\" placeholder=\"${attr(f.placeholder || '')}\" value=\"${attr(val)}\"/></label>`;\n}\n\n// ============ widget renderer (built-in + page vocabulary + custom) ============\nfunction renderWidget(node, ctx) {\n  if (!node || !node.type) return '';\n  const t = node.type;\n  const bag = ctx.bag();\n\n  // --- app-registered custom widget instance ---\n  if (t === 'widget') {\n    const w = ctx.widget(node.use);\n    if (!w) return `<div class=\"c-empty-soft\">missing widget: ${esc(node.use)}</div>`;\n    return runCustom(w.code, ctx, node.params || {});\n  }\n  if (t === 'custom') return runCustom(node.code, ctx, node.params || {});\n\n  if (t === 'hero') return `<div class=\"c-hero\"><div class=\"c-hero-emoji\">${esc(node.emoji || ctx.feat.emoji || '✨')}</div><div class=\"c-hero-title\">${esc(node.title || ctx.feat.name)}</div>${node.subtitle ? `<div class=\"c-hero-sub\">${esc(node.subtitle)}</div>` : ''}</div>`;\n  if (t === 'text') { const tone = node.tone === 'title' ? 'c-tx-title' : node.tone === 'caption' ? 'c-tx-cap' : 'c-tx-body'; return `<div class=\"c-tx ${tone}\">${esc(fill(node.text, bag))}</div>`; }\n  if (t === 'divider') return `<div class=\"c-div\"></div>`;\n  if (t === 'image') { const u = node.url || bag[node.field]; return u ? `<div class=\"c-imgwrap\"><img class=\"c-img\" src=\"${attr(u)}\"/></div>` : ''; }\n  if (t === 'field') { const v = ctx.rec && ctx.rec.data ? ctx.rec.data[node.field] : bag[node.field]; if (v == null || v === '') return ''; return `<div class=\"c-fcell\"><span class=\"c-fk\">${esc(node.label || node.field)}</span><span class=\"c-fv\">${esc(v)}</span></div>`; }\n  if (t === 'media') { const v = ctx.rec && ctx.rec.data ? ctx.rec.data[node.field] : bag[node.field]; return v ? `<div class=\"c-imgwrap c-media\"><img class=\"c-img\" src=\"${attr(v)}\"/></div>` : ''; }\n  if (t === 'result') {\n    const v = ctx.results[node.into] ?? ctx.form[node.into];\n    const label = node.label ? `<div class=\"c-lbl\">${esc(node.label)}</div>` : '';\n    if (v == null || v === '') return `<div class=\"c-result c-empty-soft\">${label}<div class=\"c-result-ph\">…waiting for magic ✨</div></div>`;\n    if (node.as === 'image') return `<div class=\"c-result\">${label}<div class=\"c-imgwrap\"><img class=\"c-img\" src=\"${attr(v)}\"/></div></div>`;\n    return `<div class=\"c-result\">${label}<div class=\"c-result-txt\">${esc(v)}</div></div>`;\n  }\n  if (t === 'qr-show') { const v = node.value != null ? fill(node.value, bag) : (ctx.rec && ctx.rec.data ? ctx.rec.data[node.field] : bag[node.field]); if (!v) return ''; return `<div class=\"c-block\">${node.label ? `<span class=\"c-lbl\">${esc(node.label)}</span>` : ''}<div class=\"c-qr\" data-qr=\"${attr(v)}\"><div class=\"c-qr-load\">generating QR…</div></div><div class=\"c-qr-val\">${esc(v)}</div></div>`; }\n\n  // inputs\n  if (['input', 'textarea', 'number', 'select', 'chips', 'toggle', 'photo', 'date', 'rating', 'relation', 'qr'].includes(t)) {\n    return fieldControl({ key: node.name, label: node.label, type: t, options: node.options, multi: node.multi, default: node.default, placeholder: node.placeholder, max: node.max, relatesTo: node.relatesTo }, ctx);\n  }\n  if (t === 'stepper') { const v = Number(ctx.form[node.name] ?? node.default ?? 0); return `<div class=\"c-block\"><span class=\"c-lbl\">${esc(node.label || '')}</span><div class=\"c-step\"><button class=\"c-step-btn j-step\" data-name=\"${attr(node.name)}\" data-dir=\"-1\">−</button><span class=\"c-step-val\">${esc(v)}</span><button class=\"c-step-btn j-step\" data-name=\"${attr(node.name)}\" data-dir=\"1\">+</button></div></div>`; }\n\n  if (t === 'button') { const style = node.style === 'soft' ? 'c-btn-soft' : 'c-btn-primary'; const busy = !!(ctx.d2.state.busy && ctx.d2.state.busy.run); return `<button class=\"c-btn ${style} j-act ${busy ? 'busy' : ''}\" data-action=\"${j(node.do || {})}\" ${busy ? 'disabled' : ''}>${busy ? `<span class=\"c-dots\"><i></i><i></i><i></i></span>` : `${node.emoji ? esc(node.emoji) + ' ' : ''}${esc(node.label || 'Go')}`}</button>`; }\n\n  // collection displays (page-level, reads a collection)\n  if (t === 'list' || t === 'feed' || t === 'gallery') {\n    const rows = node.collection ? ctx.ofCollection(node.collection) : ctx.all;\n    return renderCollection(t, node, rows, ctx);\n  }\n  return '';\n}\n\nfunction renderCollection(t, node, rows, ctx) {\n  const title = node.title ? `<div class=\"c-sec-title\">${esc(node.title)}</div>` : '';\n  if (ctx.fetching && !rows.length) return `${title}<div class=\"c-skel\"><div></div><div></div></div>`;\n  if (!rows.length) return `${title}<div class=\"c-empty-soft\">${esc(node.empty || 'Nothing here yet ✨')}</div>`;\n  if (t === 'gallery') {\n    const f = node.imageField || 'pic';\n    return `${title}<div class=\"c-gallery\">${rows.map((r) => { const u = (r.data || {})[f]; return u ? `<div class=\"c-cell\"><img class=\"c-img\" src=\"${attr(u)}\"/><span class=\"c-cell-by\">${esc(r.owner || '')}</span></div>` : ''; }).join('')}</div>`;\n  }\n  if (t === 'feed') {\n    const fs = node.fields || [];\n    return `${title}<div class=\"c-feed\">${rows.map((r) => { const d = r.data || {}; const cells = fs.map((f) => { const raw = d[f.key]; return (raw == null || raw === '') ? '' : `<div class=\"c-fcell\"><span class=\"c-fk\">${esc(f.label || f.key)}</span><span class=\"c-fv\">${esc(raw)}</span></div>`; }).join(''); return `<div class=\"c-card2\">${cells}<div class=\"c-by\">${esc(r.owner || 'someone')} · ${esc(relTime(ctx.d2, r.createdAt))}</div></div>`; }).join('')}</div>`;\n  }\n  return `${title}<div class=\"c-list\">${rows.map((r) => { const d = r.data || {}; const p = node.primary ? d[node.primary] : (Object.values(d)[0] || ''); const s = node.secondary ? d[node.secondary] : ''; return `<div class=\"c-li\"><span class=\"c-li-e\">${esc(node.emoji || '🌸')}</span><span class=\"c-li-main\"><span class=\"c-li-p\">${esc(p)}</span>${s ? `<span class=\"c-li-s\">${esc(s)}</span>` : ''}<span class=\"c-by\">${esc(r.owner || 'someone')} · ${esc(relTime(ctx.d2, r.createdAt))}</span></span></div>`; }).join('')}</div>`;\n}\n\n// run AI-authored custom code safely → {html, actions}\nfunction runCustom(code, ctx, params) {\n  const wid = 'w' + (WSEQ++);\n  try {\n    const api = readApi(ctx);\n    const build = new Function('api', 'params', 'html', code);\n    const out = build(api, params || {}, { esc, attr }) || {};\n    const htmlStr = typeof out === 'string' ? out : (out.html || '');\n    WREG[wid] = { actions: (out && out.actions) || {} };\n    return `<div class=\"c-custom\" data-wid=\"${wid}\">${htmlStr}</div>`;\n  } catch (e) {\n    return `<div class=\"c-empty-soft\">⚠️ widget error: ${esc(e.message || e)}</div>`;\n  }\n}\n\n// read-only api handed to custom code at render time\nfunction readApi(ctx) {\n  return {\n    esc, attr, fmt: ctx.d2.fmt,\n    feature: ctx.feat, screen: ctx.screen, record: ctx.rec, params: {},\n    records: () => ctx.all, ofCollection: ctx.ofCollection, byId: ctx.byId, relatedTo: ctx.relatedTo,\n    entity: ctx.entity, form: () => ctx.form, refs: () => ctx.refs, results: () => ctx.results, bag: ctx.bag,\n    me: nick(),\n  };\n}\n\n// ============ screen renderer ============\nfunction renderScreen(ctx) {\n  const s = ctx.screen; const t = s.type || 'page';\n  if (t === 'page') return (s.blocks || []).map((b) => renderWidget(b, ctx)).join('');\n\n  if (t === 'list') {\n    const rows0 = ctx.ofCollection(s.collection);\n    const q = (ctx.d2.state.search || '').toLowerCase();\n    const rows = q ? rows0.filter((r) => JSON.stringify(r.data || {}).toLowerCase().includes(q)) : rows0;\n    const ent = ctx.entity(s.collection) || {};\n    const it = s.item || {}; const lf = it.title || ent.labelField || 'name';\n    const search = s.search ? `<input class=\"c-search j-search\" placeholder=\"Search…\" value=\"${attr(ctx.d2.state.search || '')}\"/>` : '';\n    const body = !rows.length\n      ? `<div class=\"c-empty-soft\">${esc(s.empty || ('No ' + (ent.name || 'items') + ' yet ✨'))}</div>`\n      : `<div class=\"c-list\">${rows.map((r) => {\n        const d = r.data || {}; const media = it.media && d[it.media] ? `<img class=\"c-thumb\" src=\"${attr(d[it.media])}\"/>` : `<span class=\"c-li-e\">${esc(ent.emoji || '🌸')}</span>`;\n        const badge = it.badge && d[it.badge] ? `<span class=\"c-badge\">${esc(d[it.badge])}</span>` : (r.status ? `<span class=\"c-badge\">${esc(r.status)}</span>` : '');\n        const act = s.tapTo ? j({ type: 'navigate', to: s.tapTo, recordId: r._id }) : '';\n        return `<div class=\"c-li c-li-tap ${s.tapTo ? 'j-act' : ''}\" ${s.tapTo ? `data-action=\"${act}\"` : ''}>${media}<span class=\"c-li-main\"><span class=\"c-li-p\">${esc(d[lf] || 'Untitled')}</span>${it.subtitle && d[it.subtitle] ? `<span class=\"c-li-s\">${esc(d[it.subtitle])}</span>` : ''}</span>${badge}</div>`;\n      }).join('')}</div>`;\n    const fab = s.fab ? `<button class=\"c-fab j-act\" data-action=\"${j({ type: 'navigate', to: s.fab.to })}\">＋ ${esc(s.fab.label || 'New')}</button>` : '';\n    return `${search}${body}${fab}`;\n  }\n\n  if (t === 'detail') {\n    const r = ctx.rec; if (!r) return `<div class=\"c-empty-soft\">Not found 🫧</div>`;\n    const ent = ctx.entity(s.collection) || {};\n    let html = '';\n    if (s.media && r.data && r.data[s.media]) html += `<div class=\"c-imgwrap c-media\"><img class=\"c-img\" src=\"${attr(r.data[s.media])}\"/></div>`;\n    html += `<div class=\"c-hero\"><div class=\"c-hero-emoji\">${esc(ent.emoji || '✨')}</div><div class=\"c-hero-title\">${esc((r.data || {})[ent.labelField || 'name'] || s.title || 'Detail')}</div>${r.status ? `<div class=\"c-hero-sub\">${esc(r.status)}</div>` : ''}</div>`;\n    const blocks = s.blocks || (ent.fields || []).map((f) => ({ type: 'field', field: f.key, label: f.label }));\n    html += `<div class=\"c-card2\">${blocks.map((b) => renderWidget(b, ctx)).join('')}</div>`;\n    (s.related || []).forEach((rel) => {\n      const kids = ctx.relatedTo(rel.collection, rel.via, r._id);\n      const kent = ctx.entity(rel.collection) || {};\n      html += `<div class=\"c-sec-title\">${esc(rel.title || kent.name || 'Related')}</div>`;\n      if (!kids.length) html += `<div class=\"c-empty-soft\">${esc(rel.empty || 'None yet ✨')}</div>`;\n      else html += `<div class=\"c-list\">${kids.map((k) => { const d = k.data || {}; const lf = (rel.item && rel.item.title) || kent.labelField || 'name'; const sub = rel.item && rel.item.subtitle ? d[rel.item.subtitle] : ''; const act = rel.tapTo ? `class=\"c-li c-li-tap j-act\" data-action=\"${j({ type: 'navigate', to: rel.tapTo, recordId: k._id })}\"` : 'class=\"c-li\"'; return `<div ${act}><span class=\"c-li-e\">${esc(kent.emoji || '🔗')}</span><span class=\"c-li-main\"><span class=\"c-li-p\">${esc(d[lf] || 'Untitled')}</span>${sub ? `<span class=\"c-li-s\">${esc(sub)}</span>` : ''}</span></div>`; }).join('')}</div>`;\n      if (rel.fab) html += `<button class=\"c-fab-soft j-act\" data-action=\"${j({ type: 'navigate', to: rel.fab.to, preset: { [rel.via]: r._id } })}\">＋ ${esc(rel.fab.label || 'Add')}</button>`;\n    });\n    const acts = (s.actions || []).map((a) => {\n      if (a.to) return `<button class=\"c-btn c-btn-soft j-act\" data-action=\"${j({ type: 'navigate', to: a.to, recordId: r._id })}\">${a.emoji ? esc(a.emoji) + ' ' : ''}${esc(a.label)}</button>`;\n      return `<button class=\"c-btn ${a.type === 'delete' ? 'c-btn-danger' : 'c-btn-soft'} j-act\" data-action=\"${j(Object.assign({ recordId: r._id }, a))}\">${a.emoji ? esc(a.emoji) + ' ' : ''}${esc(a.label)}</button>`;\n    }).join('');\n    if (acts) html += `<div class=\"c-actions\">${acts}</div>`;\n    return html;\n  }\n\n  if (t === 'form') {\n    const ent = ctx.entity(s.collection) || {};\n    const fields = s.fields || ent.fields || [];\n    const editing = !!(ctx.frame && ctx.frame.recordId);\n    const body = fields.map((f) => {\n      if (ctx.frame && ctx.frame.preset && ctx.frame.preset[f.key] != null && f.type === 'relation') return ''; // preset relation hidden\n      return fieldControl(f, ctx);\n    }).join('');\n    const save = j({ type: 'save', collection: s.collection, after: s.after || 'back', toast: s.toast || (editing ? 'Updated! ✨' : 'Saved! 🎉') });\n    return `${body}<button class=\"c-btn c-btn-primary j-act ${ctx.d2.state.busy && ctx.d2.state.busy.run ? 'busy' : ''}\" data-action=\"${save}\">${ctx.d2.state.busy && ctx.d2.state.busy.run ? '<span class=\"c-dots\"><i></i><i></i><i></i></span>' : esc(s.submitLabel || (editing ? 'Save changes' : 'Create'))}</button>`;\n  }\n\n  if (t === 'board') {\n    const cols = s.columns || []; const sf = s.statusField || 'status';\n    const rows = ctx.ofCollection(s.collection);\n    const card = s.card || {};\n    return `<div class=\"c-board\">${cols.map((c) => {\n      const items = rows.filter((r) => String((r.status ?? (r.data || {})[sf])) === String(c.value));\n      return `<div class=\"c-col\"><div class=\"c-col-h\">${c.emoji ? esc(c.emoji) + ' ' : ''}${esc(c.label)} <span class=\"c-col-n\">${items.length}</span></div>${items.map((r) => { const d = r.data || {}; return `<div class=\"c-kcard ${s.tapTo ? 'j-act' : ''}\" ${s.tapTo ? `data-action=\"${j({ type: 'navigate', to: s.tapTo, recordId: r._id })}\"` : ''}><div class=\"c-kt\">${esc(d[card.title || 'name'] || 'Untitled')}</div>${card.subtitle && d[card.subtitle] ? `<div class=\"c-ks\">${esc(d[card.subtitle])}</div>` : ''}</div>`; }).join('') || `<div class=\"c-col-empty\">—</div>`}</div>`;\n    }).join('')}</div>${s.fab ? `<button class=\"c-fab j-act\" data-action=\"${j({ type: 'navigate', to: s.fab.to })}\">＋ ${esc(s.fab.label || 'New')}</button>` : ''}`;\n  }\n\n  if (t === 'chat') {\n    const msgs = ctx.ofCollection(s.collection).slice().sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));\n    const uf = s.userField || 'msg'; const rf = s.replyField || 'reply';\n    const bubbles = msgs.map((m) => { const d = m.data || {}; return `${d[uf] ? `<div class=\"c-msg c-me\">${esc(d[uf])}</div>` : ''}${d[rf] ? `<div class=\"c-msg c-ai\">${esc(d[rf])}</div>` : ''}`; }).join('');\n    const greeting = (!msgs.length && s.greeting) ? `<div class=\"c-msg c-ai\">${esc(s.greeting)}</div>` : '';\n    const busy = ctx.d2.state.busy && ctx.d2.state.busy.run;\n    return `<div class=\"c-chat\"><div class=\"c-log\">${greeting}${bubbles}${busy ? `<div class=\"c-msg c-ai\"><span class=\"c-dots\"><i></i><i></i><i></i></span></div>` : ''}</div><div class=\"c-chatbar\"><input class=\"c-field j-chatin\" placeholder=\"Type a message…\" ${busy ? 'disabled' : ''}/><button class=\"c-send j-act\" data-action=\"${j({ type: 'chat', collection: s.collection, assistantKey: s.assistantKey, userField: uf, replyField: rf })}\" ${busy ? 'disabled' : ''}>➤</button></div></div>`;\n  }\n\n  return `<div class=\"c-empty-soft\">Unknown screen type: ${esc(t)}</div>`;\n}\n\nexport default {\n  css: `\n  :host,.app{height:100%}\n  .app{position:fixed;inset:0;overflow:auto;box-sizing:border-box;color:#4a4458;-webkit-font-smoothing:antialiased;\n    font-family:-apple-system,\"SF Pro Rounded\",\"Baloo 2\",\"Quicksand\",system-ui,\"Segoe UI\",sans-serif;\n    background:radial-gradient(120% 80% at 0% 0%,#ffe7f2 0%,rgba(255,231,242,0) 55%),radial-gradient(120% 80% at 100% 0%,#e2f0ff 0%,rgba(226,240,255,0) 55%),radial-gradient(140% 90% at 50% 120%,#eafbe9 0%,rgba(234,251,233,0) 60%),linear-gradient(180deg,#fbf7ff,#fefcff)}\n  .wrap{max-width:520px;margin:0 auto;padding:20px 18px 120px}\n  .apptop{display:flex;align-items:center;gap:10px;margin-bottom:14px;position:sticky;top:0;z-index:10;\n    background:linear-gradient(180deg,rgba(251,247,255,.96),rgba(251,247,255,.6));backdrop-filter:blur(8px);padding:6px 0;margin:-6px 0 10px}\n  .iconbtn{border:0;background:#fff;cursor:pointer;font:inherit;font-weight:700;color:#8d81b5;padding:9px 14px;border-radius:999px;box-shadow:0 6px 16px rgba(150,130,220,.14);transition:transform .1s}\n  .iconbtn:active{transform:scale(.93)}\n  .apptitle{font-size:19px;font-weight:800;color:#544d68;flex:1;display:flex;align-items:center;gap:8px;min-width:0}\n  .apptitle span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n  .topbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}\n  .brand{font-size:30px;font-weight:800;letter-spacing:-.02em;color:#6b5bd6;display:flex;align-items:center;gap:6px}\n  .me{font-size:12px;color:#a99fc4;background:#fff;padding:6px 12px;border-radius:999px;box-shadow:0 4px 14px rgba(150,130,220,.12)}\n  .subtitle{color:#a99fc4;font-size:14.5px;margin:0 2px 18px}\n  .create{background:#fff;border-radius:26px;padding:8px 8px 8px 18px;box-shadow:0 12px 34px rgba(150,130,220,.16);display:flex;gap:8px;align-items:center;border:2px solid #f0eafc}\n  .create input{flex:1;border:0;outline:0;font-size:16px;background:transparent;color:#4a4458;font-family:inherit;padding:10px 0}\n  .create input::placeholder{color:#c9c1de}\n  .mk{border:0;cursor:pointer;font-family:inherit;font-weight:800;font-size:15px;color:#fff;padding:13px 20px;border-radius:20px;white-space:nowrap;background:linear-gradient(135deg,#ff9ecd,#a99bff);box-shadow:0 8px 20px rgba(169,155,255,.4);transition:transform .12s}\n  .mk:active{transform:scale(.96)} .mk[disabled]{opacity:.7}\n  .cerr{color:#e06a94;font-size:13px;margin:10px 4px 0}\n  .creating{margin:22px 4px;color:#9a8fc0;font-size:15px;display:flex;align-items:center;gap:10px}\n  .sec-h{margin:24px 4px 12px;font-size:13px;font-weight:800;text-transform:uppercase;letter-spacing:.08em;color:#bcb2d8}\n  .grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}\n  .fcard{background:#fff;border-radius:22px;padding:16px;cursor:pointer;text-align:left;border:2px solid #f3eefc;box-shadow:0 8px 22px rgba(150,130,220,.10);transition:transform .14s,box-shadow .14s;position:relative;overflow:hidden}\n  .fcard:before{content:\"\";position:absolute;inset:0 0 auto 0;height:6px;background:var(--a,#ffd8e6)}\n  .fcard:active{transform:scale(.97)}\n  .fc-emoji{font-size:34px;line-height:1;margin:6px 0 10px}\n  .fc-name{font-weight:800;font-size:16px;color:#544d68}\n  .fc-tag{font-size:12.5px;color:#a99fc4;margin-top:3px;line-height:1.35}\n  .empty{text-align:center;padding:48px 20px;color:#b3a9cf}.empty .big{font-size:64px;margin-bottom:10px}.empty .t{font-size:17px;font-weight:700;color:#8d81b5}\n  .c-hero{text-align:center;padding:20px 18px;border-radius:24px;margin-bottom:16px;background:linear-gradient(160deg,color-mix(in srgb,var(--a,#ffd8e6) 55%,#fff),#fff);box-shadow:0 12px 30px rgba(150,130,220,.14);border:2px solid #fff}\n  .c-hero-emoji{font-size:48px;line-height:1}.c-hero-title{font-size:23px;font-weight:800;color:#544d68;margin-top:6px}.c-hero-sub{font-size:13.5px;color:#8d81b5;margin-top:4px}\n  .c-block{display:block;margin:13px 2px}\n  .c-lbl{display:block;font-size:13px;font-weight:700;color:#9a8fc0;margin-bottom:7px}\n  .c-field{width:100%;box-sizing:border-box;border:2px solid #efeafb;border-radius:16px;padding:13px 15px;font-size:16px;font-family:inherit;color:#4a4458;background:#fff;outline:0;transition:border-color .15s}\n  .c-field:focus{border-color:var(--a,#c9b6e4)} textarea.c-field{resize:vertical}\n  .c-tx{margin:10px 2px}.c-tx-title{font-size:19px;font-weight:800;color:#544d68}.c-tx-body{font-size:15px;color:#6a6280;line-height:1.5}.c-tx-cap{font-size:12.5px;color:#a99fc4}\n  .c-div{height:2px;background:#f0eafb;border-radius:2px;margin:16px 0}\n  .c-opts{display:flex;flex-wrap:wrap;gap:9px}\n  .c-opt{border:2px solid #efeafb;background:#fff;cursor:pointer;font-family:inherit;font-size:15px;color:#6a6280;padding:11px 15px;border-radius:16px;display:flex;align-items:center;gap:7px;font-weight:600;transition:transform .1s,border-color .12s,background .12s}\n  .c-opt:active{transform:scale(.94)} .c-opt.sel{border-color:var(--a,#c9b6e4);background:color-mix(in srgb,var(--a,#c9b6e4) 22%,#fff);color:#544d68}\n  .c-opt-e{font-size:17px}\n  .c-step{display:inline-flex;align-items:center;gap:16px;background:#fff;border:2px solid #efeafb;border-radius:16px;padding:7px 14px}\n  .c-step-btn{border:0;width:34px;height:34px;border-radius:12px;cursor:pointer;font-size:20px;font-weight:800;color:#8d81b5;background:#f5f1fe;font-family:inherit}.c-step-btn:active{transform:scale(.9)}\n  .c-step-val{font-size:18px;font-weight:800;color:#544d68;min-width:24px;text-align:center}\n  .c-toggle-row{display:flex;align-items:center;justify-content:space-between;cursor:pointer;background:#fff;border:2px solid #efeafb;border-radius:16px;padding:13px 16px}.c-toggle-row .c-lbl{margin:0}\n  .c-switch{width:50px;height:30px;border-radius:999px;background:#e7e0f6;position:relative;transition:background .16s;flex:none}.c-switch.on{background:var(--a,#a99bff)}\n  .c-knob{position:absolute;top:3px;left:3px;width:24px;height:24px;border-radius:50%;background:#fff;box-shadow:0 2px 6px rgba(0,0,0,.15);transition:transform .16s}.c-switch.on .c-knob{transform:translateX(20px)}\n  .c-stars{display:flex;gap:4px}.c-star{border:0;background:none;cursor:pointer;font-size:30px;color:#e2dcf3;padding:0;line-height:1}.c-star.on{color:#ffca3a}\n  .c-photo{display:inline-flex;cursor:pointer}.c-photo-cta{background:#f5f1fe;color:#8d81b5;font-weight:700;padding:12px 18px;border-radius:16px;border:2px dashed #d9cef5;font-size:15px}\n  .c-qrrow{display:flex;gap:8px}.c-qrbtn{border:0;background:#f5f1fe;border-radius:16px;font-size:20px;padding:0 16px;cursor:pointer}\n  .c-hint{color:#b7aed4;font-size:13px}\n  .c-btn{width:100%;border:0;cursor:pointer;font-family:inherit;font-weight:800;font-size:17px;padding:16px;border-radius:20px;margin:14px 0 6px;transition:transform .12s;display:flex;align-items:center;justify-content:center;gap:8px}\n  .c-btn-primary{color:#fff;background:linear-gradient(135deg,#ff9ecd,#a99bff);box-shadow:0 10px 26px rgba(169,155,255,.4)}\n  .c-btn-soft{color:#8d81b5;background:#fff;border:2px solid #efeafb}\n  .c-btn-danger{color:#e06a94;background:#fff;border:2px solid #ffdfec}\n  .c-btn:active{transform:scale(.97)} .c-btn.busy{opacity:.9}\n  .c-actions{display:flex;flex-direction:column;gap:8px;margin-top:12px}\n  .c-result{background:#fff;border:2px solid #f0eafb;border-radius:18px;padding:15px 16px;margin:12px 2px;box-shadow:0 6px 16px rgba(150,130,220,.08)}\n  .c-result-txt{font-size:17px;color:#544d68;line-height:1.5;font-weight:600;white-space:pre-wrap}.c-result-ph{color:#c9c1de;font-size:14px}\n  .c-empty-soft{text-align:center;color:#b7aed4;font-size:14px;padding:16px;background:#fff;border:2px dashed #ece5fb;border-radius:16px;margin:10px 2px}\n  .c-imgwrap{border-radius:16px;overflow:hidden;margin-top:8px;background:#f6f2fe}.c-img{display:block;width:100%;height:auto}.c-media{margin-bottom:8px}\n  .c-search{width:100%;box-sizing:border-box;border:2px solid #efeafb;border-radius:16px;padding:12px 16px;font-size:15px;font-family:inherit;outline:0;margin-bottom:12px;background:#fff}\n  .c-sec-title{font-size:13px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:#bcb2d8;margin:20px 4px 10px}\n  .c-list{display:flex;flex-direction:column;gap:9px}\n  .c-li{display:flex;gap:12px;align-items:center;background:#fff;border:2px solid #f3eefc;border-radius:16px;padding:12px 14px;box-shadow:0 5px 14px rgba(150,130,220,.07)}\n  .c-li-tap{cursor:pointer;transition:transform .1s}.c-li-tap:active{transform:scale(.98)}\n  .c-li-e{font-size:22px}.c-thumb{width:46px;height:46px;border-radius:12px;object-fit:cover;flex:none}\n  .c-li-main{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.c-li-p{font-weight:700;color:#544d68;font-size:15.5px}.c-li-s{color:#8d81b5;font-size:13.5px}\n  .c-by{color:#c1b8db;font-size:11.5px;margin-top:3px}\n  .c-badge{background:#f0eafb;color:#8d81b5;font-size:11px;font-weight:800;padding:5px 10px;border-radius:999px;white-space:nowrap;text-transform:uppercase;letter-spacing:.03em}\n  .c-feed{display:flex;flex-direction:column;gap:11px}\n  .c-card2{background:#fff;border:2px solid #f3eefc;border-radius:18px;padding:14px 16px;box-shadow:0 5px 14px rgba(150,130,220,.07);display:flex;flex-direction:column;gap:8px}\n  .c-fcell{display:flex;justify-content:space-between;gap:12px;align-items:baseline}.c-fk{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#bcb2d8}.c-fv{font-size:15.5px;color:#544d68;font-weight:600;text-align:right;white-space:pre-wrap}\n  .c-gallery{display:grid;grid-template-columns:1fr 1fr;gap:10px}.c-cell{position:relative;border-radius:16px;overflow:hidden;background:#f6f2fe}.c-cell-by{position:absolute;left:8px;bottom:8px;background:rgba(0,0,0,.4);color:#fff;font-size:10.5px;padding:3px 8px;border-radius:999px}\n  .c-fab{position:sticky;bottom:14px;margin:16px auto 0;display:block;border:0;cursor:pointer;font-family:inherit;font-weight:800;font-size:16px;color:#fff;background:linear-gradient(135deg,#ff9ecd,#a99bff);padding:14px 26px;border-radius:999px;box-shadow:0 12px 28px rgba(169,155,255,.5)}\n  .c-fab:active{transform:scale(.96)} .c-fab-soft{border:2px dashed #d9cef5;background:#fff;color:#8d81b5;font-weight:700;padding:11px 18px;border-radius:16px;cursor:pointer;font-family:inherit;margin-top:8px;width:100%}\n  .c-board{display:flex;gap:12px;overflow-x:auto;padding-bottom:8px}.c-col{min-width:78%;background:rgba(255,255,255,.6);border-radius:18px;padding:12px;border:2px solid #f3eefc}\n  .c-col-h{font-weight:800;color:#544d68;font-size:15px;margin-bottom:10px;display:flex;align-items:center;gap:6px}.c-col-n{background:#f0eafb;color:#8d81b5;font-size:12px;padding:2px 8px;border-radius:999px}\n  .c-kcard{background:#fff;border:2px solid #f3eefc;border-radius:14px;padding:11px 13px;margin-bottom:8px;cursor:pointer;box-shadow:0 4px 10px rgba(150,130,220,.06)}.c-kcard:active{transform:scale(.98)}\n  .c-kt{font-weight:700;color:#544d68;font-size:14.5px}.c-ks{color:#8d81b5;font-size:12.5px;margin-top:3px}.c-col-empty{color:#cdc5e2;text-align:center;padding:10px;font-size:13px}\n  .c-chat{display:flex;flex-direction:column;height:calc(100vh - 150px)}\n  .c-log{flex:1;overflow:auto;display:flex;flex-direction:column;gap:8px;padding:4px}\n  .c-msg{max-width:82%;padding:11px 14px;border-radius:16px;font-size:15px;line-height:1.4;white-space:pre-wrap}\n  .c-me{align-self:flex-end;background:linear-gradient(135deg,#ff9ecd,#a99bff);color:#fff;border-bottom-right-radius:4px}\n  .c-ai{align-self:flex-start;background:#fff;border:2px solid #f0eafb;color:#544d68;border-bottom-left-radius:4px}\n  .c-chatbar{display:flex;gap:8px;padding-top:8px}.c-send{border:0;background:linear-gradient(135deg,#ff9ecd,#a99bff);color:#fff;font-size:18px;width:50px;border-radius:16px;cursor:pointer;flex:none}\n  .tabbar{position:fixed;bottom:0;left:0;right:0;display:flex;justify-content:center;gap:6px;padding:10px 12px calc(10px + env(safe-area-inset-bottom));background:rgba(255,255,255,.86);backdrop-filter:blur(12px);border-top:1px solid #f0eafb;z-index:20}\n  .tab{border:0;background:none;cursor:pointer;font-family:inherit;font-size:11px;font-weight:700;color:#bcb2d8;display:flex;flex-direction:column;align-items:center;gap:2px;padding:4px 12px;border-radius:14px;transition:color .12s,background .12s}\n  .tab .ti{font-size:20px}.tab.on{color:#6b5bd6;background:#f3effe}\n  .c-qr{margin-top:8px}.c-qr-load{color:#c9c1de;font-size:13px;text-align:center;padding:20px;border:2px dashed #ece5fb;border-radius:16px}.c-qr-val{font-size:12px;color:#a99fc4;word-break:break-all;margin-top:6px;text-align:center}\n  .c-custom{margin:6px 0}\n  .scanwrap{position:fixed;inset:0;background:rgba(84,77,104,.72);z-index:60;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;padding:20px}\n  .scanwrap video{width:min(320px,86vw);height:min(320px,86vw);object-fit:cover;border-radius:24px;border:4px solid #fff;background:#000}\n  .scanwrap .st{color:#fff;font-weight:700}\n  .c-dots{display:inline-flex;gap:5px}.c-dots i{width:8px;height:8px;border-radius:50%;background:currentColor;display:inline-block;animation:bob .6s infinite}.c-dots i:nth-child(2){animation-delay:.15s}.c-dots i:nth-child(3){animation-delay:.3s}\n  @keyframes bob{0%,100%{transform:translateY(0);opacity:.5}50%{transform:translateY(-5px);opacity:1}}\n  .c-skel div{background:linear-gradient(90deg,#f4f0fc 25%,#faf7ff 50%,#f4f0fc 75%);background-size:200% 100%;animation:sh 1.2s infinite;height:56px;border-radius:16px;margin-bottom:9px}\n  @keyframes sh{0%{background-position:200% 0}100%{background-position:-200% 0}}\n  .fade{animation:fade .3s ease both}@keyframes fade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}\n  .toast{position:fixed;left:50%;bottom:80px;transform:translateX(-50%);z-index:70;background:#544d68;color:#fff;font-weight:700;font-size:15px;padding:13px 22px;border-radius:999px;box-shadow:0 12px 30px rgba(0,0,0,.22);animation:pop .3s ease both}\n  @keyframes pop{from{opacity:0;transform:translate(-50%,10px) scale(.9)}to{opacity:1;transform:translate(-50%,0) scale(1)}}\n  `,\n\n  render(d2) {\n    WREG = {}; WSEQ = 0;\n    const view = d2.state.view || 'home';\n    const feats = d2.data('features');\n    const rows = feats ? feats.records() : [];\n\n    if (view === 'feature') {\n      const feat = rows.find((f) => f.key === d2.state.activeKey) || d2.state.activeFeat;\n      if (!feat) return `<div class=\"app\"><div class=\"wrap\"><button class=\"iconbtn j-home\">← apps</button><div class=\"empty\"><div class=\"big\">🫧</div><div class=\"t\">Loading…</div></div></div></div>`;\n      const ctx = buildCtx(d2, feat);\n      const app = ctx.app;\n      const stack = d2.state.stack || [];\n      const canBack = stack.length > 1;\n      const navBtn = canBack ? `<button class=\"iconbtn j-navback\">←</button>` : `<button class=\"iconbtn j-home\">✦</button>`;\n      const screenTitle = ctx.screen.title || feat.name;\n      const body = renderScreen(ctx);\n      const tabs = (app.nav && app.nav.length > 1) ? `<div class=\"tabbar\">${app.nav.map((id) => { const sc = app.screens.find((s) => s.id === id) || {}; const on = (stack[0] && stack[0].screenId === id) || (!stack.length && app.home === id); return `<button class=\"tab ${on ? 'on' : ''} j-tab\" data-tab=\"${attr(id)}\"><span class=\"ti\">${esc(sc.icon || '•')}</span>${esc(sc.title || id)}</button>`; }).join('')}</div>` : '';\n      return `<div class=\"app\" style=\"--a:${attr(feat.color || '#ffd8e6')}\"><div class=\"wrap fade\">\n        <div class=\"apptop\">${navBtn}<div class=\"apptitle\"><span>${esc(feat.emoji || '')} ${esc(screenTitle)}</span></div><button class=\"iconbtn j-home\">apps</button></div>\n        ${body}\n      </div>${tabs}${d2.state.scan ? scanModal(d2) : ''}${toastHtml(d2)}</div>`;\n    }\n\n    // HOME\n    const creating = !!d2.state.createBusy;\n    const cerr = d2.state.createErr ? `<div class=\"cerr\">😖 ${esc(d2.state.createErr)}</div>` : '';\n    let gridHtml;\n    if (feats && feats.loading) gridHtml = `<div class=\"grid\">${'<div class=\"fcard\" style=\"height:132px\"></div>'.repeat(4)}</div>`;\n    else if (!rows.length) gridHtml = `<div class=\"empty\"><div class=\"big\">🐣</div><div class=\"t\">No apps yet — make the first wish!</div></div>`;\n    else gridHtml = `<div class=\"grid\">${rows.map((f) => `<button class=\"fcard j-open\" data-key=\"${attr(f.key)}\" style=\"--a:${attr(f.color || '#ffd8e6')}\"><div class=\"fc-emoji\">${esc(f.emoji || '✨')}</div><div class=\"fc-name\">${esc(f.name || 'Untitled')}</div><div class=\"fc-tag\">${esc(f.tagline || '')}</div></button>`).join('')}</div>`;\n\n    const createBlock = creating\n      ? `<div class=\"creating\"><span class=\"c-dots\" style=\"color:#a99bff\"><i></i><i></i><i></i></span> dreaming up your app…</div>`\n      : `<div class=\"create\"><input class=\"j-cin\" placeholder=\"I want… a CRM for my leads 🗂️\" value=\"${attr(d2.state.prompt || '')}\"/><button class=\"mk j-create\">Create ✨</button></div>${cerr}`;\n\n    return `<div class=\"app\"><div class=\"wrap fade\">\n      <div class=\"topbar\"><div class=\"brand\"><span>🌸</span>china</div><div class=\"me\">${esc(nick())}</div></div>\n      <div class=\"subtitle\">type a wish — get a real, shared mini-app ✨</div>\n      ${createBlock}\n      <div class=\"sec-h\">Everyone's apps</div>\n      ${gridHtml}\n    </div>${toastHtml(d2)}</div>`;\n  },\n\n  mounted(d2) {\n    try { d2.data('features').loadAll(); } catch (_) {}\n    try { d2.data('assistants').loadAll(); } catch (_) {}\n\n    const curFeat = () => { const rows = d2.data('features').records(); return rows.find((f) => f.key === d2.state.activeKey) || d2.state.activeFeat; };\n    const curApp = () => { const f = curFeat(); return f ? normalizeSpec(f) : { screens: [], entities: [] }; };\n    const scEnt = (app, c) => (app.entities || []).find((e) => e.collection === c);\n    const showToast = (m) => { d2.state.toast = m; setTimeout(() => { d2.state.toast = null; }, 2400); };\n    const refreshRecords = () => { try { d2.data('records').filter({ featureKey: d2.state.activeKey }); } catch (_) {} };\n\n    const openFeature = (key) => {\n      d2.state.activeKey = key; d2.state.activeFeat = d2.data('features').records().find((f) => f.key === key) || null;\n      const app = curApp();\n      d2.state.stack = [{ screenId: app.home || (app.screens[0] && app.screens[0].id) }];\n      d2.state.form = {}; d2.state.refs = {}; d2.state.results = {}; d2.state.search = '';\n      refreshRecords();\n      d2.state.view = 'feature';\n    };\n\n    const initForm = (screen, frame) => {\n      const app = curApp(); const ent = scEnt(app, screen.collection) || {};\n      const form = {}; const refs = {};\n      if (frame.recordId) {\n        const rec = d2.data('records').records().find((r) => r._id === frame.recordId);\n        if (rec) { Object.assign(form, rec.data || {}); Object.assign(refs, rec.refs || {}); }\n      } else {\n        (screen.fields || ent.fields || []).forEach((f) => { if (f.default !== undefined) form[f.key] = f.default; });\n      }\n      if (frame.preset) Object.assign(refs, frame.preset);\n      d2.state.form = form; d2.state.refs = refs; d2.state.results = {};\n    };\n\n    const navigate = (to, opts) => {\n      const app = curApp(); const screen = app.screens.find((s) => s.id === to);\n      const frame = { screenId: to, recordId: (opts && opts.recordId) || null, preset: (opts && opts.preset) || null };\n      d2.state.stack = (d2.state.stack || []).concat([frame]);\n      if (screen && screen.type === 'form') initForm(screen, frame);\n      else { d2.state.form = {}; d2.state.results = {}; d2.state.search = ''; }\n      const log = d2.$('.c-log'); if (log) log.scrollTop = log.scrollHeight;\n    };\n    const back = () => { const s = (d2.state.stack || []).slice(); if (s.length > 1) s.pop(); d2.state.stack = s; d2.state.search = ''; };\n    const goTab = (id) => { d2.state.stack = [{ screenId: id }]; d2.state.form = {}; d2.state.results = {}; d2.state.search = ''; refreshRecords(); };\n\n    const bag = () => { const f = curFeat(); let rec = null; const fr = (d2.state.stack || []).slice(-1)[0]; if (fr && fr.recordId) rec = d2.data('records').records().find((r) => r._id === fr.recordId); return Object.assign({}, (rec && rec.data) || {}, d2.state.form || {}, d2.state.results || {}); };\n    const findAssistant = (key) => { try { return d2.data('assistants').records().find((a) => a.key === key) || null; } catch (_) { return null; } };\n\n    const doFetch = async (a) => {\n      d2.state.busy = Object.assign({}, d2.state.busy, { run: true });\n      try {\n        const b = bag();\n        const payload = { capability: a.capability, url: a.url ? fill(a.url, b) : undefined, base: a.base, query: a.query ? fill(a.query, b) : undefined, method: a.method };\n        const out = await d2.rush('china-fetch').invoke(payload);\n        const env = (out && out.data) ? out.data : out; // {status,data}\n        let data = env && env.data !== undefined ? env.data : env;\n        if (a.pick) { try { a.pick.split('.').forEach((k) => { data = data == null ? data : data[k]; }); } catch (_) {} }\n        const into = a.into || 'result';\n        const val = (typeof data === 'object') ? JSON.stringify(data, null, 2) : data;\n        d2.state.results = Object.assign({}, d2.state.results, { [into]: val });\n        d2.state.form = Object.assign({}, d2.state.form, { [into]: val });\n      } catch (e) { showToast('😖 ' + (e.message || 'fetch failed')); }\n      d2.state.busy = Object.assign({}, d2.state.busy, { run: false });\n    };\n\n    const saveRecord = async (a) => {\n      const app = curApp(); const frame = (d2.state.stack || []).slice(-1)[0] || {};\n      const collection = a.collection || (curApp().screens.find((s) => s.id === frame.screenId) || {}).collection || 'item';\n      const data = Object.assign({}, d2.state.form || {}, d2.state.results || {});\n      const refs = Object.assign({}, d2.state.refs || {});\n      try {\n        if (frame.recordId) {\n          const rec = d2.data('records').records().find((r) => r._id === frame.recordId);\n          await d2.data('records').update(Object.assign({}, rec, { data, refs, updatedAt: nowISO() }));\n        } else {\n          await d2.data('records').create({ featureKey: d2.state.activeKey, collection, data, refs, status: a.status || (data.status) || '', owner: nick(), createdAt: nowISO(), updatedAt: nowISO() });\n        }\n        refreshRecords();\n        d2.state.form = {}; d2.state.refs = {}; d2.state.results = {};\n        showToast(a.toast || 'Saved! 🎉');\n        if (a.after === 'back' || a.after == null) back(); else if (a.after) goTab(a.after);\n      } catch (e) { showToast('😖 ' + (e.message || 'save failed')); }\n    };\n\n    const runAction = async (spec) => {\n      const acts = isArr(spec) ? spec : [spec];\n      for (const a of acts) {\n        if (!a || !a.type) continue;\n        if (a.type === 'navigate') navigate(a.to, { recordId: a.recordId, preset: a.preset });\n        else if (a.type === 'back') back();\n        else if (a.type === 'home') { d2.state.view = 'home'; }\n        else if (a.type === 'tab') goTab(a.to);\n        else if (a.type === 'reset') { d2.state.form = {}; d2.state.results = {}; d2.state.refs = {}; }\n        else if (a.type === 'save') await saveRecord(a);\n        else if (a.type === 'setStatus') { const rec = d2.data('records').records().find((r) => r._id === a.recordId); if (rec) { try { await d2.data('records').update(Object.assign({}, rec, { status: a.status, updatedAt: nowISO() })); refreshRecords(); showToast(a.toast || 'Updated ✨'); } catch (e) { showToast('😖 ' + e.message); } } }\n        else if (a.type === 'delete') { try { await d2.data('records').remove(a.recordId); refreshRecords(); showToast('Deleted 🗑️'); back(); } catch (e) { showToast('😖 ' + e.message); } }\n        else if (a.type === 'ask') {\n          d2.state.busy = Object.assign({}, d2.state.busy, { run: true });\n          try { const base = findAssistant(a.assistantKey); const filled = fill(a.prompt, bag()); const prompt = base && base.base_prompt ? base.base_prompt + '\\n\\nTASK:\\n' + filled : filled; const ans = await d2.assistant(d2.params.oracle).ask(prompt); const into = a.into || 'result'; d2.state.results = Object.assign({}, d2.state.results, { [into]: ans }); d2.state.form = Object.assign({}, d2.state.form, { [into]: ans }); } catch (e) { showToast('😖 ' + (e.message || 'AI hiccup')); }\n          d2.state.busy = Object.assign({}, d2.state.busy, { run: false });\n        }\n        else if (a.type === 'draw') {\n          d2.state.busy = Object.assign({}, d2.state.busy, { run: true });\n          try { const filled = fill(a.prompt, bag()); const res = await d2.assistant(d2.params.artist).draw(filled); let url = res && res.data && res.data[0] && res.data[0].url; if (isArr(url)) url = url[0]; if (typeof url === 'string' && url.startsWith('data:')) url = await hostDataUri(d2, url); const into = a.into || 'pic'; d2.state.results = Object.assign({}, d2.state.results, { [into]: url }); d2.state.form = Object.assign({}, d2.state.form, { [into]: url }); } catch (e) { showToast('😖 ' + (e.message || 'art hiccup')); }\n          d2.state.busy = Object.assign({}, d2.state.busy, { run: false });\n        }\n        else if (a.type === 'fetch') await doFetch(a);\n        else if (a.type === 'chat') {\n          const el = d2.$('.j-chatin'); const text = (el && el.value || '').trim(); if (!text) continue;\n          if (el) el.value = '';\n          d2.state.busy = Object.assign({}, d2.state.busy, { run: true });\n          try {\n            const base = findAssistant(a.assistantKey);\n            const history = d2.data('records').records().filter((r) => r.featureKey === d2.state.activeKey && r.collection === a.collection).slice(-8).map((r) => ({ u: (r.data || {})[a.userField], a: (r.data || {})[a.replyField] }));\n            const ctxStr = history.map((h) => 'User: ' + h.u + '\\nAssistant: ' + h.a).join('\\n');\n            const prompt = (base && base.base_prompt ? base.base_prompt + '\\n\\n' : '') + (ctxStr ? 'Conversation so far:\\n' + ctxStr + '\\n\\n' : '') + 'User: ' + text + '\\nAssistant:';\n            const ans = await d2.assistant(d2.params.oracle).ask(prompt);\n            await d2.data('records').create({ featureKey: d2.state.activeKey, collection: a.collection, data: { [a.userField]: text, [a.replyField]: ans }, owner: nick(), createdAt: nowISO() });\n            refreshRecords();\n          } catch (e) { showToast('😖 ' + (e.message || 'chat hiccup')); }\n          d2.state.busy = Object.assign({}, d2.state.busy, { run: false });\n          const log = d2.$('.c-log'); if (log) log.scrollTop = log.scrollHeight;\n        }\n        else if (a.type === 'qrscan') { d2.state.scan = { field: a.field }; setTimeout(() => startScan(), 80); }\n        else if (a.type === 'qrclose') { d2.state.scan = null; stopScan(); }\n      }\n    };\n\n    const createFeature = async () => {\n      const req = (d2.state.prompt || '').trim(); if (!req || d2.state.createBusy) return;\n      d2.state.createBusy = true; d2.state.createErr = null;\n      try {\n        let spec = await d2.assistant(d2.params.architect).ask(req, { json: true });\n        if (typeof spec === 'string') spec = JSON.parse(spec);\n        if (!spec || !spec.uiSpec) throw new Error('The architect got shy — try rephrasing?');\n        const key = slug(spec.name) + '-' + rid();\n        const helpers = spec.assistants || [];\n        for (const h of helpers) { if (!h || !h.key || findAssistant(h.key)) continue; await d2.data('assistants').create({ key: h.key, name: h.name || h.key, base_prompt: h.prompt || '', mode: h.mode || 'text', createdAt: nowISO() }); }\n        if (helpers.length) { try { await d2.data('assistants').loadAll(); } catch (_) {} }\n        await d2.data('features').create({ key, name: spec.name || 'New App', emoji: spec.emoji || '✨', tagline: spec.tagline || '', color: spec.color || '#ffd8e6', uiSpec: spec.uiSpec, assistantKeys: helpers.map((h) => h.key), createdBy: nick(), createdAt: nowISO() });\n        try { await d2.data('features').loadAll(); } catch (_) {}\n        d2.state.prompt = ''; openFeature(key);\n      } catch (e) { d2.state.createErr = e.message || String(e); }\n      d2.state.createBusy = false;\n    };\n\n    // ---- camera / QR scan ----\n    let scanStream = null, scanRAF = null;\n    const stopScan = () => { if (scanRAF) cancelAnimationFrame(scanRAF); scanRAF = null; if (scanStream) { scanStream.getTracks().forEach((t) => t.stop()); scanStream = null; } };\n    const startScan = async () => {\n      const jsQR = d2.lib('jsqr@1.4.0'); const video = d2.$('.scanwrap video'); if (!video) return;\n      try {\n        scanStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });\n        video.srcObject = scanStream; await video.play();\n        const cv = document.createElement('canvas'); const cxx = cv.getContext('2d');\n        const tick = () => {\n          if (!d2.state.scan) return;\n          if (jsQR && video.videoWidth) {\n            cv.width = video.videoWidth; cv.height = video.videoHeight; cxx.drawImage(video, 0, 0, cv.width, cv.height);\n            const img = cxx.getImageData(0, 0, cv.width, cv.height); const code = jsQR(img.data, img.width, img.height);\n            if (code && code.data) { const field = d2.state.scan.field; d2.state.form = Object.assign({}, d2.state.form, { [field]: code.data }); d2.state.scan = null; stopScan(); showToast('Scanned! ✨'); return; }\n          }\n          scanRAF = requestAnimationFrame(tick);\n        };\n        scanRAF = requestAnimationFrame(tick);\n      } catch (e) { showToast('📷 camera blocked — type it instead'); d2.state.scan = null; }\n    };\n\n    // ---- delegated events ----\n    d2.on('.j-open', 'click', (e, el) => openFeature(el.dataset.key));\n    d2.on('.j-home', 'click', () => { stopScan(); d2.state.scan = null; d2.state.view = 'home'; });\n    d2.on('.j-navback', 'click', back);\n    d2.on('.j-tab', 'click', (e, el) => goTab(el.dataset.tab));\n    d2.on('.j-create', 'click', createFeature);\n    d2.on('.j-cin', 'input', (e, el) => { d2.state.prompt = el.value; });\n    d2.on('.j-cin', 'keydown', (e) => { if (e.key === 'Enter') createFeature(); });\n    d2.on('.j-search', 'input', (e, el) => { d2.state.search = el.value; });\n    d2.on('.j-input', 'input', (e, el) => { const v = el.dataset.kind === 'number' ? Number(el.value) : el.value; d2.state.form = Object.assign({}, d2.state.form, { [el.dataset.name]: v }); });\n    d2.on('.j-opt', 'click', (e, el) => { const name = el.dataset.name, value = el.dataset.value, multi = el.dataset.multi === '1'; const form = Object.assign({}, d2.state.form); if (multi) { const cur = isArr(form[name]) ? form[name].slice() : []; const i = cur.findIndex((x) => String(x) === String(value)); if (i >= 0) cur.splice(i, 1); else cur.push(value); form[name] = cur; } else form[name] = value; d2.state.form = form; });\n    d2.on('.j-relopt', 'click', (e, el) => { d2.state.refs = Object.assign({}, d2.state.refs, { [el.dataset.name]: el.dataset.id }); });\n    d2.on('.j-rating', 'click', (e, el) => { d2.state.form = Object.assign({}, d2.state.form, { [el.dataset.name]: Number(el.dataset.val) }); });\n    d2.on('.j-step', 'click', (e, el) => { const name = el.dataset.name; const form = Object.assign({}, d2.state.form); form[name] = Number(form[name] || 0) + Number(el.dataset.dir); if (form[name] < 0) form[name] = 0; d2.state.form = form; });\n    d2.on('.j-toggle', 'click', (e, el) => { const name = el.dataset.name; d2.state.form = Object.assign({}, d2.state.form, { [name]: !d2.state.form[name] }); });\n    d2.on('.j-photo', 'change', async (e, el) => { const file = el.files && el.files[0]; if (!file) return; const name = el.dataset.name; d2.state.busy = Object.assign({}, d2.state.busy, { run: true }); try { const out = await d2.drive('china-drive').uploadFromFiles([file]); const url = out && out[0] && out[0].fileUrl; if (url) d2.state.form = Object.assign({}, d2.state.form, { [name]: url }); } catch (err) { showToast('😖 upload failed'); } d2.state.busy = Object.assign({}, d2.state.busy, { run: false }); });\n    d2.on('.j-chatin', 'keydown', (e) => { if (e.key === 'Enter') { const app = curApp(); const fr = (d2.state.stack || []).slice(-1)[0]; const sc = app.screens.find((s) => s.id === (fr && fr.screenId)) || {}; runAction({ type: 'chat', collection: sc.collection, assistantKey: sc.assistantKey, userField: sc.userField || 'msg', replyField: sc.replyField || 'reply' }); } });\n    d2.on('.j-act', 'click', (e, el) => { let spec = {}; try { spec = JSON.parse(el.dataset.action || '{}'); } catch (_) {} runAction(spec); });\n    // custom-widget actions\n    d2.on('[data-caction]', 'click', (e, el) => { const host = el.closest && el.closest('[data-wid]'); const reg = host && WREG[host.dataset.wid]; const fn = reg && reg.actions && reg.actions[el.dataset.caction]; if (fn) { const feat = curFeat(); const ctx = feat ? buildCtx(d2, feat) : null; const api = ctx ? Object.assign(readApi(ctx), { navigate, back, save: (c, data, refs, status) => d2.data('records').create({ featureKey: d2.state.activeKey, collection: c, data, refs, status, owner: nick(), createdAt: nowISO(), updatedAt: nowISO() }).then(refreshRecords), update: (r) => d2.data('records').update(Object.assign({}, r, { updatedAt: nowISO() })).then(refreshRecords), remove: (id) => d2.data('records').remove(id).then(refreshRecords), ask: (p, k) => { const b = findAssistant(k); return d2.assistant(d2.params.oracle).ask((b && b.base_prompt ? b.base_prompt + '\\n\\n' : '') + p); }, fetch: (o) => d2.rush('china-fetch').invoke(o), set: (k, v) => { d2.state.results = Object.assign({}, d2.state.results, { [k]: v }); }, toast: showToast, refresh: refreshRecords }) : {}; try { fn(api, el, e); } catch (err) { showToast('😖 ' + err.message); } } });\n  },\n\n  updated(d2) {\n    // generate QR codes\n    const QR = d2.lib('qrcode@1.5.4');\n    if (QR) { d2.$$('.c-qr[data-qr]').forEach((el) => { if (el.dataset.done) return; el.dataset.done = '1'; QR.toDataURL(el.dataset.qr, { margin: 1, width: 240, color: { dark: '#544d68', light: '#ffffff' } }).then((url) => { el.innerHTML = `<img src=\"${url}\" style=\"width:100%;max-width:240px;border-radius:16px;display:block;margin:0 auto\"/>`; }).catch(() => {}); }); }\n    const log = d2.$('.c-log'); if (log) log.scrollTop = log.scrollHeight;\n  },\n};\n\nfunction scanModal(d2) {\n  return `<div class=\"scanwrap\"><div class=\"st\">Point at a QR code 📷</div><video playsinline muted></video><button class=\"c-btn c-btn-soft j-act\" style=\"max-width:240px\" data-action=\"${j({ type: 'qrclose' })}\">Cancel</button></div>`;\n}\nfunction toastHtml(d2) { return d2.state.toast ? `<div class=\"toast\">${esc(d2.state.toast)}</div>` : ''; }"}},"features":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"features"}}}},"records":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"records"}}}},"assistants":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"assistants"}}}},"architect":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"architect"}}}},"oracle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"oracle"}}}},"artist":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"artist"}}}}}}}}}},{"_id":"5ed9bed7b9e0891ce913e884","component":"data2-core-types@Component","params":{"package":"data2-core","component":"DataSource","renderable":{"dimensions":{"disabled":false,"component":"data2-core-types@Dimensions","params":{"width":1366,"exactWidth":false,"exactHeight":false,"height":768}}}}},{"_id":"5f472ae463c680de4ff296e9","component":"data2-core-types@Component","params":{"package":"data2-core","component":"Data2TableDataSource","extends":"data2-core@DataSource","renderable":{"dimensions":{"disabled":false,"component":"data2-core-types@Dimensions","params":{"width":1366,"exactWidth":false,"exactHeight":false,"height":768}}}}}]}},{"reference":"ExternalComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_id":"5f060d9b1ca96def42f3c40a","displayName":"Builder AuthStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"credentials":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"UsersDataSet"}},"sessionShared":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},"email":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"nR6Q9d7qdaPtZhJ28Q8Dy"},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"yPCJBTKo8q1Mw0B_3Kpwl"},"component":"data2-core-types@ArtboardReference"}}},"session":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"name"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"sessionMetadata":{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},{"params":{"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"active"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.status"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is active user"}]},"component":"data2-core-logical-operators@And","displayName":"is active and has _id"}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $user"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-primitives@String","params":{"content":"@data2.app"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.email"}}}}},"component":"data2-core-functions@Includes","displayName":"isData2User"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}},"component":"data2-core-flow@Step"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.d2_app"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"management"}}]},"component":"data2-core-functions@Includes","displayName":"isData2App Exception"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}},"component":"data2-core-flow@Step"},{"params":{"resultName":{"component":"data2-core-primitives@String","params":{"content":"has_access"}},"action":{"params":{"compare":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}}},{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}}}]},"component":"data2-core-logical-operators@Or"},"else":{"params":{"actions":[{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $metadata.system"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":"filter","args":[{"_id":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records.length"}}}}},"component":"data2-core-logical-operators@Not","displayName":"systems not found"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check system found"},{"params":{"actions":[{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"organization":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"cursor.current.organization._id"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"}]},"component":"data2-core-flow@Parallel"}]},"component":"data2-core-flow@Series","displayName":"common setup"},{"params":{"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"},"else":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"compare":{"params":{"components":{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"mapper":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"system"}}],"body":{"params":{"expression":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.visibility"}}}},"defaultCase":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"cases":[{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"public"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"billing-owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}},{"component":"data2-core-primitives@String","params":{"content":"member"}}]},"component":"data2-core-functions@Includes","displayName":"has role with access"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"collaborator"}}},"component":"data2-core-comparison-operators@Equal","displayName":"is collaborator"}]},"component":"data2-core-logical-operators@And","displayName":"is org member and collaborator"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is collaborator"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is collaborator and has system access"}]},"component":"data2-core-logical-operators@Or"}}},{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"private"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}}]},"component":"data2-core-functions@Includes","displayName":"is owner/admin"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is owner/admin"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is member and has system access"}]},"component":"data2-core-logical-operators@Or","displayName":"is owner/admin or has system access"}}}]},"component":"data2-core-flow@Switch"}}}},"component":"data2-core-functions@ArrayMap"}},"component":"data2-core-logical-operators@And","displayName":"has org member for all systems in request"}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"}},"component":"data2-core-flow@If"}},"component":"data2-core-flow@Step"},{"params":{"then":{"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"else":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"},"compare":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"has_access"}}}}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"sessionExpiresIn":{"component":"data2-core-primitives@Number","params":{"content":604800}},"sessionRenewal":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"identityProviders":[{"provider":{"component":"data2-core-primitives@String","params":{"content":"GOOGLE"}},"clientId":{"component":"data2-core-primitives@String","params":{"content":"55470861603-22pvfc1t49lp8cjnbfk71s5ct9hp1q1v.apps.googleusercontent.com"}}}]},"reference":"Data2BuilderAuthentication","updatedAt":"2026-08-06T19:31:23.557Z"},{"_id":"5f060ee51ca96def42f3fbd1","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"UsersTableDataSource"}},"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}}},"reference":"UsersDataSet","updatedAt":"2026-03-04T21:16:40.291Z"},{"_id":"68f938cf5fb002e33752a43d","displayName":"InternalApplicationsAuthenticationStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}}},"verifier":{"disabled":false,"component":"data2-core-types@DataSetReference","params":{"reference":"InternalApplicationsVerifierDataSet"}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"secret"}},"credentials":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}],"session":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}]},"reference":"InternalApplicationsAuthenticationStrategy","updatedAt":"2025-10-22T20:08:54.442Z","createdAt":"2025-10-22T20:04:31.198Z"},{"component":"data2-core@DataSet","reference":"SessionsDataSet","params":{"requiresConfirmation":false,"pk":"key","datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"data2sessionsdatasource"}}}},{"reference":"UsersTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@ServerSessionsDataSource","reference":"data2sessionsdatasource","params":{}},{"_id":"5f16172a0528b92601890f85","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":10000}}},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"DataComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"DataComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:12.831Z","createdAt":"2020-07-20T22:14:02.097Z"},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a59bb0fe92c9104612308fa","component":"data2-core@DataSet","system":"5e72ab6c7ff98e774545a642","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"china-assistants"}}},"reference":"china-assistantsDataSet"},{"_id":"6a59bb047a65d3cf47a8d0f2","component":"data2-core@DataSet","system":"5e72ab6c7ff98e774545a642","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"china-records"}}},"reference":"china-recordsDataSet"},{"_id":"6a59bafd8a6d78ebeaa1920d","component":"data2-core@DataSet","system":"5e72ab6c7ff98e774545a642","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"china-features"}}},"reference":"china-featuresDataSet"},{"_id":"6a59bb0ece0eeb9aa5bbc437","component":"data2-core@Data2TableDataSource","system":"5e72ab6c7ff98e774545a642","params":{"table":"china-assistants","schema":[{"component":"data2-core-types@Field","params":{"key":"key","title":"Key","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"name","title":"Name","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"base_prompt","title":"Base Prompt","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"mode","title":"Mode","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"createdAt","title":"Created At","component":"data2-core-primitives@Date"}}]},"reference":"china-assistants"},{"_id":"6a59bb03e92c9104612308e2","component":"data2-core@Data2TableDataSource","system":"5e72ab6c7ff98e774545a642","params":{"table":"china-records","schema":[{"component":"data2-core-types@Field","params":{"key":"featureKey","title":"Feature Key","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"data","title":"Data","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"owner","title":"Owner","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"createdAt","title":"Created At","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"collection","title":"Collection","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"refs","title":"Refs","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"status","title":"Status","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"updatedAt","title":"Updated At","component":"data2-core-primitives@Date"}}]},"reference":"china-records"},{"_id":"6a59bafdce0eeb9aa5bbc423","component":"data2-core@Data2TableDataSource","system":"5e72ab6c7ff98e774545a642","params":{"table":"china-features","schema":[{"component":"data2-core-types@Field","params":{"key":"key","title":"Key","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"name","title":"Name","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"emoji","title":"Emoji","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"tagline","title":"Tagline","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"color","title":"Color","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"uiSpec","title":"UI Spec","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"assistantKeys","title":"Assistant Keys","component":"data2-core-primitives@Array"}},{"component":"data2-core-types@Field","params":{"key":"createdBy","title":"Created By","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"createdAt","title":"Created At","component":"data2-core-primitives@Date"}}]},"reference":"china-features"}]}},{"_id":"5f1600b70528b92601890f75","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ServiceComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ServiceComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:20.208Z","createdAt":"2020-07-20T20:38:15.436Z"},{"reference":"ServiceComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"69efb2f4b3dfb52cbe4ee9a6","component":"data2-rush@HTTPListener","reference":"chat","system":"5e72ab6c7ff98e774545a642","params":{}},{"_id":"6a5d18c6bccedb47dfa89513","component":"data2-rush@HTTPListener","reference":"china-fetch","system":"5e72ab6c7ff98e774545a642","params":{}},{"_id":"6a59bb623eb46d792cd1ff1f","component":"data2-core-ai@Assistant","reference":"china-artist","system":"5e72ab6c7ff98e774545a642","params":{"model":"cerebras","_context":"You are THE ARTIST — you generate adorable, soft, kawaii-style illustrations for a shared app. Unless told otherwise, favor a cute pastel aesthetic: soft lighting, rounded shapes, gentle colors, charming and joyful. Render exactly what the prompt asks."}},{"_id":"6a59bb50e92c91046123092e","component":"data2-core-ai@Assistant","reference":"china-oracle","system":"5e72ab6c7ff98e774545a642","params":{"model":"cerebras","_context":"You are THE ORACLE — a universal engine that powers little features inside a shared app. You will receive a BASE INSTRUCTION describing exactly how to behave, followed by a TASK with the user's input. Follow the base instruction precisely and reply with ONLY the result the feature needs — no greetings, no preamble, no explanations, no markdown fences unless explicitly asked. Be warm and correct.","behaviour":"Reply with ONLY the answer content. Never add \"Sure!\", \"Here is\", or any wrapper. If asked to translate, output only the translation. If asked for a list, output a clean plain list. Keep it tight and delightful.","creativity":45,"answerPrefix":false}},{"_id":"6a59bb3641840d0563bcb803","component":"data2-core-ai@Assistant","reference":"china-architect","system":"5e72ab6c7ff98e774545a642","params":{"model":"cerebras","_context":"You are CHINA ARCHITECT — a world-class product designer AND software architect inside a magical, shared app-building platform. Given a plain-language wish, you design a COMPLETE multi-screen mini-app: a real data model (entities + relationships), screens (list/detail/form/board/chat/page), navigation, and optional custom widgets. Everything is SHARED across all users. Your aesthetic is KAWAII: soft pastels, rounded, playful emojis, warm friendly microcopy, joyful. You output ONLY one strict JSON object — no prose, no markdown fences.","behaviour":"Output EXACTLY one JSON object (no markdown, no text before/after):\n{\n  \"name\": string, \"emoji\": string(one emoji), \"tagline\": string(<=48 chars), \"color\": soft pastel hex (#FFB7C5 #A7D8B0 #B5C7F7 #FFE08A #C9B6E4 #FFD8B0 #B7EAF0),\n  \"assistants\": [ { \"key\": snake_case, \"name\": string, \"prompt\": system-instruction telling the Oracle to reply with ONLY the result, \"mode\": \"text\" } ],\n  \"uiSpec\": { \"version\": 2, \"entities\": [Entity], \"screens\": [Screen], \"widgets\": [Widget], \"nav\": [screenId], \"home\": screenId }\n}\n\nENTITY: { collection: snake_case, name, emoji, labelField: fieldKey, fields: [Field] }\nFIELD: { key, label, type, options?, relatesTo?, max?, default?, placeholder? }\n  type ∈ text | textarea | number | date | select(options:[{label,value,emoji?}]) | chips(options,multi?) | toggle | photo | rating(max) | relation(relatesTo:'<collection>') | qr\n\nSCREEN types (each: id, title, icon?):\n- list:  { type:'list', collection, search?, item:{title:fieldKey, subtitle?, media?(photo field), badge?}, tapTo?:detailScreenId, fab?:{to:formScreenId, label} }\n- detail:{ type:'detail', collection, media?(field), blocks?:[{type:'field',field,label}|{type:'qr-show',field,label}|{type:'text',text}], related?:[{title, collection, via:refFieldKey, item:{title,subtitle?}, tapTo?, fab?:{to,label}}], actions?:[{label,emoji,to:formScreenId}|{label,emoji,type:'setStatus',status}|{label,emoji,type:'delete'}] }\n- form:  { type:'form', collection, submitLabel?, after? }  // upserts (create OR edit); fields come from the entity\n- board: { type:'board', collection, statusField:'status', columns:[{value,label,emoji?}], card:{title,subtitle?}, tapTo?, fab?:{to,label} }\n- chat:  { type:'chat', collection, assistantKey, greeting?, userField?, replyField? }  // messages persist; history is fed back to the AI (stateful!)\n- page:  { type:'page', blocks:[Widget] }  // single-screen tool\n\nWIDGETS on a page/detail: hero{emoji,title,subtitle} · text{text,tone?} · image{url|field} · divider · result{into,as?} · field{field,label} · media{field} · qr-show{field|value,label} · inputs {type,name,label,...} (input/textarea/number/select/chips/toggle/photo/date/rating/relation/qr) · stepper{name,label} · button{label,emoji,style?,do:ACTION|[ACTION]} · list/feed/gallery{collection,...} · widget{use:widgetName,params?} · custom{code}\n\nACTIONS (button.do / detail actions):\n- {type:'save', collection?, after?:'back'|screenId, toast?}   // saves current form incl relations\n- {type:'setStatus', status}    // on a detail action, advances the record's status (for boards)\n- {type:'delete'}\n- {type:'navigate', to:screenId, preset?:{refKey:value}}\n- {type:'ask', assistantKey, prompt:'... {{field}} ...', into:resultKey}\n- {type:'draw', prompt, into}\n- {type:'fetch', capability?:'fx'|'search', url?, base?, query?, into, pick?}   // REAL live data — never invent numbers\n- {type:'reset'} · {type:'back'} · {type:'tab', to}\n\nWIDGET SYSTEM (apps advertise their own micro-interfaces): register reusable widgets in uiSpec.widgets = [{name, code}]. code is a JS FUNCTION BODY receiving (api, params, html) and returning { html:'<...>', actions:{ name:(api,el,ev)=>{} } }. READ via api.ofCollection(c)/byId/relatedTo/records()/bag()/results()/esc/attr/fmt; in actions also api.save/update/remove/navigate/back/ask/fetch/set(key,val)/toast. Hook a click with a button data-caction='name' + actions.name. Reuse the form system inside a widget by emitting inputs with class='j-input' data-name='x'. Use SINGLE-QUOTED html attributes inside backtick templates. Keep widget code small and defensive. Instantiate with {type:'widget','use':name}.\n\nRULES:\n- Always version:2 and a home screen. Use 2–4 nav tabs for multi-area apps; nav:[] + one page for a simple tool.\n- Model REAL entities & relationships. If reviews belong to a restaurant, make two entities and a relation — do NOT flatten everything into one list. Master→detail→related is the norm.\n- Give each entity a labelField + emoji. Add search to big lists; media/subtitle/badge to list items when useful.\n- Any AI use needs an assistant in 'assistants' + an 'ask' (or a 'chat' screen). Any live/current data needs a 'fetch' — never fabricate it.\n- {{field}} interpolates the current value of a field (or an AI/fetch result).\n- Kawaii always: pastel color, fitting emoji, warm microcopy.\n- Output STRICT JSON only. The user request is ALWAYS provided; never say it is missing.\n\nWORKED EXAMPLE — wish: 'a place to track restaurants and their reviews':\n{\"name\":\"Bite Book\",\"emoji\":\"🍜\",\"tagline\":\"places & reviews, linked ⭐\",\"color\":\"#FFD8B0\",\"assistants\":[],\"uiSpec\":{\"version\":2,\"home\":\"places\",\"nav\":[\"places\",\"stats\"],\"entities\":[{\"collection\":\"restaurant\",\"name\":\"Restaurant\",\"emoji\":\"🍜\",\"labelField\":\"name\",\"fields\":[{\"key\":\"name\",\"label\":\"Name\",\"type\":\"text\"},{\"key\":\"cuisine\",\"label\":\"Cuisine\",\"type\":\"select\",\"options\":[{\"label\":\"Chinese\",\"value\":\"chinese\",\"emoji\":\"🥟\"},{\"label\":\"Japanese\",\"value\":\"japanese\",\"emoji\":\"🍣\"}]},{\"key\":\"photo\",\"label\":\"Photo\",\"type\":\"photo\"}]},{\"collection\":\"review\",\"name\":\"Review\",\"emoji\":\"⭐\",\"labelField\":\"text\",\"fields\":[{\"key\":\"rating\",\"label\":\"Rating\",\"type\":\"rating\",\"max\":5},{\"key\":\"text\",\"label\":\"Review\",\"type\":\"textarea\"},{\"key\":\"restaurant\",\"label\":\"Restaurant\",\"type\":\"relation\",\"relatesTo\":\"restaurant\"}]}],\"widgets\":[{\"name\":\"scoreboard\",\"code\":\"const rv=api.ofCollection('review');const avg=rv.length?(rv.reduce((s,r)=>s+(Number((r.data||{}).rating)||0),0)/rv.length):0;return {html:`<div class='c-card2'><div class='c-fcell'><span class='c-fk'>Reviews</span><span class='c-fv'>${rv.length}</span></div><div class='c-fcell'><span class='c-fk'>Avg</span><span class='c-fv'>${avg.toFixed(1)} ⭐</span></div></div>`};\"}],\"screens\":[{\"id\":\"places\",\"type\":\"list\",\"title\":\"Places\",\"icon\":\"🍜\",\"collection\":\"restaurant\",\"search\":true,\"item\":{\"title\":\"name\",\"subtitle\":\"cuisine\",\"media\":\"photo\"},\"tapTo\":\"placeDetail\",\"fab\":{\"to\":\"placeForm\",\"label\":\"Add place\"}},{\"id\":\"placeDetail\",\"type\":\"detail\",\"title\":\"Place\",\"collection\":\"restaurant\",\"media\":\"photo\",\"blocks\":[{\"type\":\"field\",\"field\":\"cuisine\",\"label\":\"Cuisine\"}],\"related\":[{\"title\":\"Reviews\",\"collection\":\"review\",\"via\":\"restaurant\",\"item\":{\"title\":\"text\",\"subtitle\":\"rating\"},\"fab\":{\"to\":\"reviewForm\",\"label\":\"Write a review\"}}],\"actions\":[{\"label\":\"Edit\",\"emoji\":\"✏️\",\"to\":\"placeForm\"},{\"label\":\"Delete\",\"emoji\":\"🗑️\",\"type\":\"delete\"}]},{\"id\":\"placeForm\",\"type\":\"form\",\"title\":\"Place\",\"collection\":\"restaurant\",\"submitLabel\":\"Save\"},{\"id\":\"reviewForm\",\"type\":\"form\",\"title\":\"Review\",\"collection\":\"review\",\"submitLabel\":\"Post\"},{\"id\":\"stats\",\"type\":\"page\",\"title\":\"Stats\",\"icon\":\"📊\",\"blocks\":[{\"type\":\"hero\",\"emoji\":\"📊\",\"title\":\"Stats\"},{\"type\":\"widget\",\"use\":\"scoreboard\"}]}]}}","creativity":22,"maxTokens":9000,"json":true,"answerPrefix":false}}]}},{"_id":"5f1601990528b92601890f7b","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ApplicationsTableDataSource"}},"pk":"_id","autoEdit":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"params":{"column":"_id"},"component":"data2-core-types@DataSetColumnReference"}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"ApplicationsDataSet","updatedAt":"2022-12-13T21:51:13.995Z","createdAt":"2020-07-20T20:42:01.657Z"},{"reference":"ApplicationsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a59bda09f691f135af19e86","displayName":"China","reference":"_IdmGNnfTjaoxiF-1GGPg","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"china"}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Artboard","reference":"ox_78hpxQm9JTmpqYuMTY"}}},"system":"5e72ab6c7ff98e774545a642","component":"data2-core-primitives@Application"}]}},{"_id":"63ee3d38b8c9838c62fb05fb","displayName":"DriveFileComponentInstancesDataSet","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core@Drive"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"DriveFileComponentInstancesDataSet","updatedAt":"2025-12-04T21:58:32.367Z","createdAt":"2023-02-16T14:27:07.797Z"},{"_id":"5fc4faeec8da12be8bf3d08a","component":"data2-core@Data2TableDataSource","system":"5e3b796d6228227ca637d0f4","params":{"paginate":{"default":1000},"super":{},"table":"fileComponentInstances","schema":[{"component":"data2-core-types@Field","params":{"key":"component","title":"Component","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"system","title":"System","component":"data2-core-primitives@ObjectId"}},{"component":"data2-core-types@Field","params":{"key":"displayName","title":"Display Name","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"params","title":"Params","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"reference","title":"Reference","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"fileId"}},"title":"File ID","component":"data2-core-primitives@ObjectId"},"break":true}],"hooks":{"before":{"all":[{"hook":"softDelete"}],"find":[{"hook":"useCache","options":{"additionalGroupKeys":["$and.0.system","$and.1.system"],"skipCache":true}}],"create":[{"hook":"timestamp","options":{"fields":["createdAt","updatedAt"]}}],"update":[{"hook":"timestamp","options":{"fields":["updatedAt"]}}],"patch":[{"hook":"timestamp","options":{"fields":["updatedAt"]}}]},"after":{"all":[{"hook":"fileUrl","options":{"downloadField":"params.fileUrl","uploadField":"params.uploadFileUrl","enhanceSchema":false}}],"find":[{"hook":"useCache","options":{"additionalGroupKeys":["$and.0.system","$and.1.system"],"skipCache":true}}],"create":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"update":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"patch":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"remove":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}]}},"multi":["create"],"useEstimatedDocumentCount":true},"reference":"FileComponentInstancesTableDataSource","updatedAt":"2022-12-19T20:42:51.837Z","createdAt":"2020-11-30T13:59:52.294Z"},{"_id":"5f1602190528b92601890f7d","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":40000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentInstancesTableDataSource"}},"sort":{"_id":1},"pk":"_id","autoEdit":true,"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:07.089Z","createdAt":"2020-07-20T20:44:09.350Z"},{"_id":"5f16017f0528b92601890f7a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"FileComponentInstancesDataSet","updatedAt":"2023-08-03T16:08:18.193Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"60ddc1bbda3861ca65407c5a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"pagination":{"size":1},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"}},"reference":"OneFileComponentInstancesDataSet","updatedAt":"2022-02-09T19:44:38.036Z","createdAt":"2020-07-20T20:41:35.797Z"},{"reference":"ComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a59bd4c8a6d78ebeaa194a5","component":"data2-core-primitives@Artboard","system":"5e72ab6c7ff98e774545a642","displayName":"China App","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"China App"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":390,"height":844}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"ox_78hpxQm9JTmpqYuMTY"},{"_id":"40cefe31bd1f2c0859a84531","parent":"6a59bd4c8a6d78ebeaa194a5","component":"playground@china-shell","system":"5e72ab6c7ff98e774545a642","displayName":"China Shell","params":{"features":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"china-featuresDataSet"}},"records":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"china-recordsDataSet"}},"assistants":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"china-assistantsDataSet"}},"architect":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"china-architect"}},"oracle":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"china-oracle"}},"artist":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"china-artist"}},"super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"right":true,"top":true,"bottom":true,"exactX":0,"exactY":0}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":390,"height":844}}}},"reference":"p3e863FgYz9pjJv4wF3O6"}]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}