{"context":{"domain":"lucca-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":"6172b957976026c073ab9058","name":"Lucca`s Playground","domain":"lucca-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":"6a73ee571204b3d36b63499e","component":"data2-core-types@Component","system":"6172b957976026c073ab9058","params":{"extends":"data2-core-primitives@Block","component":"pixeldoomEditor","package":"lucca-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 CELL = 32\nconst TOOLS = [\n  { id: '0', label: 'Vazio', sw: '#2f3542' },\n  { id: '1', label: 'Parede 1', sw: '#b33939' },\n  { id: '2', label: 'Parede 2', sw: '#4a69bd' },\n  { id: '3', label: 'Parede 3', sw: '#38ada9' },\n  { id: '4', label: 'Parede 4', sw: '#e58e26' },\n  { id: '5', label: 'Saída', sw: '#f6e58d' },\n  { id: 'player', label: 'Início Jogador', sw: '#00e6e6' },\n  { id: 'enemy', label: 'Inimigo', sw: '#e84118' },\n]\nconst SWATCH = { '0': '#2f3542', '1': '#b33939', '2': '#4a69bd', '3': '#38ada9', '4': '#e58e26', '5': '#f6e58d' }\n\nfunction esc(v) {\n  return String(v == null ? '' : v).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\n}\nfunction blankGrid(w, h) { return new Array(w * h).fill(0) }\nfunction resizeGrid(oldGrid, oldW, oldH, newW, newH) {\n  const g = new Array(newW * newH).fill(0)\n  const mh = Math.min(oldH, newH), mw = Math.min(oldW, newW)\n  for (let y = 0; y < mh; y++) {\n    for (let x = 0; x < mw; x++) g[y * newW + x] = oldGrid[y * oldW + x] || 0\n  }\n  return g\n}\nfunction drawGrid(cv, s, draftGrid) {\n  if (!cv || !s || !s.width) return\n  const w = s.width, h = s.height\n  const grid = draftGrid || s.grid || []\n  const dpr = window.devicePixelRatio || 1\n  const cssW = w * CELL, cssH = h * CELL\n  const needW = Math.round(cssW * dpr), needH = Math.round(cssH * dpr)\n  if (cv.width !== needW || cv.height !== needH) { cv.width = needW; cv.height = needH }\n  const ctx = cv.getContext('2d')\n  ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n  for (let y = 0; y < h; y++) {\n    for (let x = 0; x < w; x++) {\n      const v = grid[y * w + x] || 0\n      ctx.fillStyle = SWATCH[String(v)] || '#2f3542'\n      ctx.fillRect(x * CELL, y * CELL, CELL, CELL)\n      ctx.strokeStyle = 'rgba(0,0,0,0.25)'\n      ctx.strokeRect(x * CELL + 0.5, y * CELL + 0.5, CELL - 1, CELL - 1)\n    }\n  }\n  const things = s.things || []\n  ctx.font = `${Math.floor(CELL * 0.7)}px sans-serif`\n  ctx.textAlign = 'center'\n  ctx.textBaseline = 'middle'\n  things.forEach((t) => { if (t.type === 'enemy') ctx.fillText('👹', t.x * CELL, t.y * CELL) })\n  const ps = s.playerStart || { x: 1.5, y: 1.5, angle: 0 }\n  ctx.save()\n  ctx.translate(ps.x * CELL, ps.y * CELL)\n  ctx.rotate(ps.angle || 0)\n  ctx.fillStyle = '#00e6e6'\n  ctx.beginPath(); ctx.moveTo(12, 0); ctx.lineTo(-8, 8); ctx.lineTo(-8, -8); ctx.closePath(); ctx.fill()\n  ctx.restore()\n}\n\nexport default {\n  css: `\n    .app{position:absolute;inset:0;display:flex;font-family:'Courier New',monospace;background:#0d0f14;color:#e8e8ee;overflow:hidden}\n    .sidebar{width:300px;flex:0 0 300px;background:#161822;border-right:1px solid #2a2e3d;padding:16px;box-sizing:border-box;overflow-y:auto;display:flex;flex-direction:column;gap:12px}\n    .sidebar h1{font-size:15px;letter-spacing:2px;margin:0 0 4px;color:#f6e58d;text-transform:uppercase}\n    .field{display:flex;flex-direction:column;gap:4px;font-size:11px;color:#9aa0b4}\n    .field input{background:#0d0f14;border:1px solid #2a2e3d;color:#e8e8ee;padding:6px 8px;border-radius:4px;font-family:inherit;box-sizing:border-box;width:100%}\n    .row2{display:flex;gap:8px}\n    .row2 .field{flex:1}\n    .toolgrid{display:grid;grid-template-columns:1fr 1fr;gap:6px}\n    .toolbtn{display:flex;align-items:center;gap:6px;background:#1d2030;border:1px solid #2a2e3d;color:#cfd2e0;padding:6px 8px;border-radius:4px;cursor:pointer;font-size:11px;font-family:inherit}\n    .toolbtn .sw{width:14px;height:14px;border-radius:3px;background:var(--c);flex:0 0 14px;display:inline-block}\n    .toolbtn.active{border-color:#f6e58d;background:#2a2410;color:#f6e58d}\n    .actions{display:flex;flex-direction:column;gap:6px;margin-top:4px}\n    .btn{background:#2a2e3d;border:1px solid #3a3f52;color:#e8e8ee;padding:8px 10px;border-radius:4px;cursor:pointer;font-family:inherit;font-size:12px}\n    .btn.primary{background:#f6e58d;color:#161822;font-weight:700;border-color:#f6e58d}\n    .msg{font-size:11px;color:#78e08f;min-height:14px}\n    .levelList{display:flex;flex-direction:column;gap:4px;font-size:11px;border-top:1px solid #2a2e3d;padding-top:10px}\n    .levelList label{color:#9aa0b4;text-transform:uppercase;font-size:10px;letter-spacing:1px}\n    .levelItem{background:#1d2030;border:1px solid #2a2e3d;border-radius:4px;padding:6px 8px;cursor:pointer}\n    .levelItem.active{border-color:#00e6e6;color:#00e6e6}\n    .canvasArea{flex:1;overflow:auto;padding:24px;box-sizing:border-box}\n    .canvasWrap{position:relative;box-shadow:0 0 0 2px #2a2e3d}\n    .grid{display:block;width:100%;height:100%;cursor:crosshair}\n  `,\n  render(d2) {\n    const s = d2.state\n    const width = s.width || 12\n    const height = s.height || 12\n    const tool = s.tool || '1'\n    const name = s.name != null ? s.name : 'Novo Nível'\n    const msg = s.msg || ''\n    const levelId = s.levelId || null\n    const ds = d2.data('levels')\n    const rows = ds && !ds.loading ? ds.records() : []\n    const listHtml = rows.length\n      ? rows.map((r) => `<div class=\"levelItem ${r._id === levelId ? 'active' : ''}\" data-key=\"${r._id}\" data-id=\"${r._id}\">${esc(r.name)} (${r.width}x${r.height})</div>`).join('')\n      : `<div style=\"color:#666\">Nenhum nível salvo ainda.</div>`\n    const wrapW = width * CELL, wrapH = height * CELL\n    return `<div class=\"app\">\n      <div class=\"sidebar\">\n        <h1>PixelDoom · Editor</h1>\n        <div class=\"field\"><label>Nome do nível</label><input class=\"nameIn\" value=\"${esc(name)}\"/></div>\n        <div class=\"row2\">\n          <div class=\"field\"><label>Largura</label><input class=\"widthIn\" type=\"number\" min=\"4\" max=\"40\" value=\"${width}\"/></div>\n          <div class=\"field\"><label>Altura</label><input class=\"heightIn\" type=\"number\" min=\"4\" max=\"40\" value=\"${height}\"/></div>\n        </div>\n        <div class=\"field\"><label>Ferramentas</label>\n          <div class=\"toolgrid\">\n            ${TOOLS.map((t) => `<button class=\"toolbtn ${tool === t.id ? 'active' : ''}\" data-tool=\"${t.id}\" style=\"--c:${t.sw}\"><span class=\"sw\"></span>${t.label}</button>`).join('')}\n          </div>\n        </div>\n        <div class=\"actions\">\n          <button class=\"btn newBtn\">＋ Novo Nível</button>\n          <button class=\"btn saveBtn\">💾 Salvar</button>\n          <button class=\"btn primary playBtn\">▶ Jogar</button>\n        </div>\n        <div class=\"msg\">${esc(msg)}</div>\n        <div class=\"levelList\">\n          <label>Níveis salvos</label>\n          ${listHtml}\n        </div>\n      </div>\n      <div class=\"canvasArea\">\n        <div class=\"canvasWrap\" style=\"width:${wrapW}px;height:${wrapH}px\">\n          <canvas class=\"grid\" data-d2-static></canvas>\n        </div>\n      </div>\n    </div>`\n  },\n  mounted(d2) {\n    if (d2.state.width === undefined) {\n      d2.setState({\n        levelId: null, name: 'Novo Nível', width: 12, height: 12,\n        grid: blankGrid(12, 12), things: [], playerStart: { x: 1.5, y: 1.5, angle: 0 },\n        tool: '1', msg: 'Escolha uma ferramenta e desenhe o nível.',\n      })\n    }\n\n    let painting = false\n    let draft = null\n\n    const cellFromEvent = (e, cv) => {\n      const rect = cv.getBoundingClientRect()\n      const w = d2.state.width, h = d2.state.height\n      const cx = Math.floor(((e.clientX - rect.left) / rect.width) * w)\n      const cy = Math.floor(((e.clientY - rect.top) / rect.height) * h)\n      return { cx: Math.max(0, Math.min(w - 1, cx)), cy: Math.max(0, Math.min(h - 1, cy)) }\n    }\n\n    const applyTool = (cx, cy, gridRef) => {\n      const s = d2.state\n      const idx = cy * s.width + cx\n      if (s.tool === 'player') {\n        s.playerStart = { x: cx + 0.5, y: cy + 0.5, angle: (s.playerStart && s.playerStart.angle) || 0 }\n        return null\n      }\n      if (s.tool === 'enemy') {\n        const things = (s.things || []).slice()\n        const at = things.findIndex((t) => t.type === 'enemy' && Math.floor(t.x) === cx && Math.floor(t.y) === cy)\n        if (at >= 0) things.splice(at, 1)\n        else things.push({ x: cx + 0.5, y: cy + 0.5, type: 'enemy' })\n        s.things = things\n        return null\n      }\n      const val = Number(s.tool)\n      const g = gridRef.slice()\n      if (val === 5) { for (let i = 0; i < g.length; i++) if (g[i] === 5) g[i] = 0 }\n      g[idx] = val\n      return g\n    }\n\n    d2.on('.grid', 'mousedown', (e, cv) => {\n      const s = d2.state\n      if (s.tool === 'player' || s.tool === 'enemy') {\n        const { cx, cy } = cellFromEvent(e, cv)\n        applyTool(cx, cy, s.grid)\n        drawGrid(cv, d2.state, null)\n        return\n      }\n      painting = true\n      draft = s.grid.slice()\n      const { cx, cy } = cellFromEvent(e, cv)\n      const g2 = applyTool(cx, cy, draft)\n      if (g2) draft = g2\n      drawGrid(cv, d2.state, draft)\n    })\n    d2.on(window, 'mousemove', (e) => {\n      if (!painting) return\n      const cv = d2.$('.grid'); if (!cv) return\n      const { cx, cy } = cellFromEvent(e, cv)\n      const g2 = applyTool(cx, cy, draft)\n      if (g2) draft = g2\n      drawGrid(cv, d2.state, draft)\n    })\n    d2.on(window, 'mouseup', () => {\n      if (painting && draft) d2.state.grid = draft\n      painting = false\n      draft = null\n    })\n\n    d2.on('.toolbtn', 'click', (e, el) => { d2.state.tool = el.dataset.tool })\n    d2.on('.nameIn', 'input', (e, el) => { d2.state.name = el.value })\n    d2.on('.widthIn', 'change', (e, el) => {\n      const nw = Math.max(4, Math.min(40, parseInt(el.value, 10) || 12))\n      const s = d2.state\n      const ng = resizeGrid(s.grid, s.width, s.height, nw, s.height)\n      d2.setState({ grid: ng, width: nw })\n    })\n    d2.on('.heightIn', 'change', (e, el) => {\n      const nh = Math.max(4, Math.min(40, parseInt(el.value, 10) || 12))\n      const s = d2.state\n      const ng = resizeGrid(s.grid, s.width, s.height, s.width, nh)\n      d2.setState({ grid: ng, height: nh })\n    })\n\n    d2.on('.newBtn', 'click', () => {\n      d2.setState({\n        levelId: null, name: 'Novo Nível', width: 12, height: 12,\n        grid: blankGrid(12, 12), things: [], playerStart: { x: 1.5, y: 1.5, angle: 0 },\n        tool: '1', msg: 'Novo nível — comece a desenhar as paredes.',\n      })\n    })\n\n    d2.on('.levelItem', 'click', (e, el) => {\n      const ds = d2.data('levels')\n      const rec = (ds.records() || []).find((r) => r._id === el.dataset.id)\n      if (!rec) return\n      d2.setState({\n        levelId: rec._id, name: rec.name, width: rec.width, height: rec.height,\n        grid: (rec.grid || []).slice(), things: (rec.things || []).map((t) => ({ ...t })),\n        playerStart: rec.playerStart ? { ...rec.playerStart } : { x: 1.5, y: 1.5, angle: 0 },\n        msg: `Carregado: ${rec.name}`,\n      })\n    })\n\n    const doSave = async () => {\n      const s = d2.state\n      const ds = d2.data('levels')\n      const payload = {\n        name: s.name || 'Nível sem nome', width: s.width, height: s.height,\n        grid: s.grid, things: s.things || [], playerStart: s.playerStart || { x: 1.5, y: 1.5, angle: 0 },\n        updatedAt: new Date().toISOString(),\n      }\n      d2.state.msg = 'Salvando…'\n      try {\n        if (s.levelId) {\n          await ds.update({ _id: s.levelId, ...payload })\n        } else {\n          const rec = await ds.create(payload)\n          let newId = (rec && rec._id) || (rec && rec.data && rec.data._id) || null\n          if (!newId) {\n            await ds.refresh()\n            const all = (ds.records() || []).slice().sort((a, b) => (a._id < b._id ? 1 : -1))\n            newId = all[0] && all[0]._id\n          }\n          d2.state.levelId = newId\n        }\n        d2.state.msg = 'Salvo!'\n      } catch (err) {\n        d2.state.msg = 'Erro ao salvar.'\n      }\n    }\n    d2.on('.saveBtn', 'click', () => { doSave() })\n    d2.on('.playBtn', 'click', async () => {\n      await doSave()\n      const store = d2.store('gameStore')\n      if (store) store.set('selectedLevelId', d2.state.levelId)\n      window.location.href = d2.param('playUrl', '/')\n    })\n  },\n  updated(d2) {\n    const cv = d2.$('.grid')\n    if (cv) drawGrid(cv, d2.state, null)\n  },\n}\n"}},"levels":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"levels"}}}},"gameStore":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"gameStore"}}}},"playUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"playUrl"}}}}}}}}}},{"_id":"6a73ee81d5c61d2c1c9a94e7","component":"data2-core-types@Component","system":"6172b957976026c073ab9058","params":{"extends":"data2-core-primitives@Block","component":"pixeldoomPlay","package":"lucca-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 WALL_COLORS = {\n  1: [179, 57, 57],\n  2: [74, 105, 189],\n  3: [56, 173, 169],\n  4: [229, 142, 38],\n}\nconst FLOOR_COLOR = [26, 26, 26]\nconst SKY_COLOR = [40, 46, 66]\nconst RENDER_W = 320\nconst RENDER_H = 200\nconst FOV = (66 * Math.PI) / 180\nconst MINI = 160\n\nfunction isWallCell(v) { return v >= 1 && v <= 4 }\nfunction clamp(v, a, b) { return Math.max(a, Math.min(b, v)) }\n\nexport default {\n  css: `\n    .app{position:absolute;inset:0;background:#000;overflow:hidden;font-family:'Courier New',monospace;color:#e8e8ee}\n    .scene{position:absolute;inset:0;width:100%;height:100%;image-rendering:pixelated;cursor:crosshair}\n    .mini{position:absolute;top:16px;right:16px;width:160px;height:160px;border:2px solid #444;background:#0d0f14;opacity:0.92}\n    .hud{position:absolute;top:16px;left:16px;display:flex;flex-direction:column;gap:4px;pointer-events:none;text-shadow:0 1px 3px #000}\n    .hudName{font-size:14px;letter-spacing:2px;color:#f6e58d;text-transform:uppercase}\n    .hudKills{font-size:13px;color:#78e08f}\n    .crosshair{position:absolute;left:50%;top:50%;width:14px;height:14px;margin:-7px;pointer-events:none}\n    .crosshair::before,.crosshair::after{content:'';position:absolute;background:#fff;opacity:.85}\n    .crosshair::before{left:6px;top:0;width:2px;height:14px}\n    .crosshair::after{left:0;top:6px;width:14px;height:2px}\n    .backBtn{position:absolute;bottom:16px;left:16px;background:#1d2030;border:1px solid #3a3f52;color:#e8e8ee;padding:8px 14px;border-radius:4px;cursor:pointer;font-family:inherit;font-size:12px;z-index:5}\n    .loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#0d0f14;color:#9aa0b4;font-size:14px;letter-spacing:2px;z-index:9}\n    .loading.hide{display:none}\n    .victory{position:absolute;inset:0;display:none;align-items:center;justify-content:center;flex-direction:column;gap:16px;background:rgba(10,12,18,0.9);z-index:10}\n    .victory.show{display:flex}\n    .victory h2{font-size:32px;color:#f6e58d;letter-spacing:4px;margin:0}\n    .victory button{background:#f6e58d;color:#161822;font-weight:700;border:0;padding:10px 20px;border-radius:4px;cursor:pointer;font-family:inherit;font-size:13px}\n    .hint{position:absolute;bottom:16px;right:16px;font-size:10px;color:#666;text-align:right;pointer-events:none}\n  `,\n  render() {\n    return `<div class=\"app\">\n      <canvas class=\"scene\" data-d2-static></canvas>\n      <canvas class=\"mini\" data-d2-static></canvas>\n      <div class=\"crosshair\" data-d2-static></div>\n      <div class=\"hud\">\n        <div class=\"hudName\">Carregando…</div>\n        <div class=\"hudKills\">Abates: 0/0</div>\n      </div>\n      <div class=\"hint\">WASD/Setas move · Q/E strafe · Espaço/Click atira</div>\n      <button class=\"backBtn\">← Voltar ao editor</button>\n      <div class=\"loading\">Carregando nível…</div>\n      <div class=\"victory\">\n        <h2>NÍVEL COMPLETO</h2>\n        <button class=\"victoryBtn\">Voltar ao editor</button>\n      </div>\n    </div>`\n  },\n  mounted(d2) {\n    const keys = {}\n    let level = null\n    let player = { x: 1.5, y: 1.5, angle: 0 }\n    let things = []\n    let kills = 0\n    let totalEnemies = 0\n    let gameWon = false\n    let ready = false\n    let lastT = null\n    const depthBuffer = new Array(RENDER_W).fill(1e9)\n\n    const goEditor = () => { window.location.href = d2.param('editorUrl', '/') }\n    d2.on('.backBtn', 'click', goEditor)\n    d2.on('.victoryBtn', 'click', goEditor)\n\n    function cellAt(gx, gy) {\n      if (gx < 0 || gy < 0 || gx >= level.width || gy >= level.height) return 1\n      return level.grid[gy * level.width + gx] || 0\n    }\n    function canStand(x, y) { return !isWallCell(cellAt(Math.floor(x), Math.floor(y))) }\n\n    function castRay(px, py, angle) {\n      const dirX = Math.cos(angle), dirY = Math.sin(angle)\n      let mapX = Math.floor(px), mapY = Math.floor(py)\n      const deltaDistX = dirX === 0 ? 1e30 : Math.abs(1 / dirX)\n      const deltaDistY = dirY === 0 ? 1e30 : Math.abs(1 / dirY)\n      let stepX, stepY, sideDistX, sideDistY\n      if (dirX < 0) { stepX = -1; sideDistX = (px - mapX) * deltaDistX } else { stepX = 1; sideDistX = (mapX + 1 - px) * deltaDistX }\n      if (dirY < 0) { stepY = -1; sideDistY = (py - mapY) * deltaDistY } else { stepY = 1; sideDistY = (mapY + 1 - py) * deltaDistY }\n      let side = 0, cellVal = 0\n      for (let i = 0; i < 64; i++) {\n        if (sideDistX < sideDistY) { sideDistX += deltaDistX; mapX += stepX; side = 0 } else { sideDistY += deltaDistY; mapY += stepY; side = 1 }\n        cellVal = cellAt(mapX, mapY)\n        if (isWallCell(cellVal)) break\n      }\n      let dist\n      if (side === 0) dist = (mapX - px + (1 - stepX) / 2) / (dirX || 1e-6)\n      else dist = (mapY - py + (1 - stepY) / 2) / (dirY || 1e-6)\n      return { dist: Math.max(dist, 0.0001), side, cellVal }\n    }\n\n    function shadeRGB(rgb, mult, fogT) {\n      const r = rgb[0] * mult, g = rgb[1] * mult, b = rgb[2] * mult\n      return [\n        r + (FLOOR_COLOR[0] - r) * fogT,\n        g + (FLOOR_COLOR[1] - g) * fogT,\n        b + (FLOOR_COLOR[2] - b) * fogT,\n      ]\n    }\n\n    function shoot() {\n      if (!ready || gameWon) return\n      const wallDist = castRay(player.x, player.y, player.angle).dist\n      let best = null, bestAngle = 999\n      for (const e of things) {\n        if (e.type !== 'enemy' || !e.alive) continue\n        const ex = e.x - player.x, ey = e.y - player.y\n        const dist = Math.hypot(ex, ey)\n        let ang = Math.atan2(ey, ex) - player.angle\n        while (ang > Math.PI) ang -= 2 * Math.PI\n        while (ang < -Math.PI) ang += 2 * Math.PI\n        if (Math.abs(ang) < 0.14 && dist < wallDist + 0.6 && Math.abs(ang) < bestAngle) { bestAngle = Math.abs(ang); best = e }\n      }\n      if (best) { best.alive = false; kills += 1 }\n    }\n    d2.on('.scene', 'click', shoot)\n    d2.on(window, 'keydown', (e) => {\n      const c = e.code\n      if (c === 'KeyW' || c === 'ArrowUp') keys.forward = true\n      else if (c === 'KeyS' || c === 'ArrowDown') keys.back = true\n      else if (c === 'KeyA' || c === 'ArrowLeft') keys.rotLeft = true\n      else if (c === 'KeyD' || c === 'ArrowRight') keys.rotRight = true\n      else if (c === 'KeyQ') keys.strafeLeft = true\n      else if (c === 'KeyE') keys.strafeRight = true\n      else if (c === 'Space') { e.preventDefault(); shoot() }\n    })\n    d2.on(window, 'keyup', (e) => {\n      const c = e.code\n      if (c === 'KeyW' || c === 'ArrowUp') keys.forward = false\n      else if (c === 'KeyS' || c === 'ArrowDown') keys.back = false\n      else if (c === 'KeyA' || c === 'ArrowLeft') keys.rotLeft = false\n      else if (c === 'KeyD' || c === 'ArrowRight') keys.rotRight = false\n      else if (c === 'KeyQ') keys.strafeLeft = false\n      else if (c === 'KeyE') keys.strafeRight = false\n    })\n\n    async function loadLevel() {\n      const store = d2.store('gameStore')\n      const ds = d2.data('levels')\n      await ds.refresh()\n      const selId = store ? store.get('selectedLevelId') : null\n      const rows = ds.records() || []\n      let rec = selId ? rows.find((r) => r._id === selId) : null\n      if (!rec) rec = rows[0]\n      if (!rec) {\n        const nameEl = d2.$('.hudName'); if (nameEl) nameEl.textContent = 'Nenhum nível encontrado'\n        return\n      }\n      level = { width: rec.width, height: rec.height, grid: rec.grid || [], name: rec.name || 'Nível' }\n      things = (rec.things || []).map((t) => ({ ...t, alive: true }))\n      const ps = rec.playerStart || { x: 1.5, y: 1.5, angle: 0 }\n      player = { x: ps.x, y: ps.y, angle: ps.angle || 0 }\n      kills = 0\n      totalEnemies = things.filter((t) => t.type === 'enemy').length\n      gameWon = false\n      const loadingEl = d2.$('.loading'); if (loadingEl) loadingEl.classList.add('hide')\n      const nameEl = d2.$('.hudName'); if (nameEl) nameEl.textContent = level.name\n      const killsEl = d2.$('.hudKills'); if (killsEl) killsEl.textContent = `Abates: 0/${totalEnemies}`\n      ready = true\n    }\n\n    function render3D(ctx) {\n      const img = ctx.createImageData(RENDER_W, RENDER_H)\n      for (let col = 0; col < RENDER_W; col++) {\n        const rayAngle = player.angle - FOV / 2 + (FOV * col) / RENDER_W\n        const { dist, side, cellVal } = castRay(player.x, player.y, rayAngle)\n        const perp = dist * Math.cos(rayAngle - player.angle)\n        depthBuffer[col] = perp\n        const lineH = Math.min(RENDER_H * 3, RENDER_H / Math.max(perp, 0.0001))\n        const drawStart = Math.floor(RENDER_H / 2 - lineH / 2)\n        const drawEnd = Math.floor(RENDER_H / 2 + lineH / 2)\n        const fogT = clamp(perp / 11, 0, 1)\n        const mult = side === 1 ? 1 : 0.7\n        const wc = shadeRGB(WALL_COLORS[cellVal] || WALL_COLORS[1], mult, fogT)\n        for (let y = 0; y < RENDER_H; y++) {\n          let color\n          if (y < drawStart) color = SKY_COLOR\n          else if (y > drawEnd) color = FLOOR_COLOR\n          else color = wc\n          const idx = (y * RENDER_W + col) * 4\n          img.data[idx] = color[0] | 0\n          img.data[idx + 1] = color[1] | 0\n          img.data[idx + 2] = color[2] | 0\n          img.data[idx + 3] = 255\n        }\n      }\n      ctx.putImageData(img, 0, 0)\n      const alive = things.filter((t) => t.type === 'enemy' && t.alive)\n      alive.sort((a, b) => {\n        const da = Math.hypot(a.x - player.x, a.y - player.y)\n        const db = Math.hypot(b.x - player.x, b.y - player.y)\n        return db - da\n      })\n      ctx.textAlign = 'center'\n      ctx.textBaseline = 'middle'\n      alive.forEach((e) => {\n        const ex = e.x - player.x, ey = e.y - player.y\n        const dist = Math.hypot(ex, ey)\n        let ang = Math.atan2(ey, ex) - player.angle\n        while (ang > Math.PI) ang -= 2 * Math.PI\n        while (ang < -Math.PI) ang += 2 * Math.PI\n        if (Math.abs(ang) > FOV / 2 + 0.4 || dist < 0.15) return\n        const screenX = (0.5 + Math.tan(ang) / Math.tan(FOV / 2) / 2) * RENDER_W\n        const col = Math.floor(screenX)\n        if (col < -20 || col > RENDER_W + 20) return\n        const cCol = clamp(col, 0, RENDER_W - 1)\n        if (dist > depthBuffer[cCol] + 0.35) return\n        const size = clamp(RENDER_H / dist, 10, RENDER_H * 1.6)\n        const fogT = clamp(dist / 11, 0, 1)\n        ctx.globalAlpha = 1 - fogT * 0.7\n        ctx.font = `${size}px sans-serif`\n        ctx.fillText('👹', screenX, RENDER_H / 2 + size * 0.08)\n        ctx.globalAlpha = 1\n      })\n    }\n\n    function renderMini(ctx) {\n      ctx.clearRect(0, 0, MINI, MINI)\n      ctx.fillStyle = '#0d0f14'\n      ctx.fillRect(0, 0, MINI, MINI)\n      if (!level) return\n      const cs = Math.min(MINI / level.width, MINI / level.height)\n      for (let y = 0; y < level.height; y++) {\n        for (let x = 0; x < level.width; x++) {\n          const v = level.grid[y * level.width + x] || 0\n          if (v === 0) continue\n          ctx.fillStyle = v === 5 ? 'rgb(246,229,141)' : '#666'\n          ctx.fillRect(x * cs, y * cs, cs, cs)\n        }\n      }\n      things.filter((t) => t.type === 'enemy' && t.alive).forEach((e) => {\n        ctx.fillStyle = '#e84118'\n        ctx.beginPath(); ctx.arc(e.x * cs, e.y * cs, Math.max(2, cs * 0.3), 0, 7); ctx.fill()\n      })\n      ctx.save()\n      ctx.translate(player.x * cs, player.y * cs)\n      ctx.rotate(player.angle)\n      ctx.fillStyle = '#00e6e6'\n      ctx.beginPath(); ctx.moveTo(6, 0); ctx.lineTo(-4, 4); ctx.lineTo(-4, -4); ctx.closePath(); ctx.fill()\n      ctx.restore()\n    }\n\n    function checkWin() {\n      if (gameWon || !ready) return\n      if (kills >= totalEnemies) {\n        const onExit = cellAt(Math.floor(player.x), Math.floor(player.y)) === 5\n        if (onExit) {\n          gameWon = true\n          const v = d2.$('.victory'); if (v) v.classList.add('show')\n        }\n      }\n    }\n\n    const scene = d2.$('.scene')\n    const mini = d2.$('.mini')\n    if (!scene || !mini) return\n    scene.width = RENDER_W\n    scene.height = RENDER_H\n    const sctx = scene.getContext('2d')\n    mini.width = MINI\n    mini.height = MINI\n    const mctx = mini.getContext('2d')\n\n    loadLevel()\n\n    d2.raf((t) => {\n      if (lastT == null) lastT = t\n      const dt = Math.min((t - lastT) / 1000, 0.05)\n      lastT = t\n      if (!ready) return\n      if (!gameWon) {\n        const moveSpeed = 3, rotSpeed = 2.4, strafeSpeed = 2.6\n        let dx = 0, dy = 0\n        if (keys.forward) { dx += Math.cos(player.angle) * moveSpeed * dt; dy += Math.sin(player.angle) * moveSpeed * dt }\n        if (keys.back) { dx -= Math.cos(player.angle) * moveSpeed * dt; dy -= Math.sin(player.angle) * moveSpeed * dt }\n        if (keys.strafeLeft) { dx += Math.cos(player.angle - Math.PI / 2) * strafeSpeed * dt; dy += Math.sin(player.angle - Math.PI / 2) * strafeSpeed * dt }\n        if (keys.strafeRight) { dx += Math.cos(player.angle + Math.PI / 2) * strafeSpeed * dt; dy += Math.sin(player.angle + Math.PI / 2) * strafeSpeed * dt }\n        if (keys.rotLeft) player.angle -= rotSpeed * dt\n        if (keys.rotRight) player.angle += rotSpeed * dt\n        if (dx !== 0) { const nx = player.x + dx; if (canStand(nx + Math.sign(dx) * 0.2, player.y)) player.x = nx }\n        if (dy !== 0) { const ny = player.y + dy; if (canStand(player.x, ny + Math.sign(dy) * 0.2)) player.y = ny }\n        checkWin()\n      }\n      render3D(sctx)\n      renderMini(mctx)\n      const killsEl = d2.$('.hudKills')\n      if (killsEl) killsEl.textContent = `Abates: ${kills}/${totalEnemies}`\n    })\n  },\n}\n"}},"levels":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"levels"}}}},"gameStore":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"gameStore"}}}},"editorUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"editorUrl"}}}}}}}}}},{"_id":"6abc78f94d5ed83db05a347c","component":"data2-core-types@Component","system":"6172b957976026c073ab9058","params":{"extends":"data2-core-primitives@Block","component":"yoink-canvas-stage","package":"lucca-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: `\n    :host { all: initial; }\n    canvas.stage { position:absolute; inset:0; width:100%; height:100%; display:block; }\n  `,\n  render() {\n    return `<canvas class=\"stage\"></canvas>`\n  },\n  mounted(d2) {\n    const canvas = d2.$('.stage')\n    let gpu = null\n    let ctx2d = null\n    let status = 'idle'\n\n    const unsub = d2.bus.on('yoink:status', (s) => { status = s || 'idle' })\n\n    const WGSL = `\nstruct Uniforms { resolution: vec2<f32>, pad: vec2<f32> };\n@group(0) @binding(0) var<uniform> u: Uniforms;\nstruct VOut {\n  @builtin(position) pos: vec4<f32>,\n  @location(0) uv: vec2<f32>,\n  @location(1) size: vec2<f32>,\n  @location(2) fill: vec4<f32>,\n  @location(3) border: vec4<f32>,\n  @location(4) borderPx: f32,\n};\n@vertex\nfn vs(\n  @location(0) corner: vec2<f32>,\n  @location(1) iOffset: vec2<f32>,\n  @location(2) iSize: vec2<f32>,\n  @location(3) iFill: vec4<f32>,\n  @location(4) iBorder: vec4<f32>,\n  @location(5) iBorderPx: f32,\n) -> VOut {\n  let px = iOffset + corner * iSize;\n  let clip = vec2<f32>(\n    (px.x / u.resolution.x) * 2.0 - 1.0,\n    1.0 - (px.y / u.resolution.y) * 2.0\n  );\n  var out: VOut;\n  out.pos = vec4<f32>(clip, 0.0, 1.0);\n  out.uv = corner;\n  out.size = iSize;\n  out.fill = iFill;\n  out.border = iBorder;\n  out.borderPx = iBorderPx;\n  return out;\n}\n@fragment\nfn fs(in: VOut) -> @location(0) vec4<f32> {\n  let bx = in.borderPx / max(in.size.x, 1.0);\n  let by = in.borderPx / max(in.size.y, 1.0);\n  let edge = in.uv.x < bx || in.uv.x > (1.0 - bx) || in.uv.y < by || in.uv.y > (1.0 - by);\n  if (edge) { return in.border; }\n  return in.fill;\n}\n    `\n\n    async function initGPU() {\n      if (!navigator.gpu) return false\n      try {\n        const adapter = await navigator.gpu.requestAdapter()\n        if (!adapter) return false\n        const device = await adapter.requestDevice()\n        const context = canvas.getContext('webgpu')\n        if (!context) return false\n        const format = navigator.gpu.getPreferredCanvasFormat()\n        context.configure({ device, format, alphaMode: 'premultiplied' })\n        const shaderModule = device.createShaderModule({ code: WGSL })\n        const pipeline = device.createRenderPipeline({\n          layout: 'auto',\n          vertex: {\n            module: shaderModule,\n            entryPoint: 'vs',\n            buffers: [\n              { arrayStride: 8, stepMode: 'vertex', attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x2' }] },\n              { arrayStride: 52, stepMode: 'instance', attributes: [\n                { shaderLocation: 1, offset: 0, format: 'float32x2' },\n                { shaderLocation: 2, offset: 8, format: 'float32x2' },\n                { shaderLocation: 3, offset: 16, format: 'float32x4' },\n                { shaderLocation: 4, offset: 32, format: 'float32x4' },\n                { shaderLocation: 5, offset: 48, format: 'float32' },\n              ] },\n            ],\n          },\n          fragment: { module: shaderModule, entryPoint: 'fs', targets: [{ format }] },\n          primitive: { topology: 'triangle-list' },\n        })\n        const quad = new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1])\n        const quadBuffer = device.createBuffer({ size: quad.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST })\n        device.queue.writeBuffer(quadBuffer, 0, quad)\n        const uniformBuffer = device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST })\n        const bindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: uniformBuffer } }] })\n        const instanceBuffer = device.createBuffer({ size: 52 * 8, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST })\n        gpu = { device, context, pipeline, quadBuffer, uniformBuffer, bindGroup, instanceBuffer }\n        return true\n      } catch (e) {\n        return false\n      }\n    }\n\n    function layoutFor(key, cssW) {\n      const pad = 20\n      const w = Math.max(0, cssW - pad * 2)\n      if (key === 'login') {\n        return [\n          { x: pad, y: 130, w, h: 54 },\n          { x: pad, y: 198, w, h: 54 },\n          { x: pad, y: 266, w, h: 54, isAction: true },\n        ]\n      }\n      return [\n        { x: pad, y: 140, w, h: 54 },\n        { x: pad, y: 208, w, h: 54, isAction: true },\n        { x: pad, y: 300, w, h: 84 },\n      ]\n    }\n\n    function colorFor(rect, status) {\n      const ink = [0.10, 0.10, 0.10, 1]\n      const faint = [0.10, 0.10, 0.10, 0.15]\n      const paper = [0.961, 0.961, 0.957, 1]\n      const err = [0.75, 0.22, 0.17, 1]\n      if (rect.isAction && status === 'loading') return { fill: faint, border: ink, borderPx: 2 }\n      if (status === 'error') return { fill: paper, border: err, borderPx: 1 }\n      return { fill: paper, border: ink, borderPx: 1 }\n    }\n\n    function rectsFor(cssW, key, status) {\n      return layoutFor(key, cssW).map((r) => ({ ...r, ...colorFor(r, status) }))\n    }\n\n    function draw2D(cssW, cssH, dpr, rects) {\n      if (!ctx2d) ctx2d = canvas.getContext('2d')\n      const ctx = ctx2d\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n      ctx.clearRect(0, 0, cssW, cssH)\n      ctx.fillStyle = '#f5f5f3'\n      ctx.fillRect(0, 0, cssW, cssH)\n      for (const r of rects) {\n        ctx.fillStyle = `rgba(${r.fill[0] * 255}, ${r.fill[1] * 255}, ${r.fill[2] * 255}, ${r.fill[3]})`\n        ctx.fillRect(r.x, r.y, r.w, r.h)\n        ctx.strokeStyle = `rgba(${r.border[0] * 255}, ${r.border[1] * 255}, ${r.border[2] * 255}, ${r.border[3]})`\n        ctx.lineWidth = r.borderPx\n        ctx.strokeRect(r.x + r.borderPx / 2, r.y + r.borderPx / 2, Math.max(0, r.w - r.borderPx), Math.max(0, r.h - r.borderPx))\n      }\n    }\n\n    function drawGPU(rects, resW, resH, dpr) {\n      const { device, context, pipeline, quadBuffer, uniformBuffer, bindGroup, instanceBuffer } = gpu\n      device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([resW, resH, 0, 0]))\n      const data = new Float32Array(13 * Math.max(1, rects.length))\n      rects.forEach((r, i) => {\n        const o = i * 13\n        data[o + 0] = r.x * dpr; data[o + 1] = r.y * dpr\n        data[o + 2] = r.w * dpr; data[o + 3] = r.h * dpr\n        data[o + 4] = r.fill[0]; data[o + 5] = r.fill[1]; data[o + 6] = r.fill[2]; data[o + 7] = r.fill[3]\n        data[o + 8] = r.border[0]; data[o + 9] = r.border[1]; data[o + 10] = r.border[2]; data[o + 11] = r.border[3]\n        data[o + 12] = r.borderPx * dpr\n      })\n      device.queue.writeBuffer(instanceBuffer, 0, data)\n      const encoder = device.createCommandEncoder()\n      const pass = encoder.beginRenderPass({\n        colorAttachments: [{ view: context.getCurrentTexture().createView(), clearValue: { r: 0.961, g: 0.961, b: 0.957, a: 1 }, loadOp: 'clear', storeOp: 'store' }],\n      })\n      pass.setPipeline(pipeline)\n      pass.setVertexBuffer(0, quadBuffer)\n      pass.setVertexBuffer(1, instanceBuffer)\n      pass.setBindGroup(0, bindGroup)\n      pass.draw(6, rects.length)\n      pass.end()\n      device.queue.submit([encoder.finish()])\n    }\n\n    function sizeCanvas() {\n      const rect = canvas.getBoundingClientRect()\n      const dpr = Math.max(1, window.devicePixelRatio || 1)\n      const w = Math.max(1, Math.round(rect.width * dpr))\n      const h = Math.max(1, Math.round(rect.height * dpr))\n      if (canvas.width !== w || canvas.height !== h) { canvas.width = w; canvas.height = h }\n      return { cssW: rect.width || 1, cssH: rect.height || 1, dpr }\n    }\n\n    function draw() {\n      const key = d2.param('layoutKey') || 'downloader'\n      const { cssW, cssH, dpr } = sizeCanvas()\n      const rects = rectsFor(cssW, key, status)\n      if (gpu) drawGPU(rects, canvas.width, canvas.height, dpr)\n      else draw2D(cssW, cssH, dpr, rects)\n    }\n\n    const ro = new ResizeObserver(() => draw())\n    ro.observe(canvas)\n    d2.onUnmount(() => { ro.disconnect(); unsub() })\n\n    initGPU().finally(() => draw())\n    d2.raf(() => { draw(); return true })\n  },\n}\n"}},"layoutKey":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layoutKey"}}}}}}}}}},{"_id":"6abc78fa06969715dec69c80","component":"data2-core-types@Component","system":"6172b957976026c073ab9058","params":{"extends":"data2-core-primitives@Block","component":"yoink-downloader","package":"lucca-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: `\n    :host { all: initial; }\n    .app { position:relative; width:100%; height:100%; box-sizing:border-box; font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; color:#1a1a1a; }\n    .top { position:absolute; top:24px; left:20px; right:20px; display:flex; align-items:baseline; justify-content:space-between; }\n    .brand { font-size:12px; letter-spacing:3px; text-transform:uppercase; opacity:.5; }\n    .signout { font-size:11px; opacity:.5; background:none; border:none; cursor:pointer; font-family:inherit; text-decoration:underline; padding:0; }\n    h1 { position:absolute; top:56px; left:20px; right:20px; font-size:22px; margin:0; font-weight:600; }\n    .sub { position:absolute; top:86px; left:20px; right:20px; font-size:13px; opacity:.55; margin:0; }\n    .field { position:absolute; left:20px; right:20px; top:140px; height:54px; display:flex; align-items:center; }\n    input.url { width:100%; box-sizing:border-box; background:transparent; border:none; outline:none; font:inherit; font-size:15px; padding:0 16px; color:#1a1a1a; }\n    input.url::placeholder { color:#1a1a1a; opacity:.35; }\n    button.go { position:absolute; left:20px; right:20px; top:208px; height:54px; font:inherit; font-weight:600; font-size:15px; letter-spacing:.5px; background:transparent; border:none; cursor:pointer; color:#1a1a1a; }\n    button.go:disabled { opacity:.4; cursor:default; }\n    .status { position:absolute; left:20px; right:20px; top:272px; font-size:13px; opacity:.7; }\n    .status.err { color:#c0392b; opacity:1; }\n    .result { position:absolute; left:20px; right:20px; top:300px; height:84px; padding:16px; box-sizing:border-box; display:flex; flex-direction:column; justify-content:center; gap:8px; }\n    .result .name { font-size:13px; word-break:break-all; }\n    a.dl { font:inherit; font-weight:600; font-size:14px; text-decoration:none; color:#1a1a1a; border-bottom:1px solid currentColor; align-self:flex-start; padding-bottom:2px; }\n    .hint { position:absolute; bottom:20px; left:0; right:0; font-size:11px; opacity:.4; text-align:center; }\n  `,\n  render(d2) {\n    const esc = (s) => String(s == null ? '' : s).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]))\n    const status = d2.state.status || 'idle'\n    const busy = status === 'loading'\n    const result = d2.state.result\n    const auth = d2.auth('authStrategy')\n    const email = auth && auth.user && auth.user.email\n    return `\n      <div class=\"app\">\n        <div class=\"top\">\n          <div class=\"brand\">yoink${email ? ' · ' + esc(email) : ''}</div>\n          <button class=\"signout\">sign out</button>\n        </div>\n        <h1>Paste a link.</h1>\n        <p class=\"sub\">Any public video/audio URL. We fetch a direct download.</p>\n        <div class=\"field\"><input class=\"url\" type=\"url\" placeholder=\"https://…\" value=\"${esc(d2.state.url)}\" /></div>\n        <button class=\"go\" ${busy ? 'disabled' : ''}>${busy ? 'Fetching…' : 'Yoink it'}</button>\n        <div class=\"status ${status === 'error' ? 'err' : ''}\">${esc(d2.state.message)}</div>\n        <div class=\"result\" style=\"${result ? '' : 'visibility:hidden'}\">\n          <div class=\"name\">${result ? esc(result.filename || 'download') : ''}</div>\n          ${result ? `<a class=\"dl\" href=\"${esc(result.url)}\" target=\"_blank\" rel=\"noopener\">Open download ↗</a>` : ''}\n        </div>\n        <div class=\"hint\">wireframe · v0 · canvas/webgpu stage</div>\n      </div>\n    `\n  },\n  mounted(d2) {\n    d2.on('.url', 'input', (e) => { d2.state.url = e.target.value })\n    d2.on('.url', 'keydown', (e) => { if (e.key === 'Enter') go() })\n\n    async function go() {\n      const url = (d2.state.url || '').trim()\n      if (!url) { d2.setState({ status: 'error', message: 'Paste a URL first.' }); d2.bus.emit('yoink:status', 'error'); return }\n      d2.setState({ status: 'loading', message: '', result: null })\n      d2.bus.emit('yoink:status', 'loading')\n      try {\n        const res = await d2.rush('extractRush').invoke({ url })\n        let payload = res\n        if (payload && typeof payload === 'object' && payload.status === undefined && payload.data !== undefined) payload = payload.data\n        if (!payload || payload.status === 'error') {\n          const err = payload && payload.error\n          const msg = (err && (err.code || err.message)) || 'Could not fetch that link.'\n          d2.setState({ status: 'error', message: String(msg), result: null })\n          d2.bus.emit('yoink:status', 'error')\n          return\n        }\n        d2.setState({ status: 'success', message: 'Ready.', result: { url: payload.url, filename: payload.filename } })\n        d2.bus.emit('yoink:status', 'success')\n      } catch (e) {\n        d2.setState({ status: 'error', message: (e && e.message) || 'Request failed.', result: null })\n        d2.bus.emit('yoink:status', 'error')\n      }\n    }\n    d2.on('.go', 'click', go)\n\n    d2.on('.signout', 'click', async () => {\n      try { await d2.auth('authStrategy').logout() } catch (e) {}\n      d2.bus.emit('yoink:status', 'idle')\n      d2.router('router').goTo('/login')\n    })\n  },\n}\n"}},"extractRush":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"extractRush"}}}},"authStrategy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"authStrategy"}}}},"router":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"router"}}}}}}}}}},{"_id":"6abeecc5ba309d7a9face034","component":"data2-core-types@Component","system":"6172b957976026c073ab9058","params":{"extends":"data2-core-primitives@Block","component":"Rubik_s_Cube","package":"lucca-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: `\n    :host { display: block; width: 100%; height: 100%; }\n    .rb { position: relative; width: 100%; height: 100%; min-height: 480px; overflow: hidden;\n      background: radial-gradient(120% 80% at 50% 30%, #2a2f45 0%, #12141f 70%);\n      color: #f3f4f8; font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n      touch-action: none; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent;\n      display: flex; flex-direction: column; }\n    .hud { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px 0; }\n    .title { font-weight: 700; font-size: 18px; letter-spacing: .3px; }\n    .stats { display: flex; gap: 16px; font-variant-numeric: tabular-nums; font-size: 14px; opacity: .85; }\n    .stats b { font-weight: 600; }\n    .stage { flex: 1; position: relative; display: flex; align-items: center; justify-content: center; }\n    .cube { position: relative; width: 0; height: 0; transform-style: preserve-3d; }\n    .cubie { position: absolute; transform-style: preserve-3d; }\n    .f { position: absolute; left: 0; top: 0; width: var(--s); height: var(--s); margin: calc(var(--s) / -2) 0 0 calc(var(--s) / -2);\n      background: #0b0c10; backface-visibility: hidden; -webkit-backface-visibility: hidden; box-sizing: border-box; }\n    .f.c::after { content: \"\"; position: absolute; inset: 6%; border-radius: 14%; background: var(--c);\n      box-shadow: inset 0 -2px 4px rgba(0,0,0,.25), inset 0 2px 3px rgba(255,255,255,.25); }\n    .hint { text-align: center; font-size: 13px; opacity: .55; padding: 0 24px; min-height: 18px; }\n    .bar { display: flex; gap: 10px; justify-content: center; padding: 14px 16px calc(18px + env(safe-area-inset-bottom)); }\n    .bar button { flex: 1; max-width: 140px; height: 48px; border: 0; border-radius: 14px; font-size: 15px; font-weight: 600;\n      color: #f3f4f8; background: rgba(255,255,255,.1); touch-action: manipulation; }\n    .bar button.primary { background: #4f7cff; }\n    .bar button:active { transform: scale(.97); }\n    .win { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; pointer-events: none; }\n    .win.show { display: flex; }\n    .win div { background: rgba(18,20,31,.85); padding: 18px 26px; border-radius: 18px; text-align: center; font-size: 15px;\n      box-shadow: 0 10px 40px rgba(0,0,0,.4); animation: pop .35s ease-out; }\n    .win strong { display: block; font-size: 22px; margin-bottom: 4px; }\n    @keyframes pop { from { transform: scale(.8); opacity: 0; } to { transform: scale(1); opacity: 1; } }\n  `,\n\n  render() {\n    return `\n      <div class=\"rb\" data-d2-static>\n        <div class=\"hud\">\n          <div class=\"title\">Cube</div>\n          <div class=\"stats\"><span>Moves <b class=\"moves\">0</b></span><span><b class=\"time\">0:00</b></span></div>\n        </div>\n        <div class=\"stage\"><div class=\"cube\"></div><div class=\"win\"><div><strong>Solved!</strong><span class=\"winmsg\"></span></div></div></div>\n        <div class=\"hint\">Drag the background to spin · swipe a face to turn it</div>\n        <div class=\"bar\">\n          <button class=\"undo\">Undo</button>\n          <button class=\"reset\">Reset</button>\n          <button class=\"scramble primary\">Scramble</button>\n        </div>\n      </div>`;\n  },\n\n  mounted(d2, root) {\n    const $ = (s) => root.querySelector(s);\n    const rb = $('.rb'), stage = $('.stage'), cubeEl = $('.cube');\n    if (!rb || rb.__init) return;\n    rb.__init = true;\n\n    // ---------- math (3x3 row-major, CSS coords: x right, y down, z toward viewer) ----------\n    const I = () => [[1,0,0],[0,1,0],[0,0,1]];\n    const mul = (A, B) => A.map((r) => [0,1,2].map((c) => r[0]*B[0][c] + r[1]*B[1][c] + r[2]*B[2][c]));\n    const mv = (A, v) => A.map((r) => r[0]*v[0] + r[1]*v[1] + r[2]*v[2]);\n    const cross = (a, b) => [a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0]];\n    const dot = (a, b) => a[0]*b[0] + a[1]*b[1] + a[2]*b[2];\n    const rnd = (v) => v.map((x) => Math.round(x));\n    const rot = (a, t) => {\n      const [x, y, z] = a, c = Math.cos(t), s = Math.sin(t), C = 1 - c;\n      return [[c+x*x*C, x*y*C-z*s, x*z*C+y*s], [y*x*C+z*s, c+y*y*C, y*z*C-x*s], [z*x*C-y*s, z*y*C+x*s, c+z*z*C]];\n    };\n    const css = (M, t = [0,0,0]) =>\n      `matrix3d(${M[0][0]},${M[1][0]},${M[2][0]},0,${M[0][1]},${M[1][1]},${M[2][1]},0,${M[0][2]},${M[1][2]},${M[2][2]},0,${t[0]},${t[1]},${t[2]},1)`;\n\n    // ---------- cube model ----------\n    const FACES = [ // local normal, css placement, color, test on solved position\n      { n: [0,0,1],  t: 'translateZ(calc(var(--s)/2))',                  c: '#2ecc71', k: (p) => p[2] === 1 },\n      { n: [0,0,-1], t: 'rotateY(180deg) translateZ(calc(var(--s)/2))',  c: '#3498ff', k: (p) => p[2] === -1 },\n      { n: [1,0,0],  t: 'rotateY(90deg) translateZ(calc(var(--s)/2))',   c: '#ff4757', k: (p) => p[0] === 1 },\n      { n: [-1,0,0], t: 'rotateY(-90deg) translateZ(calc(var(--s)/2))',  c: '#ff9f1a', k: (p) => p[0] === -1 },\n      { n: [0,-1,0], t: 'rotateX(90deg) translateZ(calc(var(--s)/2))',   c: '#f5f6fa', k: (p) => p[1] === -1 },\n      { n: [0,1,0],  t: 'rotateX(-90deg) translateZ(calc(var(--s)/2))',  c: '#ffd32a', k: (p) => p[1] === 1 },\n    ];\n    let S = 60, cubies = [];\n    let view = mul(rot([1,0,0], -0.5), rot([0,1,0], -0.65));\n\n    const build = () => {\n      cubeEl.innerHTML = '';\n      cubies = [];\n      for (let x = -1; x <= 1; x++) for (let y = -1; y <= 1; y++) for (let z = -1; z <= 1; z++) {\n        if (!x && !y && !z) continue;\n        const el = document.createElement('div');\n        el.className = 'cubie';\n        const home = [x, y, z];\n        FACES.forEach((F, i) => {\n          const f = document.createElement('div');\n          const colored = F.k(home);\n          f.className = 'f' + (colored ? ' c' : '');\n          f.dataset.fi = i;\n          f.style.transform = F.t;\n          if (colored) f.style.setProperty('--c', F.c);\n          el.appendChild(f);\n        });\n        const cb = { pos: home.slice(), R: I(), el };\n        el.__cb = cb;\n        cubies.push(cb);\n        cubeEl.appendChild(el);\n      }\n      paintAll();\n    };\n    const paint = (cb, A = null) => {\n      const M = A ? mul(A, cb.R) : cb.R;\n      const p = A ? mv(A, cb.pos) : cb.pos;\n      cb.el.style.transform = css(M, p.map((v) => v * S));\n    };\n    const paintAll = () => cubies.forEach((c) => paint(c));\n    const paintView = () => { cubeEl.style.transform = css(view); };\n\n    const layout = () => {\n      const r = stage.getBoundingClientRect();\n      const span = Math.min(r.width * 0.62, r.height * 0.55, 420);\n      S = Math.max(24, span / 3);\n      rb.style.setProperty('--s', S + 'px');\n      stage.style.perspective = S * 14 + 'px';\n      paintAll();\n    };\n\n    // ---------- moves ----------\n    let busy = false, queue = [], history = [], moves = 0, startT = 0, timerId = 0, scrambled = false;\n    const movesEl = $('.moves'), timeEl = $('.time'), winEl = $('.win'), winMsg = $('.winmsg');\n    const fmt = (ms) => { const s = Math.floor(ms / 1000); return Math.floor(s / 60) + ':' + String(s % 60).padStart(2, '0'); };\n    const tick = () => { if (startT) timeEl.textContent = fmt(Date.now() - startT); };\n\n    const layerOf = (axis, idx) => cubies.filter((c) => Math.round(dot(c.pos, axis)) === idx);\n    const commit = (axis, idx, q) => {\n      const A = rot(axis, q * Math.PI / 2);\n      layerOf(axis, idx).forEach((c) => { c.pos = rnd(mv(A, c.pos)); c.R = mul(A, c.R).map(rnd); paint(c); });\n    };\n    const animate = (layer, axis, from, to, ms, done) => {\n      const t0 = performance.now();\n      const step = (now) => {\n        const k = Math.min(1, (now - t0) / ms), e = 1 - Math.pow(1 - k, 3);\n        const A = rot(axis, from + (to - from) * e);\n        layer.forEach((c) => paint(c, A));\n        if (k < 1) requestAnimationFrame(step); else done();\n      };\n      requestAnimationFrame(step);\n    };\n    // move = { axis, idx, q } ; q in {-2, -1, 1, 2}\n    const run = () => {\n      if (busy || !queue.length) return;\n      busy = true;\n      const m = queue.shift();\n      animate(layerOf(m.axis, m.idx), m.axis, m.from || 0, m.q * Math.PI / 2, m.ms || 200, () => {\n        commit(m.axis, m.idx, m.q);\n        busy = false;\n        if (m.user) onUserMove(m);\n        run();\n      });\n    };\n    const push = (m) => { queue.push(m); run(); };\n\n    const onUserMove = (m) => {\n      if (!m.undo) history.push(m);\n      moves += m.undo ? -1 : 1;\n      movesEl.textContent = Math.max(0, moves);\n      try { navigator.vibrate && navigator.vibrate(8); } catch (e) {}\n      if (scrambled && !startT && !m.undo) { startT = Date.now(); timerId = setInterval(tick, 250); }\n      checkSolved();\n    };\n    const isSolved = () => {\n      const seen = {};\n      for (const c of cubies) for (const [i, F] of FACES.entries()) {\n        if (!c.el.children[i].classList.contains('c')) continue;\n        const n = rnd(mv(c.R, F.n)).join(',');\n        (seen[n] = seen[n] || new Set()).add(F.c);\n      }\n      return Object.values(seen).every((s) => s.size === 1);\n    };\n    const checkSolved = () => {\n      if (!scrambled || !isSolved()) return;\n      scrambled = false;\n      clearInterval(timerId);\n      const took = startT ? Date.now() - startT : 0;\n      startT = 0;\n      winMsg.textContent = `${moves} moves · ${fmt(took)}`;\n      winEl.classList.add('show');\n      try { navigator.vibrate && navigator.vibrate([30, 40, 30]); } catch (e) {}\n      setTimeout(() => winEl.classList.remove('show'), 2600);\n    };\n\n    const AXES = [[1,0,0],[0,1,0],[0,0,1]];\n    const scramble = () => {\n      if (busy || queue.length) return;\n      let last = -1;\n      for (let i = 0; i < 22; i++) {\n        let a; do { a = Math.floor(Math.random() * 3); } while (a === last);\n        last = a;\n        const idx = [-1, 0, 1][Math.floor(Math.random() * 3)];\n        queue.push({ axis: AXES[a], idx, q: Math.random() < 0.5 ? 1 : -1, ms: 90 });\n      }\n      history = []; moves = 0; movesEl.textContent = '0';\n      clearInterval(timerId); startT = 0; timeEl.textContent = '0:00';\n      winEl.classList.remove('show');\n      scrambled = true;\n      run();\n    };\n    const reset = () => {\n      if (busy) return;\n      queue = []; history = []; moves = 0; scrambled = false;\n      clearInterval(timerId); startT = 0;\n      movesEl.textContent = '0'; timeEl.textContent = '0:00';\n      winEl.classList.remove('show');\n      build();\n    };\n    const undo = () => {\n      if (busy || queue.length || !history.length) return;\n      const m = history.pop();\n      push({ axis: m.axis, idx: m.idx, q: -m.q, ms: 180, user: true, undo: true });\n    };\n    $('.scramble').addEventListener('click', scramble);\n    $('.reset').addEventListener('click', reset);\n    $('.undo').addEventListener('click', undo);\n\n    // ---------- gestures ----------\n    let g = null;\n    const toScreen = (d) => { const v = mv(view, d); return [v[0], v[1]]; };\n\n    stage.addEventListener('pointerdown', (ev) => {\n      if (g) return;\n      const face = ev.target.closest && ev.target.closest('.f');\n      g = { id: ev.pointerId, x0: ev.clientX, y0: ev.clientY, x: ev.clientX, y: ev.clientY, mode: 'orbit' };\n      if (face && face.classList.contains('c') && !busy && !queue.length) {\n        const cb = face.parentElement.__cb;\n        const n = rnd(mv(cb.R, FACES[+face.dataset.fi].n));\n        g.mode = 'pending'; g.cb = cb; g.n = n;\n      }\n      try { stage.setPointerCapture(ev.pointerId); } catch (e) {}\n      ev.preventDefault();\n    });\n\n    stage.addEventListener('pointermove', (ev) => {\n      if (!g || ev.pointerId !== g.id) return;\n      const dx = ev.clientX - g.x, dy = ev.clientY - g.y;\n      g.x = ev.clientX; g.y = ev.clientY;\n      if (g.mode === 'orbit') {\n        const len = Math.hypot(dx, dy);\n        if (len > 0) { view = mul(rot([-dy / len, dx / len, 0], len * 0.009), view); paintView(); }\n        return;\n      }\n      const tx = ev.clientX - g.x0, ty = ev.clientY - g.y0;\n      if (g.mode === 'pending') {\n        if (Math.hypot(tx, ty) < 10) return;\n        // pick the in-face direction whose screen projection best matches the swipe\n        let best = null;\n        for (const d of AXES.concat(AXES.map((a) => a.map((v) => -v)))) {\n          if (Math.abs(dot(d, g.n)) > 0.5) continue;\n          const s = toScreen(d), sl = Math.hypot(s[0], s[1]) || 1;\n          const score = (tx * s[0] + ty * s[1]) / sl;\n          if (!best || score > best.score) best = { d, s: [s[0] / sl, s[1] / sl], sl, score };\n        }\n        const axis = cross(g.n, best.d); // rotating +θ about n×d moves the sticker toward +d\n        g.mode = 'turn';\n        g.axis = axis;\n        g.dir = best.s;\n        g.scale = best.sl; // screen shrink of that direction\n        g.idx = Math.round(dot(g.cb.pos, axis));\n        g.layer = layerOf(axis, g.idx);\n      }\n      if (g.mode === 'turn') {\n        const along = tx * g.dir[0] + ty * g.dir[1];\n        g.angle = along / (S * 2.2 * Math.max(g.scale, 0.35)) * (Math.PI / 2);\n        const A = rot(g.axis, g.angle);\n        g.layer.forEach((c) => paint(c, A));\n      }\n    });\n\n    const end = (ev) => {\n      if (!g || ev.pointerId !== g.id) return;\n      const cur = g; g = null;\n      try { stage.releasePointerCapture(ev.pointerId); } catch (e) {}\n      if (cur.mode !== 'turn') return;\n      const quarter = cur.angle / (Math.PI / 2);\n      let q = Math.round(quarter);\n      if (q === 0 && Math.abs(quarter) > 0.28) q = Math.sign(quarter); // a short flick still turns\n      q = Math.max(-2, Math.min(2, q));\n      if (q === 0) {\n        busy = true;\n        animate(cur.layer, cur.axis, cur.angle, 0, 140, () => { busy = false; paintAll(); run(); });\n        return;\n      }\n      queue.unshift({ axis: cur.axis, idx: cur.idx, q, from: cur.angle, ms: 160, user: true });\n      run();\n    };\n    stage.addEventListener('pointerup', end);\n    stage.addEventListener('pointercancel', end);\n\n    build();\n    paintView();\n    layout();\n    if (typeof ResizeObserver !== 'undefined') new ResizeObserver(layout).observe(stage);\n    else window.addEventListener('resize', layout);\n  },\n};\n"}}}}}}}},{"_id":"6abf00de492457561460ce5d","component":"data2-core-types@Component","system":"6172b957976026c073ab9058","params":{"extends":"data2-core-primitives@Block","component":"Star_Fox_Mobile_Player","package":"lucca-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":"// Mobile SNES player for Star Fox: the player picks their own ROM on the device (it never\n// leaves the browser), EmulatorJS + snes9x (Super FX) run from the \"starfox-emulator\" Drive,\n// the phone's tilt steers the D-pad and touching the screen fires.\nconst MEDIA = 'https://lucca-playground.data2.media/file/';\nconst FILES = {\n  'loader.js': 'sjn4NtjRSdexz2quJ5Icu',\n  'emulator.js': 'H9sfJ9h2M4sL9l0V8lTRF',\n  'GameManager.js': '7-WiYcktgqc2iE805n4WB',\n  'compression.js': 'Z_xhOCdoDAvuDa0e8_MoL',\n  'gamepad.js': 'eQ7YsA53xxlQhATIzf1Lk',\n  'nipplejs.js': 'tQ3GRf6D_m2B_DgJ5FwCv',\n  'shaders.js': 'QSnBt1zhdtx9pOlWWit7k',\n  'storage.js': 'D59vCRKTT6jeJNnN2g0UB',\n  'socket.io.min.js': 'UUZqWxMwZg2qimoR3Nw8b',\n  'emulator.css': 'MNqXIt8RLPj-pB2WuqG7Q',\n  'snes9x-wasm.data': 'pVUBC-nZEN82gyuTpPsRw',\n  'snes9x-legacy-wasm.data': 'myfEzZFCCUes_kFsjUUCd',\n  'snes9x.json': 'YNbaUeVsno2mDYsEOtp39',\n  // the core .data files are 7z archives; these workers unpack cores and zipped ROMs\n  'extract7z.js': 'CPfp383FjI0u02qrXlbzl',\n  'extractzip.js': 'iqumdLN_ZjOfnsttklMob',\n  'libunrar.js': 'chOV7VBt3LvWNwsFSwxGk',\n  'libunrar.wasm': '8ftfUbTkQF-wO5oIGWIt1',\n};\n\n// libretro joypad ids as EmulatorJS's simulateInput expects them\nconst BTN = { B: 0, Y: 1, SELECT: 2, START: 3, UP: 4, DOWN: 5, LEFT: 6, RIGHT: 7, A: 8, X: 9, L: 10, R: 11 };\n\nconst DEAD_ZONE = 4;   // degrees of tilt ignored around neutral\nconst FULL_TILT = 20;  // degrees of tilt that hold the direction permanently\nconst YAW_GAIN = 1.0;      // degrees of steering per degree of yaw (turning the phone left/right)\nconst YAW_RECENTER = 1.2;  // s: yaw leaks back to neutral, so you never have to hold your body turned\nconst STEER_SMOOTH = 0.06; // s: low-pass time constant on the combined steering angle\nconst INVERT_STEER = true; // horizontal axis flipped: rolling/turning right steers left\nconst INVERT_YAW = false;  // flip yaw alone if it ever disagrees with roll on a device\nconst INVERT_ROLL = true;  // flip roll alone (tilting like a steering wheel)\n\nconst SHOOT_OPTIONS = ['B', 'Y', 'A', 'X'];\n\nfunction emulatorPage(romUrl, romName) {\n  const paths = {};\n  for (const [name, ref] of Object.entries(FILES)) paths[name] = MEDIA + ref;\n  const cfg = {\n    EJS_player: '#game',\n    EJS_core: 'snes',\n    EJS_gameName: romName.replace(/\\.[^.]+$/, ''),\n    EJS_gameUrl: romUrl,\n    EJS_pathtodata: MEDIA,\n    EJS_paths: paths,\n    EJS_DEBUG_XX: true,          // load the unminified sources individually (that's what the Drive holds)\n    EJS_startOnLoaded: true,\n    EJS_language: 'en-US',\n    EJS_disableAutoLang: false,  // loader's flag is inverted: false skips the localization fetch\n    EJS_threads: false,\n    EJS_color: '#ff6a2b',\n    EJS_backgroundColor: '#000',\n    EJS_Buttons: { netplay: false, saveState: false, loadState: false, screenRecord: false, screenshot: false, cacheManager: false, gamepad: false },\n  };\n  const assigns = Object.entries(cfg).map(([k, v]) => `window.${k}=${JSON.stringify(v)};`).join('');\n  return `<!doctype html><html><head><meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no\">\n<style>\nhtml,body{margin:0;height:100%;background:#000;overflow:hidden;touch-action:none}\n#game{width:100%;height:100%}\n.ejs_virtualGamepad_parent,.ejs_menu_bar,.ejs_context_menu{display:none!important}\n</style></head><body><div id=\"game\"></div>\n<script>${assigns}</script>\n<script src=\"${paths['loader.js']}\"></script>\n</body></html>`;\n}\n\nexport default {\n  css: `\n    :host{display:block;width:100%;height:100%}\n    .sf{position:relative;width:100%;height:100%;min-height:320px;overflow:hidden;background:#05060d;color:#e9ecff;\n      font-family:ui-sans-serif,system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif;touch-action:none;user-select:none;-webkit-user-select:none}\n    .sf:fullscreen{width:100vw;height:100vh}\n    .stage{position:absolute;inset:0}\n    .emu{position:absolute;inset:0;width:100%;height:100%;border:0;display:block;background:#000}\n    .touch{position:absolute;inset:0;z-index:2;display:none}\n    .playing .touch{display:block}\n    .hud{position:absolute;inset:0;z-index:3;pointer-events:none;display:none}\n    .playing .hud{display:block}\n    .hb{pointer-events:auto;position:absolute;border:1px solid rgba(255,255,255,.35);background:rgba(10,12,30,.45);color:#fff;\n      font-family:inherit;font-size:12px;font-weight:600;line-height:1;letter-spacing:.08em;border-radius:999px;padding:10px 14px;-webkit-tap-highlight-color:transparent}\n    .hb:active,.hb.on{background:rgba(255,106,43,.75);border-color:#ff6a2b}\n    .hb.start{top:max(10px,env(safe-area-inset-top));left:50%;transform:translateX(-50%)}\n    .hb.cal{top:max(10px,env(safe-area-inset-top));left:max(10px,env(safe-area-inset-left))}\n    .hb.bomb{bottom:max(14px,env(safe-area-inset-bottom));right:max(14px,env(safe-area-inset-right));width:68px;height:68px;padding:0;border-radius:50%}\n    .tilt{position:absolute;bottom:max(14px,env(safe-area-inset-bottom));left:max(14px,env(safe-area-inset-left));width:64px;height:64px;\n      border-radius:50%;border:1px solid rgba(255,255,255,.3);background:rgba(10,12,30,.35)}\n    .tilt i{position:absolute;left:50%;top:50%;width:14px;height:14px;margin:-7px 0 0 -7px;border-radius:50%;background:#ff6a2b;\n      box-shadow:0 0 10px #ff6a2b;transform:translate(0,0)}\n    .intro{position:absolute;inset:0;z-index:4;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;\n      padding:24px;text-align:center;background:radial-gradient(ellipse at 50% 120%,#2a1240 0%,#0b0b22 45%,#05060d 75%)}\n    .intro::before{content:\"\";position:absolute;inset:0;pointer-events:none;opacity:.7;\n      background-image:radial-gradient(1px 1px at 12% 22%,#fff 50%,transparent 51%),radial-gradient(1px 1px at 72% 14%,#fff 50%,transparent 51%),\n      radial-gradient(1.5px 1.5px at 38% 68%,#cfd6ff 50%,transparent 51%),radial-gradient(1px 1px at 86% 58%,#fff 50%,transparent 51%),\n      radial-gradient(1px 1px at 22% 84%,#fff 50%,transparent 51%),radial-gradient(1.5px 1.5px at 58% 36%,#ffd9c7 50%,transparent 51%),\n      radial-gradient(1px 1px at 92% 88%,#fff 50%,transparent 51%),radial-gradient(1px 1px at 6% 52%,#cfd6ff 50%,transparent 51%)}\n    .playing .intro{display:none}\n    .intro > *{position:relative}\n    h1{margin:0;font-size:26px;letter-spacing:.18em;font-weight:800}\n    .sub{margin:0;font-size:13px;opacity:.7;max-width:340px;line-height:1.45}\n    .btn{appearance:none;border:0;border-radius:12px;padding:14px 22px;min-width:220px;font-family:inherit;font-size:15px;font-weight:700;line-height:1;letter-spacing:.06em;cursor:pointer;\n      -webkit-tap-highlight-color:transparent}\n    .pick{background:#1b1f3f;color:#fff;border:1px solid rgba(255,255,255,.25)}\n    .go{background:#ff6a2b;color:#140700}\n    .go[disabled]{opacity:.35}\n    .msg{min-height:18px;font-size:13px;color:#ffb38f;max-width:340px}\n    .help{list-style:none;margin:6px 0 0;padding:0;font-size:12px;opacity:.65;line-height:1.7}\n    .file{display:none}\n    .toast{position:absolute;left:50%;bottom:max(14px,env(safe-area-inset-bottom));transform:translateX(-50%);max-width:60%;font-size:12px;\n      text-align:center;color:#ffb38f;background:rgba(5,6,13,.7);border-radius:8px;padding:6px 10px;pointer-events:none}\n    .toast:empty{display:none}\n  `,\n\n  render() {\n    return `<div class=\"sf\">\n      <div class=\"stage\" data-d2-static></div>\n      <div class=\"touch\" data-d2-static></div>\n      <div class=\"hud\" data-d2-static>\n        <button class=\"hb cal\" type=\"button\">LEVEL</button>\n        <button class=\"hb start\" type=\"button\">START</button>\n        <button class=\"hb bomb\" type=\"button\">BOMB</button>\n        <div class=\"tilt\"><i></i></div>\n        <div class=\"toast\"></div>\n      </div>\n      <div class=\"intro\" data-d2-static>\n        <h1>FLIGHT DECK</h1>\n        <p class=\"sub\">Load your own Star Fox (SNES) ROM. The file stays on this device and is never uploaded.</p>\n        <input class=\"file\" type=\"file\" accept=\".sfc,.smc,.zip,.7z,.bin\">\n        <button class=\"btn pick\" type=\"button\">Choose ROM</button>\n        <button class=\"btn go\" type=\"button\" disabled>Launch</button>\n        <p class=\"msg\"></p>\n        <ul class=\"help\">\n          <li>Tilt the phone to steer — hold it the way you like, it levels on launch</li>\n          <li>Touch anywhere to shoot (hold for rapid fire)</li>\n          <li>START pauses &middot; BOMB fires a bomb &middot; LEVEL re-centres the tilt</li>\n        </ul>\n      </div>\n    </div>`;\n  },\n\n  mounted(d2) {\n    const sf = d2.$('.sf');\n    const stage = d2.$('.stage');\n    const touch = d2.$('.touch');\n    const fileInput = d2.$('.file');\n    const goBtn = d2.$('.go');\n    const msg = d2.$('.msg');\n    const dot = d2.$('.tilt i');\n    const toast = d2.$('.toast');\n\n    const shootName = String(d2.param('shootButton') || 'Y').toUpperCase();\n    const SHOOT = BTN[SHOOT_OPTIONS.includes(shootName) ? shootName : 'Y'];\n\n    let rom = null;          // { url, name }\n    let frame = null;\n    let firing = 0;          // active touches on the play surface\n    let fireTick = 0;\n    const held = new Set();  // buttons currently pressed by this component\n    const tilt = { roll: 0, rollWeight: 0, pitch: 0, yaw: 0, ok: false };\n    const neutral = { roll: 0, pitch: 0 };\n    let prevR = null, prevT = 0;\n    let steer = 0, lastFrame = 0;\n    let needsLevel = true;\n    const acc = { x: 0, y: 0 };\n\n    const say = (t) => { msg.textContent = t || ''; toast.textContent = sf.classList.contains('playing') ? (t || '') : ''; };\n\n    const gm = () => {\n      try { return frame && frame.contentWindow && frame.contentWindow.EJS_emulator && frame.contentWindow.EJS_emulator.gameManager; }\n      catch (e) { return null; }\n    };\n    const press = (id, down) => {\n      const g = gm();\n      if (!g) return;\n      if (down === held.has(id)) return;\n      down ? held.add(id) : held.delete(id);\n      try { g.simulateInput(0, id, down ? 1 : 0); } catch (e) {}\n    };\n    const resumeAudio = () => {\n      try {\n        const ctx = frame.contentWindow.EJS_emulator.Module.AL.currentCtx.audioCtx;\n        if (ctx && ctx.state !== 'running') ctx.resume();\n      } catch (e) {}\n    };\n\n    // --- ROM selection: read locally, hand the emulator a blob: URL ---\n    d2.on('.pick', 'click', () => fileInput.click());\n    fileInput.addEventListener('change', () => {\n      const f = fileInput.files && fileInput.files[0];\n      if (!f) return;\n      if (rom) URL.revokeObjectURL(rom.url);\n      rom = { url: URL.createObjectURL(f), name: f.name };\n      goBtn.disabled = false;\n      say(f.name + ' ready');\n    });\n\n    // --- Tilt: device orientation -> gravity in screen coordinates ---\n    const screenAngle = () => {\n      const a = (screen.orientation && typeof screen.orientation.angle === 'number') ? screen.orientation.angle : (window.orientation || 0);\n      return ((a % 360) + 360) % 360;\n    };\n    const wrap = (deg) => ((deg + 540) % 360) - 180;\n    // W3C device->earth rotation matrix (Z-X'-Y'' Euler: alpha, beta, gamma)\n    const rotation = (a, b, g) => {\n      const cA = Math.cos(a), sA = Math.sin(a), cB = Math.cos(b), sB = Math.sin(b), cG = Math.cos(g), sG = Math.sin(g);\n      return [\n        [cA * cG - sA * sB * sG, -cB * sA, cA * sG + cG * sA * sB],\n        [cG * sA + cA * sB * sG, cA * cB, sA * sG - cA * cG * sB],\n        [-cB * sG, sB, cB * cG],\n      ];\n    };\n    const onOrient = (e) => {\n      if (e.beta == null || e.gamma == null) return;\n      const r = Math.PI / 180;\n      const R = rotation((e.alpha || 0) * r, e.beta * r, e.gamma * r);\n      // \"up\" in device coordinates is the earth-z row of R\n      const ux = R[2][0], uy = R[2][1];\n\n      // Roll = steering-wheel rotation about the screen normal, read from where gravity points in the\n      // screen plane. It does not depend on the reported screen angle, and fades out as the phone goes\n      // flat (gravity leaves the screen plane), where yaw takes over the same motion.\n      const inPlane = Math.hypot(ux, uy);\n      const wheel = Math.atan2(uy, ux) / r;   // grows when the phone turns clockwise, as seen by the player\n      tilt.rollWeight = Math.max(0, Math.min(1, (inPlane - 0.2) / 0.3));\n\n      // Pitch = elevation of the screen's top edge (unchanged behaviour)\n      const t = screenAngle() * r;\n      const sy = ux * Math.sin(t) + uy * Math.cos(t);\n      tilt.pitch = Math.asin(Math.max(-1, Math.min(1, sy))) / r;\n\n      // Yaw = rotation about the earth's vertical between two samples, from the incremental rotation\n      // R_t * R_(t-1)^T. Integrated with a leak so compass drift and a turned body both re-centre.\n      const now = e.timeStamp || performance.now();\n      if (prevR && e.alpha != null) {\n        const dt = Math.max(0, Math.min(0.25, (now - prevT) / 1000));\n        let d10 = 0, d01 = 0, d00 = 0, d11 = 0;\n        for (let k = 0; k < 3; k++) {\n          d10 += R[1][k] * prevR[0][k]; d01 += R[0][k] * prevR[1][k];\n          d00 += R[0][k] * prevR[0][k]; d11 += R[1][k] * prevR[1][k];\n        }\n        const step = Math.atan2(d10 - d01, d00 + d11) / r;  // + = counter-clockwise seen from above (turning left)\n        if (Math.abs(step) < 30) tilt.yaw = (tilt.yaw - step) * Math.exp(-dt / YAW_RECENTER);  // + = turned right\n        tilt.yaw = Math.max(-45, Math.min(45, tilt.yaw));\n      }\n      prevR = R; prevT = now;\n\n      if (needsLevel) {\n        neutral.roll = wheel; neutral.pitch = tilt.pitch; tilt.yaw = 0; steer = 0; needsLevel = false;\n      }\n      tilt.roll = wrap(wheel - neutral.roll);\n      tilt.ok = true;\n    };\n    window.addEventListener('deviceorientation', onOrient);\n\n    const axis = (deg) => {\n      const m = Math.abs(deg);\n      if (m < DEAD_ZONE) return 0;\n      return Math.sign(deg) * Math.min(1, (m - DEAD_ZONE) / (FULL_TILT - DEAD_ZONE));\n    };\n    // The SNES pad is digital: pulse the direction with a duty cycle proportional to the tilt.\n    const pulse = (v, k) => {\n      const m = Math.abs(v);\n      if (m === 0) { acc[k] = 0; return 0; }\n      if (m >= 0.97) return Math.sign(v);\n      acc[k] += m;\n      if (acc[k] >= 1) { acc[k] -= 1; return Math.sign(v); }\n      return 0;\n    };\n\n    d2.raf(() => {\n      if (!sf.isConnected) return;\n      if (!gm()) return;\n      const invert = !!d2.param('invertPitch');\n      // horizontal: roll + yaw, low-passed so the pulses into the digital pad follow a smooth curve\n      const nowMs = performance.now();\n      const dt = lastFrame ? Math.min(0.1, (nowMs - lastFrame) / 1000) : 1 / 60;\n      lastFrame = nowMs;\n      const yawSign = INVERT_YAW ? -1 : 1;\n      const rollSign = INVERT_ROLL ? -1 : 1;\n      const target = tilt.ok ? rollSign * tilt.roll * tilt.rollWeight + YAW_GAIN * yawSign * tilt.yaw : 0;\n      steer += (target - steer) * (1 - Math.exp(-dt / STEER_SMOOTH));\n      const x = axis(INVERT_STEER ? -steer : steer);\n      // tilting the top edge away (nose forward) presses UP, like pushing a flight stick\n      let y = tilt.ok ? axis(neutral.pitch - tilt.pitch) : 0;\n      if (invert) y = -y;\n      dot.style.transform = `translate(${(x * 22).toFixed(1)}px,${(-y * 22).toFixed(1)}px)`;\n      const px = pulse(x, 'x'), py = pulse(y, 'y');\n      press(BTN.RIGHT, px > 0); press(BTN.LEFT, px < 0);\n      press(BTN.UP, py > 0); press(BTN.DOWN, py < 0);\n      // shooting: held touch = rapid fire (press 4 frames, release 4 frames)\n      if (firing > 0) {\n        const auto = d2.param('autoFire') !== false;\n        fireTick++;\n        press(SHOOT, auto ? (fireTick % 8) < 4 : true);\n      } else {\n        fireTick = 0;\n        press(SHOOT, false);\n      }\n    });\n\n    // --- Touch surface: any touch shoots ---\n    const touchDown = (e) => { e.preventDefault(); resumeAudio(); firing = e.touches ? e.touches.length : 1; };\n    const touchUp = (e) => { e.preventDefault(); firing = e.touches ? e.touches.length : 0; };\n    touch.addEventListener('touchstart', touchDown, { passive: false });\n    touch.addEventListener('touchend', touchUp, { passive: false });\n    touch.addEventListener('touchcancel', touchUp, { passive: false });\n    touch.addEventListener('mousedown', (e) => { resumeAudio(); firing = 1; e.preventDefault(); });\n    window.addEventListener('mouseup', () => { if (firing && !('ontouchstart' in window)) firing = 0; });\n\n    // --- HUD buttons: held for as long as the finger is down ---\n    const holdButton = (sel, id) => {\n      const el = d2.$(sel);\n      const down = (e) => { e.preventDefault(); e.stopPropagation(); resumeAudio(); el.classList.add('on'); press(id, true); };\n      const up = (e) => { e.preventDefault(); e.stopPropagation(); el.classList.remove('on'); press(id, false); };\n      el.addEventListener('touchstart', down, { passive: false });\n      el.addEventListener('touchend', up, { passive: false });\n      el.addEventListener('touchcancel', up, { passive: false });\n      el.addEventListener('mousedown', down);\n      el.addEventListener('mouseup', up);\n      el.addEventListener('mouseleave', up);\n    };\n    holdButton('.hb.start', BTN.START);\n    holdButton('.hb.bomb', BTN.A);\n    d2.on('.hb.cal', 'click', () => { needsLevel = true; });\n\n    // --- Launch: permissions need this user gesture, then boot the emulator in an iframe ---\n    d2.on('.go', 'click', () => {\n      if (!rom) return;\n      const DOE = window.DeviceOrientationEvent;\n      if (DOE && typeof DOE.requestPermission === 'function') {\n        DOE.requestPermission().then((s) => { if (s !== 'granted') say('Motion access denied — steering is disabled. Reload to try again.'); }).catch(() => {});\n      }\n      try { if (sf.requestFullscreen) sf.requestFullscreen({ navigationUI: 'hide' }).catch(() => {}); } catch (e) {}\n      try { if (screen.orientation && screen.orientation.lock) screen.orientation.lock('landscape').catch(() => {}); } catch (e) {}\n\n      stage.innerHTML = '';\n      frame = document.createElement('iframe');\n      frame.className = 'emu';\n      frame.title = 'SNES emulator';\n      frame.setAttribute('allow', 'autoplay; fullscreen; gamepad; accelerometer; gyroscope');\n      frame.srcdoc = emulatorPage(rom.url, rom.name);\n      stage.appendChild(frame);\n      needsLevel = true;\n      sf.classList.add('playing');\n      setTimeout(() => { if (!tilt.ok) say('No tilt sensor reported — steering needs a phone with a gyroscope.'); }, 3000);\n    });\n  },\n};\n"}},"autoFire":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"autoFire"}}}},"invertPitch":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"invertPitch"}}}},"shootButton":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shootButton"}}}}}}}}}}]}},{"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":"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"},{"_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":"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"},{"_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"},{"_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"},{"_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"},{"reference":"ServiceComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"ComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"69483e4605714c8c39688670","parent":"69483e4605714c8c3968866c","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"1","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(103,83,209,1)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"height":90,"exactHeight":90,"exactWidth":90,"width":90}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"exactX":false,"exactY":false,"x":96,"y":2}},"opacity":{"disabled":false,"component":"data2-core-types@Opacity","params":{"value":100}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"index":0,"reference":"1"},{"_id":"69483e4605714c8c3968866d","parent":"69483e4605714c8c3968866c","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"2","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(103,83,209,1)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"height":90,"exactHeight":90,"exactWidth":90,"width":90}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"exactX":1.8199999999999998,"exactY":1.63,"x":2,"y":2}},"opacity":{"disabled":false,"component":"data2-core-types@Opacity","params":{"value":100}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"index":1,"reference":"2"},{"_id":"69483e4605714c8c3968866e","parent":"69483e4605714c8c3968866c","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"3","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(103,83,209,1)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"height":90,"exactHeight":90,"exactWidth":90,"width":90}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"exactX":1.96,"exactY":96.42,"x":2,"y":96}},"opacity":{"disabled":false,"component":"data2-core-types@Opacity","params":{"value":100}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"index":2,"reference":"3"},{"_id":"69483e4605714c8c3968866f","parent":"69483e4605714c8c3968866c","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"4","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(103,83,209,1)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"height":90,"exactHeight":90,"exactWidth":90,"width":90}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"exactX":1.75,"exactY":189.75,"x":2,"y":190}},"text":"","opacity":{"disabled":false,"component":"data2-core-types@Opacity","params":{"value":100}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"index":3,"reference":"4"},{"_id":"69483e4605714c8c3968865f","parent":"69483e4605714c8c3968865c","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"5","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(103,83,209,1)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"height":90,"exactHeight":90,"exactWidth":90,"width":90}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"exactX":1.75,"exactY":189.75,"x":2,"y":190}},"text":"","opacity":{"disabled":false,"component":"data2-core-types@Opacity","params":{"value":100}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"index":0,"reference":"5"},{"_id":"69483e4605714c8c3968865c","parent":"69483e4605714c8c3968865b","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"x","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":282,"height":282,"exactWidth":282,"exactHeight":282}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"x":0,"y":0,"exactX":0,"exactY":0}},"style":{"transform":{"component":"data2-core-primitives@String","params":{"content":"rotatex(90deg) translatey(141px)  translatez(-141px)"}},"transform-origin":{"component":"data2-core-primitives@String","params":{"content":"center center"}},"transform-style":{"component":"data2-core-primitives@String","params":{"content":"preserve-3d"}}},"childrenOverflow":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"opacity":{"disabled":true,"component":"data2-core-types@Opacity","params":{"value":100}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"index":2,"reference":"x"},{"_id":"6abf0132c5d15a59091af242","component":"data2-core-primitives@Artboard","system":"6172b957976026c073ab9058","displayName":"Star Fox Player","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Star Fox Player"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":844,"height":390}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"ar6uy-4StLhTwXRuZpsIb"},{"_id":"69483e4605714c8c3968865e","parent":"69483e4605714c8c3968865c","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"6","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(103,83,209,1)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"height":90,"exactHeight":90,"exactWidth":90,"width":90}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"exactX":1.96,"exactY":96.42,"x":2,"y":96}},"opacity":{"disabled":false,"component":"data2-core-types@Opacity","params":{"value":100}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"index":1,"reference":"6"},{"_id":"69483e4605714c8c3968865d","parent":"69483e4605714c8c3968865c","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"7","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(103,83,209,1)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"height":90,"exactHeight":90,"exactWidth":90,"width":90}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"exactX":1.8199999999999998,"exactY":1.63,"x":2,"y":2}},"opacity":{"disabled":false,"component":"data2-core-types@Opacity","params":{"value":100}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"index":2,"reference":"7"},{"_id":"69483e4605714c8c39688660","parent":"69483e4605714c8c3968865c","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"8","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(103,83,209,1)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"height":90,"exactHeight":90,"exactWidth":90,"width":90}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"exactX":false,"exactY":false,"y":2,"x":96}},"opacity":{"disabled":false,"component":"data2-core-types@Opacity","params":{"value":100}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"index":3,"reference":"8"},{"_id":"69483e4605714c8c39688661","parent":"69483e4605714c8c3968865c","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"9","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(103,83,209,1)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"height":90,"exactHeight":90,"exactWidth":90,"width":90}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"exactX":false,"exactY":false,"y":2,"x":190}},"opacity":{"disabled":false,"component":"data2-core-types@Opacity","params":{"value":100}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"index":4,"reference":"9"},{"_id":"69483e4605714c8c39688662","parent":"69483e4605714c8c3968865c","component":"data2-core-primitives@Block","system":"6172b957976026c073ab9058","displayName":"shadow4","params":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{"leftTop":"99999"},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(205,168,255,0.84)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":185,"height":159,"exactWidth":184.99,"exactHeight":158.98}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"x":156,"y":-24,"exactX":155.82,"exactY":-23.78}},"filter":[{"component":"data2-core-types@BitmapFilter","params":{"type":"blur","unit":"px","value":77}}],"opacity":{"disabled":false,"component":"data2-core-types@Opacity","params":{"value":0}},"transitions":[{"component":"data2-core-types@Transition","params":{"scope":"ALL","duration":"100","delay":"0","easing":"cubic-bezier(0.7368421052631579,0.21437499999999998,0.7828947368421053,0.262375)"}}]},"reference":"shadow4"},{"_id":"26ca53bf5adeefb93f7f7f45","parent":"6abf0132c5d15a59091af242","component":"lucca-playground@Star_Fox_Mobile_Player","system":"6172b957976026c073ab9058","displayName":"Star Fox Player","params":{"pin":"stretch","shootButton":"Y","invertPitch":false,"autoFire":true,"super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"right":true,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":844,"height":390}}}},"reference":"b44qH3TY_lo_rd3k-SekR"}]}},{"_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"},{"reference":"ApplicationsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6abf028056470c270674d98c","displayName":"Star Fox","reference":"8_ADUrnpvFzJNZ5wqmMRB","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"starfox"}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Artboard","reference":"ar6uy-4StLhTwXRuZpsIb"}}},"system":"6172b957976026c073ab9058","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a73eb729b80399cc1ea52fb","component":"data2-core@Store","system":"6172b957976026c073ab9058","params":{"initialData":{"selectedLevelId":null}},"reference":"gameStore"}]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}