{"context":{"domain":"massayuki-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":"686bd24b4fcbdb97284775e3","name":"Massayuki Honda","domain":"massayuki-playground","visibility":"public","settings":{"defaultAccessPolicy":"private"},"updatedAt":"2026-08-14T19:26:29.927Z","createdAt":"2025-07-07T12:56:41.025Z"}]}},{"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":"6ac563db06e81c2076f3bcec","component":"data2-core-types@Component","system":"686bd24b4fcbdb97284775e3","params":{"extends":"data2-core-primitives@Block","component":"Invest_Manager","package":"massayuki-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 BRL = new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' })\nconst NUM = new Intl.NumberFormat('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })\nconst money = (v) => BRL.format(Number(v) || 0)\nconst esc = (v) => String(v == null ? '' : v).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\nconst norm = (t) => String(t || '').toUpperCase().replace(/[^A-Z0-9]/g, '')\nconst parse = (v) => {\n  if (typeof v === 'number') return v\n  let s = String(v == null ? '' : v).replace(/[^0-9,.-]/g, '')\n  if (s.includes(',')) s = s.replace(/\\./g, '').replace(',', '.')\n  const n = parseFloat(s)\n  return isNaN(n) ? 0 : n\n}\nconst CATS = [\n  { key: 'acoes', label: 'Ações' },\n  { key: 'fiis', label: 'FIIs' },\n  { key: 'outros', label: 'ETFs e caixa' },\n]\n\nfunction shrink(file) {\n  return new Promise((resolve, reject) => {\n    const img = new Image()\n    const url = URL.createObjectURL(file)\n    img.onload = () => {\n      const max = 1800\n      const k = Math.min(1, max / Math.max(img.width, img.height))\n      const cv = document.createElement('canvas')\n      cv.width = Math.round(img.width * k)\n      cv.height = Math.round(img.height * k)\n      cv.getContext('2d').drawImage(img, 0, 0, cv.width, cv.height)\n      URL.revokeObjectURL(url)\n      resolve(cv.toDataURL('image/jpeg', 0.9))\n    }\n    img.onerror = () => { URL.revokeObjectURL(url); reject(new Error('imagem inválida')) }\n    img.src = url\n  })\n}\n\nfunction readAnswer(res, depth) {\n  depth = depth || 0\n  if (depth > 5 || res == null) return []\n  let out = res\n  if (typeof out === 'string') {\n    const s = out.replace(/```json|```/g, '').trim()\n    const m = s.match(/[\\[{][\\s\\S]*[\\]}]/)\n    try { out = JSON.parse(m ? m[0] : s) } catch (e) { return [] }\n  }\n  if (Array.isArray(out)) {\n    if (out.some((x) => x && typeof x === 'object' && ('ticker' in x || 'preco' in x))) return out\n    for (const x of out) { const r = readAnswer(x, depth + 1); if (r.length) return r }\n    return []\n  }\n  if (typeof out === 'object') {\n    for (const k of ['ativos', 'assets', 'data', 'answer', 'content', 'text', 'result']) {\n      if (out[k] != null) { const r = readAnswer(out[k], depth + 1); if (r.length) return r }\n    }\n    for (const v of Object.values(out)) { if (v && typeof v !== 'number') { const r = readAnswer(v, depth + 1); if (r.length) return r } }\n  }\n  return []\n}\n\nasync function importFile(d2, file) {\n  const S = d2.state\n  if (!file || !String(file.type || '').startsWith('image/')) {\n    S.msg = { type: 'err', text: 'Envie uma imagem (PNG ou JPG).' }\n    return\n  }\n  if (S.importing) return\n  S.view = 'main'\n  S.armed = false\n  S.importing = true\n  S.msg = null\n  try {\n    let url = await shrink(file)\n    file = null\n    const a = d2.assistant(d2.params.extrator)\n    const res = await a.ask('Extraia o ticker e o preço atual de cada ativo visível nesta imagem.', { images: [url], json: true })\n    url = null\n    let items = readAnswer(res)\n    if (!items.length) items = readAnswer(a.answer)\n    if (res && res.isData2Error) throw new Error(res.code === 401 ? 'sem permissão para usar o extrator' : (res.message || 'erro no extrator'))\n    const ds = d2.data('ativos')\n    const map = {}\n    ;(ds.records() || []).forEach((r) => { map[norm(r.ticker)] = r })\n    const now = new Date().toISOString()\n    const updated = {}\n    const missing = []\n    const jobs = []\n    items.forEach((it) => {\n      const t = norm(it.ticker)\n      const p = parse(it.preco != null ? it.preco : it.price)\n      if (!t || !(p > 0)) return\n      const r = map[t]\n      if (r && r._id) {\n        const old = Number(r.preco) || 0\n        updated[t] = { de: old, para: p }\n        const patch = { ...r, preco: p, atualizadoEm: now }\n        if (old > 0 && Math.abs(old - p) > 0.0001) patch.precoAnterior = old\n        jobs.push(ds.update(patch))\n      } else if (!missing.find((m) => m.ticker === t)) {\n        missing.push({ ticker: t, preco: p })\n      }\n    })\n    await Promise.all(jobs)\n    const n = Object.keys(updated).length\n    S.updated = updated\n    S.missing = missing\n    if (!items.length) S.msg = { type: 'err', text: 'Não encontrei ativos nessa imagem.' }\n    else S.msg = { type: 'ok', text: `${n} ${n === 1 ? 'cotação atualizada' : 'cotações atualizadas'}` + (missing.length ? ` · ${missing.length} fora da sua lista` : '') }\n  } catch (err) {\n    S.msg = { type: 'err', text: 'Não consegui ler a imagem: ' + (err && err.message ? err.message : err) }\n  } finally {\n    S.importing = false\n  }\n}\n\nconst LOGO = {}\nconst logoUrl = (t) => `https://icons.brapi.dev/icons/${t}.svg`\nfunction logo(d2, ticker, plain) {\n  const t = norm(ticker)\n  d2.state.logoTick\n  let s = 'fail'\n  if (!plain && t && typeof Image !== 'undefined') {\n    s = LOGO[t]\n    if (!s) {\n      LOGO[t] = s = 'loading'\n      const img = new Image()\n      img.onload = () => { LOGO[t] = img.naturalWidth > 1 ? 'ok' : 'fail'; d2.state.logoTick = (d2.state.logoTick || 0) + 1 }\n      img.onerror = () => { LOGO[t] = 'fail'; d2.state.logoTick = (d2.state.logoTick || 0) + 1 }\n      img.src = logoUrl(t)\n    }\n  }\n  return s === 'ok'\n    ? `<img class=\"lg\" src=\"${logoUrl(t)}\" alt=\"\" loading=\"lazy\">`\n    : `<span class=\"lg ini\">${esc(t.slice(0, 2))}</span>`\n}\n\nfunction renderChart(d2, ticker) {\n  const hs = d2.data('aportes')\n  const list = ((hs && hs.records()) || []).slice().sort((a, b) => String(a.data || '').localeCompare(String(b.data || '')))\n  const pts = []\n  list.forEach((h) => {\n    const it = (Array.isArray(h.itens) ? h.itens : []).find((i) => norm(i.ticker) === ticker)\n    if (it && Number(it.preco) > 0) pts.push({ d: h.data, p: Number(it.preco), q: Number(it.quantidade) || 0, tot: Number(it.total) || 0 })\n  })\n  let body\n  if (!pts.length) {\n    body = `<p class=\"free\" style=\"font-size:13px;padding:12px 0\">Esse ativo ainda não aparece em nenhum aporte salvo. O gráfico usa só os preços dos aportes que você salvar.</p>`\n  } else {\n    const W = 640, H = 220, pl = 58, pr = 18, pt = 16, pb = 34\n    const ps = pts.map((x) => x.p)\n    let mn = Math.min(...ps), mx = Math.max(...ps)\n    if (mx - mn < 1e-9) { mn = mn * 0.95; mx = mx * 1.05 } else { const pad = (mx - mn) * 0.12; mn -= pad; mx += pad }\n    const X = (i) => (pts.length === 1 ? pl + (W - pl - pr) / 2 : pl + (i * (W - pl - pr)) / (pts.length - 1))\n    const Y = (p) => pt + (1 - (p - mn) / (mx - mn)) * (H - pt - pb)\n    const line = pts.map((x, i) => `${i ? 'L' : 'M'}${X(i).toFixed(1)},${Y(x.p).toFixed(1)}`).join(' ')\n    const grid = [0, 0.5, 1].map((f) => {\n      const v = mn + (mx - mn) * (1 - f)\n      const y = pt + f * (H - pt - pb)\n      return `<line x1=\"${pl}\" x2=\"${W - pr}\" y1=\"${y}\" y2=\"${y}\" class=\"gl\"/><text x=\"${pl - 8}\" y=\"${y + 4}\" class=\"ax\" text-anchor=\"end\">${NUM.format(v)}</text>`\n    }).join('')\n    const step = Math.max(1, Math.ceil(pts.length / 6))\n    const xl = pts.map((x, i) => (i % step === 0 || i === pts.length - 1)\n      ? `<text x=\"${X(i)}\" y=\"${H - 10}\" class=\"ax\" text-anchor=\"middle\">${new Date(x.d).toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', year: '2-digit' })}</text>` : '').join('')\n    const dots = pts.map((x, i) => `<circle cx=\"${X(i)}\" cy=\"${Y(x.p)}\" r=\"${x.q > 0 ? 5 : 3.5}\" class=\"${x.q > 0 ? 'pb' : 'pn'}\"><title>${fmtDT(x.d)} · ${money(x.p)}${x.q > 0 ? ` · comprou ${x.q}` : ''}</title></circle>`).join('')\n    const chg = pts.length > 1 ? (pts[pts.length - 1].p / pts[0].p - 1) * 100 : 0\n    body = `<div class=\"cst\"><span>${pts.length} aporte(s) · ponto cheio = aporte em que você comprou</span>${pts.length > 1 ? `<span class=\"var ${chg >= 0 ? 'u' : 'n'}\">${chg >= 0 ? '+' : ''}${chg.toFixed(1).replace('.', ',')}% desde o primeiro</span>` : ''}</div>\n      <svg viewBox=\"0 0 ${W} ${H}\" class=\"chart\">${grid}<path d=\"${line}\" class=\"ln\"/>${dots}${xl}</svg>\n      <table class=\"ctab\"><thead><tr><th>Data do aporte</th><th>Preço</th><th style=\"text-align:center\">Qtd</th><th>Total</th></tr></thead>\n      <tbody>${pts.slice().reverse().map((x, i) => `<tr data-key=\"cp-${i}\"><td>${esc(fmtDT(x.d))}</td><td>${money(x.p)}</td><td style=\"text-align:center\">${x.q || '-'}</td><td>${money(x.tot)}</td></tr>`).join('')}</tbody></table>`\n  }\n  return `<div class=\"ovl\"><div class=\"ovp\"><div class=\"ovh\"><span class=\"tk\">${logo(d2, ticker)}<b>${esc(ticker)}</b><span class=\"free\" style=\"margin-left:8px\">preço nos aportes salvos</span></span><button class=\"btn closechart\">Fechar</button></div>${body}</div></div>`\n}\n\nfunction fmtDT(x) {\n  const d = new Date(x)\n  if (isNaN(d.getTime())) return '-'\n  return d.toLocaleDateString('pt-BR') + ' às ' + d.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })\n}\n\nfunction snapshot(d2) {\n  const draft = d2.state.draft || {}\n  const recs = (d2.data('ativos').records() || []).slice().sort((a, b) => (Number(a.ordem) || 0) - (Number(b.ordem) || 0))\n  const v = (r, k) => {\n    const key = (r._id || '') + ':' + k\n    return draft[key] != null ? parse(draft[key]) : Number(r[k]) || 0\n  }\n  return recs.map((r) => {\n    const preco = v(r, 'preco')\n    const quantidade = v(r, 'quantidade')\n    const valor = v(r, 'valor')\n    return {\n      ticker: String(r.ticker || '').toUpperCase(),\n      categoria: r.categoria || 'outros',\n      preco: preco,\n      precoAnterior: Number(r.precoAnterior) || 0,\n      quantidade: r.valorDireto ? 0 : quantidade,\n      valorDireto: !!r.valorDireto,\n      valor: r.valorDireto ? valor : 0,\n      total: Math.round((r.valorDireto ? valor : preco * quantidade) * 100) / 100,\n    }\n  })\n}\n\nfunction renderHist(d2, current) {\n  const S = d2.state\n  const hs = d2.data('aportes')\n  if (!hs) return `<div class=\"wrap\"><p class=\"empty\">Tabela de aportes não vinculada.</p></div>`\n  if (hs.loading) return `<div class=\"wrap\"><p class=\"empty\">Carregando histórico…</p></div>`\n  const list = (hs.records() || []).slice().sort((a, b) => String(b.data || '').localeCompare(String(a.data || '')))\n  if (!list.length) {\n    return `<div class=\"wrap\"><div class=\"hempty\"><b>Nenhum aporte salvo ainda</b><span>Use \"Salvar aporte\" para registrar os preços e as quantidades do dia.</span><button class=\"btn back\">Voltar</button></div></div>`\n  }\n  const sel = list.find((x) => x._id && x._id === S.histSel) || list[0]\n  const now = {}\n  current.forEach((r) => { now[norm(r.ticker)] = Number(r.preco) || 0 })\n  const itens = Array.isArray(sel.itens) ? sel.itens : []\n  const sums = {}\n  CATS.forEach((c) => { sums[c.key] = 0 })\n  itens.forEach((i) => { const k = sums[i.categoria] != null ? i.categoria : 'outros'; sums[k] += Number(i.total) || 0 })\n\n  const side = list.map((h, i) => `<div class=\"hi ${h === sel ? 'on' : ''}\" data-key=\"${esc(h._id || 'p' + i)}\" data-id=\"${esc(h._id || '')}\">\n      <button class=\"hdel\" data-id=\"${esc(h._id || '')}\" title=\"Excluir aporte\">×</button>\n      <span>${esc(fmtDT(h.data))}</span>\n      <div class=\"hv\">${money(h.total)}</div>\n      ${(() => { const b = (Array.isArray(h.itens) ? h.itens : []).filter((x) => (Number(x.total) || 0) > 0.005); const dn = b.filter((x) => x.comprado).length; return b.length ? `<span class=\"hx ${dn === b.length ? 'all' : ''}\">${dn === b.length ? '✓ tudo comprado' : `${dn} de ${b.length} comprados`}</span>` : '' })()}\n      ${h.nota ? `<span>${esc(h.nota)}</span>` : ''}\n    </div>`).join('')\n\n  const row = (it, i) => {\n    const t = norm(it.ticker)\n    const p = Number(it.preco) || 0\n    const n = now[t]\n    const q = Number(it.quantidade) || 0\n    const tot = Number(it.total) || 0\n    let v = '<span class=\"free\">-</span>'\n    if (!it.valorDireto && p > 0 && n > 0) {\n      const pc = (n / p - 1) * 100\n      v = `<span class=\"var ${pc >= 0 ? 'u' : 'n'}\" title=\"hoje ${money(n)}\">${pc >= 0 ? '+' : ''}${pc.toFixed(1).replace('.', ',')}%</span>`\n    }\n    return `<tr data-key=\"h-${esc(t)}-${i}\" class=\"${tot > 0 ? 'has' : 'zero'}${it.comprado && tot > 0 ? ' done' : ''}\">\n      <td><span class=\"tk\">${tot > 0 ? `<input type=\"checkbox\" class=\"exec\" data-h=\"${esc(sel._id || '')}\" data-i=\"${itens.indexOf(it)}\" ${it.comprado ? 'checked' : ''} title=\"Já comprei\">` : '<span class=\"exsp\"></span>'}${logo(d2, it.ticker, it.valorDireto)}<span class=\"tkn\" data-t=\"${esc(t)}\" title=\"Ver preço nos aportes\">${esc(it.ticker)}</span></span></td>\n      <td>${it.valorDireto ? '<span class=\"free\">valor livre</span>' : money(p)}</td>\n      <td style=\"text-align:center\">${it.valorDireto ? '' : (q || '-')}</td>\n      <td class=\"tot\">${money(tot)}</td>\n      <td>${v}</td>\n    </tr>`\n  }\n\n  const cols = CATS.map((c) => {\n    const l = itens.filter((i) => (sums[i.categoria] != null ? i.categoria : 'outros') === c.key)\n    const lv = S.histOnly ? l.filter((i) => (Number(i.total) || 0) > 0.005) : l\n    return `<section class=\"card\" data-key=\"hc-${c.key}\">\n      <div class=\"ch\"><div class=\"row1\"><h2>${c.label}<em>${S.histOnly ? `${lv.length} de ${l.length}` : l.length}</em></h2><span class=\"sub\">${money(sums[c.key])}</span></div></div>\n      <table><thead><tr><th>Ativo</th><th>Preço</th><th style=\"text-align:center\">Qtd</th><th>Total</th><th>Até hoje</th></tr></thead>\n      <tbody>${lv.length ? lv.map(row).join('') : `<tr data-key=\"hempty-${c.key}\"><td colspan=\"5\" class=\"emptyrow\">Nada comprado nesta categoria</td></tr>`}</tbody></table>\n    </section>`\n  }).join('')\n\n  const buys = itens.filter((x) => (Number(x.total) || 0) > 0.005)\n  const doneN = buys.filter((x) => x.comprado).length\n  return `<div class=\"hwrap\">\n    <aside class=\"hl\"><div class=\"hlt\">Aportes salvos (${list.length})</div>${side}</aside>\n    <div>\n      <div class=\"hh\"><div><h2>Aporte de ${esc(fmtDT(sel.data))}</h2>${sel.nota ? `<span class=\"free\">${esc(sel.nota)}</span>` : ''}${buys.length ? `<div class=\"exprog ${doneN === buys.length ? 'all' : ''}\">${doneN === buys.length ? '✓ Tudo comprado' : `Comprado ${doneN} de ${buys.length} · marque cada ativo conforme executar na corretora`}</div>` : ''}</div><span class=\"big\">${money(sel.total)}</span></div>\n      <div class=\"cols hc\">${cols}</div>\n    </div>\n  </div>`\n}\n\nexport default {\n  css: `\n:host{--bg:#16181d;--sf:#1f2229;--ln:#2c3038;--tx:#e8eaed;--mu:#8a8f98;--ac:#2ecc71;--ac2:#1e8f4e;--in:#2a2e36;--dn:#ff6b6b}\n*{box-sizing:border-box}\n.app{height:100%;overflow:auto;background:var(--bg);color:var(--tx);font:14px/1.4 Inter,\"Segoe UI\",system-ui,sans-serif}\n.empty{padding:40px;color:var(--mu)}\n.top{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:18px 28px;border-bottom:1px solid var(--ln);position:sticky;top:0;background:var(--bg);z-index:2}\n.brand{display:flex;align-items:center;gap:10px}\n.logo{width:34px;height:34px;border-radius:9px;background:var(--ac);display:flex;align-items:center;justify-content:center;color:var(--bg);font-weight:700;font-size:15px}\n.brand h1{margin:0;font-size:17px;font-weight:600}\n.brand small{display:block;color:var(--mu);font-size:12px}\n.brand small.uso{font-size:11px;margin-top:1px;cursor:help}\n.brand small.uso.warn{color:#f5b041}\n.brand small.uso.crit{color:var(--dn)}\n.kpis{display:flex;align-items:center;gap:22px}\n.kpi{text-align:right}\n.kpi span{display:block;color:var(--mu);font-size:11px}\n.kpi b{font-size:14px;font-weight:600}\n.kpi.main b{font-size:22px;color:var(--ac)}\n.btn{background:transparent;border:1px solid var(--ln);color:var(--tx);border-radius:8px;padding:8px 12px;font:inherit;font-size:12px;cursor:pointer}\n.btn:hover{border-color:var(--mu)}\n.btn[disabled]{opacity:.6;cursor:progress}\n.wrap{padding:18px 28px 28px}\n.drop{display:flex;align-items:center;gap:14px;border:1.5px dashed var(--ln);border-radius:12px;padding:14px 16px;cursor:pointer;transition:border-color .15s,background .15s}\n.drop:hover,.drop.over{border-color:var(--ac);background:rgba(46,204,113,.06)}\n.drop:focus{outline:0}\n.drop.armed{border-style:solid;border-color:var(--ac);background:rgba(46,204,113,.10)}\n.drop.armed .ic{background:var(--ac);color:var(--bg)}\n.drop .ic{width:40px;height:40px;border-radius:10px;background:var(--in);display:flex;align-items:center;justify-content:center;color:var(--ac);flex:none}\n.drop .ic svg{width:20px;height:20px}\n.drop .t{flex:1;min-width:0}\n.drop .t b{display:block;font-weight:600}\n.drop .t span{color:var(--mu);font-size:12px}\n.kbd{font-size:11px;border:1px solid var(--ln);border-radius:5px;padding:1px 6px;color:var(--mu)}\n.spin{width:18px;height:18px;border:2px solid var(--ln);border-top-color:var(--ac);border-radius:50%;animation:sp .8s linear infinite}\n@keyframes sp{to{transform:rotate(360deg)}}\n.msg{margin-top:10px;font-size:13px;display:flex;flex-wrap:wrap;align-items:center;gap:8px}\n.msg.ok{color:var(--ac)} .msg.err{color:var(--dn)}\n.miss{margin-top:10px;display:flex;flex-wrap:wrap;gap:8px;align-items:center}\n.miss .lb{color:var(--mu);font-size:12px}\n.chip{display:flex;align-items:center;gap:6px;background:var(--sf);border:1px solid var(--ln);border-radius:8px;padding:4px 6px 4px 10px;font-size:12px}\n.chip em{font-style:normal;color:var(--mu)}\n.chip button{background:var(--in);border:0;color:var(--tx);border-radius:6px;padding:3px 7px;font:inherit;font-size:11px;cursor:pointer}\n.chip button:hover{background:var(--ac2)}\n.link{background:none;border:0;color:var(--mu);font:inherit;font-size:12px;cursor:pointer;text-decoration:underline}\n.cols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px;margin-top:18px;align-items:start}\n@media (max-width:1600px){.cols{grid-template-columns:repeat(2,minmax(0,1fr))}}\n@media (max-width:1100px){.cols{grid-template-columns:repeat(2,minmax(0,1fr))}}\n@media (max-width:720px){.cols{grid-template-columns:minmax(0,1fr)}}\n.card{background:var(--sf);border:1px solid var(--ln);border-radius:12px;overflow:hidden}\n.ch{padding:14px 16px 10px}\n.ch .row1{display:flex;justify-content:space-between;align-items:baseline}\n.ch h2{margin:0;font-size:15px;font-weight:600}\n.ch h2 em{font-style:normal;color:var(--mu);font-weight:400;font-size:12px;margin-left:6px}\n.ch .sub{color:var(--ac);font-weight:600;font-size:16px}\n.bar{height:4px;background:var(--in);border-radius:4px;margin-top:10px;overflow:hidden}\n.bar i{display:block;height:100%;background:var(--ac);border-radius:4px}\n.pct{color:var(--mu);font-size:11px;margin-top:4px}\ntable{width:100%;border-collapse:collapse;font-size:13px}\nth{color:var(--mu);font-weight:400;font-size:11px;text-align:right;padding:6px 8px;border-top:1px solid var(--ln);white-space:nowrap}\nth:first-child{text-align:left;padding-left:16px}\ntd{padding:5px 8px;border-top:1px solid var(--ln);text-align:right;white-space:nowrap}\ntd:first-child{text-align:left;padding-left:16px;font-weight:500}\ntr.has td.tot{color:var(--tx)}\ntd.tot{color:var(--mu)}\n.dot{display:inline-block;width:6px;height:6px;border-radius:50%;margin-right:7px;background:transparent;vertical-align:middle}\ntr.up .dot{background:var(--ac)}\n.in{width:64px;background:var(--in);border:1px solid transparent;color:var(--tx);border-radius:6px;padding:4px 6px;font:inherit;font-size:13px;text-align:right;outline:0}\n.in:focus{border-color:var(--ac)}\n.in.q{width:46px;text-align:center}\n.in.v{width:90px}\n.in.p{background:transparent}\n.in.p:hover,.in.p:focus{background:var(--in)}\n.d{display:inline-block;width:12px;font-size:10px;margin-left:3px;text-align:left}\n.d.u{color:var(--ac)} .d.n{color:var(--dn)}\n.free{color:var(--mu);font-size:11px}\n.del{background:none;border:0;color:var(--mu);cursor:pointer;font-size:14px;opacity:0;padding:0 8px 0 0}\ntr:hover .del{opacity:1}\n.del:hover{color:var(--dn)}\n.cf{padding:10px 16px;border-top:1px solid var(--ln)}\n.add{background:none;border:0;color:var(--mu);font:inherit;font-size:12px;cursor:pointer;padding:0}\n.add:hover{color:var(--ac)}\n.btn.pri{background:var(--ac);color:var(--bg);border-color:var(--ac);font-weight:600}\n.btn.pri:hover{background:#3ddc80}\n.hwrap{display:grid;grid-template-columns:250px minmax(0,1fr);gap:16px;padding:18px 28px 28px;align-items:start}\n.hl{display:flex;flex-direction:column;gap:8px;position:sticky;top:90px}\n.hlt{color:var(--mu);font-size:12px;margin-bottom:2px}\n.hi{background:var(--sf);border:1px solid var(--ln);border-radius:10px;padding:10px 12px;cursor:pointer;position:relative}\n.hi:hover{border-color:var(--mu)}\n.hi.on{border-color:var(--ac)}\n.hi span{display:block;color:var(--mu);font-size:12px;padding-right:16px}\n.hi .hv{color:var(--tx);font-weight:600;font-size:15px;margin:2px 0}\n.hi.on .hv{color:var(--ac)}\n.hdel{position:absolute;top:6px;right:6px;background:none;border:0;color:var(--mu);cursor:pointer;font-size:14px;opacity:0}\n.hi:hover .hdel{opacity:1}\n.hdel:hover{color:var(--dn)}\n.hh{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:14px}\n.hh h2{margin:0;font-size:17px;font-weight:600}\n.hh .big{color:var(--ac);font-size:20px;font-weight:600}\n.hc{margin-top:0}\n@media (max-width:1300px){.hc{grid-template-columns:repeat(2,minmax(0,1fr))}}\ntr.zero td{color:var(--mu)}\n.var{font-size:12px}\n.var.u{color:var(--ac)} .var.n{color:var(--dn)}\n.hempty{display:flex;flex-direction:column;align-items:flex-start;gap:8px;background:var(--sf);border:1px solid var(--ln);border-radius:12px;padding:24px}\n.hempty span{color:var(--mu);font-size:13px}\n.tkn{cursor:pointer}\n.tkn:hover{color:var(--ac);text-decoration:underline}\n.in.m{width:78px}\ntd.tot.ok,tr.has td.tot.ok{color:var(--ac)}\ntd.tot.under,tr.has td.tot.under{color:#f5b041}\ntd.tot.over,tr.has td.tot.over{color:var(--dn)}\n.orc{display:flex;flex-direction:column;align-items:flex-end}\n.orcin{width:120px;background:var(--in);border:1px solid transparent;color:var(--tx);border-radius:6px;padding:4px 8px;font:inherit;font-size:14px;font-weight:600;text-align:right;outline:0}\n.orcin:focus{border-color:var(--ac)}\n.kpi b.ovr{color:var(--dn)}\n.os{display:block;font-size:11px;color:var(--mu);margin-top:2px}\n.os.ok{color:var(--ac)} .os.under{color:#f5b041} .os.over{color:var(--dn)}\n.ovl{position:fixed;inset:0;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;z-index:20;padding:24px}\n.ovp{width:min(720px,100%);max-height:90vh;overflow:auto;background:var(--sf);border:1px solid var(--ln);border-radius:14px;padding:18px 20px}\n.ovh{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}\n.cst{display:flex;justify-content:space-between;gap:12px;color:var(--mu);font-size:12px;margin-bottom:6px}\n.chart{width:100%;height:auto;display:block}\n.chart .gl{stroke:var(--ln);stroke-width:1}\n.chart .ax{fill:var(--mu);font-size:11px}\n.chart .ln{fill:none;stroke:var(--ac);stroke-width:2.5}\n.chart .pb{fill:var(--ac);stroke:var(--sf);stroke-width:2}\n.chart .pn{fill:var(--sf);stroke:var(--ac);stroke-width:2}\n.ctab{margin-top:10px}\n.grip{cursor:grab;color:var(--mu);opacity:.35;display:inline-flex;align-items:center;margin:0 8px 0 -8px;padding:4px 3px;user-select:none;border-radius:4px}\n.grip:hover{background:var(--in)}\n.grip svg{pointer-events:none}\n.grip.off{visibility:hidden;pointer-events:none}\n.sth{cursor:pointer;user-select:none}\n.sth:hover{color:var(--tx)}\n.sth.on{color:var(--ac)}\n.tools b{color:var(--tx);font-weight:500}\n.tools .link{font-size:12px}\n.tools{display:flex;align-items:center;justify-content:flex-end;gap:12px;margin-top:16px;font-size:12px;color:var(--mu)}\n.sortsel{background:var(--in);color:var(--tx);border:1px solid transparent;border-radius:6px;padding:5px 8px;font:inherit;font-size:12px;margin-left:6px;outline:0;cursor:pointer}\n.sortsel:focus{border-color:var(--ac)}\n.exec{accent-color:var(--ac);width:15px;height:15px;margin:0 9px 0 0;cursor:pointer;flex:none}\n.exsp{display:inline-block;width:24px;flex:none}\ntr.done .tkn{color:var(--ac)}\n.exprog{font-size:12px;color:#f5b041;margin-top:4px}\n.exprog.all{color:var(--ac)}\n.hi .hx{color:#f5b041}\n.hi .hx.all{color:var(--ac)}\n.grip{touch-action:none}\n.app.dragmode{user-select:none;cursor:grabbing}\n.app.dragmode *{cursor:grabbing !important}\n.btn.histonly.on{border-color:var(--ac);color:var(--ac)}\ntr:hover .grip{opacity:1}\n.grip:active{cursor:grabbing}\ntr.dragging{opacity:.4}\ntr.dropb td{box-shadow:inset 0 2px 0 var(--ac)}\ntr.dropa td{box-shadow:inset 0 -2px 0 var(--ac)}\n.btn.onlybtn.on{border-color:var(--ac);color:var(--ac)}\ntd.emptyrow{color:var(--mu);font-weight:400;text-align:center;padding:16px}\n.mrow{display:flex;align-items:center;gap:8px;margin-top:10px;font-size:12px;color:var(--mu)}\n.metain{width:120px;background:var(--in);border:1px solid transparent;color:var(--tx);border-radius:6px;padding:4px 8px;font:inherit;font-size:13px;font-weight:600;text-align:right;outline:0}\n.metain:focus{border-color:var(--ac)}\n.mrow .os{display:inline;margin:0 0 0 4px}\n.bar i.over{background:var(--dn)}\ntd.lp{color:var(--mu)}\n.tk{display:inline-flex;align-items:center}\n.lg{width:24px;height:24px;border-radius:7px;margin-right:10px;object-fit:cover;flex:none;display:inline-block;background:#fff}\n.lg.ini{background:var(--in);color:var(--mu);font-size:10px;font-weight:600;line-height:24px;text-align:center;letter-spacing:.02em}\n.chip .lg{width:18px;height:18px;line-height:18px;border-radius:5px;margin-right:2px;font-size:8px}\n.savebar{display:flex;align-items:center;gap:10px;background:var(--sf);border:1px solid var(--ac2);border-radius:12px;padding:12px 14px;margin-bottom:14px}\n.savebar .st{flex:1;min-width:0}\n.savebar .st b{display:block;font-weight:600}\n.savebar .st span{color:var(--mu);font-size:12px}\n.note{width:240px;background:var(--in);border:1px solid transparent;color:var(--tx);border-radius:8px;padding:8px 10px;font:inherit;font-size:13px;outline:0}\n.note:focus{border-color:var(--ac)}\n`,\n\n  render(d2) {\n    const ds = d2.data('ativos')\n    if (!ds) return `<div class=\"app\"><p class=\"empty\">Tabela de ativos não vinculada.</p></div>`\n    if (ds.loading) return `<div class=\"app\"><p class=\"empty\">Carregando ativos…</p></div>`\n    const S = d2.state\n    const draft = S.draft || {}\n    const upd = S.updated || {}\n    const recs = (ds.records() || []).slice().sort((a, b) => (Number(a.ordem) || 0) - (Number(b.ordem) || 0))\n\n    const val = (r, k) => {\n      const key = (r._id || '') + ':' + k\n      return draft[key] != null ? parse(draft[key]) : Number(r[k]) || 0\n    }\n    const shown = (r, k, dec) => {\n      const key = (r._id || '') + ':' + k\n      if (draft[key] != null) return draft[key]\n      const n = Number(r[k]) || 0\n      if (!dec) return n ? String(n) : ''\n      return n ? NUM.format(n) : ''\n    }\n    const total = (r) => (r.valorDireto ? val(r, 'valor') : val(r, 'preco') * val(r, 'quantidade'))\n\n    const sums = {}\n    CATS.forEach((c) => { sums[c.key] = 0 })\n    recs.forEach((r) => {\n      const k = sums[r.categoria] != null ? r.categoria : 'outros'\n      sums[k] += total(r)\n    })\n    const grand = Object.values(sums).reduce((a, b) => a + b, 0)\n    const msums = {}\n    CATS.forEach((c) => { msums[c.key] = 0 })\n    const metas = S.metas || {}\n    CATS.forEach((c) => { msums[c.key] = parse(metas[c.key] != null ? metas[c.key] : '') })\n    const mgrand = Object.values(msums).reduce((a, b) => a + b, 0)\n    const orc = parse(S.orcamento != null ? S.orcamento : '')\n    let orcStatus = ''\n    if (orc > 0) {\n      const df = orc - grand\n      orcStatus = df > 0.005 ? `<small class=\"os under\">faltam ${money(df)}</small>` : df < -0.005 ? `<small class=\"os over\">passou ${money(-df)}</small>` : '<small class=\"os ok\">no orçamento</small>'\n    }\n    const last = recs.reduce((m, r) => (r.atualizadoEm && r.atualizadoEm > m ? r.atualizadoEm : m), '')\n    const lastTxt = last ? new Date(last).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }) : 'nunca'\n\n    const row = (r, i) => {\n      const id = esc(r._id || '')\n      const t = norm(r.ticker)\n      const u = upd[t]\n      const tot = total(r)\n      const cur = Number(r.preco) || 0\n      const prev = Number(r.precoAnterior) || 0\n      let delta = '<span class=\"d\"></span>'\n      if (!r.valorDireto && cur > 0 && prev > 0 && Math.abs(cur - prev) > 0.0001) {\n        const pc = (cur / prev - 1) * 100\n        const tip = `last price ${money(prev)} (${pc >= 0 ? '+' : ''}${pc.toFixed(2).replace('.', ',')}%)`\n        delta = cur > prev ? `<span class=\"d u\" title=\"${tip}\">▲</span>` : `<span class=\"d n\" title=\"${tip}\">▼</span>`\n      }\n      const lastCell = r.valorDireto ? '' : (prev > 0 ? NUM.format(prev) : '<span class=\"free\">-</span>')\n\n      const priceCell = r.valorDireto\n        ? `<span class=\"free\">valor livre</span>`\n        : `<input class=\"in p\" data-id=\"${id}\" data-k=\"preco\" value=\"${esc(shown(r, 'preco', true))}\" placeholder=\"0,00\">${delta}`\n      const qtyCell = r.valorDireto ? '' : `<input class=\"in q\" data-id=\"${id}\" data-k=\"quantidade\" value=\"${esc(shown(r, 'quantidade', false))}\" placeholder=\"0\">`\n      const totCell = r.valorDireto\n        ? `<input class=\"in v\" data-id=\"${id}\" data-k=\"valor\" value=\"${esc(shown(r, 'valor', true))}\" placeholder=\"R$ 0,00\">`\n        : money(tot)\n      return `<tr data-key=\"${esc(r._id || 'pending-' + i)}\" data-rid=\"${id}\" data-cat=\"${esc(sums[r.categoria] != null ? r.categoria : 'outros')}\" class=\"${u ? 'up' : ''} ${tot > 0 ? 'has' : ''}\">\n        <td><span class=\"tk\"><span class=\"grip ${sortBy !== 'manual' ? 'off' : ''}\" data-id=\"${id}\" title=\"Arraste para reordenar\"><svg viewBox=\"0 0 8 14\" width=\"8\" height=\"14\" fill=\"currentColor\"><circle cx=\"2\" cy=\"2\" r=\"1.3\"/><circle cx=\"6\" cy=\"2\" r=\"1.3\"/><circle cx=\"2\" cy=\"7\" r=\"1.3\"/><circle cx=\"6\" cy=\"7\" r=\"1.3\"/><circle cx=\"2\" cy=\"12\" r=\"1.3\"/><circle cx=\"6\" cy=\"12\" r=\"1.3\"/></svg></span><span class=\"dot\"></span>${logo(d2, r.ticker, r.valorDireto)}<span class=\"tkn\" data-t=\"${esc(t)}\" title=\"Ver preço nos aportes\">${esc(String(r.ticker || '').toUpperCase())}</span></span></td>\n        <td class=\"lp\">${lastCell}</td>\n        <td>${priceCell}</td>\n        <td>${qtyCell}</td>\n        <td class=\"tot\">${totCell}</td>\n        <td style=\"padding:0\"><button class=\"del\" data-id=\"${id}\" data-t=\"${esc(r.ticker)}\" title=\"Remover\">×</button></td>\n      </tr>`\n    }\n\n    const sk = S.sortKey || ''\n    const sd = S.sortDir === 'desc' ? -1 : 1\n    const sortBy = sk ? 'custom' : 'manual'\n    const SK_LABEL = { ticker: 'Ativo', precoAnterior: 'Last price', preco: 'Preço', quantidade: 'Qtd', total: 'Total' }\n    const sortRows = (arr) => {\n      const a = arr.slice()\n      if (!sk) return a\n      const kv = (r) => {\n        if (sk === 'ticker') return String(r.ticker || '')\n        if (sk === 'total') return total(r)\n        if (r.valorDireto) return null\n        return Number(r[sk]) || 0\n      }\n      a.sort((x, y) => {\n        const vx = kv(x), vy = kv(y)\n        if (vx === null && vy === null) return 0\n        if (vx === null) return 1\n        if (vy === null) return -1\n        if (typeof vx === 'string') return vx.localeCompare(vy) * sd\n        return (vx - vy) * sd\n      })\n      return a\n    }\n    const th = (k, label, al) => `<th class=\"sth ${sk === k ? 'on' : ''}\" data-sk=\"${k}\"${al ? ` style=\"text-align:${al}\"` : ''} title=\"Ordenar por ${label}\">${label}${sk === k ? (sd > 0 ? ' ▲' : ' ▼') : ''}</th>`\n\n    const cols = CATS.map((c) => {\n      const list = recs.filter((r) => (sums[r.categoria] != null ? r.categoria : 'outros') === c.key)\n      const vis = S.onlyBuy ? list.filter((r) => total(r) > 0.005) : list\n      const pct = grand > 0 ? (sums[c.key] / grand) * 100 : 0\n      const mt = msums[c.key]\n      let mst = ''\n      let mcls = ''\n      if (mt > 0) {\n        const TOL = 100\n        const df = mt - sums[c.key]\n        if (df < -TOL) { mcls = 'over'; mst = `<small class=\"os over\">passou ${money(-df)}</small>` }\n        else if (df > TOL) { mcls = 'under'; mst = `<small class=\"os under\">faltam ${money(df)}</small>` }\n        else {\n          mcls = 'ok'\n          const det = Math.abs(df) < 0.005 ? '' : df > 0 ? ` · faltam ${money(df)}` : ` · passou ${money(-df)}`\n          mst = `<small class=\"os ok\">na meta${det}</small>`\n        }\n      }\n      const barW = mt > 0 ? Math.min(100, (sums[c.key] / mt) * 100) : pct\n      return `<section class=\"card\" data-key=\"col-${c.key}\">\n        <div class=\"ch\">\n          <div class=\"row1\"><h2>${c.label}<em>${S.onlyBuy ? `${vis.length} de ${list.length}` : list.length}</em></h2><span class=\"sub\">${money(sums[c.key])}</span></div>\n          <div class=\"mrow\"><span>Meta</span><input class=\"metain\" data-cat=\"${c.key}\" value=\"${esc(metas[c.key] != null ? metas[c.key] : '')}\" placeholder=\"R$ 0,00\">${mst}</div>\n          <div class=\"bar\"><i class=\"${mcls}\" style=\"width:${barW.toFixed(1)}%\"></i></div>\n          <div class=\"pct\">${pct.toFixed(1).replace('.', ',')}% do aporte${mt > 0 ? ` · ${Math.round((sums[c.key] / mt) * 100)}% da meta` : ''}</div>\n        </div>\n        <table>\n          <thead><tr>${th('ticker', 'Ativo')}${th('precoAnterior', 'Last price')}${th('preco', 'Preço')}${th('quantidade', 'Qtd', 'center')}${th('total', 'Total')}<th></th></tr></thead>\n          <tbody>${vis.length ? sortRows(vis).map(row).join('') : `<tr data-key=\"empty-${c.key}\"><td colspan=\"6\" class=\"emptyrow\">Nada para comprar nesta categoria</td></tr>`}</tbody>\n        </table>\n        <div class=\"cf\"><button class=\"add\" data-cat=\"${c.key}\">+ Adicionar ativo</button></div>\n      </section>`\n    }).join('')\n\n    const miss = (S.missing || []).length\n      ? `<div class=\"miss\"><span class=\"lb\">Fora da sua lista:</span>${S.missing.map((m) =>\n        `<span class=\"chip\" data-key=\"m-${esc(m.ticker)}\">${logo(d2, m.ticker)}<b>${esc(m.ticker)}</b><em>${money(m.preco)}</em>\n          ${CATS.map((c) => `<button class=\"addm\" data-t=\"${esc(m.ticker)}\" data-p=\"${m.preco}\" data-cat=\"${c.key}\">+ ${c.label}</button>`).join('')}\n        </span>`).join('')}<button class=\"link dismiss\">ignorar</button></div>`\n      : ''\n\n    const dropInner = S.importing\n      ? `<div class=\"ic\"><div class=\"spin\"></div></div><div class=\"t\"><b>Lendo a print…</b><span>Extraindo tickers e preço atual. A imagem não é salva.</span></div>`\n      : `<div class=\"ic\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 8h.01\"/><path d=\"M12 21H6a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v6\"/><path d=\"m3 16 5-5c.93-.9 2.07-.9 3 0l4 4\"/><path d=\"M19 22v-6\"/><path d=\"m22 19-3-3-3 3\"/></svg></div>\n         <div class=\"t\"><b>${S.armed ? 'Pronto, cole a print agora (Ctrl+V)' : 'Atualizar cotações pela print'}</b><span>${S.armed ? 'A área está ativa. ' : 'Clique aqui e cole com <span class=\"kbd\">Ctrl+V</span> (também funciona arrastando a imagem). '}Só o ticker e o preço atual são usados; a imagem é descartada.</span></div>`\n\n    return `<div class=\"app\">\n      <header class=\"top\">\n        <div class=\"brand\"><div class=\"logo\">IM</div><div><h1>Invest manager</h1><small>Cotações atualizadas em ${esc(lastTxt)}</small>${S.uso && S.uso.limite ? (() => { const used = Math.max(0, S.uso.limite - S.uso.restantes); const pc = used / S.uso.limite; return `<small class=\"uso ${pc >= 0.9 ? 'crit' : pc >= 0.7 ? 'warn' : ''}\" title=\"Requisições usadas na brapi na janela móvel de 30 dias. Registrado em ${esc(fmtDT(S.uso.em))}\">brapi: ${used.toLocaleString('pt-BR')}/${S.uso.limite.toLocaleString('pt-BR')} req. (30 dias)</small>` })() : ''}</div></div>\n        <div class=\"kpis\">\n          <div class=\"kpi orc\"><span>Orçamento do aporte</span><input class=\"orcin\" value=\"${esc(S.orcamento != null ? S.orcamento : '')}\" placeholder=\"R$ 0,00\"></div>\n          ${mgrand > 0 ? `<div class=\"kpi\"><span>Soma das metas</span><b class=\"${orc > 0 && mgrand > orc + 0.005 ? 'ovr' : ''}\">${money(mgrand)}</b></div>` : ''}\n          <div class=\"kpi main\"><span>Total a investir</span><b>${money(grand)}</b>${orcStatus}</div>\n          ${S.view === 'hist' ? `<button class=\"btn histonly ${S.histOnly ? 'on' : ''}\">${S.histOnly ? 'Mostrar tudo' : 'Mostrar só o que comprei'}</button>` : `<button class=\"btn onlybtn ${S.onlyBuy ? 'on' : ''}\">${S.onlyBuy ? 'Mostrar tudo' : 'Mostrar só o que vou comprar'}</button>`}\n          <button class=\"btn zero\">Zerar quantidades</button>\n          <button class=\"btn refresh\" ${S.refreshing ? 'disabled' : ''}>${S.refreshing ? 'Atualizando…' : 'Atualizar preços'}</button>\n          ${S.view === 'hist' ? '<button class=\"btn back\">Voltar</button>' : '<button class=\"btn histbtn\">Histórico</button>'}\n          <button class=\"btn pri save\">${S.saving ? 'Salvando…' : 'Salvar aporte'}</button>\n        </div>\n      </header>\n      ${S.view === 'hist' ? renderHist(d2, recs) : `<div class=\"wrap\">\n        ${S.confirmSave ? `<div class=\"savebar\"><div class=\"st\"><b>Salvar aporte de hoje · ${money(grand)}</b><span>Guarda a data, o preço de todos os ativos (inclusive os sem compra) e as quantidades escolhidas.</span></div><input class=\"note\" placeholder=\"Observação (opcional)\" maxlength=\"120\"><button class=\"btn cancelsave\">Cancelar</button><button class=\"btn pri dosave\">${S.saving ? 'Salvando…' : 'Confirmar'}</button></div>` : ''}\n        <div class=\"drop ${S.over ? 'over' : ''} ${S.armed ? 'armed' : ''}\" tabindex=\"0\">${dropInner}</div>\n        ${S.msg ? `<div class=\"msg ${S.msg.type}\">${esc(S.msg.text)}</div>` : ''}\n        ${miss}\n        ${sk ? `<div class=\"tools\"><span>Ordenado por <b>${SK_LABEL[sk] || sk}</b> ${sd > 0 ? '(crescente)' : '(decrescente)'} · para arrastar e reordenar, <button class=\"link clrsort\">volte para a sua ordem</button></span></div>` : ''}\n        <div class=\"cols\">${cols}</div>\n      </div>`}\n      ${S.chart ? renderChart(d2, S.chart) : ''}\n    </div>`\n  },\n\n  mounted(d2) {\n    const S = d2.state\n    const ds0 = d2.data('ativos')\n    if (ds0 && ds0.loadAll) ds0.loadAll()\n    const hs0 = d2.data('aportes')\n    if (hs0 && hs0.loadAll) hs0.loadAll()\n    try { const ov = window.localStorage.getItem('invest-orcamento'); if (ov != null) S.orcamento = ov } catch (err) {}\n    d2.on('.orcin', 'input', (e, el) => { S.orcamento = el.value; try { window.localStorage.setItem('invest-orcamento', el.value) } catch (err) {} })\n    d2.on('.orcin', 'keydown', (e, el) => { if (e.key === 'Enter') el.blur() })\n    try { const mv = window.localStorage.getItem('invest-metas'); if (mv) S.metas = JSON.parse(mv) } catch (err) {}\n    d2.on('.metain', 'input', (e, el) => {\n      S.metas = { ...(S.metas || {}), [el.dataset.cat]: el.value }\n      try { window.localStorage.setItem('invest-metas', JSON.stringify(S.metas)) } catch (err) {}\n    })\n    d2.on('.metain', 'keydown', (e, el) => { if (e.key === 'Enter') el.blur() })\n    d2.on('.tkn', 'click', (e, el) => { if (el.dataset.t) S.chart = el.dataset.t })\n    d2.on('.closechart', 'click', () => { S.chart = null })\n    d2.on('.ovl', 'click', (e, el) => { if (e.target === el) S.chart = null })\n    d2.on(window, 'keydown', (e) => { if (e.key === 'Escape' && S.chart) S.chart = null })\n\n\n    const findRec = (id) => (d2.data('ativos').records() || []).find((r) => r._id === id)\n    const nextOrder = (cat) => (d2.data('ativos').records() || []).filter((r) => r.categoria === cat).reduce((m, r) => Math.max(m, Number(r.ordem) || 0), 0) + 1\n\n    d2.on('.in', 'input', (e, el) => {\n      S.draft = { ...(S.draft || {}), [el.dataset.id + ':' + el.dataset.k]: el.value }\n    })\n    d2.on('.in', 'keydown', (e, el) => {\n      if (e.key === 'Enter') { el.blur(); return }\n      if (e.key === 'Tab' && (el.classList.contains('q') || el.classList.contains('v'))) {\n        const all = d2.$('.in.q, .in.v')\n        const nx = all[all.indexOf(el) + (e.shiftKey ? -1 : 1)]\n        if (nx) { e.preventDefault(); nx.focus(); try { nx.select() } catch (err) {} }\n      }\n    })\n    d2.on('.in', 'focus', (e, el) => { if (el.classList.contains('q') || el.classList.contains('v')) { try { el.select() } catch (err) {} } })\n    const loadUso = async () => {\n      try {\n        const rr = await fetch('https://massayuki-playground.data2apis.com/rush/invest-uso-brapi', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })\n        if (!rr.ok) return\n        const j = await rr.json()\n        const u = j && j.data && j.data.limite != null ? j.data : j\n        if (u && u.limite) S.uso = { limite: Number(u.limite), restantes: Number(u.restantes), em: u.em }\n      } catch (err) {}\n    }\n    loadUso()\n    d2.on('.onlybtn', 'click', () => { S.onlyBuy = !S.onlyBuy })\n    d2.on('.refresh', 'click', async () => {\n      if (S.refreshing) return\n      S.refreshing = true\n      S.view = 'main'\n      S.msg = { type: 'ok', text: 'Buscando preços na brapi… isso leva alguns segundos.' }\n      const ds = d2.data('ativos')\n      const antes = {}\n      ;(ds.records() || []).forEach((r) => { antes[norm(r.ticker)] = Number(r.preco) || 0 })\n      try {\n        const resp = await fetch('https://massayuki-playground.data2apis.com/rush/invest-atualizar-precos', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })\n        let out = null\n        try { out = await resp.json() } catch (err) {}\n        if (!resp.ok) throw new Error(resp.status === 401 || resp.status === 403 ? 'sem permissão (confira se o seu IP mudou)' : 'erro ' + resp.status)\n        const r = (out && out.data && out.data.ok != null) ? out.data : out\n        if (ds.loadAll) await ds.loadAll()\n        else if (ds.refresh) await ds.refresh()\n        const upd = {}\n        ;(ds.records() || []).forEach((x) => {\n          const t = norm(x.ticker)\n          if (!x.valorDireto && antes[t] != null && Math.abs((Number(x.preco) || 0) - antes[t]) > 0.0001) upd[t] = { de: antes[t], para: Number(x.preco) || 0 }\n        })\n        S.updated = upd\n        const n = r && r.atualizados != null ? r.atualizados : Object.keys(upd).length\n        const mud = r && Array.isArray(r.mudaram) ? r.mudaram.length : Object.keys(upd).length\n        const sem = r && Array.isArray(r.semCotacao) && r.semCotacao.length ? ` · sem cotação: ${r.semCotacao.join(', ')}` : ''\n        S.msg = { type: 'ok', text: `Preços atualizados pela brapi: ${n} ativo(s) consultado(s), ${mud} com preço diferente${sem}.` }\n        if (r && r.uso && Number(r.uso.limite) > 0 && Number(r.uso.restantes) >= 0) S.uso = { limite: Number(r.uso.limite), restantes: Number(r.uso.restantes), em: new Date().toISOString() }\n        else loadUso()\n      } catch (err) {\n        S.msg = { type: 'err', text: 'Não consegui atualizar os preços: ' + (err && err.message ? err.message : err) }\n      } finally {\n        S.refreshing = false\n      }\n    })\n    try { const sv = JSON.parse(window.localStorage.getItem('invest-sort2') || 'null'); if (sv && sv.k) { S.sortKey = sv.k; S.sortDir = sv.d } } catch (err) {}\n    const saveSort = () => { try { window.localStorage.setItem('invest-sort2', JSON.stringify({ k: S.sortKey || '', d: S.sortDir || 'asc' })) } catch (err) {} }\n    d2.on('.sth', 'click', (e, el) => {\n      const k = el.dataset.sk\n      if (!k) return\n      if (S.sortKey !== k) { S.sortKey = k; S.sortDir = 'asc' }\n      else if (S.sortDir !== 'desc') S.sortDir = 'desc'\n      else { S.sortKey = ''; S.sortDir = 'asc' }\n      saveSort()\n    })\n    d2.on('.clrsort', 'click', () => { S.sortKey = ''; S.sortDir = 'asc'; saveSort() })\n    d2.on('.exec', 'change', async (e, el) => {\n      const hs = d2.data('aportes')\n      const rec = (hs.records() || []).find((h) => h._id === el.dataset.h)\n      if (!rec) return\n      const idx = Number(el.dataset.i)\n      const itens = (Array.isArray(rec.itens) ? rec.itens : []).map((it, i) => (i === idx ? { ...it, comprado: el.checked } : it))\n      await hs.update({ ...rec, itens: itens })\n    })\n    d2.on('.histonly', 'click', () => { S.histOnly = !S.histOnly })\n    d2.on('.in', 'change', async (e, el) => {\n      const key = el.dataset.id + ':' + el.dataset.k\n      const rec = findRec(el.dataset.id)\n      if (rec) {\n        const patch = { ...rec, [el.dataset.k]: parse(el.value) }\n        if (el.dataset.k === 'preco') {\n          patch.atualizadoEm = new Date().toISOString()\n          const old = Number(rec.preco) || 0\n          if (old > 0 && Math.abs(old - patch.preco) > 0.0001) patch.precoAnterior = old\n        }\n        await d2.data('ativos').update(patch)\n      }\n      const dr = { ...(S.draft || {}) }\n      delete dr[key]\n      S.draft = dr\n    })\n\n    d2.on('.drop', 'click', (e, el) => { if (!S.importing) { S.armed = true; try { el.focus({ preventScroll: true }) } catch (err) {} } })\n    d2.on(document, 'pointerdown', (e) => {\n      if (!S.armed) return\n      const path = (e.composedPath && e.composedPath()) || []\n      if (!path.some((n) => n && n.classList && n.classList.contains('drop'))) S.armed = false\n    })\n    d2.on(document, 'paste', (e) => {\n      const items = Array.from((e.clipboardData && e.clipboardData.items) || [])\n      const it = items.find((i) => String(i.type).startsWith('image/'))\n      if (it) { e.preventDefault(); importFile(d2, it.getAsFile()) }\n    })\n    const isFileDrag = (e) => !!(e.dataTransfer && Array.from(e.dataTransfer.types || []).includes('Files'))\n    d2.on('.app', 'dragover', (e) => { if (!isFileDrag(e)) return; e.preventDefault(); if (!S.over) S.over = true })\n    d2.on('.app', 'dragleave', (e) => { if (!e.relatedTarget && S.over) S.over = false })\n    d2.on('.app', 'drop', (e) => {\n      if (!isFileDrag(e)) return\n      e.preventDefault()\n      S.over = false\n      const f = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0]\n      importFile(d2, f)\n    })\n\n    let drag = null\n    const clearMarks = () => d2.$$('tr.dropb, tr.dropa').forEach((n) => n.classList.remove('dropb', 'dropa'))\n    const endDrag = () => {\n      clearMarks()\n      d2.$$('tr.dragging').forEach((n) => n.classList.remove('dragging'))\n      const ap = d2.$('.app')\n      if (ap) ap.classList.remove('dragmode')\n    }\n    d2.on('.grip', 'pointerdown', (e, el) => {\n      if (e.button != null && e.button !== 0) return\n      e.preventDefault()\n      const tr = el.closest('tr')\n      if (!tr || !el.dataset.id) return\n      drag = { id: el.dataset.id, cat: tr.dataset.cat, target: null, after: false }\n      tr.classList.add('dragging')\n      const ap = d2.$('.app')\n      if (ap) ap.classList.add('dragmode')\n    })\n    d2.on(window, 'pointermove', (e) => {\n      if (!drag) return\n      const sr = d2.root && d2.root.getRootNode ? d2.root.getRootNode() : document\n      const hit = sr.elementFromPoint ? sr.elementFromPoint(e.clientX, e.clientY) : document.elementFromPoint(e.clientX, e.clientY)\n      const row = hit && hit.closest ? hit.closest('tr[data-rid]') : null\n      clearMarks()\n      if (!row || row.dataset.cat !== drag.cat || row.dataset.rid === drag.id) { drag.target = null; return }\n      const rc = row.getBoundingClientRect()\n      drag.after = e.clientY > rc.top + rc.height / 2\n      drag.target = row.dataset.rid\n      row.classList.add(drag.after ? 'dropa' : 'dropb')\n    })\n    d2.on(window, 'pointercancel', () => { drag = null; endDrag() })\n    d2.on(window, 'pointerup', async () => {\n      if (!drag) return\n      const d = drag\n      drag = null\n      endDrag()\n      if (!d.target || d.target === d.id) return\n      const ds = d2.data('ativos')\n      const catOf = (r) => (['acoes', 'fiis', 'outros'].includes(r.categoria) ? r.categoria : 'outros')\n      const list = (ds.records() || []).filter((r) => r._id && catOf(r) === d.cat).sort((a, b) => (Number(a.ordem) || 0) - (Number(b.ordem) || 0))\n      const from = list.findIndex((r) => r._id === d.id)\n      if (from < 0) return\n      const mv = list.splice(from, 1)[0]\n      let to = list.findIndex((r) => r._id === d.target)\n      if (to < 0) return\n      if (d.after) to += 1\n      list.splice(to, 0, mv)\n      await Promise.all(list.map((r, i) => (Number(r.ordem) !== i + 1 ? ds.update({ ...r, ordem: i + 1 }) : null)).filter(Boolean))\n    })\n\n    d2.on('.addm', 'click', async (e, el) => {\n      const t = el.dataset.t\n      const p = Number(el.dataset.p) || 0\n      const cat = el.dataset.cat\n      S.missing = (S.missing || []).filter((m) => m.ticker !== t)\n      S.updated = { ...(S.updated || {}), [t]: { de: 0, para: p } }\n      await d2.data('ativos').create({ ticker: t, categoria: cat, preco: p, quantidade: 0, ordem: nextOrder(cat), atualizadoEm: new Date().toISOString() })\n    })\n    d2.on('.dismiss', 'click', () => { S.missing = [] })\n\n    d2.on('.add', 'click', async (e, el) => {\n      const cat = el.dataset.cat\n      const raw = await d2.dialog.prompt('Ticker do novo ativo (ex: VALE3)')\n      const t = norm(raw)\n      if (!t) return\n      if ((d2.data('ativos').records() || []).some((r) => norm(r.ticker) === t)) {\n        S.msg = { type: 'err', text: `${t} já está na lista.` }\n        return\n      }\n      await d2.data('ativos').create({ ticker: t, categoria: cat, preco: 0, quantidade: 0, ordem: nextOrder(cat) })\n    })\n\n    d2.on('.del', 'click', async (e, el) => {\n      if (!el.dataset.id) return\n      const ok = await d2.dialog.confirm(`Remover ${el.dataset.t} da lista?`)\n      if (ok) await d2.data('ativos').remove(el.dataset.id)\n    })\n\n    d2.on('.histbtn', 'click', () => {\n      const h = d2.data('aportes')\n      if (h && h.refresh) h.refresh()\n      S.histSel = null\n      S.view = 'hist'\n    })\n    d2.on('.back', 'click', () => { S.view = 'main' })\n    d2.on('.hi', 'click', (e, el) => {\n      if (e.target && e.target.closest && e.target.closest('.hdel')) return\n      if (el.dataset.id) S.histSel = el.dataset.id\n    })\n    d2.on('.hdel', 'click', async (e, el) => {\n      if (!el.dataset.id) return\n      const ok = await d2.dialog.confirm('Excluir este aporte do histórico?')\n      if (ok) {\n        await d2.data('aportes').remove(el.dataset.id)\n        if (S.histSel === el.dataset.id) S.histSel = null\n      }\n    })\n    d2.on('.save', 'click', () => {\n      if (S.saving) return\n      S.view = 'main'\n      S.confirmSave = true\n    })\n    d2.on('.cancelsave', 'click', () => { S.confirmSave = false })\n    d2.on('.note', 'keydown', (e) => { if (e.key === 'Enter') { const b = d2.$('.dosave'); if (b) b.click() } })\n    d2.on('.dosave', 'click', async () => {\n      if (S.saving) return\n      const ne = d2.$('.note')\n      const note = ne ? ne.value : ''\n      S.saving = true\n      try {\n        const itens = snapshot(d2)\n        const total = Math.round(itens.reduce((s, i) => s + (Number(i.total) || 0), 0) * 100) / 100\n        const when = new Date().toISOString()\n        await d2.data('aportes').create({ data: when, total: total, itens: itens, nota: String(note || '').trim() })\n        S.confirmSave = false\n        S.msg = { type: 'ok', text: `Aporte de ${money(total)} salvo em ${fmtDT(when)}. Marque o que for comprando no Histórico.` }\n      } catch (err) {\n        S.msg = { type: 'err', text: 'Não consegui salvar o aporte: ' + (err && err.message ? err.message : err) }\n      } finally {\n        S.saving = false\n      }\n    })\n\n    d2.on('.zero', 'click', async () => {\n      const ok = await d2.dialog.confirm('Zerar todas as quantidades e valores?')\n      if (!ok) return\n      const ds = d2.data('ativos')\n      const jobs = (ds.records() || []).filter((r) => r._id && ((Number(r.quantidade) || 0) !== 0 || (Number(r.valor) || 0) !== 0))\n        .map((r) => ds.update({ ...r, quantidade: 0, valor: 0 }))\n      await Promise.all(jobs)\n      S.draft = {}\n    })\n  },\n}"}},"aportes":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"aportes"}}}},"ativos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ativos"}}}},"extrator":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"extrator"}}}}}}}}}},{"_id":"6ac6ab5c5da28ca8d58c2780","component":"data2-core-types@Component","system":"686bd24b4fcbdb97284775e3","params":{"extends":"data2-core-primitives@Block","component":"GI_App_Principal","package":"massayuki-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 EL = { Pyro: '#e04a45', Hydro: '#3fb0dc', Anemo: '#3fc3ad', Electro: '#9a7ae8', Dendro: '#8fcc3d', Cryo: '#9cc4f0', Geo: '#f0b866' };\nconst DS = ['chars', 'roster', 'teams', 'versions', 'runs', 'users'];\n\nconst esc = (s) => String(s == null ? '' : s).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\nconst fmt = (n) => Math.round(n || 0).toLocaleString('pt-BR');\nconst signed = (p) => (p >= 0 ? '+' : '') + p.toFixed(1).replace('.', ',') + '%';\nconst buildStr = (c, r) => (c > 0 ? 'C' + c : '') + (r > 0 ? 'R' + r : '');\nconst mean = (xs) => (xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : 0);\nconst sleep = (ms) => new Promise((res) => setTimeout(res, ms));\nconst parseNum = (t) => { const n = parseFloat(String(t || '').replace(/\\./g, '').replace(',', '.')); return isNaN(n) || n <= 0 ? null : n; };\nconst keyOfBuilds = (builds) => builds.map((b) => b.characterId + ':C' + (b.constellation || 0) + 'R' + (b.refinement || 0)).sort().join('|');\nconst groupOf = (ids) => ids.slice().sort().join('|');\nconst dateBR = (d) => { if (!d) return ''; const x = new Date(d); return isNaN(x) ? '' : x.toLocaleDateString('pt-BR'); };\n\nasync function loadDs(h) {\n  if (!h) return;\n  for (let i = 0; i < 50 && h.fetching; i++) await sleep(100);\n  await h.loadAll();\n  const info = h.pageInfo ? h.pageInfo() : null;\n  if (info && info.total && h.records().length < info.total) { await sleep(200); await h.loadAll(); }\n}\n\nfunction model(d2) {\n  const rec = (n) => (d2.data(n) ? d2.data(n).records() : []);\n  const users = rec('users');\n  const email = String(d2.param('ownerEmail', '') || '').toLowerCase();\n  const me = users.find((u) => String(u.email || '').toLowerCase() === email);\n  const uid = me ? me._id : null;\n  const chars = rec('chars');\n  const charById = {};\n  chars.forEach((c) => { charById[c._id] = c; });\n  const roster = rec('roster').filter((r) => r.ownerId === uid);\n  const rosterBy = {};\n  roster.forEach((r) => { rosterBy[r.characterId] = r; });\n  const teams = rec('teams').filter((t) => t.ownerId === uid);\n  const versions = rec('versions').filter((v) => v.ownerId === uid);\n  const runs = rec('runs').filter((r) => r.ownerId === uid);\n  const runsByVersion = {};\n  runs.forEach((r) => { (runsByVersion[r.versionId] = runsByVersion[r.versionId] || []).push(r); });\n  const currentBuilds = (ids) => ids.map((id) => ({ characterId: id, constellation: (rosterBy[id] && rosterBy[id].constellation) || 0, refinement: (rosterBy[id] && rosterBy[id].refinement) || 0 }));\n  const teamInfo = teams.map((t) => {\n    const vs = versions.filter((v) => v.teamId === t._id).map((v) => {\n      const rs = runsByVersion[v._id] || [];\n      const vals = rs.map((r) => r.dps);\n      return { v, runs: rs, avg: mean(vals), count: vals.length };\n    });\n    const curKey = keyOfBuilds(currentBuilds(t.memberOrder || []));\n    vs.forEach((x) => { x.isCurrent = x.v.compositionKey === curKey; });\n    const withRuns = vs.filter((x) => x.count > 0);\n    const best = withRuns.slice().sort((a, b) => b.avg - a.avg)[0] || null;\n    return { t, versions: vs, best, curKey, current: vs.find((x) => x.isCurrent) || null };\n  });\n  return { me, uid, chars, charById, rosterBy, teams, teamInfo, versions, runs, currentBuilds };\n}\n\nfunction avatar(c, build, size, opts) {\n  opts = opts || {};\n  const color = c && EL[c.element];\n  const bw = size >= 70 ? 4 : 3;\n  const ring = color\n    ? `background:linear-gradient(#1e1717,#1e1717) padding-box,conic-gradient(from 210deg,${color},#ffffff 18%,${color} 36%,${color}55 62%,${color}) border-box;box-shadow:inset 0 0 0 2px #0b0909,0 0 14px ${color}66;`\n    : 'background:linear-gradient(#1e1717,#1e1717) padding-box,linear-gradient(#3a2a2a,#3a2a2a) border-box;';\n  const name = c ? c.namePt : '?';\n  const short = c && /\\s/.test(name) && !/^Viajante /.test(name) && c.aliases && c.aliases.length ? c.aliases.slice().sort((a, b) => a.length - b.length)[0] : name;\n  const inner = c && c.iconUrl\n    ? `<img src=\"${esc(c.iconUrl)}\" alt=\"\" style=\"width:100%;height:100%;border-radius:50%;object-fit:cover;display:block\">`\n    : `<span style=\"font-size:${Math.round(size * 0.3)}px\">${esc(short.replace(/^Viajante /, 'V').slice(0, 2))}</span>`;\n  const b = build ? buildStr(build.constellation, build.refinement) : '';\n  const badge = b && opts.badge !== false ? `<span class=\"badge\" style=\"font-size:${size >= 70 ? 11 : 9}px\">${b}</span>` : '';\n  return `<span class=\"avw\" style=\"width:${size}px;height:${size}px\" title=\"${esc(name + (b ? ' ' + b : ''))}\"><span class=\"av\" style=\"width:${size}px;height:${size}px;border:${bw}px solid transparent;${ring}\">${inner}</span>${badge}</span>`;\n}\n\nconst css = `\n:host{all:initial}\n*{box-sizing:border-box}\n.app{position:relative;min-height:100%;height:100%;overflow:auto;background:#0b0909;color:#fff;font-family:Manrope,'Segoe UI',system-ui,sans-serif;padding:28px 32px 40px}\n.wrap{max-width:1440px;margin:0 auto;display:flex;flex-direction:column;gap:22px}\n.kicker{font-size:12px;font-weight:700;color:#e8a3a3;letter-spacing:.1em;text-transform:uppercase}\nh1{margin:0;font-size:30px;font-weight:800}\nh2{margin:0;font-size:16px;font-weight:800}\nh3{margin:0;font-size:16px;font-weight:800}\n.muted{font-size:12px;color:#e8a3a3}\n.row{display:flex;flex-wrap:wrap;align-items:center;gap:12px}\n.between{justify-content:space-between}\n.input{height:44px;border-radius:12px;border:1px solid #3a2a2a;background:#161212;color:#fff;padding:0 14px;font:inherit;font-size:14px}\n.input.dark{background:#0b0909}\n.btn{height:44px;padding:0 18px;border-radius:12px;border:1px solid #3a2a2a;background:transparent;color:#fff;font:inherit;font-size:14px;font-weight:700;cursor:pointer}\n.btn.primary{background:#b3121f;border-color:#b3121f;font-weight:800}\n.btn.sm{height:36px;padding:0 14px;font-size:13px;border-radius:10px}\n.btn[disabled]{opacity:.45;cursor:default}\n.seg{display:flex;border:1px solid #3a2a2a;border-radius:10px;overflow:hidden}\n.seg button{height:36px;padding:0 12px;border:none;background:transparent;color:#fff;font:inherit;font-size:12px;font-weight:700;cursor:pointer}\n.seg button.on{background:#b3121f}\n.cols{display:flex;flex-wrap:wrap;gap:24px;align-items:flex-start}\n.left{flex:1 1 520px;min-width:0;display:flex;flex-direction:column;gap:8px}\n.panel{flex:1 1 560px;min-width:0;background:#161212;border-radius:18px;padding:24px;display:flex;flex-direction:column;gap:22px}\n.card{width:100%;text-align:left;background:#161212;border:2px solid #161212;border-radius:14px;padding:12px 16px;display:grid;grid-template-columns:32px minmax(0,1fr) auto;gap:14px;align-items:center;color:#fff;font:inherit;cursor:pointer}\n.card.sel{background:#241b1b;border-color:#b3121f}\n.card .rank{font-size:18px;font-weight:700;color:#e8a3a3}\n.card .lbl{font-size:14px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.mem{display:flex;gap:10px;margin-top:8px}\n.dps{font-size:22px;font-weight:700;text-align:right}\n.tag{display:inline-block;font-size:10px;font-weight:800;padding:2px 7px;border-radius:999px;background:#3a2a2a;color:#fff;margin-left:6px;vertical-align:middle}\n.more{align-self:flex-start;height:30px;padding:0 10px;border:none;background:transparent;color:#e8a3a3;font:inherit;font-size:12px;font-weight:700;cursor:pointer}\n.child{margin-left:28px}\n.avw{position:relative;display:inline-block;flex-shrink:0}\n.av{display:flex;align-items:center;justify-content:center;border-radius:50%;font-weight:800;overflow:hidden}\n.badge{position:absolute;right:-7px;bottom:-4px;background:#b3121f;color:#fff;font-weight:800;padding:1px 5px;border-radius:999px;border:2px solid #161212}\n.big{font-size:36px;font-weight:700;line-height:1}\n.grid4{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}\n.stat{background:#211a1a;border-radius:12px;padding:12px 14px;display:flex;flex-direction:column;gap:2px}\n.stat b{font-size:20px}\n.memcol{display:flex;flex-direction:column;align-items:center;gap:10px;min-width:0}\n.memcol .n{font-size:13px;font-weight:700;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.chips{display:flex;flex-wrap:wrap;gap:8px}\n.chip{height:34px;padding:0 12px;border-radius:999px;border:1px solid #3a2a2a;background:transparent;color:#fff;font:inherit;font-size:12px;font-weight:700;cursor:pointer}\n.chip.on{background:#b3121f;border-color:#b3121f}\n.bar{display:flex;height:14px;border-radius:999px;overflow:hidden;gap:2px;background:#0b0909}\ntable{width:100%;border-collapse:collapse;font-size:14px}\nth{text-align:left;padding:8px 10px;font-size:11px;font-weight:700;color:#e8a3a3;border-bottom:1px solid #3a2a2a}\ntd{padding:4px 10px;border-bottom:1px solid #211a1a}\ntd.r,th.r{text-align:right}\n.notebtn{width:100%;min-height:32px;text-align:left;border:none;background:transparent;color:#fff;font:inherit;font-size:13px;cursor:pointer;padding:4px 6px;border-radius:8px}\n.notebtn:hover{background:#211a1a}\n.icon{width:36px;height:36px;border:none;border-radius:10px;background:transparent;color:#e8a3a3;cursor:pointer;font-size:16px}\n.overlay{position:fixed;inset:0;background:#050404cc;display:flex;align-items:center;justify-content:center;padding:24px;z-index:10}\n.modal{width:100%;max-width:620px;background:#161212;border:1px solid #3a2a2a;border-radius:20px;padding:28px;display:flex;flex-direction:column;gap:16px}\n.warn{background:#211a1a;border:1px solid #b3121f;border-radius:12px;padding:12px 14px;font-size:14px;line-height:1.5}\n.note{background:#211a1a;border-radius:12px;padding:10px 14px;font-size:13px;line-height:1.5}\nselect{height:42px;border-radius:10px;border:1px solid #3a2a2a;background:#0b0909;color:#fff;padding:0 8px;font:inherit;font-size:13px;font-weight:700;width:100%}\nlabel.f{font-size:12px;font-weight:700;color:#e8a3a3}\n.loading{display:flex;align-items:center;justify-content:center;height:60vh;font-size:15px;color:#e8a3a3}\n`;\n\nexport default {\n  css,\n  async mounted(d2) {\n    d2.setState({ loading: true });\n    const pick = (ev, el, attr) => {\n      if (el && el.getAttribute && el.getAttribute(attr) != null) return el;\n      const path = ev && ev.composedPath ? ev.composedPath() : [];\n      for (const n of path) { if (n && n.getAttribute && n.getAttribute(attr) != null) return n; }\n      const t = ev && ev.target;\n      return t && t.closest ? t.closest('[' + attr + ']') : null;\n    };\n\n    d2.on('[data-act]', 'click', (ev, mt) => {\n      const el = pick(ev, mt, 'data-act');\n      if (!el) return;\n      act(d2, el.getAttribute('data-act'), el.dataset);\n    });\n    d2.on('[data-in]', 'input', (ev, mt) => {\n      const el = pick(ev, mt, 'data-in');\n      if (el) d2.state[el.getAttribute('data-in')] = el.value;\n    });\n    d2.on('[data-sel]', 'change', (ev, mt) => {\n      const el = pick(ev, mt, 'data-sel');\n      if (!el) return;\n      const i = +el.getAttribute('data-sel');\n      const m = (d2.state.editMembers || []).slice();\n      m[i] = el.value;\n      d2.state.editMembers = m;\n    });\n    d2.on('[data-key-enter]', 'keydown', (ev, mt) => {\n      const el = pick(ev, mt, 'data-key-enter');\n      if (!el) return;\n      if (ev.key === 'Enter') act(d2, el.getAttribute('data-key-enter'), el.dataset);\n      if (ev.key === 'Escape') d2.setState({ editRun: null });\n    });\n\n    await Promise.all(DS.map((n) => loadDs(d2.data(n))));\n    d2.setState({ loading: false });\n  },\n  render(d2) {\n    if (d2.state.loading !== false) return `<div class=\"app\"><div class=\"loading\">Carregando seus times…</div></div>`;\n    const M = model(d2);\n    if (!M.uid) return `<div class=\"app\"><div class=\"loading\">Usuário não encontrado para o e-mail configurado.</div></div>`;\n    const s = d2.state;\n    const q = String(s.q || '').trim().toLowerCase();\n    const showAll = !!s.showAll;\n    const expanded = s.expanded || {};\n\n    const ranked = M.teamInfo.slice().sort((a, b) => (b.best ? b.best.avg : -1) - (a.best ? a.best.avg : -1));\n    ranked.forEach((x, i) => { x.rank = i + 1; });\n    const selId = s.sel && ranked.find((x) => x.t._id === s.sel) ? s.sel : (ranked[0] ? ranked[0].t._id : null);\n    const firstName = (x) => (M.charById[x.t.firstCharacterId] || {}).namePt || '';\n    const hasChar = (x, txt) => (x.t.memberOrder || []).some((id) => ((M.charById[id] || {}).namePt || '').toLowerCase().includes(txt) || ((M.charById[id] || {}).aliases || []).some((a) => a.toLowerCase().includes(txt)));\n    const firstMatches = (x, txt) => { const c = M.charById[x.t.firstCharacterId] || {}; return (c.namePt || '').toLowerCase().includes(txt) || (c.aliases || []).some((a) => a.toLowerCase().includes(txt)); };\n\n    const card = (x, child) => {\n      const show = x.best;\n      const builds = show ? show.v.builds : M.currentBuilds(x.t.memberOrder || []);\n      const old = show && !show.isCurrent;\n      return `<button class=\"card ${x.t._id === selId ? 'sel' : ''} ${child ? 'child' : ''}\" data-act=\"select\" data-id=\"${x.t._id}\" data-key=\"c-${x.t._id}\">\n        <span class=\"rank\">${x.rank}</span>\n        <span style=\"min-width:0\"><span class=\"lbl\">${esc(x.t.label)}${old ? '<span class=\"tag\">build anterior</span>' : ''}</span>\n          <span class=\"mem\">${builds.map((b) => avatar(M.charById[b.characterId], b, 46)).join('')}</span></span>\n        <span><span class=\"dps\" style=\"display:block\">${show ? fmt(show.avg) : '—'}</span><span class=\"muted\" style=\"display:block;text-align:right\">${show ? show.count + ' testes' : 'sem testes'}</span></span>\n      </button>`;\n    };\n\n    let list = '';\n    let support = '';\n    let info = '';\n    if (q) {\n      const main = ranked.filter((x) => firstMatches(x, q));\n      const sup = ranked.filter((x) => !firstMatches(x, q) && hasChar(x, q));\n      list = main.map((x) => card(x)).join('');\n      if (sup.length) support = `<div class=\"row\" style=\"padding-top:10px;border-top:1px solid #211a1a\"><h3 style=\"font-size:14px\">Com \"${esc(s.q.trim())}\" em outra posição</h3><span class=\"muted\">${sup.length} times</span></div>` + sup.map((x) => card(x)).join('');\n      info = main.length + ' em primeiro';\n      if (!main.length && !sup.length) list = `<div class=\"note\">Nenhum time com esse personagem.</div>`;\n    } else if (showAll) {\n      list = ranked.map((x) => card(x)).join('');\n      info = ranked.length + ' times';\n    } else {\n      const groups = {}; const order = [];\n      ranked.forEach((x) => { const f = x.t.firstCharacterId; if (!groups[f]) { groups[f] = []; order.push(f); } groups[f].push(x); });\n      order.forEach((f) => {\n        const g = groups[f]; const others = g.slice(1);\n        const open = expanded[f] !== undefined ? expanded[f] : others.some((o) => o.t._id === selId);\n        list += card(g[0]);\n        if (others.length) list += `<button class=\"more\" data-act=\"toggleGroup\" data-f=\"${f}\" data-open=\"${open ? 1 : 0}\">${open ? 'Ocultar outros times de ' + esc(firstName(g[0])) : '+ ' + others.length + (others.length > 1 ? ' times' : ' time') + ' de ' + esc(firstName(g[0]))}</button>`;\n        if (open) others.forEach((o) => { list += card(o, true); });\n      });\n      info = order.length + ' personagens · ' + ranked.length + ' times';\n    }\n\n    const cur = ranked.find((x) => x.t._id === selId);\n    const panel = cur ? renderPanel(d2, M, cur) : `<div class=\"panel\"><div class=\"note\">Nenhum time ainda.</div></div>`;\n\n    const header = `<div class=\"row between\">\n      <div><div class=\"kicker\">Genshin · DPS</div><h1>Times</h1><div class=\"muted\">${esc(M.me.email)}</div></div>\n      <div class=\"row\">\n        <label class=\"f\" for=\"busca\">Filtrar por personagem</label>\n        <input id=\"busca\" class=\"input\" style=\"width:260px\" placeholder=\"Ex.: Furina\" value=\"${esc(s.q || '')}\" data-in=\"q\">\n      </div></div>`;\n\n    const left = `<div class=\"left\">\n      <div class=\"row between\"><div class=\"row\"><h2>Ranking</h2><span class=\"muted\">${info}</span></div>\n      <div class=\"seg\"><button class=\"${showAll ? '' : 'on'}\" data-act=\"view\" data-v=\"best\">Melhor por personagem</button><button class=\"${showAll ? 'on' : ''}\" data-act=\"view\" data-v=\"all\">Todos os times</button></div></div>\n      ${list}${support}</div>`;\n\n    return `<div class=\"app\"><div class=\"wrap\">${header}<div class=\"cols\">${left}${panel}</div></div>${renderModals(d2, M, cur)}</div>`;\n  }\n};\n\nfunction renderPanel(d2, M, x) {\n  const s = d2.state;\n  const vs = x.versions.slice().sort((a, b) => new Date(a.v.createdAt || 0) - new Date(b.v.createdAt || 0));\n  let vSel = vs.find((y) => y.v._id === s.selVersion) || x.current || x.best || vs[vs.length - 1] || null;\n  const builds = vSel ? vSel.v.builds : M.currentBuilds(x.t.memberOrder || []);\n  const vals = vSel ? vSel.runs.map((r) => r.dps) : [];\n  const avg = mean(vals);\n  const min = vals.length ? Math.min.apply(null, vals) : 0;\n  const max = vals.length ? Math.max.apply(null, vals) : 0;\n  const firstId = (x.t.memberOrder || [])[0];\n  const firstChar = M.charById[firstId] || {};\n\n  const versionChips = vs.length > 1 ? `<div class=\"chips\">${vs.map((y) => {\n    const diff = y.v.builds.filter((b) => { const o = (vSel ? vSel.v.builds : []).find((z) => z.characterId === b.characterId); return !o || o.constellation !== b.constellation || o.refinement !== b.refinement; });\n    const label = (y.isCurrent ? 'Atual' : 'Anterior') + ' · ' + (y.count ? fmt(y.avg) : 'sem testes');\n    return `<button class=\"chip ${vSel && y.v._id === vSel.v._id ? 'on' : ''}\" data-act=\"selVersion\" data-id=\"${y.v._id}\" title=\"${esc(y.v.builds.map((b) => ((M.charById[b.characterId] || {}).namePt || '') + ' ' + buildStr(b.constellation, b.refinement)).join(' · '))}\">${label}</button>`;\n  }).join('')}</div>` : '';\n\n  const brRuns = vSel ? vSel.runs.filter((r) => r.breakdown && Object.keys(r.breakdown).length) : [];\n  const total = (br) => Object.keys(br).reduce((a, k) => a + (+br[k] || 0), 0);\n  let contrib;\n  if (brRuns.length) {\n    const parts = builds.map((b) => {\n      const share = mean(brRuns.map((r) => (+r.breakdown[b.characterId] || 0) / (total(r.breakdown) || 1)));\n      const c = M.charById[b.characterId] || {};\n      return { name: c.namePt || '?', share, dmg: mean(brRuns.map((r) => +r.breakdown[b.characterId] || 0)), color: EL[c.element] || '#6b5a5a' };\n    });\n    contrib = `<div class=\"bar\">${parts.map((p) => `<div title=\"${esc(p.name)}\" style=\"width:${(p.share * 100).toFixed(2)}%;min-width:4px;background:${p.color}\"></div>`).join('')}</div>\n      <div class=\"grid4\">${parts.map((p) => `<div><div class=\"row\" style=\"gap:6px\"><span style=\"width:10px;height:10px;border-radius:50%;background:${p.color}\"></span><b style=\"font-size:12px\">${esc(p.name)}</b></div><div style=\"font-size:20px;font-weight:700\">${Math.round(p.share * 100)}%</div><div class=\"muted\">${fmt(p.dmg)} de dano</div></div>`).join('')}</div>`;\n  } else {\n    contrib = `<div class=\"note\">Aparece quando a versão tiver testes registrados por print, que trazem o dano de cada personagem.</div>`;\n  }\n\n  const rows = vSel ? vSel.runs.slice().sort((a, b) => new Date(b.createdAt || 0) - new Date(a.createdAt || 0)).map((r) => {\n    const share = r.breakdown && total(r.breakdown) ? Math.round((+r.breakdown[firstId] || 0) / total(r.breakdown) * 100) + '%' : '—';\n    const editing = s.editRun === r._id;\n    const note = editing\n      ? `<input class=\"input dark\" style=\"height:34px;width:100%\" value=\"${esc(s.noteDraft || '')}\" data-in=\"noteDraft\" data-key-enter=\"saveNote\" data-id=\"${r._id}\" placeholder=\"Enter salva · Esc cancela\">`\n      : `<button class=\"notebtn\" data-act=\"editNote\" data-id=\"${r._id}\" title=\"Editar observação\">${esc(r.note || '—')}</button>`;\n    return `<tr data-key=\"r-${r._id}\"><td class=\"muted\">${dateBR(r.createdAt)}</td><td class=\"r\"><b>${fmt(r.dps)}</b></td><td class=\"r muted\">${avg ? signed((r.dps - avg) / avg * 100) : ''}</td><td class=\"r\">${share}</td><td>${note}</td><td><button class=\"icon\" data-act=\"askDeleteRun\" data-id=\"${r._id}\" title=\"Excluir teste\">🗑</button></td></tr>`;\n  }).join('') : '';\n\n  const parsed = parseNum(s.draft);\n  const sameAsCurrent = vSel && vSel.isCurrent;\n  const newAvg = parsed ? (vals.reduce((a, b) => a + b, 0) + parsed) / (vals.length + 1) : 0;\n  const preview = parsed && sameAsCurrent && avg ? `<div class=\"row between note\"><span>Nova média</span><span><b>${fmt(newAvg)}</b> <span class=\"muted\">${signed((newAvg - avg) / avg * 100)}</span></span></div>` : '';\n  const addLabel = sameAsCurrent || !vs.length ? 'Registrar dano' : 'Registrar com o build atual';\n  const addHint = !sameAsCurrent && vs.length ? `<div class=\"note\">Esta versão usa um build anterior. O teste vai para a versão com o build atual do seu roster (criada se ainda não existir).</div>` : '';\n\n  return `<div class=\"panel\">\n    <div class=\"row between\" style=\"align-items:flex-start\">\n      <div style=\"display:flex;flex-direction:column;gap:4px\">\n        <div class=\"muted\">#${x.rank} no ranking${vSel && !vSel.isCurrent ? ' · versão com build anterior' : ''}</div>\n        <div style=\"font-size:24px;font-weight:800\">${esc(x.t.label)}</div>\n        <div class=\"row\" style=\"gap:8px;margin-top:6px\"><button class=\"btn sm\" data-act=\"openEdit\">Editar</button><button class=\"btn sm\" data-act=\"openDelete\" style=\"color:#e8a3a3\">Excluir</button></div>\n      </div>\n      <div style=\"text-align:right\"><div class=\"big\">${vals.length ? fmt(avg) : '—'}</div><div class=\"muted\">DPS médio</div></div>\n    </div>\n    ${versionChips}\n    <div class=\"grid4\">${builds.map((b) => { const c = M.charById[b.characterId]; return `<div class=\"memcol\">${avatar(c, b, 80)}<span class=\"n\">${esc(c ? c.namePt : '?')}</span></div>`; }).join('')}</div>\n    <div class=\"grid4\">\n      <div class=\"stat\"><span class=\"muted\">Testes</span><b>${vals.length}</b></div>\n      <div class=\"stat\"><span class=\"muted\">Mínimo</span><b>${vals.length ? fmt(min) : '—'}</b></div>\n      <div class=\"stat\"><span class=\"muted\">Máximo</span><b>${vals.length ? fmt(max) : '—'}</b></div>\n      <div class=\"stat\"><span class=\"muted\">Dispersão</span><b>${vals.length > 1 ? ((max - min) / avg * 100).toFixed(1).replace('.', ',') + '%' : '—'}</b></div>\n    </div>\n    <div style=\"display:flex;flex-direction:column;gap:12px\"><div class=\"row between\"><h3>Contribuição no time</h3><span class=\"muted\">${brRuns.length ? 'média de ' + brRuns.length + ' teste(s) com print' : ''}</span></div>${contrib}</div>\n    <div class=\"cols\" style=\"gap:24px\">\n      <div style=\"flex:1 1 240px;min-width:0;display:flex;flex-direction:column;gap:12px\">\n        <h3>Novo teste manual</h3>\n        <label class=\"f\" for=\"dano\">Dano (DPS) · Enter registra</label>\n        <input id=\"dano\" class=\"input dark\" style=\"height:52px;font-size:20px;font-weight:700\" inputmode=\"numeric\" placeholder=\"Ex.: 198.500\" value=\"${esc(s.draft || '')}\" data-in=\"draft\" data-key-enter=\"addRun\">\n        <label class=\"f\" for=\"nota\">Observação (opcional)</label>\n        <input id=\"nota\" class=\"input dark\" placeholder=\"Ex.: rotação alternativa\" value=\"${esc(s.noteNew || '')}\" data-in=\"noteNew\" data-key-enter=\"addRun\">\n        ${preview}${addHint}\n        <button class=\"btn primary\" data-act=\"addRun\" ${s.busy ? 'disabled' : ''}>${addLabel}</button>\n      </div>\n      <div style=\"flex:2 1 340px;min-width:0;display:flex;flex-direction:column;gap:12px\">\n        <h3>Testes registrados</h3>\n        <div style=\"overflow-x:auto\"><table><thead><tr><th>Data</th><th class=\"r\">Dano</th><th class=\"r\">vs média</th><th class=\"r\">${esc(firstChar.namePt || '1º')} %</th><th>Observação</th><th></th></tr></thead><tbody>${rows || '<tr><td colspan=\"6\" class=\"muted\">Nenhum teste nesta versão.</td></tr>'}</tbody></table></div>\n      </div>\n    </div>\n  </div>`;\n}\n\nfunction renderModals(d2, M, x) {\n  const s = d2.state;\n  if (s.modal === 'deleteRun') {\n    return `<div class=\"overlay\"><div class=\"modal\"><h2 style=\"font-size:22px\">Excluir este teste?</h2><div class=\"warn\">O teste de <b>${fmt(s.modalDps)}</b> será apagado e a média recalculada.</div><div class=\"row\"><button class=\"btn primary\" data-act=\"deleteRun\" data-id=\"${s.modalId}\" ${s.busy ? 'disabled' : ''}>Excluir</button><button class=\"btn\" data-act=\"closeModal\">Cancelar</button></div></div></div>`;\n  }\n  if (!x) return '';\n  if (s.modal === 'delete') {\n    const vs = x.versions;\n    const totalRuns = vs.reduce((a, y) => a + y.count, 0);\n    const vSel = vs.find((y) => y.v._id === s.selVersion) || x.current || x.best || vs[vs.length - 1];\n    const one = vs.length > 1 && vSel ? `<button class=\"btn\" data-act=\"deleteVersion\" data-id=\"${vSel.v._id}\" ${s.busy ? 'disabled' : ''}>Excluir só esta versão (${vSel.count} testes)</button>` : '';\n    return `<div class=\"overlay\"><div class=\"modal\"><h2 style=\"font-size:22px\">Excluir \"${esc(x.t.label)}\"?</h2>\n      ${totalRuns ? `<div class=\"warn\">Este time tem <b>${totalRuns} testes registrados</b>${vs.length > 1 ? ' em ' + vs.length + ' versões' : ''}. Eles serão apagados e não dá pra recuperar.</div>` : ''}\n      <div class=\"row\">${one}<button class=\"btn primary\" data-act=\"deleteTeam\" ${s.busy ? 'disabled' : ''}>Excluir o time inteiro</button><button class=\"btn\" data-act=\"closeModal\">Cancelar</button></div></div></div>`;\n  }\n  if (s.modal === 'edit') {\n    const members = s.editMembers || (x.t.memberOrder || []).slice();\n    const owned = Object.keys(M.rosterBy).filter((id) => M.rosterBy[id].owned !== false && M.charById[id]).sort((a, b) => M.charById[a].namePt.localeCompare(M.charById[b].namePt));\n    const dup = members.some((m, i) => members.indexOf(m) !== i);\n    const changed = !dup && groupOf(members) !== groupOf(x.t.memberOrder || []);\n    const existing = changed ? M.teamInfo.find((y) => groupOf(y.t.memberOrder || []) === groupOf(members)) : null;\n    const name = s.editName !== undefined ? s.editName : x.t.label;\n    const runs = x.versions.reduce((a, y) => a + y.count, 0);\n    const cantRename = changed || dup || !name.trim() || name.trim() === x.t.label;\n    return `<div class=\"overlay\"><div class=\"modal\"><h2 style=\"font-size:22px\">Editar time</h2>\n      ${runs ? `<div class=\"note\">Este time tem <b>${runs} testes registrados</b>. Renomear não muda nada neles. Trocar um personagem não altera este time: cria outro.</div>` : ''}\n      <label class=\"f\" for=\"enome\">Nome do time</label><input id=\"enome\" class=\"input dark\" value=\"${esc(name)}\" data-in=\"editName\">\n      <div class=\"grid4\">${members.map((m, i) => `<div style=\"display:flex;flex-direction:column;gap:6px;min-width:0\"><label class=\"f\">${i === 0 ? '1º personagem' : 'Slot ' + (i + 1)}</label><select data-sel=\"${i}\" style=\"border-color:${m !== (x.t.memberOrder || [])[i] ? '#b3121f' : '#3a2a2a'}\">${owned.map((id) => `<option value=\"${id}\" ${id === m ? 'selected' : ''}>${esc(M.charById[id].namePt)}</option>`).join('')}</select></div>`).join('')}</div>\n      ${dup ? '<div class=\"muted\"><b>Um personagem não pode aparecer duas vezes no time.</b></div>' : ''}\n      ${changed && !existing ? `<div class=\"warn row between\"><span style=\"flex:1 1 260px\">Essa composição ainda não existe. Ela vira um <b>time novo</b>, sem testes; este time continua como está.</span><button class=\"btn sm primary\" data-act=\"createFromEdit\" ${s.busy ? 'disabled' : ''}>Criar time novo</button></div>` : ''}\n      ${existing ? `<div class=\"warn row between\"><span style=\"flex:1 1 260px\">Você já tem esse time: <b>${esc(existing.t.label)}</b>.</span><button class=\"btn sm primary\" data-act=\"goTeam\" data-id=\"${existing.t._id}\">Ir para esse time</button></div>` : ''}\n      <div class=\"row\"><button class=\"btn primary\" data-act=\"saveRename\" ${cantRename || s.busy ? 'disabled' : ''}>Salvar nome</button><button class=\"btn\" data-act=\"closeModal\">Cancelar</button></div></div></div>`;\n  }\n  return '';\n}\n\nasync function recalcVersion(d2, versionId) {\n  const runs = d2.data('runs').records().filter((r) => r.versionId === versionId);\n  const vals = runs.map((r) => +r.dps || 0);\n  const v = d2.data('versions').records().find((y) => y._id === versionId);\n  if (v) await d2.data('versions').update(Object.assign({}, v, { avgDps: Math.round(mean(vals) * 10) / 10, runCount: vals.length }));\n}\n\nasync function act(d2, a, data) {\n  const s = d2.state;\n  const M = model(d2);\n  const ranked = M.teamInfo.slice().sort((x, y) => (y.best ? y.best.avg : -1) - (x.best ? x.best.avg : -1));\n  const cur = ranked.find((x) => x.t._id === s.sel) || ranked[0];\n  const run = async (fn) => { if (s.busy) return; d2.setState({ busy: true }); try { await fn(); } catch (e) { d2.log(e); await d2.dialog.alert('Não foi possível salvar: ' + (e && e.message ? e.message : e)); } d2.setState({ busy: false }); };\n\n  if (a === 'select') return d2.setState({ sel: data.id, selVersion: null, draft: '', noteNew: '', editRun: null });\n  if (a === 'view') return d2.setState({ showAll: data.v === 'all' });\n  if (a === 'toggleGroup') { const e = Object.assign({}, s.expanded || {}); e[data.f] = data.open !== '1'; return d2.setState({ expanded: e }); }\n  if (a === 'selVersion') return d2.setState({ selVersion: data.id, editRun: null });\n  if (a === 'closeModal') return d2.setState({ modal: null, editMembers: null, editName: undefined });\n  if (a === 'openEdit') return d2.setState({ modal: 'edit', editMembers: null, editName: undefined });\n  if (a === 'openDelete') return d2.setState({ modal: 'delete' });\n  if (a === 'goTeam') return d2.setState({ sel: data.id, selVersion: null, modal: null, editMembers: null, editName: undefined, q: '' });\n  if (a === 'editNote') { const r = M.runs.find((x) => x._id === data.id); return d2.setState({ editRun: data.id, noteDraft: r ? r.note || '' : '' }); }\n  if (a === 'askDeleteRun') { const r = M.runs.find((x) => x._id === data.id); return d2.setState({ modal: 'deleteRun', modalId: data.id, modalDps: r ? r.dps : 0 }); }\n\n  if (a === 'saveNote') return run(async () => {\n    const r = d2.data('runs').records().find((x) => x._id === data.id);\n    if (r) await d2.data('runs').update(Object.assign({}, r, { note: String(s.noteDraft || '').trim() }));\n    d2.setState({ editRun: null });\n  });\n\n  if (a === 'deleteRun') return run(async () => {\n    const r = d2.data('runs').records().find((x) => x._id === data.id);\n    if (!r) return;\n    await d2.data('runs').remove(r);\n    await recalcVersion(d2, r.versionId);\n    d2.setState({ modal: null });\n  });\n\n  if (a === 'addRun') return run(async () => {\n    if (!cur) return;\n    const dps = parseNum(s.draft);\n    if (!dps) { await d2.dialog.alert('Digite um valor de dano válido.'); return; }\n    const vs = cur.versions;\n    const vSel = vs.find((y) => y.v._id === s.selVersion) || cur.current || cur.best || null;\n    let versionId;\n    if (vSel && vSel.isCurrent) versionId = vSel.v._id;\n    else {\n      const builds = M.currentBuilds(cur.t.memberOrder || []);\n      const key = keyOfBuilds(builds);\n      const existing = d2.data('versions').records().find((v) => v.ownerId === M.uid && v.teamId === cur.t._id && v.compositionKey === key);\n      if (existing) versionId = existing._id;\n      else {\n        const created = await d2.data('versions').create({ ownerId: M.uid, teamId: cur.t._id, compositionKey: key, builds, avgDps: 0, runCount: 0, createdAt: new Date().toISOString() });\n        versionId = (created && (created._id || (created.data && created.data._id))) || null;\n        if (!versionId) { await loadDs(d2.data('versions')); const v = d2.data('versions').records().find((x) => x.teamId === cur.t._id && x.compositionKey === key); versionId = v && v._id; }\n      }\n    }\n    if (!versionId) throw new Error('versão não encontrada');\n    await d2.data('runs').create({ ownerId: M.uid, teamId: cur.t._id, versionId, dps, note: String(s.noteNew || '').trim(), source: 'manual', createdAt: new Date().toISOString() });\n    await loadDs(d2.data('runs'));\n    await recalcVersion(d2, versionId);\n    d2.setState({ draft: '', noteNew: '', selVersion: versionId });\n  });\n\n  if (a === 'saveRename') return run(async () => {\n    const name = String(s.editName || '').trim();\n    if (!name || !cur) return;\n    const t = d2.data('teams').records().find((x) => x._id === cur.t._id);\n    await d2.data('teams').update(Object.assign({}, t, { label: name }));\n    d2.setState({ modal: null, editName: undefined, editMembers: null });\n  });\n\n  if (a === 'createFromEdit') return run(async () => {\n    const members = s.editMembers;\n    if (!members || !cur) return;\n    const label = (M.charById[members[0]] || {}).namePt || 'Novo time';\n    const created = await d2.data('teams').create({ ownerId: M.uid, groupKey: groupOf(members), label, memberOrder: members, firstCharacterId: members[0], createdAt: new Date().toISOString() });\n    await loadDs(d2.data('teams'));\n    const t = d2.data('teams').records().find((x) => x.ownerId === M.uid && x.groupKey === groupOf(members));\n    d2.setState({ sel: (created && created._id) || (t && t._id), selVersion: null, modal: null, editMembers: null, editName: undefined, showAll: true });\n  });\n\n  if (a === 'deleteVersion') return run(async () => {\n    const runs = d2.data('runs').records().filter((r) => r.versionId === data.id);\n    for (const r of runs) await d2.data('runs').remove(r);\n    const v = d2.data('versions').records().find((x) => x._id === data.id);\n    if (v) await d2.data('versions').remove(v);\n    d2.setState({ modal: null, selVersion: null });\n  });\n\n  if (a === 'deleteTeam') return run(async () => {\n    if (!cur) return;\n    const runs = d2.data('runs').records().filter((r) => r.teamId === cur.t._id);\n    for (const r of runs) await d2.data('runs').remove(r);\n    const vs = d2.data('versions').records().filter((v) => v.teamId === cur.t._id);\n    for (const v of vs) await d2.data('versions').remove(v);\n    const t = d2.data('teams').records().find((x) => x._id === cur.t._id);\n    if (t) await d2.data('teams').remove(t);\n    d2.setState({ modal: null, sel: null, selVersion: null });\n  });\n}\n"}},"chars":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"chars"}}}},"ownerEmail":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ownerEmail"}}}},"roster":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"roster"}}}},"runs":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"runs"}}}},"teams":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"teams"}}}},"users":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"users"}}}},"versions":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"versions"}}}}}}}}}},{"_id":"5ed9bed7b9e0891ce913e884","component":"data2-core-types@Component","params":{"package":"data2-core","component":"DataSource","renderable":{"dimensions":{"disabled":false,"component":"data2-core-types@Dimensions","params":{"width":1366,"exactWidth":false,"exactHeight":false,"height":768}}}}},{"_id":"5f472ae463c680de4ff296e9","component":"data2-core-types@Component","params":{"package":"data2-core","component":"Data2TableDataSource","extends":"data2-core@DataSource","renderable":{"dimensions":{"disabled":false,"component":"data2-core-types@Dimensions","params":{"width":1366,"exactWidth":false,"exactHeight":false,"height":768}}}}}]}},{"reference":"ExternalComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_id":"5f060d9b1ca96def42f3c40a","displayName":"Builder AuthStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"credentials":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"UsersDataSet"}},"sessionShared":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},"email":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"nR6Q9d7qdaPtZhJ28Q8Dy"},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"yPCJBTKo8q1Mw0B_3Kpwl"},"component":"data2-core-types@ArtboardReference"}}},"session":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"name"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"sessionMetadata":{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},{"params":{"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"active"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.status"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is active user"}]},"component":"data2-core-logical-operators@And","displayName":"is active and has _id"}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $user"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-primitives@String","params":{"content":"@data2.app"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.email"}}}}},"component":"data2-core-functions@Includes","displayName":"isData2User"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}},"component":"data2-core-flow@Step"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.d2_app"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"management"}}]},"component":"data2-core-functions@Includes","displayName":"isData2App Exception"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}},"component":"data2-core-flow@Step"},{"params":{"resultName":{"component":"data2-core-primitives@String","params":{"content":"has_access"}},"action":{"params":{"compare":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}}},{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}}}]},"component":"data2-core-logical-operators@Or"},"else":{"params":{"actions":[{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $metadata.system"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":"filter","args":[{"_id":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records.length"}}}}},"component":"data2-core-logical-operators@Not","displayName":"systems not found"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check system found"},{"params":{"actions":[{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"organization":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"cursor.current.organization._id"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"}]},"component":"data2-core-flow@Parallel"}]},"component":"data2-core-flow@Series","displayName":"common setup"},{"params":{"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"},"else":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"compare":{"params":{"components":{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"mapper":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"system"}}],"body":{"params":{"expression":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.visibility"}}}},"defaultCase":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"cases":[{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"public"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"billing-owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}},{"component":"data2-core-primitives@String","params":{"content":"member"}}]},"component":"data2-core-functions@Includes","displayName":"has role with access"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"collaborator"}}},"component":"data2-core-comparison-operators@Equal","displayName":"is collaborator"}]},"component":"data2-core-logical-operators@And","displayName":"is org member and collaborator"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is collaborator"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is collaborator and has system access"}]},"component":"data2-core-logical-operators@Or"}}},{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"private"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}}]},"component":"data2-core-functions@Includes","displayName":"is owner/admin"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is owner/admin"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is member and has system access"}]},"component":"data2-core-logical-operators@Or","displayName":"is owner/admin or has system access"}}}]},"component":"data2-core-flow@Switch"}}}},"component":"data2-core-functions@ArrayMap"}},"component":"data2-core-logical-operators@And","displayName":"has org member for all systems in request"}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"}},"component":"data2-core-flow@If"}},"component":"data2-core-flow@Step"},{"params":{"then":{"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"else":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"},"compare":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"has_access"}}}}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"sessionExpiresIn":{"component":"data2-core-primitives@Number","params":{"content":604800}},"sessionRenewal":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"identityProviders":[{"provider":{"component":"data2-core-primitives@String","params":{"content":"GOOGLE"}},"clientId":{"component":"data2-core-primitives@String","params":{"content":"55470861603-22pvfc1t49lp8cjnbfk71s5ct9hp1q1v.apps.googleusercontent.com"}}}]},"reference":"Data2BuilderAuthentication","updatedAt":"2026-08-06T19:31:23.557Z"},{"_id":"5f060ee51ca96def42f3fbd1","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"UsersTableDataSource"}},"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}}},"reference":"UsersDataSet","updatedAt":"2026-03-04T21:16:40.291Z"},{"_id":"68f938cf5fb002e33752a43d","displayName":"InternalApplicationsAuthenticationStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}}},"verifier":{"disabled":false,"component":"data2-core-types@DataSetReference","params":{"reference":"InternalApplicationsVerifierDataSet"}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"secret"}},"credentials":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}],"session":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}]},"reference":"InternalApplicationsAuthenticationStrategy","updatedAt":"2025-10-22T20:08:54.442Z","createdAt":"2025-10-22T20:04:31.198Z"},{"component":"data2-core@DataSet","reference":"SessionsDataSet","params":{"requiresConfirmation":false,"pk":"key","datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"data2sessionsdatasource"}}}},{"reference":"UsersTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@ServerSessionsDataSource","reference":"data2sessionsdatasource","params":{}},{"_id":"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":"6ac6ab5fe48a29f4b8cae8f7","component":"data2-core-primitives@Artboard","system":"686bd24b4fcbdb97284775e3","displayName":"GI App","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"GI App"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"Qpqb7TvlxhDpWdizOWQq-"},{"_id":"be43487c540488ddc5b807fe","parent":"6ac6ab5fe48a29f4b8cae8f7","component":"massayuki-playground@GI_App_Principal","system":"686bd24b4fcbdb97284775e3","displayName":"GI App Principal","params":{"chars":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"gi-personagensDataSet"}},"roster":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"gi-rosterDataSet"}},"teams":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"gi-timesDataSet"}},"versions":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"gi-versoesDataSet"}},"runs":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"gi-testesDataSet"}},"users":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"gi-usuariosDataSet"}},"ownerEmail":"alex.mhonda@gmail.com","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":1440,"height":900}}}},"reference":"65EX15Uf_B7WYRetjpHb3"}]}},{"_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":"6ac6ab8a721e51b15dc8e588","displayName":"Genshin DPS","reference":"Wrj4Ixu7giyk5phlVj7uX","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"genshin"}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Artboard","reference":"Qpqb7TvlxhDpWdizOWQq-"}}},"system":"686bd24b4fcbdb97284775e3","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6ac6a655f121324ec0275230","component":"data2-core@DataSet","system":"686bd24b4fcbdb97284775e3","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"gi-usuarios"}}},"reference":"gi-usuariosDataSet"},{"_id":"6ac6a6663cd42e411b475985","component":"data2-core@DataSet","system":"686bd24b4fcbdb97284775e3","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"gi-testes"}}},"reference":"gi-testesDataSet"},{"_id":"6ac6a661f121324ec0275240","component":"data2-core@DataSet","system":"686bd24b4fcbdb97284775e3","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"gi-versoes"}}},"reference":"gi-versoesDataSet"},{"_id":"6ac6a65d721e51b15dc8d7ea","component":"data2-core@DataSet","system":"686bd24b4fcbdb97284775e3","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"gi-times"}}},"reference":"gi-timesDataSet"},{"_id":"6ac6a659d0e6a463e356d2a5","component":"data2-core@DataSet","system":"686bd24b4fcbdb97284775e3","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"gi-roster"}}},"reference":"gi-rosterDataSet"},{"_id":"6ac6a64e78f3730f24d0458d","component":"data2-core@DataSet","system":"686bd24b4fcbdb97284775e3","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"gi-personagens"}}},"reference":"gi-personagensDataSet"},{"_id":"6ac6a655978bee86fda054ba","component":"data2-core@Data2TableDataSource","system":"686bd24b4fcbdb97284775e3","params":{"table":"gi-usuarios","schema":[{"component":"data2-core-types@Field","params":{"key":"email","title":"E-mail","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"googleId","title":"Google ID","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"name","title":"Nome","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"uid","title":"UID Genshin","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"nickname","title":"Nickname","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"onboarded","title":"Boas-vindas concluídas","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"lastSyncAt","title":"Última sync","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"lastExportAt","title":"Último backup","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"password","title":"Senha","component":"data2-core-types@Password"}},{"component":"data2-core-types@Field","params":{"key":"role","title":"Papel","component":"data2-core-primitives@String"}}]},"reference":"gi-usuarios"},{"_id":"6ac6a66629b16dbc168733c8","component":"data2-core@Data2TableDataSource","system":"686bd24b4fcbdb97284775e3","params":{"table":"gi-testes","schema":[{"component":"data2-core-types@Field","params":{"key":"ownerId","title":"Dono (usuário)","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"teamId","title":"Time","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"versionId","title":"Versão","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"dps","title":"DPS","component":"data2-core-primitives@Number","required":true}},{"component":"data2-core-types@Field","params":{"key":"totalDamage","title":"Dano total","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"breakdown","title":"Dano por personagem","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"note","title":"Observação / rotação","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"source","title":"Origem (manual/print/importacao)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"createdAt","title":"Registrado em","component":"data2-core-primitives@Date"}}]},"reference":"gi-testes"},{"_id":"6ac6a6613cd42e411b475973","component":"data2-core@Data2TableDataSource","system":"686bd24b4fcbdb97284775e3","params":{"table":"gi-versoes","schema":[{"component":"data2-core-types@Field","params":{"key":"ownerId","title":"Dono (usuário)","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"teamId","title":"Time","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"compositionKey","title":"Chave personagens + build","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"builds","title":"Build de cada personagem","component":"data2-core-primitives@Array"}},{"component":"data2-core-types@Field","params":{"key":"avgDps","title":"DPS médio","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"runCount","title":"Qtd. testes","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"createdAt","title":"Criada em","component":"data2-core-primitives@Date"}}]},"reference":"gi-versoes"},{"_id":"6ac6a65d78f3730f24d045cb","component":"data2-core@Data2TableDataSource","system":"686bd24b4fcbdb97284775e3","params":{"table":"gi-times","schema":[{"component":"data2-core-types@Field","params":{"key":"ownerId","title":"Dono (usuário)","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"groupKey","title":"Chave dos 4 personagens","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"label","title":"Nome do time","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"memberOrder","title":"Ordem dos personagens","component":"data2-core-primitives@Array"}},{"component":"data2-core-types@Field","params":{"key":"firstCharacterId","title":"1º personagem","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"createdAt","title":"Criado em","component":"data2-core-primitives@Date"}}]},"reference":"gi-times"},{"_id":"6ac6a65978f3730f24d045b1","component":"data2-core@Data2TableDataSource","system":"686bd24b4fcbdb97284775e3","params":{"table":"gi-roster","schema":[{"component":"data2-core-types@Field","params":{"key":"ownerId","title":"Dono (usuário)","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"characterId","title":"Personagem","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"owned","title":"Tem o personagem","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"constellation","title":"Constelação","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"refinement","title":"Refino signature (0 = sem)","component":"data2-core-primitives@Number"}}]},"reference":"gi-roster"},{"_id":"6ac6a64d3cd42e411b47592c","component":"data2-core@Data2TableDataSource","system":"686bd24b4fcbdb97284775e3","params":{"table":"gi-personagens","schema":[{"component":"data2-core-types@Field","params":{"key":"namePt","title":"Nome (PT)","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"nameEn","title":"Nome (EN)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"aliases","title":"Aliases","component":"data2-core-primitives@Array"}},{"component":"data2-core-types@Field","params":{"key":"element","title":"Elemento","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"rarity","title":"Raridade","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"enkaIds","title":"IDs Enka","component":"data2-core-primitives@Array"}},{"component":"data2-core-types@Field","params":{"key":"signatureWeaponId","title":"ID arma signature","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"signatureWeaponName","title":"Arma signature","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"iconUrl","title":"Ícone","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"hidden","title":"Oculto","component":"data2-core-primitives@Boolean"}}]},"reference":"gi-personagens"}]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}