├── .envrc ├── .github ├── actions │ └── extend-space │ │ └── action.yaml └── workflows │ ├── test-app.yaml │ └── test-zome.yaml ├── .gitignore ├── README.md ├── flake.lock ├── flake.nix ├── run_test_app.sh ├── run_test_zome.sh └── templates ├── app ├── collection │ ├── dnas │ │ └── {{dna_role_name}} │ │ │ └── zomes │ │ │ └── coordinator │ │ │ └── {{snake_case coordinator_zome_manifest.name}} │ │ │ └── tests │ │ │ └── {{snake_case collection_name}}.rs.hbs │ ├── tests │ │ └── src │ │ │ └── {{dna_role_name}} │ │ │ └── {{coordinator_zome_manifest.name}} │ │ │ └── {{kebab_case collection_name}}.test.ts.hbs │ └── ui │ │ └── src │ │ └── {{dna_role_name}} │ │ └── {{kebab_case coordinator_zome_manifest.name}} │ │ ├── elements │ │ └── {{kebab_case collection_name}}.ts.hbs │ │ ├── mocks.ts.hbs │ │ ├── {{kebab_case coordinator_zome_manifest.name}}-client.ts.hbs │ │ └── {{kebab_case coordinator_zome_manifest.name}}-store.ts.hbs ├── coordinator-zome.instructions.hbs ├── coordinator-zome │ ├── dnas │ │ └── {{dna_role_name}} │ │ │ ├── dna.nix.hbs │ │ │ └── zomes │ │ │ └── coordinator │ │ │ └── {{zome_manifest.name}} │ │ │ ├── Cargo.toml.hbs │ │ │ ├── tests │ │ │ └── common │ │ │ │ └── mod.rs.hbs │ │ │ └── zome.nix.hbs │ ├── tests │ │ └── src │ │ │ └── {{dna_role_name}} │ │ │ └── {{zome_manifest.name}} │ │ │ └── setup.ts.hbs │ └── ui │ │ └── src │ │ ├── holochain-app.ts.hbs │ │ └── {{dna_role_name}} │ │ └── {{zome_manifest.name}} │ │ ├── {{#if (and (not (eq (pascal_case zome_manifest.name) "Profiles")) (not (eq (pascal_case zome_manifest.name) "FileStorage")))}}context.ts{{¡if}}.hbs │ │ ├── {{#if (and (not (eq (pascal_case zome_manifest.name) "Profiles")) (not (eq (pascal_case zome_manifest.name) "FileStorage")))}}mocks.ts{{¡if}}.hbs │ │ ├── {{#if (and (not (eq (pascal_case zome_manifest.name) "Profiles")) (not (eq (pascal_case zome_manifest.name) "FileStorage")))}}types.ts{{¡if}}.hbs │ │ ├── {{#if (and (not (eq (pascal_case zome_manifest.name) "Profiles")) (not (eq (pascal_case zome_manifest.name) "FileStorage")))}}{{kebab_case zome_manifest.name}}-client.ts{{¡if}}.hbs │ │ └── {{#if (and (not (eq (pascal_case zome_manifest.name) "Profiles")) (not (eq (pascal_case zome_manifest.name) "FileStorage")))}}{{kebab_case zome_manifest.name}}-store.ts{{¡if}}.hbs ├── dna.instructions.hbs ├── dna │ ├── dnas │ │ └── {{dna_name}} │ │ │ └── dna.nix.hbs │ └── happ.nix.hbs ├── entry-type.instructions.hbs ├── entry-type │ ├── dnas │ │ └── {{dna_role_name}} │ │ │ └── zomes │ │ │ └── coordinator │ │ │ └── {{snake_case coordinator_zome_manifest.name}} │ │ │ └── tests │ │ │ ├── common │ │ │ └── mod.rs.hbs │ │ │ └── {{snake_case entry_type.name}}.rs.hbs │ ├── tests │ │ └── src │ │ │ └── {{dna_role_name}} │ │ │ └── {{coordinator_zome_manifest.name}} │ │ │ └── {{kebab_case entry_type.name}}.test.ts.hbs │ └── ui │ │ └── src │ │ └── {{dna_role_name}} │ │ └── {{kebab_case coordinator_zome_manifest.name}} │ │ ├── elements │ │ ├── create-{{kebab_case entry_type.name}}.ts.hbs │ │ ├── {{#each entry_type.fields}}{{#if linked_from}}{{kebab_case (plural ..¡entry_type.name)}}-for-{{kebab_case linked_from.name}}.ts{{¡if}}{{¡each}}.hbs │ │ ├── {{#if crud.update}}edit-{{kebab_case entry_type.name}}.ts{{¡if}}.hbs │ │ ├── {{kebab_case entry_type.name}}-detail.ts.hbs │ │ └── {{kebab_case entry_type.name}}-summary.ts.hbs │ │ ├── mocks.ts.hbs │ │ ├── types.ts.hbs │ │ ├── {{kebab_case coordinator_zome_manifest.name}}-client.ts.hbs │ │ └── {{kebab_case coordinator_zome_manifest.name}}-store.ts.hbs ├── field-types │ ├── ActionHash │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs │ ├── AgentPubKey │ │ ├── SearchAgent │ │ │ ├── detail │ │ │ │ ├── imports.hbs │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs │ ├── DnaHash │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs │ ├── EntryHash │ │ ├── Image │ │ │ ├── detail │ │ │ │ ├── imports.hbs │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs │ ├── Enum │ │ ├── Select │ │ │ ├── detail │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs │ ├── String │ │ ├── TextArea │ │ │ ├── detail │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── TextField │ │ │ ├── detail │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs │ ├── Timestamp │ │ ├── DateTimePicker │ │ │ ├── detail │ │ │ │ ├── imports.hbs │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs │ ├── Vec │ │ ├── detail │ │ │ └── render.hbs │ │ ├── edit │ │ │ └── render.hbs │ │ └── type.hbs │ ├── bool │ │ ├── Checkbox │ │ │ ├── detail │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── Switch │ │ │ ├── detail │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs │ ├── f32 │ │ ├── Slider │ │ │ ├── detail │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs │ ├── i32 │ │ ├── Slider │ │ │ ├── detail │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── TextField │ │ │ ├── detail │ │ │ │ └── render.hbs │ │ │ └── edit │ │ │ │ ├── imports.hbs │ │ │ │ ├── render.hbs │ │ │ │ └── to-value.hbs │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs │ └── u32 │ │ ├── Slider │ │ ├── detail │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ │ ├── TextField │ │ ├── detail │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ │ ├── sample-js.hbs │ │ ├── sample1.hbs │ │ ├── sample2.hbs │ │ └── type.hbs ├── integrity-zome │ └── dnas │ │ └── {{dna_role_name}} │ │ ├── dna.nix.hbs │ │ └── zomes │ │ └── integrity │ │ └── {{replace zome_manifest.name "_integrity" "" }} │ │ └── zome.nix.hbs ├── link-type │ ├── dnas │ │ └── {{dna_role_name}} │ │ │ └── zomes │ │ │ └── coordinator │ │ │ └── {{snake_case coordinator_zome_manifest.name}} │ │ │ └── tests │ │ │ └── {{#if to_referenceable}}{{snake_case from_referenceable.name}}_to_{{snake_case (plural to_referenceable.name)}}.rs{{¡if}}.hbs │ ├── tests │ │ └── src │ │ │ └── {{dna_role_name}} │ │ │ └── {{coordinator_zome_manifest.name}} │ │ │ └── {{#if to_referenceable}}{{kebab_case from_referenceable.name}}-to-{{kebab_case (plural to_referenceable.name)}}.test.ts{{¡if}}.hbs │ └── ui │ │ └── src │ │ └── {{dna_role_name}} │ │ └── {{kebab_case coordinator_zome_manifest.name}} │ │ ├── elements │ │ ├── {{#if (and bidireccional to_referenceable)}}{{kebab_case (plural from_referenceable.name)}}-for-{{kebab_case to_referenceable.name}}.ts{{¡if}}.hbs │ │ └── {{#if to_referenceable}}{{kebab_case (plural to_referenceable.name)}}-for-{{kebab_case from_referenceable.name}}.ts{{¡if}}.hbs │ │ ├── mocks.ts.hbs │ │ ├── {{kebab_case coordinator_zome_manifest.name}}-client.ts.hbs │ │ └── {{kebab_case coordinator_zome_manifest.name}}-store.ts.hbs ├── web-app.instructions.hbs └── web-app │ ├── .envrc.hbs │ ├── .github │ └── workflows │ │ └── test.yaml.hbs │ ├── .gitignore.hbs │ ├── .prettierrc.hbs │ ├── Cargo.toml.hbs │ ├── README.md.hbs │ ├── eslint.config.js.hbs │ ├── flake.nix.hbs │ ├── happ.nix.hbs │ ├── package.json.hbs │ ├── pnpm-workspace.yaml.hbs │ ├── tests │ ├── package.json.hbs │ ├── src │ │ └── app-path.ts.hbs │ ├── tsconfig.json.hbs │ └── vitest.config.ts.hbs │ └── ui │ ├── .gitignore.hbs │ ├── index.html.hbs │ ├── lit-localize.json.hbs │ ├── package.json.hbs │ ├── src │ ├── app-styles.ts.hbs │ ├── context.ts.hbs │ ├── holochain-app.ts.hbs │ └── home-page.ts.hbs │ ├── tsconfig.json.hbs │ └── vite.config.ts.hbs └── zome ├── collection ├── docs │ └── elements │ │ └── {{kebab_case collection_name}}.md.hbs ├── tests │ └── src │ │ └── {{kebab_case collection_name}}.test.ts.hbs ├── ui │ └── src │ │ ├── elements │ │ └── {{kebab_case collection_name}}.ts.hbs │ │ ├── mocks.ts.hbs │ │ ├── {{kebab_case coordinator_zome_manifest.name}}-client.ts.hbs │ │ └── {{kebab_case coordinator_zome_manifest.name}}-store.ts.hbs └── zomes │ └── coordinator │ └── {{snake_case coordinator_zome_manifest.name}} │ └── tests │ └── {{snake_case collection_name}}.rs.hbs ├── coordinator-zome.instructions.hbs ├── coordinator-zome ├── dnas │ └── {{dna_role_name}} │ │ └── zomes │ │ └── coordinator │ │ └── {{zome_manifest.name}} │ │ ├── Cargo.toml.hbs │ │ └── tests │ │ └── common │ │ └── mod.rs.hbs └── ui │ └── demo │ └── index.html.hbs ├── dna.instructions.hbs ├── entry-type ├── docs │ └── elements │ │ ├── create-{{kebab_case entry_type.name}}.md.hbs │ │ ├── {{#each entry_type.fields}}{{#if linked_from}}{{kebab_case (plural ..¡entry_type.name)}}-for-{{kebab_case linked_from.name}}.md{{¡if}}{{¡each}}.hbs │ │ ├── {{#if crud.update}}edit-{{kebab_case entry_type.name}}.md{{¡if}}.hbs │ │ ├── {{kebab_case entry_type.name}}-detail.md.hbs │ │ └── {{kebab_case entry_type.name}}-summary.md.hbs ├── tests │ └── src │ │ └── {{kebab_case entry_type.name}}.test.ts.hbs ├── ui │ └── src │ │ ├── elements │ │ ├── create-{{kebab_case entry_type.name}}.ts.hbs │ │ ├── {{#each entry_type.fields}}{{#if linked_from}}{{kebab_case (plural ..¡entry_type.name)}}-for-{{kebab_case linked_from.name}}.ts{{¡if}}{{¡each}}.hbs │ │ ├── {{#if crud.update}}edit-{{kebab_case entry_type.name}}.ts{{¡if}}.hbs │ │ ├── {{kebab_case entry_type.name}}-detail.ts.hbs │ │ └── {{kebab_case entry_type.name}}-summary.ts.hbs │ │ ├── mocks.ts.hbs │ │ ├── types.ts.hbs │ │ ├── {{kebab_case coordinator_zome_manifest.name}}-client.ts.hbs │ │ └── {{kebab_case coordinator_zome_manifest.name}}-store.ts.hbs └── zomes │ └── coordinator │ └── {{snake_case coordinator_zome_manifest.name}} │ └── tests │ ├── common │ └── mod.rs.hbs │ └── {{snake_case entry_type.name}}.rs.hbs ├── field-types ├── ActionHash │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs ├── AgentPubKey │ ├── SearchAgent │ │ ├── detail │ │ │ ├── imports.hbs │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs ├── DnaHash │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs ├── EntryHash │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs ├── Enum │ ├── Select │ │ ├── detail │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs ├── String │ ├── TextArea │ │ ├── detail │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ ├── TextField │ │ ├── detail │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs ├── Timestamp │ ├── DateTimePicker │ │ ├── detail │ │ │ ├── imports.hbs │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs ├── Vec │ ├── detail │ │ └── render.hbs │ ├── edit │ │ └── render.hbs │ └── type.hbs ├── bool │ ├── Checkbox │ │ ├── detail │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ ├── Switch │ │ ├── detail │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs ├── f32 │ ├── Slider │ │ ├── detail │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs ├── i32 │ ├── Slider │ │ ├── detail │ │ │ └── render.hbs │ │ └── edit │ │ │ ├── imports.hbs │ │ │ ├── render.hbs │ │ │ └── to-value.hbs │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs └── u32 │ ├── Slider │ ├── detail │ │ └── render.hbs │ └── edit │ │ ├── imports.hbs │ │ ├── render.hbs │ │ └── to-value.hbs │ ├── sample-js.hbs │ ├── sample1.hbs │ ├── sample2.hbs │ └── type.hbs ├── link-type ├── docs │ └── elements │ │ ├── {{#if (and bidireccional to_referenceable)}}{{kebab_case (plural from_referenceable.name)}}-for-{{kebab_case to_referenceable.name}}.md{{¡if}}.hbs │ │ └── {{#if to_referenceable}}{{kebab_case (plural to_referenceable.name)}}-for-{{kebab_case from_referenceable.name}}.md{{¡if}}.hbs ├── tests │ └── src │ │ └── {{#if to_referenceable}}{{kebab_case from_referenceable.name}}-to-{{kebab_case (plural to_referenceable.name)}}.test.ts{{¡if}}.hbs ├── ui │ └── src │ │ ├── elements │ │ ├── {{#if (and bidireccional to_referenceable)}}{{kebab_case (plural from_referenceable.name)}}-for-{{kebab_case to_referenceable.name}}.ts{{¡if}}.hbs │ │ └── {{#if to_referenceable}}{{kebab_case (plural to_referenceable.name)}}-for-{{kebab_case from_referenceable.name}}.ts{{¡if}}.hbs │ │ ├── mocks.ts.hbs │ │ ├── {{kebab_case coordinator_zome_manifest.name}}-client.ts.hbs │ │ └── {{kebab_case coordinator_zome_manifest.name}}-store.ts.hbs └── zomes │ └── coordinator │ └── {{snake_case coordinator_zome_manifest.name}} │ └── tests │ └── {{#if to_referenceable}}{{snake_case from_referenceable.name}}_to_{{snake_case (plural to_referenceable.name)}}.rs{{¡if}}.hbs ├── web-app.instructions.hbs └── web-app ├── .envrc.hbs ├── .github ├── actions │ └── extend-space │ │ └── action.yaml.hbs └── workflows │ ├── build-and-cache-zomes.yaml.hbs │ ├── publish-docs.yml.hbs │ ├── test.yaml.hbs │ └── update-flake-inputs.yaml.hbs ├── .gitignore.hbs ├── .prettierrc.hbs ├── Cargo.toml.hbs ├── README.md.hbs ├── docs ├── .gitignore.hbs ├── .vitepress │ ├── config.mts.hbs │ └── theme │ │ ├── custom.css.hbs │ │ └── index.js.hbs ├── index.md.hbs ├── introduction.md.hbs ├── package.json.hbs ├── setup.md.hbs └── {{kebab_case app_name}}-store.md.hbs ├── eslint.config.js.hbs ├── flake.nix.hbs ├── package.json.hbs ├── pnpm-workspace.yaml.hbs ├── tests ├── package.json.hbs ├── src │ └── setup.ts.hbs ├── tsconfig.json.hbs └── vitest.config.ts.hbs ├── tsconfig.json.hbs ├── ui ├── .gitignore.hbs ├── demo │ └── index.html.hbs ├── lit-localize.json.hbs ├── package.json.hbs ├── src │ ├── context.ts.hbs │ ├── elements │ │ └── {{kebab_case app_name}}-context.ts.hbs │ ├── index.ts.hbs │ ├── mocks.ts.hbs │ ├── types.ts.hbs │ ├── {{kebab_case app_name}}-client.ts.hbs │ └── {{kebab_case app_name}}-store.ts.hbs ├── tsconfig.json.hbs └── vite.config.ts.hbs ├── workdir ├── dna.nix.hbs ├── dna.yaml.hbs ├── happ.nix.hbs └── happ.yaml.hbs └── zomes ├── coordinator └── {{snake_case app_name}} │ ├── Cargo.toml.hbs │ ├── src │ └── lib.rs.hbs │ ├── tests │ └── common │ │ └── mod.rs.hbs │ └── zome.nix.hbs └── integrity └── {{snake_case app_name}} ├── Cargo.toml.hbs ├── src └── lib.rs.hbs └── zome.nix.hbs /.envrc: -------------------------------------------------------------------------------- 1 | use flake . 2 | -------------------------------------------------------------------------------- /.github/actions/extend-space/action.yaml: -------------------------------------------------------------------------------- 1 | # Extends disk space on github hosted runners 2 | 3 | 4 | name: "Extend space" 5 | description: "Teases out as much free space as possible" 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Create mountpoint for extended space 11 | shell: "bash" 12 | run: sudo mkdir /mnt/extended 13 | 14 | - name: Maximize build space 15 | uses: easimon/maximize-build-space@v6 16 | # uses: AdityaGarg8/remove-unwanted-software@v1 17 | with: 18 | remove-dotnet: true 19 | remove-android: true 20 | remove-haskell: true 21 | build-mount-path: /mnt/extended 22 | root-reserve-mb: 512 23 | swap-size-mb: 1024 24 | 25 | # after we've got the extended space mounted, we can use overlayfs and bind 26 | # mounts as appropriate to make this space available to all paths that are 27 | # expected to be written to. 28 | # 29 | # for new directories (such as /nix), a bind mount is enough for existing 30 | # directories (such as $HOME and /tmp), we use an overlay to preserve access 31 | # to the existing files, while redirecting changes to the extended space. 32 | - name: Use extended space for /nix, /tmp, /var/tmp, and $HOME 33 | shell: "bash" 34 | run: | 35 | export EXTENDED_PATH=/mnt/extended 36 | sudo mkdir $EXTENDED_PATH/{tmp,nix,home} 37 | sudo mkdir $EXTENDED_PATH/tmp/{upper,work} 38 | sudo mv /tmp{,_lower} 39 | sudo mkdir /tmp 40 | sudo mount -t overlay overlay \ 41 | -o lowerdir=/tmp_lower,upperdir=$EXTENDED_PATH/tmp/upper,workdir=$EXTENDED_PATH/tmp/work \ 42 | /tmp 43 | sudo chown $(id -u):$(id -g) /tmp 44 | sudo mkdir -p $EXTENDED_PATH/var/tmp/{upper,work} 45 | sudo mv /var/tmp{,_lower} 46 | sudo mkdir -p /var/tmp 47 | sudo mount -t overlay overlay \ 48 | -o lowerdir=/var/tmp_lower,upperdir=$EXTENDED_PATH/var/tmp/upper,workdir=$EXTENDED_PATH/var/tmp/work \ 49 | /var/tmp 50 | sudo chown $(id -u):$(id -g) /var/tmp 51 | sudo mkdir /nix 52 | sudo mount -o bind $EXTENDED_PATH/nix /nix 53 | sudo mkdir $EXTENDED_PATH/home/{upper,work} 54 | sudo mv ${HOME} ${HOME}_lower 55 | sudo mkdir ${HOME} 56 | sudo chown $(id -u):$(id -g) $HOME 57 | sudo mount -t overlay overlay \ 58 | -o lowerdir=${HOME}_lower,upperdir=$EXTENDED_PATH/home/upper,workdir=$EXTENDED_PATH/home/work \ 59 | $HOME 60 | sudo chown $(id -u):$(id -g) $HOME 61 | df -h 62 | -------------------------------------------------------------------------------- /.github/workflows/test-app.yaml: -------------------------------------------------------------------------------- 1 | name: Test app custom template 2 | on: 3 | # Trigger the workflow on push or pull request, 4 | # but only for the main branch 5 | push: 6 | branches: [ main, develop ] 7 | pull_request: 8 | branches: [ main, develop ] 9 | 10 | jobs: 11 | testbuild: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Install nix 17 | uses: cachix/install-nix-action@v27 18 | with: 19 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 20 | nix_path: nixpkgs=channel:nixos-24.05 21 | 22 | - uses: cachix/cachix-action@v15 23 | with: 24 | name: holochain-ci 25 | 26 | - uses: cachix/cachix-action@v15 27 | with: 28 | name: holochain-open-dev 29 | 30 | - name: Test app template 31 | run: | 32 | nix develop --no-update-lock-file --accept-flake-config --command bash -c "sh run_test_app.sh" 33 | 34 | -------------------------------------------------------------------------------- /.github/workflows/test-zome.yaml: -------------------------------------------------------------------------------- 1 | name: Test zome custom template 2 | on: 3 | # Trigger the workflow on push or pull request, 4 | # but only for the main branch 5 | push: 6 | branches: [ main, develop ] 7 | pull_request: 8 | branches: [ main, develop ] 9 | 10 | jobs: 11 | testbuild: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Install nix 17 | uses: cachix/install-nix-action@v27 18 | with: 19 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 20 | nix_path: nixpkgs=channel:nixos-24.05 21 | 22 | - uses: cachix/cachix-action@v15 23 | with: 24 | name: holochain-ci 25 | 26 | - uses: cachix/cachix-action@v15 27 | with: 28 | name: holochain-open-dev 29 | 30 | - name: Test zome template 31 | run: | 32 | nix develop --no-update-lock-file --accept-flake-config --command bash -c "sh run_test_zome.sh" 33 | 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cargo 2 | target 3 | .direnv 4 | result* 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Holochain Open Dev Templates 2 | 3 | Custom templates for the [scaffolding tool](https://github.com/holochain/scaffolding), to develop apps and modules using the holochain-open-dev style! 4 | 5 | There are two templates available: 6 | 7 | - `app`: fully blown app, following the open-dev patterns and with the profiles zome pre-installed. 8 | - `zome`: template to build zomes like the ones you can find in [open-dev](https://github.com/holochain-open-dev), with the profiles zome already pre-installed. 9 | 10 | ## Using the templates 11 | 12 | > WARNING: these templates are only compatible with holochain v0.3.x. 13 | 14 | 0. If you haven't already, set up the holochain-open-dev binary cache: 15 | 16 | ```bash 17 | nix run nixpkgs#cachix use holochain-open-dev 18 | ``` 19 | 20 | This is necessary to enable fetching of most dependencies from the cache, rather than rebuilding all of them from the scratch. 21 | 22 | 1a. To scaffold a new **app** project, run this: 23 | 24 | ```bash 25 | nix run github:holochain-open-dev/templates#hc-scaffold-app-template -- web-app 26 | ``` 27 | 28 | 1b. To scaffold a new **zome** project, run this: 29 | 30 | ```bash 31 | nix run github:holochain-open-dev/templates#hc-scaffold-zome-template -- web-app 32 | ``` 33 | 34 | 2. If you already have an existing project, add the `holochain-open-dev/templates` repository as input to your flake, and use it in the packages or your `devShell`: 35 | 36 | ```diff 37 | { 38 | description = "Template for Holochain app development"; 39 | 40 | inputs = { 41 | holonix.url = "github:holochain/holonix/main-0.3"; 42 | nixpkgs.follows = "holonix/nixpkgs"; 43 | flake-parts.follows = "holonix/flake-parts"; 44 | 45 | + scaffolding.url = "github:holochain-open-dev/templates"; 46 | }; 47 | 48 | outputs = inputs: 49 | inputs.flake-parts.lib.mkFlake 50 | { 51 | inherit inputs; 52 | } 53 | { 54 | systems = builtins.attrNames inputs.holonix.devShells; 55 | perSystem = 56 | { inputs' 57 | , config 58 | , pkgs 59 | , system 60 | , ... 61 | }: { 62 | devShells.default = pkgs.mkShell { 63 | inputsFrom = [ inputs'.holonix.devShells.default ]; 64 | packages = [ 65 | pkgs.nodejs_20 66 | # more packages go here 67 | + ] ++ [ 68 | + # inputs'.scaffolding.packages.hc-scaffold-zome-template # if your repository is a zome 69 | + inputs'.scaffolding.packages.hc-scaffold-app-template # if your repository is an app 70 | ]; 71 | }; 72 | }; 73 | }; 74 | } 75 | ``` 76 | 77 | --- 78 | 79 | After this set up, you will be able to `nix develop` from inside your repository, and use the scaffolding tool as normal: 80 | 81 | ```bash 82 | hc scaffold dna 83 | hc scaffold zome 84 | ... 85 | ``` 86 | 87 | And all the `hc scaffold` commands will already be using this custom template. 88 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Template for Holochain app development"; 3 | 4 | inputs = { 5 | holonix.url = "github:holochain/holonix/main-0.3"; 6 | nixpkgs.follows = "holonix/nixpkgs"; 7 | flake-parts.follows = "holonix/flake-parts"; 8 | scaffolding.url = "github:holochain/scaffolding/holochain-0.3"; 9 | }; 10 | 11 | nixConfig = { 12 | extra-substituters = [ "https://holochain-open-dev.cachix.org" ]; 13 | extra-trusted-public-keys = [ 14 | "holochain-open-dev.cachix.org-1:3Tr+9in6uo44Ga7qiuRIfOTFXog+2+YbyhwI/Z6Cp4U=" 15 | ]; 16 | }; 17 | 18 | outputs = inputs@{ flake-parts, holonix, ... }: 19 | flake-parts.lib.mkFlake { inherit inputs; } { 20 | systems = builtins.attrNames holonix.devShells; 21 | perSystem = { config, pkgs, system, inputs', ... }: { 22 | devShells.default = pkgs.mkShell { 23 | inputsFrom = [ holonix.devShells.${system}.default ]; 24 | 25 | packages = with pkgs; [ nodejs_20 ]; 26 | }; 27 | 28 | packages.hc-scaffold-app-template = 29 | inputs.scaffolding.lib.wrapCustomTemplate { 30 | inherit pkgs system; 31 | customTemplatePath = ./templates/app; 32 | }; 33 | 34 | packages.hc-scaffold-zome-template = 35 | inputs.scaffolding.lib.wrapCustomTemplate { 36 | inherit pkgs system; 37 | customTemplatePath = ./templates/zome; 38 | }; 39 | }; 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /run_test_app.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | set -e 3 | 4 | DIR=$(pwd) 5 | 6 | nix shell --accept-flake-config .#hc-scaffold-app-template --command bash -c " 7 | cd /tmp 8 | rm -rf forum-lit-open-dev 9 | 10 | hc-scaffold web-app forum-lit-open-dev --setup-nix true -F --package-manager pnpm 11 | " 12 | 13 | cd /tmp/forum-lit-open-dev 14 | 15 | nix develop --no-update-lock-file --accept-flake-config --override-input scaffolding "path:$DIR" --command bash -c " 16 | 17 | set -e 18 | hc-scaffold dna forum 19 | 20 | hc-scaffold zome posts --integrity dnas/forum/zomes/integrity/ --coordinator dnas/forum/zomes/coordinator/ 21 | hc-scaffold entry-type post --reference-entry-hash false --crud crud --link-from-original-to-each-update true --fields title:String:TextField,needs:Vec\:TextField 22 | hc-scaffold entry-type comment --reference-entry-hash false --crud crud --link-from-original-to-each-update false --fields post_hash:ActionHash::Post 23 | hc-scaffold entry-type like --reference-entry-hash false --crud crd --fields like_hash:Option\::Like,image_hash:EntryHash:Image,agent:AgentPubKey:SearchAgent 24 | hc-scaffold entry-type certificate --reference-entry-hash false --crud cr --fields post_hash:ActionHash::Post,agent:AgentPubKey::certified,certifications_hashes:Vec\::Certificate,certificate_type:Enum::CertificateType:TypeOne.TypeTwo,dna_hash:DnaHash 25 | 26 | hc-scaffold collection global all_posts post 27 | hc-scaffold collection by-author posts_by_author post 28 | hc-scaffold collection global all_posts_entry_hash post:EntryHash 29 | hc-scaffold collection by-author posts_by_author_entry_hash post:EntryHash 30 | 31 | hc-scaffold link-type post like --delete true --bidirectional false 32 | hc-scaffold link-type comment like --delete true --bidirectional false 33 | hc-scaffold link-type certificate like --delete false --bidirectional false 34 | hc-scaffold link-type agent:creator post --delete false --bidirectional false 35 | 36 | git add . 37 | 38 | sed -i 's/TODO:REPLACE_ME_WITH_THE_DNA_WITH_THE_PROFILES_ZOME/forum/g' ui/src/holochain-app.ts 39 | nix run github:holochain-open-dev/profiles/nixify#scaffold --refresh -- --local-dna-to-add-the-zome-to forum --local-npm-package-to-add-the-ui-to ui 40 | 41 | pnpm i 42 | 43 | pnpm -F ui format 44 | pnpm -F ui lint 45 | pnpm -F ui build 46 | 47 | pnpm t 48 | " 49 | -------------------------------------------------------------------------------- /run_test_zome.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | set -e 3 | 4 | DIR=$(pwd) 5 | 6 | nix shell --accept-flake-config .#hc-scaffold-zome-template --command bash -c " 7 | cd /tmp 8 | rm -rf posts-open-dev 9 | mkdir posts-open-dev 10 | cd posts-open-dev 11 | 12 | hc-scaffold web-app posts --setup-nix true -F --package-manager pnpm 13 | " 14 | 15 | cd /tmp/posts-open-dev/posts 16 | 17 | nix develop --no-update-lock-file --accept-flake-config --override-input scaffolding "path:$DIR" --command bash -c " 18 | set -e 19 | 20 | hc-scaffold entry-type post --zome posts_integrity --reference-entry-hash false --crud crud --link-from-original-to-each-update true --fields title:String:TextField,needs:Vec\:TextField 21 | hc-scaffold entry-type comment --zome posts_integrity --reference-entry-hash false --crud crud --link-from-original-to-each-update false --fields post_hash:ActionHash::Post 22 | hc-scaffold entry-type like --zome posts_integrity --reference-entry-hash false --crud crd --fields like_hash:Option\::Like,agent:AgentPubKey:SearchAgent 23 | hc-scaffold entry-type certificate --zome posts_integrity --reference-entry-hash false --crud cr --fields post_hash:ActionHash::Post,agent:AgentPubKey::certified,certifications_hashes:Vec\::Certificate,certificate_type:Enum::CertificateType:TypeOne.TypeTwo,dna_hash:DnaHash 24 | 25 | hc-scaffold collection --zome posts_integrity global all_posts post 26 | hc-scaffold collection --zome posts_integrity by-author posts_by_author post 27 | hc-scaffold collection --zome posts_integrity global all_posts_entry_hash post:EntryHash 28 | hc-scaffold collection --zome posts_integrity by-author posts_by_author_entry_hash post:EntryHash 29 | 30 | hc-scaffold link-type --zome posts_integrity post like --delete true --bidirectional false 31 | hc-scaffold link-type --zome posts_integrity comment like --delete true --bidirectional false 32 | hc-scaffold link-type --zome posts_integrity certificate like --delete false --bidirectional false 33 | hc-scaffold link-type --zome posts_integrity agent:creator post --delete false --bidirectional false 34 | 35 | git add . 36 | 37 | pnpm i 38 | 39 | pnpm -F @holochain-open-dev/posts format 40 | pnpm -F @holochain-open-dev/posts lint 41 | pnpm -F @holochain-open-dev/posts build 42 | 43 | pnpm i 44 | 45 | pnpm build:happ 46 | pnpm -F tests test 47 | " 48 | -------------------------------------------------------------------------------- /templates/app/collection/dnas/{{dna_role_name}}/zomes/coordinator/{{snake_case coordinator_zome_manifest.name}}/tests/{{snake_case collection_name}}.rs.hbs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_variables)] 3 | #![allow(unused_imports)] 4 | 5 | use std::time::Duration; 6 | 7 | use hdk::prelude::*; 8 | use holochain::{conductor::config::ConductorConfig, sweettest::*}; 9 | 10 | mod common; 11 | use common::{create_{{snake_case referenceable.name}}, sample_{{snake_case referenceable.name}}_1}; 12 | 13 | #[tokio::test(flavor = "multi_thread")] 14 | async fn create_a_{{snake_case referenceable.name}}_and_get_{{snake_case collection_name}}() { 15 | // Use prebuilt dna file 16 | let dna_path = std::env::current_dir() 17 | .unwrap() 18 | .join(std::env::var("DNA_PATH").expect("DNA_PATH not set, must be run using nix flake check")); 19 | let dna = SweetDnaFile::from_bundle(&dna_path).await.unwrap(); 20 | 21 | // Set up conductors 22 | let mut conductors = SweetConductorBatch::from_config(2, ConductorConfig::default()).await; 23 | let apps = conductors.setup_app("{{dna_role_name}}", &[dna]).await.unwrap(); 24 | conductors.exchange_peer_info().await; 25 | 26 | let ((alice,), (bobbo,)) = apps.into_tuples(); 27 | 28 | let alice_zome = alice.zome("{{snake_case coordinator_zome_manifest.name}}"); 29 | let bob_zome = bobbo.zome("{{snake_case coordinator_zome_manifest.name}}"); 30 | 31 | let sample = sample_{{snake_case referenceable.name}}_1(&conductors[0], &alice_zome).await; 32 | 33 | // Alice creates a {{pascal_case referenceable.name}} 34 | let record: Record = create_{{snake_case referenceable.name}}(&conductors[0], &alice_zome, sample.clone()).await; 35 | 36 | await_consistency(Duration::from_secs(60), [&alice, &bobbo]) 37 | .await 38 | .expect("Timed out waiting for consistency"); 39 | 40 | let links: Vec = conductors[1] 41 | .call(&bob_zome, "get_{{snake_case collection_name}}", {{#if (eq collection_type.type "Global")}}(){{else}}alice_zome.cell_id().agent_pubkey().clone(){{/if}}) 42 | .await; 43 | 44 | assert_eq!(links.len(), 1); 45 | {{#if (eq referenceable.hash_type "EntryHash")}} 46 | assert_eq!(links[0].target.clone().into_entry_hash().unwrap(), record.action().entry_hash().unwrap().clone()); 47 | {{else}} 48 | assert_eq!(links[0].target.clone().into_action_hash().unwrap(), record.signed_action.hashed.hash); 49 | {{/if}} 50 | } 51 | -------------------------------------------------------------------------------- /templates/app/collection/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case collection_name}}.test.ts.hbs: -------------------------------------------------------------------------------- 1 | import { assert, test } from "vitest"; 2 | 3 | import { runScenario, dhtSync } from '@holochain/tryorama'; 4 | import { ActionHash, Record, EntryHash } from '@holochain/client'; 5 | import { decode } from '@msgpack/msgpack'; 6 | import { EntryRecord } from '@holochain-open-dev/utils'; 7 | import { toPromise } from '@holochain-open-dev/signals'; 8 | 9 | import { {{pascal_case referenceable.name}} } from '../../../../ui/src/{{dna_role_name}}/{{snake_case coordinator_zome_manifest.name}}/types.js'; 10 | import { sample{{pascal_case referenceable.name}} } from '../../../../ui/src/{{dna_role_name}}/{{snake_case coordinator_zome_manifest.name}}/mocks.js'; 11 | import { setup } from './setup.js'; 12 | 13 | test('create a {{pascal_case referenceable.name}} and get {{lower_case collection_name}}', async () => { 14 | await runScenario(async scenario => { 15 | const { alice, bob } = await setup(scenario); 16 | 17 | // Bob gets {{lower_case collection_name}} 18 | let collectionOutput = await toPromise(bob.store.{{camel_case collection_name}}{{#if (eq collection_type.type "ByAuthor")}}.get(alice.player.agentPubKey){{/if}}); 19 | assert.equal(collectionOutput.size, 0); 20 | 21 | // Alice creates a {{pascal_case referenceable.name}} 22 | const {{camel_case referenceable.name}}: EntryRecord<{{pascal_case referenceable.name}}> = await alice.store.client.create{{pascal_case referenceable.name}}(await sample{{pascal_case referenceable.name}}(alice.store.client)); 23 | assert.ok({{camel_case referenceable.name}}); 24 | 25 | await dhtSync( 26 | [alice.player, bob.player], 27 | alice.player.cells[0].cell_id[0] 28 | ); 29 | 30 | // Bob gets {{lower_case collection_name}} again 31 | collectionOutput = await toPromise(bob.store.{{camel_case collection_name}}{{#if (eq collection_type.type "ByAuthor")}}.get(alice.player.agentPubKey){{/if}}); 32 | assert.equal(collectionOutput.size, 1); 33 | assert.deepEqual({{camel_case referenceable.name}}.{{#if (eq referenceable.hash_type "ActionHash")}}actionHash{{else}}entryHash{{/if}}, Array.from(collectionOutput.keys())[0]); 34 | }); 35 | }); 36 | 37 | -------------------------------------------------------------------------------- /templates/app/collection/ui/src/{{dna_role_name}}/{{kebab_case coordinator_zome_manifest.name}}/elements/{{kebab_case collection_name}}.ts.hbs: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from 'lit'; 2 | import { state, customElement, property } from 'lit/decorators.js'; 3 | import { AgentPubKey, EntryHash, ActionHash, Record } from '@holochain/client'; 4 | import { SignalWatcher } from '@holochain-open-dev/signals'; 5 | import { consume } from '@lit/context'; 6 | import { localized, msg } from '@lit/localize'; 7 | import { hashProperty, wrapPathInSvg } from '@holochain-open-dev/elements'; 8 | import { mdiInformationOutline } from '@mdi/js'; 9 | 10 | import '@holochain-open-dev/elements/dist/elements/display-error.js'; 11 | import '@shoelace-style/shoelace/dist/components/spinner/spinner.js'; 12 | import '@shoelace-style/shoelace/dist/components/icon/icon.js'; 13 | 14 | import { appStyles } from '../../../app-styles.js'; 15 | import './{{kebab_case referenceable.name}}-summary.js'; 16 | import { {{pascal_case coordinator_zome_manifest.name}}Store } from '../{{kebab_case coordinator_zome_manifest.name}}-store.js'; 17 | import { {{camel_case coordinator_zome_manifest.name}}StoreContext } from '../context.js'; 18 | 19 | /** 20 | * @element {{kebab_case collection_name}} 21 | */ 22 | @localized() 23 | @customElement('{{kebab_case collection_name}}') 24 | export class {{pascal_case collection_name}} extends SignalWatcher(LitElement) { 25 | {{#if (eq collection_type.type "ByAuthor")}} 26 | 27 | /** 28 | * REQUIRED. The author for which the {{plural (pascal_case referenceable.name)}} should be fetched 29 | */ 30 | @property(hashProperty('author')) 31 | author!: AgentPubKey; 32 | {{/if}} 33 | 34 | /** 35 | * @internal 36 | */ 37 | @consume({ context: {{camel_case coordinator_zome_manifest.name}}StoreContext, subscribe: true }) 38 | {{camel_case coordinator_zome_manifest.name}}Store!: {{pascal_case coordinator_zome_manifest.name}}Store; 39 | 40 | {{#if (eq collection_type.type "ByAuthor")}} 41 | firstUpdated() { 42 | if (this.author === undefined) { 43 | throw new Error(`The author property is required for the {{pascal_case collection_name}} element`); 44 | } 45 | } 46 | {{/if}} 47 | 48 | renderList(hashes: Array<{{referenceable.hash_type}}>) { 49 | if (hashes.length === 0) 50 | return html`
51 | 56 | ${msg("No {{lower_case (plural referenceable.name)}} found")} 57 |
`; 58 | 59 | return html` 60 |
61 | ${hashes.map(hash => 62 | html`<{{kebab_case referenceable.name}}-summary .{{camel_case referenceable.name}}Hash=${hash}>` 63 | )} 64 |
65 | `; 66 | } 67 | 68 | render() { 69 | const map = this.{{camel_case coordinator_zome_manifest.name}}Store.{{camel_case collection_name}}{{#if (eq collection_type.type "ByAuthor")}}.get(this.author){{/if}}.get(); 70 | 71 | switch (map.status) { 72 | case 'pending': 73 | return html`
74 | 75 |
`; 76 | case 'error': 77 | return html``; 81 | case 'completed': 82 | return this.renderList(Array.from(map.value.keys())); 83 | } 84 | } 85 | 86 | static styles = appStyles; 87 | } 88 | -------------------------------------------------------------------------------- /templates/app/collection/ui/src/{{dna_role_name}}/{{kebab_case coordinator_zome_manifest.name}}/mocks.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope (concat "export class " (pascal_case coordinator_zome_manifest.name) "ZomeMock extends ZomeMock implements AppClient {" ) }} 3 | {{previous_scope_content}} 4 | 5 | {{#if (eq collection_type.type "ByAuthor")}} 6 | async get_{{snake_case collection_name}}(author: AgentPubKey): Promise> { 7 | const records: Record[] = Array.from(this.{{camel_case (plural referenceable.name)}}.values()).map(r => r.revisions[r.revisions.length - 1]).filter(r => r.signed_action.hashed.content.author.toString() === author.toString()); 8 | return Promise.all(records.map(async record => ({ 9 | base: author, 10 | target: {{#if (eq referenceable.hash_type "EntryHash")}}(record.signed_action.hashed.content as NewEntryAction).entry_hash{{else}}record.signed_action.hashed.hash{{/if}}, 11 | author: record.signed_action.hashed.content.author, 12 | timestamp: record.signed_action.hashed.content.timestamp, 13 | zome_index: 0, 14 | link_type: 0, 15 | tag: new Uint8Array(), 16 | create_link_hash: await fakeActionHash() 17 | }))); 18 | } 19 | 20 | {{else}} 21 | async get_{{snake_case collection_name}}(): Promise> { 22 | const records: Record[] = Array.from(this.{{camel_case (plural referenceable.name)}}.values()).map(r => r.revisions[r.revisions.length - 1]); 23 | const base = await fakeEntryHash(); 24 | return Promise.all(records.map(async record => ({ 25 | base, 26 | target: {{#if (eq referenceable.hash_type "EntryHash")}}(record.signed_action.hashed.content as NewEntryAction).entry_hash{{else}}record.signed_action.hashed.hash{{/if}}, 27 | author: record.signed_action.hashed.content.author, 28 | timestamp: record.signed_action.hashed.content.timestamp, 29 | zome_index: 0, 30 | link_type: 0, 31 | tag: new Uint8Array(), 32 | create_link_hash: await fakeActionHash() 33 | }))); 34 | } 35 | {{/if}} 36 | 37 | {{/match_scope}} 38 | {{/merge}} 39 | 40 | -------------------------------------------------------------------------------- /templates/app/collection/ui/src/{{dna_role_name}}/{{kebab_case coordinator_zome_manifest.name}}/{{kebab_case coordinator_zome_manifest.name}}-client.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope (concat "export class " (pascal_case coordinator_zome_manifest.name) "Client extends ZomeClient<" (pascal_case coordinator_zome_manifest.name) "Signal> {" ) }} 3 | {{previous_scope_content}} 4 | 5 | /** {{title_case collection_name}} */ 6 | 7 | async get{{pascal_case collection_name}}({{#if (eq collection_type.type "ByAuthor")}}author: AgentPubKey{{else}}{{/if}}): Promise> { 8 | return this.callZome('get_{{snake_case collection_name}}', {{#if (eq collection_type.type "ByAuthor")}}author{{else}}undefined{{/if}}); 9 | } 10 | 11 | {{/match_scope}} 12 | {{/merge}} 13 | -------------------------------------------------------------------------------- /templates/app/collection/ui/src/{{dna_role_name}}/{{kebab_case coordinator_zome_manifest.name}}/{{kebab_case coordinator_zome_manifest.name}}-store.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope (concat "export class " (pascal_case coordinator_zome_manifest.name) "Store {" ) }} 3 | {{previous_scope_content}} 4 | 5 | /** {{title_case collection_name}} */ 6 | 7 | {{#if (eq collection_type.type "ByAuthor")}} 8 | {{camel_case collection_name}} = new LazyHoloHashMap((author: AgentPubKey) => 9 | pipe( 10 | collectionSignal( 11 | this.client, 12 | () => this.client.get{{pascal_case collection_name}}(author), 13 | '{{pascal_case collection_name}}' 14 | ), 15 | {{camel_case collection_name}} => slice(this.{{camel_case (plural referenceable.name)}}, {{camel_case collection_name}}.map(l => l.target)) 16 | ) 17 | ); 18 | {{else}} 19 | {{camel_case collection_name}} = pipe( 20 | collectionSignal( 21 | this.client, 22 | () => this.client.get{{pascal_case collection_name}}(), 23 | '{{pascal_case collection_name}}' 24 | ), 25 | {{camel_case collection_name}} => slice(this.{{camel_case (plural referenceable.name)}}, {{camel_case collection_name}}.map(l => l.target)) 26 | ); 27 | {{/if}} 28 | {{/match_scope}} 29 | {{/merge}} 30 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome.instructions.hbs: -------------------------------------------------------------------------------- 1 | We recommended that you format your UI files with: 2 | 3 | pnpm -F ui format 4 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/dnas/{{dna_role_name}}/dna.nix.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope "zomes = {"}} 3 | {{previous_scope_content}} 4 | {{zome_manifest.name}} = self'.packages.{{zome_manifest.name}}; 5 | {{/match_scope}} 6 | {{/merge}} 7 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/dnas/{{dna_role_name}}/zomes/coordinator/{{zome_manifest.name}}/Cargo.toml.hbs: -------------------------------------------------------------------------------- 1 | {{previous_file_content}} 2 | 3 | [dev-dependencies] 4 | fixt = "0.3.1" 5 | futures = { version = "0.3.1", default-features = false } 6 | hdk = { workspace = true, features = ["encoding", "test_utils"] } 7 | holochain = { workspace = true } 8 | tokio = { version = "1.3", features = ["full"] } 9 | 10 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/dnas/{{dna_role_name}}/zomes/coordinator/{{zome_manifest.name}}/tests/common/mod.rs.hbs: -------------------------------------------------------------------------------- 1 | use hdk::prelude::*; 2 | use holochain::sweettest::*; 3 | 4 | use {{snake_case zome_manifest.name}}_integrity::*; 5 | 6 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/dnas/{{dna_role_name}}/zomes/coordinator/{{zome_manifest.name}}/zome.nix.hbs: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | 3 | { 4 | perSystem = 5 | { inputs' 6 | , self' 7 | , system 8 | , ... 9 | }: { 10 | packages.{{zome_manifest.name}} = inputs.hc-infra.outputs.builders.${system}.rustZome { 11 | workspacePath = inputs.self.outPath; 12 | crateCargoToml = ./Cargo.toml; 13 | cargoArtifacts = inputs'.hc-infra.packages.zomeCargoArtifacts; 14 | }; 15 | 16 | # Test only this zome and its integrity in isolation 17 | checks.{{zome_manifest.name }}= inputs.hc-infra.outputs.builders.${system}.sweettest { 18 | workspacePath = inputs.self.outPath; 19 | dna = (inputs.hc-infra.outputs.builders.${system}.dna { 20 | dnaManifest = builtins.toFile "dna.yaml" '' 21 | --- 22 | manifest_version: "1" 23 | name: test_dna 24 | integrity: 25 | network_seed: ~ 26 | properties: ~ 27 | origin_time: 1709638576394039 28 | zomes: 29 | - name: {{zome_manifest.name}}_integrity 30 | coordinator: 31 | zomes: 32 | - name: {{zome_manifest.name}} 33 | hash: ~ 34 | dependencies: 35 | - name: {{zome_manifest.name}}_integrity 36 | dylib: ~ 37 | ''; 38 | zomes = { 39 | {{zome_manifest.name}}_integrity = self'.packages.{{zome_manifest.name}}_integrity; 40 | {{zome_manifest.name}} = self'.packages.{{zome_manifest.name}}; 41 | }; 42 | }).meta.debug; 43 | crateCargoToml = ./Cargo.toml; 44 | cargoArtifacts = inputs'.hc-infra.packages.holochainCargoArtifacts; 45 | }; 46 | 47 | }; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/tests/src/{{dna_role_name}}/{{zome_manifest.name}}/setup.ts.hbs: -------------------------------------------------------------------------------- 1 | import { 2 | AgentPubKey, 3 | EntryHash, 4 | NewEntryAction, 5 | ActionHash, 6 | Record, 7 | AppBundleSource, 8 | fakeActionHash, 9 | fakeAgentPubKey, 10 | fakeEntryHash, 11 | fakeDnaHash, 12 | AppCallZomeRequest, 13 | AppWebsocket, 14 | encodeHashToBase64 15 | } from '@holochain/client'; 16 | import { encode } from '@msgpack/msgpack'; 17 | import { Scenario } from '@holochain/tryorama'; 18 | import { EntryRecord } from '@holochain-open-dev/utils'; 19 | import { appPath } from '../../app-path.js'; 20 | import { {{pascal_case zome_manifest.name}}Client } from '../../../../ui/src/{{dna_role_name}}/{{zome_manifest.name}}/{{kebab_case zome_manifest.name}}-client.js'; 21 | import { {{pascal_case zome_manifest.name}}Store } from '../../../../ui/src/{{dna_role_name}}/{{zome_manifest.name}}/{{kebab_case zome_manifest.name}}-store.js'; 22 | 23 | export async function setup(scenario: Scenario) { 24 | // Add 2 players with the test hApp to the Scenario. The returned players 25 | // can be destructured. 26 | const [alice, bob] = await scenario.addPlayersWithApps([ 27 | { appBundleSource: { path: appPath } }, 28 | { appBundleSource: { path: appPath } }, 29 | ]); 30 | 31 | // Shortcut peer discovery through gossip and register all agents in every 32 | // conductor of the scenario. 33 | await scenario.shareAllAgents(); 34 | 35 | const aliceStore = new {{pascal_case zome_manifest.name}}Store( 36 | new {{pascal_case zome_manifest.name}}Client(alice.appWs as any, '{{snake_case dna_role_name}}', '{{snake_case zome_manifest.name}}') 37 | ); 38 | 39 | const bobStore = new {{pascal_case zome_manifest.name}}Store( 40 | new {{pascal_case zome_manifest.name}}Client(bob.appWs as any, '{{snake_case dna_role_name}}', '{{snake_case zome_manifest.name}}') 41 | ); 42 | 43 | // Shortcut peer discovery through gossip and register all agents in every 44 | // conductor of the scenario. 45 | await scenario.shareAllAgents(); 46 | 47 | return { 48 | alice: { 49 | player: alice, 50 | store: aliceStore, 51 | }, 52 | bob: { 53 | player: bob, 54 | store: bobStore, 55 | }, 56 | }; 57 | } 58 | 59 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/ui/src/holochain-app.ts.hbs: -------------------------------------------------------------------------------- 1 | import { {{camel_case zome_manifest.name}}StoreContext } from './{{dna_role_name}}/{{kebab_case zome_manifest.name}}/context.js'; 2 | import { {{pascal_case zome_manifest.name}}Client } from './{{dna_role_name}}/{{kebab_case zome_manifest.name}}/{{kebab_case zome_manifest.name}}-client.js'; 3 | import { {{pascal_case zome_manifest.name}}Store } from './{{dna_role_name}}/{{kebab_case zome_manifest.name}}/{{kebab_case zome_manifest.name}}-store.js'; 4 | 5 | {{#merge previous_file_content}} 6 | {{#match_scope "export class HolochainApp extends SignalWatcher(LitElement) {"}} 7 | @provide({ context: {{camel_case zome_manifest.name}}StoreContext }) 8 | @property() 9 | _{{camel_case zome_manifest.name}}Store!: {{pascal_case zome_manifest.name}}Store; 10 | 11 | {{#merge previous_scope_content}} 12 | {{#match_scope "async initStores(appClient: AppClient) {"}} 13 | {{previous_scope_content}} 14 | this._{{camel_case zome_manifest.name}}Store = new {{pascal_case zome_manifest.name}}Store(new {{pascal_case zome_manifest.name}}Client(appClient, '{{dna_role_name}}')); 15 | {{/match_scope}} 16 | {{/merge}} 17 | {{/match_scope}} 18 | {{/merge}} 19 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/ui/src/{{dna_role_name}}/{{zome_manifest.name}}/{{#if (and (not (eq (pascal_case zome_manifest.name) "Profiles")) (not (eq (pascal_case zome_manifest.name) "FileStorage")))}}context.ts{{¡if}}.hbs: -------------------------------------------------------------------------------- 1 | import { createContext } from '@lit/context'; 2 | import { {{pascal_case zome_manifest.name}}Store } from './{{kebab_case zome_manifest.name}}-store.js'; 3 | 4 | export const {{camel_case zome_manifest.name}}StoreContext = createContext<{{pascal_case zome_manifest.name}}Store>( 5 | '{{snake_case zome_manifest.name}}/store' 6 | ); 7 | 8 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/ui/src/{{dna_role_name}}/{{zome_manifest.name}}/{{#if (and (not (eq (pascal_case zome_manifest.name) "Profiles")) (not (eq (pascal_case zome_manifest.name) "FileStorage")))}}mocks.ts{{¡if}}.hbs: -------------------------------------------------------------------------------- 1 | import { 2 | AgentPubKeyMap, 3 | decodeEntry, 4 | fakeEntry, 5 | fakeCreateAction, 6 | fakeUpdateEntry, 7 | fakeDeleteEntry, 8 | fakeRecord, 9 | pickBy, 10 | ZomeMock, 11 | RecordBag, 12 | entryState, 13 | HoloHashMap, 14 | HashType, 15 | hash 16 | } from "@holochain-open-dev/utils"; 17 | import { 18 | decodeHashFromBase64, 19 | NewEntryAction, 20 | AgentPubKey, 21 | ActionHash, 22 | EntryHash, 23 | Delete, 24 | AppClient, 25 | fakeAgentPubKey, 26 | fakeDnaHash, 27 | Link, 28 | fakeActionHash, 29 | SignedActionHashed, 30 | fakeEntryHash, 31 | Record, 32 | } from "@holochain/client"; 33 | import { {{pascal_case zome_manifest.name}}Client } from './{{kebab_case zome_manifest.name}}-client.js' 34 | 35 | export class {{pascal_case zome_manifest.name}}ZomeMock extends ZomeMock implements AppClient { 36 | constructor( 37 | myPubKey?: AgentPubKey 38 | ) { 39 | super("{{snake_case zome_manifest.name}}_test", "{{snake_case zome_manifest.name}}", myPubKey); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/ui/src/{{dna_role_name}}/{{zome_manifest.name}}/{{#if (and (not (eq (pascal_case zome_manifest.name) "Profiles")) (not (eq (pascal_case zome_manifest.name) "FileStorage")))}}types.ts{{¡if}}.hbs: -------------------------------------------------------------------------------- 1 | import { 2 | Record, 3 | ActionHash, 4 | DnaHash, 5 | SignedActionHashed, 6 | EntryHash, 7 | AgentPubKey, 8 | Create, 9 | Update, 10 | Delete, 11 | CreateLink, 12 | DeleteLink 13 | } from '@holochain/client'; 14 | import { ActionCommittedSignal } from '@holochain-open-dev/utils'; 15 | 16 | export type {{pascal_case zome_manifest.name}}Signal = ActionCommittedSignal; 17 | 18 | export type EntryTypes = never; 19 | 20 | export type LinkTypes = string; 21 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/ui/src/{{dna_role_name}}/{{zome_manifest.name}}/{{#if (and (not (eq (pascal_case zome_manifest.name) "Profiles")) (not (eq (pascal_case zome_manifest.name) "FileStorage")))}}{{kebab_case zome_manifest.name}}-client.ts{{¡if}}.hbs: -------------------------------------------------------------------------------- 1 | import { 2 | SignedActionHashed, 3 | CreateLink, 4 | Link, 5 | DeleteLink, 6 | Delete, 7 | AppClient, 8 | Record, 9 | ActionHash, 10 | EntryHash, 11 | AgentPubKey, 12 | } from '@holochain/client'; 13 | import { isSignalFromCellWithRole, EntryRecord, ZomeClient } from '@holochain-open-dev/utils'; 14 | 15 | import { {{pascal_case zome_manifest.name}}Signal } from './types.js'; 16 | 17 | export class {{pascal_case zome_manifest.name}}Client extends ZomeClient<{{pascal_case zome_manifest.name}}Signal> { 18 | 19 | constructor(public client: AppClient, public roleName: string, public zomeName = '{{snake_case zome_manifest.name}}') { 20 | super(client, roleName, zomeName); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /templates/app/coordinator-zome/ui/src/{{dna_role_name}}/{{zome_manifest.name}}/{{#if (and (not (eq (pascal_case zome_manifest.name) "Profiles")) (not (eq (pascal_case zome_manifest.name) "FileStorage")))}}{{kebab_case zome_manifest.name}}-store.ts{{¡if}}.hbs: -------------------------------------------------------------------------------- 1 | import { 2 | collectionSignal, 3 | liveLinksSignal, 4 | deletedLinksSignal, 5 | allRevisionsOfEntrySignal, 6 | latestVersionOfEntrySignal, 7 | immutableEntrySignal, 8 | deletesForEntrySignal, 9 | pipe, 10 | AsyncComputed 11 | } from "@holochain-open-dev/signals"; 12 | import { slice, HashType, retype, EntryRecord, LazyHoloHashMap } from "@holochain-open-dev/utils"; 13 | import { NewEntryAction, Record, ActionHash, EntryHash, AgentPubKey } from '@holochain/client'; 14 | 15 | import { {{pascal_case zome_manifest.name}}Client } from './{{kebab_case zome_manifest.name}}-client.js'; 16 | 17 | export class {{pascal_case zome_manifest.name}}Store { 18 | 19 | constructor(public client: {{pascal_case zome_manifest.name}}Client) {} 20 | 21 | } 22 | -------------------------------------------------------------------------------- /templates/app/dna.instructions.hbs: -------------------------------------------------------------------------------- 1 | If you want the UI to work, you must include the profiles zome in one of your DNAs. Usually you want to include it in the DNA that everyone joins at first when installing the app. You only need to include it in *one* of the DNAs of the hApp. 2 | 3 | Do you want the profiles zome to be installed in the DNA you just scaffolded? 4 | 5 | If you do, scaffold the profiles zome in this DNA by running this command: 6 | 7 | sed -i 's/TODO:REPLACE_ME_WITH_THE_DNA_WITH_THE_PROFILES_ZOME/{{dna_name}}/g' ui/src/holochain-app.ts 8 | nix run github:holochain-open-dev/profiles/nixify#scaffold -- --local-dna-to-add-the-zome-to {{dna_name}} --local-npm-package-to-add-the-ui-to ui 9 | 10 | Note that the UI for this app won't work until the profiles zome is scaffolded. 11 | -------------------------------------------------------------------------------- /templates/app/dna/dnas/{{dna_name}}/dna.nix.hbs: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | 3 | { 4 | # Import all ./zomes/coordinator/*/zome.nix and ./zomes/integrity/*/zome.nix 5 | imports = ( 6 | map (m: "${./.}/zomes/coordinator/${m}/zome.nix") 7 | (builtins.attrNames (builtins.readDir ./zomes/coordinator)) 8 | ) 9 | ++ 10 | ( 11 | map (m: "${./.}/zomes/integrity/${m}/zome.nix") 12 | (builtins.attrNames (builtins.readDir ./zomes/integrity)) 13 | ) 14 | ; 15 | perSystem = 16 | { inputs' 17 | , self' 18 | , lib 19 | , system 20 | , ... 21 | }: { 22 | packages.{{dna_name}} = inputs.hc-infra.outputs.builders.${system}.dna { 23 | dnaManifest = ./workdir/dna.yaml; 24 | zomes = { 25 | # Include here the zome packages for this DNA, e.g.: 26 | # profiles_integrity = inputs'.profiles.packages.profiles_integrity; 27 | # This overrides all the "bundled" properties for the DNA manifest 28 | }; 29 | }; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /templates/app/dna/happ.nix.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope "dnas = {"}} 3 | {{previous_scope_content}} 4 | {{dna_name}} = self'.packages.{{dna_name}}; 5 | {{/match_scope}} 6 | {{/merge}} 7 | -------------------------------------------------------------------------------- /templates/app/entry-type.instructions.hbs: -------------------------------------------------------------------------------- 1 | {{#each entry_type.fields}} 2 | {{#if (and widget (eq widget "Image"))}} 3 | You have scaffolded an entry type with an image field. The image must be stored in the "file-storage" zome, that you can find at https://github.com/holochain-open-dev/file-storage. You must include this zome at least once in your app to enable "Image" as a field type. 4 | 5 | If you haven't already, scaffold the file-storage zome in one of your DNAs by running these commands: 6 | 7 | nix run github:holochain-open-dev/file-storage/nixify#scaffold 8 | 9 | Then, set up the `FileStorageClient` together with `` in your `ui/src/holochain-app.ts`. 10 | {{/if}} 11 | {{/each}} 12 | -------------------------------------------------------------------------------- /templates/app/entry-type/ui/src/{{dna_role_name}}/{{kebab_case coordinator_zome_manifest.name}}/elements/{{kebab_case entry_type.name}}-summary.ts.hbs: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from 'lit'; 2 | import { state, property, customElement } from 'lit/decorators.js'; 3 | import { EntryHash, Record, ActionHash } from '@holochain/client'; 4 | import { EntryRecord } from '@holochain-open-dev/utils'; 5 | import { SignalWatcher } from '@holochain-open-dev/signals'; 6 | import { hashProperty } from '@holochain-open-dev/elements'; 7 | import { consume } from '@lit/context'; 8 | 9 | import { localized, msg } from '@lit/localize'; 10 | 11 | {{#uniq_lines}} 12 | import '@shoelace-style/shoelace/dist/components/card/card.js'; 13 | import '@shoelace-style/shoelace/dist/components/spinner/spinner.js'; 14 | import '@holochain-open-dev/elements/dist/elements/display-error.js'; 15 | {{#each entry_type.fields}} 16 | {{#if widget}} 17 | {{> (concat field_type.type "/" widget "/detail/imports") }} 18 | 19 | {{/if}} 20 | {{/each}} 21 | {{/uniq_lines}} 22 | 23 | import { appStyles } from '../../../app-styles.js'; 24 | import { {{pascal_case coordinator_zome_manifest.name}}Store } from '../{{kebab_case coordinator_zome_manifest.name}}-store.js'; 25 | import { {{camel_case coordinator_zome_manifest.name}}StoreContext } from '../context.js'; 26 | import { {{pascal_case entry_type.name}}{{#each entry_type.fields}}{{#if (eq field_type.type "Enum")}}, {{field_type.label}}{{/if}}{{/each}} } from '../types.js'; 27 | 28 | /** 29 | * @element {{kebab_case entry_type.name}}-summary 30 | * @fires {{kebab_case entry_type.name}}-selected: detail will contain { {{camel_case entry_type.name}}Hash } 31 | */ 32 | @localized() 33 | @customElement('{{kebab_case entry_type.name}}-summary') 34 | export class {{pascal_case entry_type.name}}Summary extends SignalWatcher(LitElement) { 35 | 36 | /** 37 | * REQUIRED. The hash of the {{pascal_case entry_type.name}} to show 38 | */ 39 | @property(hashProperty('{{kebab_case entry_type.name}}-hash')) 40 | {{camel_case entry_type.name}}Hash!: {{#if entry_type.reference_entry_hash}}EntryHash{{else}}ActionHash{{/if}}; 41 | 42 | /** 43 | * @internal 44 | */ 45 | @consume({ context: {{camel_case coordinator_zome_manifest.name}}StoreContext, subscribe: true }) 46 | {{camel_case coordinator_zome_manifest.name}}Store!: {{pascal_case coordinator_zome_manifest.name}}Store; 47 | 48 | renderSummary(entryRecord: EntryRecord<{{pascal_case entry_type.name}}>) { 49 | return html` 50 |
51 | 52 | {{#each entry_type.fields}} 53 | {{#if widget}} 54 |
55 | ${msg("{{title_case field_name}}")} 56 | {{#if (not (eq cardinality "vector") )}} 57 | {{> (concat field_type.type "/" widget "/detail/render") field_name=field_name variable_to_read=(concat "entryRecord.entry." (snake_case field_name) ) }} 58 | {{else}} 59 | {{> Vec/detail/render variable_to_read=(concat "entryRecord.entry." (snake_case field_name) ) field_name=field_name field_type=field_type widget=widget }} 60 | {{/if}} 61 |
62 | 63 | {{/if}} 64 | {{/each}} 65 |
66 | `; 67 | } 68 | 69 | render{{pascal_case entry_type.name}}() { 70 | const {{camel_case entry_type.name}} = this.{{camel_case coordinator_zome_manifest.name}}Store.{{camel_case (plural entry_type.name)}}.get(this.{{camel_case entry_type.name}}Hash).{{#if crud.update}}latestVersion{{else}}entry{{/if}}.get(); 71 | 72 | switch ({{camel_case entry_type.name}}.status) { 73 | case 'pending': 74 | return html`
76 | 77 |
`; 78 | case 'error': 79 | return html``; 83 | case 'completed': 84 | return this.renderSummary({{camel_case entry_type.name}}.value); 85 | } 86 | } 87 | 88 | render() { 89 | return html` this.dispatchEvent(new CustomEvent('{{kebab_case entry_type.name}}-selected', { 90 | composed: true, 91 | bubbles: true, 92 | detail: { 93 | {{camel_case entry_type.name}}Hash: this.{{camel_case entry_type.name}}Hash 94 | } 95 | }))}> 96 | ${this.render{{pascal_case entry_type.name}}()} 97 | `; 98 | } 99 | 100 | 101 | static styles = appStyles; 102 | } 103 | -------------------------------------------------------------------------------- /templates/app/entry-type/ui/src/{{dna_role_name}}/{{kebab_case coordinator_zome_manifest.name}}/types.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#if (includes previous_file_content "export type EntryTypes = never;")}} 2 | {{replace previous_file_content "export type EntryTypes = never;" (concat "export type EntryTypes = ({ type: '" (pascal_case entry_type.name) "'; } & " (pascal_case entry_type.name) ");")}} 3 | {{else}} 4 | {{replace previous_file_content "export type EntryTypes =" (concat "export type EntryTypes = ({ type: '" (pascal_case entry_type.name) "'; } & " (pascal_case entry_type.name) ") | ")}} 5 | {{/if}} 6 | 7 | {{#each entry_type.fields}} 8 | {{#if (eq field_type.type "Enum")}} 9 | export interface {{field_type.label}} { 10 | type: 11 | {{#each field_type.variants}} 12 | | '{{this}}' 13 | {{/each}}; 14 | } 15 | {{/if}} 16 | {{/each}} 17 | 18 | export interface {{pascal_case entry_type.name}} { {{#each entry_type.fields}} 19 | {{#if (not (eq cardinality "vector" ) )}} 20 | {{snake_case field_name}}: {{> (concat field_type.type "/type") }}{{#if (eq cardinality "option")}} | undefined{{/if}}; 21 | {{else}} 22 | {{snake_case field_name}}: Array<{{> (concat field_type.type "/type") }}>; 23 | {{/if}} 24 | {{/each}} 25 | } 26 | 27 | -------------------------------------------------------------------------------- /templates/app/field-types/ActionHash/sample-js.hbs: -------------------------------------------------------------------------------- 1 | (await fakeActionHash()) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/ActionHash/sample1.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(ActionHash) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/ActionHash/sample2.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(ActionHash) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/ActionHash/type.hbs: -------------------------------------------------------------------------------- 1 | ActionHash 2 | -------------------------------------------------------------------------------- /templates/app/field-types/AgentPubKey/SearchAgent/detail/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@holochain-open-dev/profiles/dist/elements/agent-avatar.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/AgentPubKey/SearchAgent/detail/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /templates/app/field-types/AgentPubKey/SearchAgent/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@holochain-open-dev/profiles/dist/elements/search-agent.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/AgentPubKey/SearchAgent/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/AgentPubKey/SearchAgent/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/AgentPubKey/sample-js.hbs: -------------------------------------------------------------------------------- 1 | (await fakeAgentPubKey()) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/AgentPubKey/sample1.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(AgentPubKey) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/AgentPubKey/sample2.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(AgentPubKey) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/AgentPubKey/type.hbs: -------------------------------------------------------------------------------- 1 | AgentPubKey 2 | -------------------------------------------------------------------------------- /templates/app/field-types/DnaHash/sample-js.hbs: -------------------------------------------------------------------------------- 1 | (await fakeDnaHash()) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/DnaHash/sample1.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(DnaHash) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/DnaHash/sample2.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(DnaHash) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/DnaHash/type.hbs: -------------------------------------------------------------------------------- 1 | DnaHash 2 | -------------------------------------------------------------------------------- /templates/app/field-types/EntryHash/Image/detail/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@holochain-open-dev/file-storage/dist/elements/show-image.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/EntryHash/Image/detail/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/EntryHash/Image/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@holochain-open-dev/file-storage/dist/elements/upload-files.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/EntryHash/Image/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/EntryHash/Image/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/EntryHash/sample-js.hbs: -------------------------------------------------------------------------------- 1 | (await fakeEntryHash()) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/EntryHash/sample1.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(EntryHash) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/EntryHash/sample2.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(EntryHash) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/EntryHash/type.hbs: -------------------------------------------------------------------------------- 1 | EntryHash 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Enum/Select/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{#each field_type.variants}}{{#unless @last}} {{../variable_to_read}}.type === '{{pascal_case this}}' ?{{/unless}} msg("{{title_case this}}"){{#unless @last}} :{{/unless}} {{/each}} } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Enum/Select/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/select/select.js'; 2 | import '@shoelace-style/shoelace/dist/components/option/option.js'; 3 | -------------------------------------------------------------------------------- /templates/app/field-types/Enum/Select/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | {{#each field_type.variants}} 3 | ${msg("{{title_case this}}")} 4 | {{/each}} 5 | 6 | -------------------------------------------------------------------------------- /templates/app/field-types/Enum/Select/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Enum/sample-js.hbs: -------------------------------------------------------------------------------- 1 | { type: '{{lookup field_type.variants 0}}' } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Enum/sample1.hbs: -------------------------------------------------------------------------------- 1 | {{field_type.label}}::{{lookup field_type.variants 0}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Enum/sample2.hbs: -------------------------------------------------------------------------------- 1 | {{#if (eq (len field_type.variants) 1)}} 2 | {{field_type.label}}::{{lookup field_type.variants 0}} 3 | {{else}} 4 | {{field_type.label}}::{{lookup field_type.variants 1}} 5 | {{/if}} 6 | -------------------------------------------------------------------------------- /templates/app/field-types/Enum/type.hbs: -------------------------------------------------------------------------------- 1 | {{pascal_case field_type.label}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/String/TextArea/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/String/TextArea/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/textarea/textarea.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/String/TextArea/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/String/TextArea/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/String/TextField/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/String/TextField/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/input/input.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/String/TextField/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/String/TextField/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/String/sample-js.hbs: -------------------------------------------------------------------------------- 1 | "Lorem ipsum 2" 2 | -------------------------------------------------------------------------------- /templates/app/field-types/String/sample1.hbs: -------------------------------------------------------------------------------- 1 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit.".to_string() 2 | -------------------------------------------------------------------------------- /templates/app/field-types/String/sample2.hbs: -------------------------------------------------------------------------------- 1 | "Lorem ipsum 2".to_string() 2 | -------------------------------------------------------------------------------- /templates/app/field-types/String/type.hbs: -------------------------------------------------------------------------------- 1 | string 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Timestamp/DateTimePicker/detail/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/format-date/format-date.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Timestamp/DateTimePicker/detail/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Timestamp/DateTimePicker/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/input/input.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Timestamp/DateTimePicker/edit/render.hbs: -------------------------------------------------------------------------------- 1 | e.preventDefault()} {{#if required}} required{{/if}}{{#if edit}} .defaultValue=${new Date({{default_value}}/1000).toLocaleString()} {{/if}}> 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/Timestamp/DateTimePicker/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | new Date({{variable_to_read}}).valueOf() * 1000 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Timestamp/sample-js.hbs: -------------------------------------------------------------------------------- 1 | Date.now() * 1000 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Timestamp/sample1.hbs: -------------------------------------------------------------------------------- 1 | Timestamp::from_micros(1674053334548000) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Timestamp/sample2.hbs: -------------------------------------------------------------------------------- 1 | Timestamp::from_micros(1674059334548000) 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Timestamp/type.hbs: -------------------------------------------------------------------------------- 1 | number 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Vec/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}}.map(el => html`{{> (concat field_type.type "/" widget "/detail/render") variable_to_read="el"}}`)} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/Vec/edit/render.hbs: -------------------------------------------------------------------------------- 1 |
2 | ${msg("{{title_case field_name}}")} 3 | 4 | ${repeat(this._{{camel_case field_name}}Fields, i => i, index => html`
{{> (concat field_type.type "/" widget "/edit/render") edit=edit label="" field_name=field_name default_value=(concat "currentRecord.entry." (snake_case field_name) "[index]") }} { this._{{camel_case field_name}}Fields = this._{{camel_case field_name}}Fields.filter(i => i !== index) } }>
`)} 5 | { this._{{camel_case field_name}}Fields = [...this._{{camel_case field_name}}Fields, Math.max(...this._{{camel_case field_name}}Fields) + 1]; } }>${msg("Add {{title_case field_name}}")} 6 |
7 | -------------------------------------------------------------------------------- /templates/app/field-types/Vec/type.hbs: -------------------------------------------------------------------------------- 1 | Array<{{field_type.type}}> 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/Checkbox/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} ? 'Yes' : 'No' } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/Checkbox/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/Checkbox/edit/render.hbs: -------------------------------------------------------------------------------- 1 | ${msg("{{title_case field_name}}")} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/Checkbox/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} === 'on' 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/Switch/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} ? 'Yes' : 'No' } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/Switch/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/switch/switch.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/Switch/edit/render.hbs: -------------------------------------------------------------------------------- 1 | ${msg("{{title_case field_name}}")} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/Switch/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} === 'on' 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/sample-js.hbs: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/sample1.hbs: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/sample2.hbs: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /templates/app/field-types/bool/type.hbs: -------------------------------------------------------------------------------- 1 | boolean 2 | -------------------------------------------------------------------------------- /templates/app/field-types/f32/Slider/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/f32/Slider/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/range/range.js' 2 | -------------------------------------------------------------------------------- /templates/app/field-types/f32/Slider/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/f32/Slider/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/f32/sample-js.hbs: -------------------------------------------------------------------------------- 1 | 1.5 2 | -------------------------------------------------------------------------------- /templates/app/field-types/f32/sample1.hbs: -------------------------------------------------------------------------------- 1 | 0.5 2 | -------------------------------------------------------------------------------- /templates/app/field-types/f32/sample2.hbs: -------------------------------------------------------------------------------- 1 | 1.5 2 | -------------------------------------------------------------------------------- /templates/app/field-types/f32/type.hbs: -------------------------------------------------------------------------------- 1 | number 2 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/Slider/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/Slider/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/range/range.js' 2 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/Slider/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/Slider/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/TextField/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/TextField/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/input/input.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/TextField/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/TextField/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/sample-js.hbs: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/sample1.hbs: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/sample2.hbs: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /templates/app/field-types/i32/type.hbs: -------------------------------------------------------------------------------- 1 | number 2 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/Slider/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/Slider/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/range/range.js' 2 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/Slider/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/Slider/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/TextField/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/TextField/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/input/input.js'; 2 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/TextField/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/TextField/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/sample-js.hbs: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/sample1.hbs: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/sample2.hbs: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /templates/app/field-types/u32/type.hbs: -------------------------------------------------------------------------------- 1 | number 2 | -------------------------------------------------------------------------------- /templates/app/integrity-zome/dnas/{{dna_role_name}}/dna.nix.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope "zomes = {"}} 3 | {{previous_scope_content}} 4 | {{zome_manifest.name}} = self'.packages.{{zome_manifest.name}}; 5 | {{/match_scope}} 6 | {{/merge}} 7 | -------------------------------------------------------------------------------- /templates/app/integrity-zome/dnas/{{dna_role_name}}/zomes/integrity/{{replace zome_manifest.name "_integrity" "" }}/zome.nix.hbs: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | 3 | { 4 | perSystem = 5 | { inputs' 6 | , system 7 | , ... 8 | }: { 9 | packages.{{zome_manifest.name}} = inputs.hc-infra.outputs.builders.${system}.rustZome { 10 | workspacePath = inputs.self.outPath; 11 | crateCargoToml = ./Cargo.toml; 12 | cargoArtifacts = inputs'.hc-infra.packages.zomeCargoArtifacts; 13 | }; 14 | }; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /templates/app/link-type/ui/src/{{dna_role_name}}/{{kebab_case coordinator_zome_manifest.name}}/mocks.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope (concat "export class " (pascal_case coordinator_zome_manifest.name) "ZomeMock extends ZomeMock implements AppClient {" ) }} 3 | {{previous_scope_content}} 4 | 5 | /** {{title_case (plural to_referenceable.name)}} for {{title_case from_referenceable.name}} */ 6 | {{camel_case from_referenceable.name}}To{{pascal_case to_referenceable.name}} = new HoloHashMap<{{from_referenceable.hash_type}}, Link[]>(); 7 | {{#if bidirectional}} 8 | {{camel_case to_referenceable.name}}To{{pascal_case from_referenceable.name}} = new HoloHashMap<{{to_referenceable.hash_type}}, Link[]>(); 9 | {{/if}} 10 | 11 | async get_{{snake_case (plural to_referenceable.name)}}_for_{{snake_case from_referenceable.name}}({{camel_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}): Promise> { 12 | return this.{{camel_case from_referenceable.name}}To{{pascal_case to_referenceable.name}}.get({{camel_case from_referenceable.singular_arg}}) || []; 13 | } 14 | 15 | async add_{{snake_case to_referenceable.name}}_for_{{snake_case from_referenceable.name}}(input: { {{snake_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}; {{snake_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}} }): Promise { 16 | const existing = this.{{camel_case from_referenceable.name}}To{{pascal_case to_referenceable.name}}.get(input.{{snake_case from_referenceable.singular_arg}}) || []; 17 | this.{{camel_case from_referenceable.name}}To{{pascal_case to_referenceable.name}}.set(input.{{snake_case from_referenceable.singular_arg}}, [...existing, { 18 | base: input.{{snake_case from_referenceable.singular_arg}}, 19 | target: input.{{snake_case to_referenceable.singular_arg}}, 20 | author: this.myPubKey, 21 | timestamp: Date.now() * 1000, 22 | zome_index: 0, 23 | link_type: 0, 24 | tag: new Uint8Array(), 25 | create_link_hash: await fakeActionHash() 26 | }]); 27 | {{#if bidirectional}} 28 | const existingReverse = this.{{camel_case to_referenceable.name}}To{{pascal_case from_referenceable.name}}.get(input.{{snake_case to_referenceable.singular_arg}}) || []; 29 | this.{{camel_case to_referenceable.name}}To{{pascal_case from_referenceable.name}}.set(input.{{snake_case to_referenceable.singular_arg}}, [...existingReverse, { 30 | base: input.{{snake_case to_referenceable.singular_arg}}, 31 | target: input.{{snake_case from_referenceable.singular_arg}}, 32 | author: this.myPubKey, 33 | timestamp: Date.now() * 1000, 34 | zome_index: 0, 35 | link_type: 0, 36 | tag: new Uint8Array(), 37 | create_link_hash: await fakeActionHash() 38 | }]); 39 | {{/if}} 40 | } 41 | 42 | {{#if bidirectional}} 43 | async get_{{snake_case (plural from_referenceable.name)}}_for_{{snake_case to_referenceable.name}}({{camel_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}}): Promise> { 44 | return this.{{camel_case to_referenceable.name}}To{{pascal_case from_referenceable.name}}.get({{camel_case to_referenceable.singular_arg}}) || []; 45 | } 46 | {{/if}} 47 | 48 | {{/match_scope}} 49 | {{/merge}} 50 | -------------------------------------------------------------------------------- /templates/app/link-type/ui/src/{{dna_role_name}}/{{kebab_case coordinator_zome_manifest.name}}/{{kebab_case coordinator_zome_manifest.name}}-client.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope (concat "export class " (pascal_case coordinator_zome_manifest.name) "Client extends ZomeClient<" (pascal_case coordinator_zome_manifest.name) "Signal> {" ) }} 3 | {{previous_scope_content}} 4 | 5 | /** {{title_case (plural to_referenceable.name)}} for {{title_case from_referenceable.name}} */ 6 | 7 | async get{{pascal_case (plural to_referenceable.name)}}For{{pascal_case from_referenceable.name}}({{camel_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}): Promise> { 8 | return this.callZome('get_{{snake_case (plural to_referenceable.name)}}_for_{{snake_case from_referenceable.name}}', {{camel_case from_referenceable.singular_arg}}); 9 | } 10 | {{#if delete}} 11 | 12 | async getDeleted{{pascal_case (plural to_referenceable.name)}}For{{pascal_case from_referenceable.name}}({{camel_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}): Promise, SignedActionHashed[]]>> { 13 | return this.callZome('get_deleted_{{snake_case (plural to_referenceable.name)}}_for_{{snake_case from_referenceable.name}}', {{camel_case from_referenceable.singular_arg}}); 14 | } 15 | {{/if}} 16 | 17 | add{{pascal_case to_referenceable.name}}For{{pascal_case from_referenceable.name}}({{camel_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}, {{camel_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}}): Promise { 18 | return this.callZome('add_{{snake_case to_referenceable.name}}_for_{{snake_case from_referenceable.name}}', { 19 | base_{{snake_case from_referenceable.singular_arg}}: {{camel_case from_referenceable.singular_arg}}, 20 | target_{{snake_case to_referenceable.singular_arg}}: {{camel_case to_referenceable.singular_arg}}, 21 | }); 22 | } 23 | 24 | {{#if delete}} 25 | remove{{pascal_case to_referenceable.name}}For{{pascal_case from_referenceable.name}}({{camel_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}, {{camel_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}}): Promise { 26 | return this.callZome('remove_{{snake_case to_referenceable.name}}_for_{{snake_case from_referenceable.name}}', { 27 | base_{{snake_case from_referenceable.singular_arg}}: {{camel_case from_referenceable.singular_arg}}, 28 | target_{{snake_case to_referenceable.singular_arg}}: {{camel_case to_referenceable.singular_arg}}, 29 | }); 30 | } 31 | 32 | {{/if}} 33 | {{#if bidirectional}} 34 | async get{{pascal_case (plural from_referenceable.name)}}For{{pascal_case to_referenceable.name}}({{camel_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}}): Promise> { 35 | return this.callZome('get_{{snake_case (plural from_referenceable.name)}}_for_{{snake_case to_referenceable.name}}', {{camel_case to_referenceable.singular_arg}}); 36 | } 37 | {{#if delete}} 38 | 39 | async getDeleted{{pascal_case (plural from_referenceable.name)}}For{{pascal_case to_referenceable.name}}({{camel_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}}): Promise, SignedActionHashed[]]>> { 40 | return this.callZome('get_deleted_{{snake_case (plural from_referenceable.name)}}_for_{{snake_case to_referenceable.name}}', {{camel_case to_referenceable.singular_arg}}); 41 | } 42 | {{/if}} 43 | {{/if}} 44 | {{/match_scope}} 45 | {{/merge}} 46 | -------------------------------------------------------------------------------- /templates/app/web-app.instructions.hbs: -------------------------------------------------------------------------------- 1 | Notice that this is an empty skeleton for a Holochain web-app, so: 2 | 3 | - It won't compile until you add a DNA to it, and then add a zome to that DNA. 4 | - The UI is empty, you'll need to import the appropriate components to the top level app component. 5 | 6 | Set up your development environment with: 7 | 8 | cd {{kebab_case app_name}} 9 | nix develop 10 | pnpm install 11 | 12 | In order to be able to revert any next scaffolding steps, it is also recommended to commit the skeleton now: 13 | 14 | git add . 15 | git commit -m "web-app skeleton scaffolded" 16 | 17 | To continue scaffolding your app, add a new dna: 18 | 19 | hc scaffold dna 20 | 21 | And then add a zome to that dna: 22 | 23 | hc scaffold zome 24 | 25 | After that, at any point in time you can start the demo with: 26 | 27 | pnpm start 28 | 29 | You can run the tests with: 30 | 31 | pnpm test 32 | 33 | And you can build the .webhapp production package with: 34 | 35 | pnpm package 36 | -------------------------------------------------------------------------------- /templates/app/web-app/.envrc.hbs: -------------------------------------------------------------------------------- 1 | use flake . 2 | -------------------------------------------------------------------------------- /templates/app/web-app/.github/workflows/test.yaml.hbs: -------------------------------------------------------------------------------- 1 | name: "test" 2 | on: 3 | # Trigger the workflow on push or pull request, 4 | # but only for the main branch 5 | push: 6 | branches: [ main, develop ] 7 | pull_request: 8 | branches: [ main, develop ] 9 | 10 | jobs: 11 | test-and-build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Install nix 17 | uses: cachix/install-nix-action@v27 18 | with: 19 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 20 | nix_path: nixpkgs=channel:nixos-24.05 21 | 22 | - uses: cachix/cachix-action@v15 23 | with: 24 | name: holochain-ci 25 | 26 | - uses: cachix/cachix-action@v15 27 | with: 28 | name: holochain-open-dev 29 | 30 | - name: Install and test 31 | run: | 32 | nix develop --no-update-lock-file --accept-flake-config --command bash -c "pnpm i && pnpm t && pnpm package" 33 | -------------------------------------------------------------------------------- /templates/app/web-app/.gitignore.hbs: -------------------------------------------------------------------------------- 1 | ## editors 2 | /.idea 3 | /.vscode 4 | 5 | ## system files 6 | .DS_Store 7 | 8 | ## npm 9 | node_modules/ 10 | /npm-debug.log 11 | 12 | ## testing 13 | /coverage/ 14 | 15 | ## temp folders 16 | /.tmp/ 17 | 18 | # build 19 | /_site/ 20 | dist/ 21 | /out-tsc/ 22 | 23 | storybook-static 24 | *.tsbuildinfo 25 | 26 | # holochain 27 | *.happ 28 | *.webhapp 29 | *.zip 30 | *.dna 31 | .hc* 32 | .hc 33 | 34 | # cargo 35 | /target/ 36 | /.cargo/ 37 | 38 | .direnv/ 39 | -------------------------------------------------------------------------------- /templates/app/web-app/.prettierrc.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "useTabs": true, 4 | "arrowParens": "avoid", 5 | "plugins": ["@trivago/prettier-plugin-sort-imports"], 6 | "importOrder": ["", "^[./]"], 7 | "importOrderSeparation": true, 8 | "importOrderSortSpecifiers": true, 9 | "importOrderParserPlugins": ["typescript", "decorators-legacy"] 10 | } 11 | -------------------------------------------------------------------------------- /templates/app/web-app/Cargo.toml.hbs: -------------------------------------------------------------------------------- 1 | [profile.dev] 2 | opt-level = "z" 3 | 4 | [profile.release] 5 | opt-level = "z" 6 | 7 | [workspace] 8 | members = ["dnas/*/zomes/coordinator/*", "dnas/*/zomes/integrity/*"] 9 | resolver = "2" 10 | 11 | [workspace.dependencies] 12 | holochain = { version = "{{holochain_version}}", default-features = false, features = [ 13 | "test_utils", 14 | ] } 15 | hdi = "{{hdi_version}}" 16 | hdk = "{{hdk_version}}" 17 | -------------------------------------------------------------------------------- /templates/app/web-app/README.md.hbs: -------------------------------------------------------------------------------- 1 | # {{title_case app_name}} 2 | 3 | ## Environment Setup 4 | 5 | > PREREQUISITE: set up the [holochain development environment](https://developer.holochain.org/docs/install/). 6 | 7 | Enter the nix shell by running this in the root folder of the repository: 8 | 9 | ```bash 10 | nix develop 11 | pnpm install 12 | ``` 13 | 14 | **Run all the other instructions in this README from inside this nix shell, otherwise they won't work**. 15 | 16 | ## Running 2 agents 17 | 18 | ```bash 19 | pnpm start 20 | ``` 21 | 22 | This will create a network of 2 nodes connected to each other and their respective UIs. 23 | It will also bring up the Holochain Playground for advanced introspection of the conductors. 24 | 25 | ## Running the backend tests 26 | 27 | ```bash 28 | pnpm test 29 | ``` 30 | 31 | ## Bootstrapping a network 32 | 33 | Create a custom network of nodes connected to each other and their respective UIs with: 34 | 35 | ```bash 36 | pnpm network 3 37 | ``` 38 | 39 | Substitute the "3" for the number of nodes that you want to bootstrap in your network. 40 | This will also bring up the Holochain Playground for advanced introspection of the conductors. 41 | 42 | ## Packaging 43 | 44 | To package the web happ: 45 | ``` bash 46 | pnpm run package 47 | ``` 48 | 49 | You'll have the `{{app_name}}.webhapp` in `workdir`. This is what you should distribute so that the Holochain Launcher can install it. 50 | 51 | ## Documentation 52 | 53 | This repository is using these tools: 54 | - [PNPM Workspaces](https://pnpm.io/workspaces): npm replacement that supports git dependencies with subdirectories. 55 | - [hc](https://github.com/holochain/holochain/tree/develop/crates/hc): Holochain CLI to easily manage Holochain development instances. 56 | - [@holochain/tryorama](https://www.npmjs.com/package/@holochain/tryorama): test framework. 57 | - [@holochain/client](https://www.npmjs.com/package/@holochain/client): client library to connect to Holochain from the UI. 58 | - [@holochain-playground/cli](https://www.npmjs.com/package/@holochain-playground/cli): introspection tooling to understand what's going on in the Holochain nodes. 59 | -------------------------------------------------------------------------------- /templates/app/web-app/eslint.config.js.hbs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | import eslint from '@eslint/js'; 4 | import tseslint from 'typescript-eslint'; 5 | import eslintConfigPrettier from "eslint-config-prettier"; 6 | 7 | export default tseslint.config( 8 | eslint.configs.recommended, 9 | ...tseslint.configs.recommended, 10 | eslintConfigPrettier, 11 | { 12 | rules: { 13 | '@typescript-eslint/no-unused-vars': "warn" 14 | } 15 | } 16 | ); 17 | -------------------------------------------------------------------------------- /templates/app/web-app/flake.nix.hbs: -------------------------------------------------------------------------------- 1 | { 2 | description = "Template for Holochain app development"; 3 | 4 | inputs = { 5 | holonix.url = "github:holochain/holonix/main-0.3"; 6 | 7 | nixpkgs.follows = "holonix/nixpkgs"; 8 | flake-parts.follows = "holonix/flake-parts"; 9 | 10 | hc-infra.url = "github:holochain-open-dev/infrastructure"; 11 | scaffolding.url = "github:holochain-open-dev/templates"; 12 | playground.url = "github:darksoil-studio/holochain-playground"; 13 | }; 14 | 15 | outputs = inputs: 16 | inputs.flake-parts.lib.mkFlake 17 | { 18 | inherit inputs; 19 | } 20 | { 21 | imports = [ 22 | ./happ.nix 23 | ]; 24 | 25 | systems = builtins.attrNames inputs.holonix.devShells; 26 | perSystem = 27 | { inputs' 28 | , config 29 | , pkgs 30 | , system 31 | , ... 32 | }: { 33 | devShells.default = pkgs.mkShell { 34 | inputsFrom = [ 35 | inputs'.hc-infra.devShells.synchronized-pnpm 36 | inputs'.holonix.devShells.default 37 | ]; 38 | packages = [ 39 | inputs'.scaffolding.packages.hc-scaffold-app-template 40 | inputs'.playground.packages.hc-playground 41 | ]; 42 | }; 43 | }; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /templates/app/web-app/happ.nix.hbs: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | 3 | { 4 | # Import all `dnas/*/dna.nix` files 5 | imports = ( 6 | map (m: "${./.}/dnas/${m}/dna.nix") 7 | (builtins.attrNames (if builtins.pathExists ./dnas then builtins.readDir ./dnas else {} )) 8 | ); 9 | 10 | perSystem = 11 | { inputs' 12 | , lib 13 | , self' 14 | , system 15 | , ... 16 | }: { 17 | packages.{{app_name}} = inputs.hc-infra.outputs.builders.${system}.happ { 18 | happManifest = ./workdir/happ.yaml; 19 | dnas = { 20 | # Include here the DNA packages for this hApp, e.g.: 21 | # my_dna = inputs'.some_input.packages.my_dna; 22 | # This overrides all the "bundled" properties for the hApp manifest 23 | }; 24 | }; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /templates/app/web-app/package.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{app_name}}-dev", 3 | "private": true, 4 | "scripts": { 5 | "start": "AGENTS=2 pnpm network", 6 | "network": "hc s clean && pnpm run build:happ && BOOTSTRAP_PORT=$(port) SIGNAL_PORT=$(port) UI_PORT=8888 concurrently -k \"pnpm -F ui start\" \"pnpm run launch\" \"hc playground\" \"pnpm local-services\"", 7 | "launch": "echo pass | RUST_LOG=warn hc launch --piped -n $AGENTS workdir/{{app_name}}.happ --ui-port $UI_PORT network --bootstrap http://127.0.0.1:$BOOTSTRAP_PORT webrtc ws://127.0.0.1:$SIGNAL_PORT", 8 | "local-services": "hc run-local-services --bootstrap-port $BOOTSTRAP_PORT --signal-port $SIGNAL_PORT", 9 | "test": "pnpm run build:happ && nix flake check -L && pnpm -F tests test", 10 | "build:happ": "nix build -L .#{{app_name}} -o workdir/{{app_name}}.happ", 11 | "package": "nix build -L .#{{app_name}}.meta.release -o workdir/{{app_name}}.happ && pnpm -F ui package && hc web-app pack workdir" 12 | }, 13 | "devDependencies": { 14 | "@trivago/prettier-plugin-sort-imports": "^4.3.0", 15 | "@eslint/js": "^9.0.0", 16 | "concurrently": "^6.2.1", 17 | "eslint": "^9.0.0", 18 | "eslint-config-prettier": "^9.1.0", 19 | "new-port-cli": "^1.0.0", 20 | "prettier": "^3.2.5", 21 | "typescript-eslint": "^8.0.0", 22 | "typescript": "^5.4.5" 23 | }, 24 | "type": "module", 25 | "engines": { 26 | "pnpm": ">=9.0.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /templates/app/web-app/pnpm-workspace.yaml.hbs: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'ui' 3 | - 'tests' 4 | -------------------------------------------------------------------------------- /templates/app/web-app/tests/package.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tests", 3 | "private": true, 4 | "scripts": { 5 | "test": "vitest run" 6 | }, 7 | "dependencies": { 8 | "@msgpack/msgpack": "^2.8.0", 9 | "@holochain/client": "^0.17.1", 10 | "@holochain/tryorama": "{{tryorama_version}}", 11 | "@holochain-open-dev/signals": "^0.300.6", 12 | "@holochain-open-dev/utils": "^0.300.2", 13 | "typescript": "^5.4.5", 14 | "vitest": "^1.4.0" 15 | }, 16 | "type": "module" 17 | } 18 | -------------------------------------------------------------------------------- /templates/app/web-app/tests/src/app-path.ts.hbs: -------------------------------------------------------------------------------- 1 | import { dirname } from 'path'; 2 | import { fileURLToPath } from 'url'; 3 | 4 | export const appPath = 5 | dirname(fileURLToPath(import.meta.url)) + '/../../workdir/{{app_name}}.happ'; 6 | -------------------------------------------------------------------------------- /templates/app/web-app/tests/tsconfig.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /templates/app/web-app/tests/vitest.config.ts.hbs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | //@ts-ignore 3 | import pkg from "./package.json"; 4 | 5 | export default defineConfig({ 6 | test: { 7 | poolOptions: { 8 | threads: { 9 | singleThread: true, 10 | }, 11 | }, 12 | testTimeout: 60 * 1000 * 3, // 3 mins 13 | deps: { 14 | optimizer: { 15 | ssr: { 16 | enabled: true, 17 | //@ts-ignore 18 | include: Object.keys(pkg.dependencies), 19 | exclude: ["@holochain/client"], 20 | }, 21 | }, 22 | }, 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /templates/app/web-app/ui/.gitignore.hbs: -------------------------------------------------------------------------------- 1 | ## editors 2 | /.idea 3 | /.vscode 4 | 5 | ## system files 6 | .DS_Store 7 | 8 | ## npm 9 | /node_modules/ 10 | /npm-debug.log 11 | 12 | ## testing 13 | /coverage/ 14 | 15 | ## temp folders 16 | /.tmp/ 17 | 18 | # build 19 | /_site/ 20 | /dist/ 21 | /out-tsc/ 22 | 23 | locales/ 24 | .rollup.cache 25 | *.tsbuildinfo 26 | -------------------------------------------------------------------------------- /templates/app/web-app/ui/index.html.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 43 | {{title_case app_name}} 44 | 45 | 46 | 47 | 48 | 49 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /templates/app/web-app/ui/lit-localize.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/lit/lit/main/packages/localize-tools/config.schema.json", 3 | "sourceLocale": "en", 4 | "targetLocales": [], 5 | "tsConfig": "./tsconfig.json", 6 | "output": { 7 | "mode": "runtime", 8 | "outputDir": "./locales", 9 | "localeCodesModule": "./locales/locales.js", 10 | "language": "js" 11 | }, 12 | "interchange": { 13 | "format": "xliff", 14 | "xliffDir": "./xliff/" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /templates/app/web-app/ui/package.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "start": "vite --port $UI_PORT --clearScreen false", 7 | "build": "vite build", 8 | "lint": "eslint src && prettier \"**/*.ts\" --check --ignore-path .gitignore", 9 | "format": "eslint src && prettier \"**/*.ts\" --write --ignore-path .gitignore", 10 | "package": "pnpm build && cd dist && bestzip ../dist.zip *", 11 | "localize:extract": "lit-localize extract", 12 | "localize:build": "lit-localize build" 13 | }, 14 | "dependencies": { 15 | "@holochain/client": "^0.17.1", 16 | "@holochain-open-dev/elements": "^0.300.1", 17 | "@holochain-open-dev/signals": "^0.300.6", 18 | "@holochain-open-dev/utils": "^0.300.2", 19 | "@lit/context": "^1.0.0", 20 | "@lit/localize": "^0.12.0", 21 | "@mdi/js": "^7.2.0", 22 | "@msgpack/msgpack": "^2.8.0", 23 | "@shoelace-style/shoelace": "^2.11.0", 24 | "lit": "^3.0.0", 25 | "urlpattern-polyfill": "^10.0.0" 26 | }, 27 | "devDependencies": { 28 | "@custom-elements-manifest/analyzer": "^0.9.4", 29 | "@lit/localize-tools": "^0.6.3", 30 | "bestzip": "^2.2.0", 31 | "typescript": "^5.4.5", 32 | "tslib": "^2.6.2", 33 | "vite": "^4.0.0", 34 | "vite-plugin-static-copy": "^0.13.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /templates/app/web-app/ui/src/app-styles.ts.hbs: -------------------------------------------------------------------------------- 1 | import { css } from 'lit'; 2 | import { sharedStyles } from '@holochain-open-dev/elements'; 3 | 4 | export const appStyles = [ 5 | css` 6 | .top-bar { 7 | align-items: center; 8 | background-color: var(--sl-color-primary-500); 9 | padding: 16px; 10 | } 11 | `, 12 | sharedStyles 13 | ]; 14 | -------------------------------------------------------------------------------- /templates/app/web-app/ui/src/context.ts.hbs: -------------------------------------------------------------------------------- 1 | import { Router } from "@holochain-open-dev/elements"; 2 | import { createContext } from "@lit/context"; 3 | 4 | export const rootRouterContext = createContext("router"); 5 | 6 | -------------------------------------------------------------------------------- /templates/app/web-app/ui/src/holochain-app.ts.hbs: -------------------------------------------------------------------------------- 1 | 2 | import { LitElement, css, html } from 'lit'; 3 | import { property, state, customElement } from 'lit/decorators.js'; 4 | import { AppWebsocket, AppClient, ActionHash } from '@holochain/client'; 5 | import { SignalWatcher } from '@holochain-open-dev/signals'; 6 | import { Router } from '@holochain-open-dev/elements'; 7 | import { provide } from '@lit/context'; 8 | import { localized, msg } from '@lit/localize'; 9 | import { 10 | Profile, 11 | ProfilesClient, 12 | ProfilesStore, 13 | profilesStoreContext 14 | } from '@holochain-open-dev/profiles'; 15 | import { EntryRecord } from '@holochain-open-dev/utils'; 16 | 17 | import '@holochain-open-dev/elements/dist/elements/display-error.js'; 18 | import '@holochain-open-dev/profiles/dist/elements/profile-prompt.js'; 19 | import '@holochain-open-dev/profiles/dist/elements/my-profile.js'; 20 | import '@shoelace-style/shoelace/dist/components/spinner/spinner.js'; 21 | import '@shoelace-style/shoelace/dist/components/icon-button/icon-button.js'; 22 | 23 | import { rootRouterContext } from './context.js'; 24 | import { appStyles } from './app-styles.js'; 25 | import './home-page.js'; 26 | 27 | @localized() 28 | @customElement('holochain-app') 29 | export class HolochainApp extends SignalWatcher(LitElement) { 30 | @state() _loading = true; 31 | @state() _view = { view: 'main' }; 32 | @state() _error: unknown | undefined; 33 | 34 | @provide({ context: profilesStoreContext }) 35 | @property() 36 | _profilesStore!: ProfilesStore; 37 | 38 | _client!: AppClient; 39 | 40 | @provide({ context: rootRouterContext }) 41 | router = new Router(this, [ 42 | { 43 | path: "/", 44 | enter: () => { 45 | // Redirect to "/home/" 46 | this.router.goto("/home/"); 47 | return false; 48 | }, 49 | }, 50 | { 51 | path: "/home/*", 52 | render: () => html``, 53 | }, 54 | { 55 | path: "/my-profile", 56 | render: () => this.renderMyProfilePage(), 57 | }, 58 | ]); 59 | 60 | async firstUpdated() { 61 | try { 62 | this._client = await AppWebsocket.connect(); 63 | await this.initStores(this._client); 64 | } catch (e: unknown) { 65 | this._error = e; 66 | } finally { 67 | this._loading = false; 68 | } 69 | } 70 | 71 | // Don't change this 72 | async initStores(appClient: AppClient) { 73 | this._profilesStore = new ProfilesStore(new ProfilesClient(appClient, 'TODO:REPLACE_ME_WITH_THE_DNA_WITH_THE_PROFILES_ZOME')); 74 | } 75 | 76 | renderMyProfilePage() { 77 | return html` 78 |
79 |
80 | this.router.pop()} 83 | > 84 | ${msg("{{title_case app_name}}")} 85 |
86 | 87 | 88 |
89 | `; 90 | } 91 | 92 | render() { 93 | if (this._loading) 94 | return html`
98 | 99 |
`; 100 | 101 | if (this._error) 102 | return html` 103 |
104 | 105 | 106 |
107 | `; 108 | 109 | return html` 110 | 111 | ${this.router.outlet()} 112 | 113 | `; 114 | } 115 | 116 | static styles = [ 117 | css` 118 | :host { 119 | display: flex; 120 | flex: 1; 121 | } 122 | `, 123 | ...appStyles, 124 | ]; 125 | } 126 | -------------------------------------------------------------------------------- /templates/app/web-app/ui/src/home-page.ts.hbs: -------------------------------------------------------------------------------- 1 | import { Router, Routes } from "@holochain-open-dev/elements"; 2 | import { AsyncResult, SignalWatcher } from "@holochain-open-dev/signals"; 3 | import { ProfilesStore, profilesStoreContext } from "@holochain-open-dev/profiles"; 4 | import { LitElement, css, html } from "lit"; 5 | import { customElement, property } from "lit/decorators.js"; 6 | import { EntryRecord } from "@holochain-open-dev/utils"; 7 | import { consume } from "@lit/context"; 8 | import { msg } from "@lit/localize"; 9 | 10 | import "@shoelace-style/shoelace/dist/components/tab-group/tab-group.js"; 11 | import "@shoelace-style/shoelace/dist/components/tab-panel/tab-panel.js"; 12 | import '@holochain-open-dev/profiles/dist/elements/profile-list-item-skeleton.js'; 13 | import '@holochain-open-dev/profiles/dist/elements/agent-avatar.js'; 14 | 15 | import { rootRouterContext } from "./context.js"; 16 | import { appStyles } from './app-styles.js'; 17 | 18 | @customElement("home-page") 19 | export class HomePage extends SignalWatcher(LitElement) { 20 | 21 | /** 22 | * @internal 23 | */ 24 | @consume({ context: rootRouterContext, subscribe: true }) 25 | rootRouter!: Router; 26 | 27 | /** 28 | * @internal 29 | */ 30 | @consume({ context: profilesStoreContext, subscribe: true }) 31 | @property() 32 | _profilesStore!: ProfilesStore; 33 | 34 | routes = new Routes(this, [ 35 | { 36 | path: "", 37 | enter: () => { 38 | this.routes.goto("tab1/"); 39 | return false; 40 | }, 41 | }, 42 | { 43 | path: "tab1/*", 44 | render: () => html`
TODO: replace me
`, 45 | }, 46 | { 47 | path: "tab2/*", 48 | render: () => html`
TODO: replace me
`, 49 | }, 50 | { 51 | path: "tab3/", 52 | render: () => html`
TODO: replace me
`, 53 | }, 54 | ]); 55 | 56 | renderMyProfile() { 57 | const myProfile = this._profilesStore.myProfile.get(); 58 | 59 | switch (myProfile.status) { 60 | case 'pending': 61 | return html``; 62 | case 'error': 63 | return html``; 68 | case 'completed': 69 | return html`
74 | 75 | ${myProfile.value?.entry.nickname} 76 |
`; 77 | } 78 | } 79 | 80 | render() { 81 | return html` 82 |
83 |
84 | ${msg("{{title_case app_name}}")} 85 | 86 |
87 | ${this.renderMyProfile()} 88 |
89 |
90 | 91 | 92 | { 96 | this.routes.goto("orders/"); 97 | }} 98 | >${msg("Orders")} 100 | { 104 | this.routes.goto("producers/"); 105 | }} 106 | >${msg("Producers")} 108 | { 112 | this.routes.goto("members/"); 113 | }} 114 | >${msg("Members")} 116 | 117 | ${this.routes.outlet()} 118 | 119 |
120 | `; 121 | } 122 | 123 | static styles = [ 124 | css` 125 | :host { 126 | display: flex; 127 | flex: 1; 128 | } 129 | 130 | `, 131 | ...appStyles, 132 | ]; 133 | } 134 | -------------------------------------------------------------------------------- /templates/app/web-app/ui/tsconfig.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "noEmitOnError": true, 7 | "lib": ["es2018", "dom"], 8 | "strict": true, 9 | "esModuleInterop": false, 10 | "allowSyntheticDefaultImports": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "outDir": "dist", 14 | "sourceMap": true, 15 | "inlineSources": true, 16 | "rootDir": "./src", 17 | "incremental": true, 18 | "skipLibCheck": true 19 | }, 20 | "include": ["src/**/*.ts"] 21 | } 22 | -------------------------------------------------------------------------------- /templates/app/web-app/ui/vite.config.ts.hbs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import path from "path"; 3 | import { viteStaticCopy } from "vite-plugin-static-copy"; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | viteStaticCopy({ 8 | targets: [ 9 | { 10 | src: path.resolve( 11 | __dirname, 12 | "node_modules/@shoelace-style/shoelace/dist/assets" 13 | ), 14 | dest: path.resolve(__dirname, "dist/shoelace"), 15 | }, 16 | ], 17 | }), 18 | ], 19 | }); 20 | 21 | -------------------------------------------------------------------------------- /templates/zome/collection/docs/elements/{{kebab_case collection_name}}.md.hbs: -------------------------------------------------------------------------------- 1 | # `<{{kebab_case collection_name}}>` 2 | 3 | ## Usage 4 | 5 | 0. If you haven't already, [go through the setup for the module](/setup). 6 | 7 | 1. Import the `<{{kebab_case collection_name}}>` element somewhere in the javascript side of your web-app like this: 8 | 9 | ```js 10 | import '@holochain-open-dev/{{kebab_case coordinator_zome_manifest.name}}/dist/elements/{{kebab_case collection_name}}.js' 11 | ``` 12 | 13 | 2. Use it in the html side of your web-app like this: 14 | 15 | ```html 16 | <{{kebab_case collection_name}}> 17 | 18 | ``` 19 | 20 | > [!WARNING] 21 | > Like all the elements in this module, `<{{kebab_case collection_name}}>` needs to be placed inside an initialized `<{{kebab_case coordinator_zome_manifest.name}}-context>`. 22 | 23 | ## Demo 24 | 25 | Here is an interactive demo of the element: 26 | 27 | 28 | 29 | 30 | 83 | 84 | ## API Reference 85 | 86 | `<{{kebab_case collection_name}}>` is a [custom element](https://web.dev/articles/custom-elements-v1), which means that it can be used in any web app or website. Here is the reference for its API: 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /templates/zome/collection/tests/src/{{kebab_case collection_name}}.test.ts.hbs: -------------------------------------------------------------------------------- 1 | import { assert, test } from "vitest"; 2 | 3 | import { runScenario, dhtSync } from '@holochain/tryorama'; 4 | import { ActionHash, Record, EntryHash } from '@holochain/client'; 5 | import { decode } from '@msgpack/msgpack'; 6 | import { EntryRecord } from '@holochain-open-dev/utils'; 7 | import { toPromise } from '@holochain-open-dev/signals'; 8 | 9 | import { {{pascal_case referenceable.name}} } from '../../ui/src/types.js'; 10 | import { sample{{pascal_case referenceable.name}} } from '../../ui/src/mocks.js'; 11 | import { setup } from './setup.js'; 12 | 13 | test('create a {{pascal_case referenceable.name}} and get {{lower_case collection_name}}', async () => { 14 | await runScenario(async scenario => { 15 | const { alice, bob } = await setup(scenario); 16 | 17 | // Bob gets {{lower_case collection_name}} 18 | let collectionOutput = await toPromise(bob.store.{{camel_case collection_name}}{{#if (eq collection_type.type "ByAuthor")}}.get(alice.player.agentPubKey){{else}}{{/if}}); 19 | assert.equal(collectionOutput.size, 0); 20 | 21 | // Alice creates a {{pascal_case referenceable.name}} 22 | const {{camel_case referenceable.name}}: EntryRecord<{{pascal_case referenceable.name}}> = await alice.store.client.create{{pascal_case referenceable.name}}(await sample{{pascal_case referenceable.name}}(alice.store.client)); 23 | assert.ok({{camel_case referenceable.name}}); 24 | 25 | await dhtSync( 26 | [alice.player, bob.player], 27 | alice.player.cells[0].cell_id[0] 28 | ); 29 | 30 | // Bob gets {{lower_case collection_name}} again 31 | collectionOutput = await toPromise(bob.store.{{camel_case collection_name}}{{#if (eq collection_type.type "ByAuthor")}}.get(alice.player.agentPubKey){{else}}{{/if}}); 32 | assert.equal(collectionOutput.size, 1); 33 | assert.deepEqual({{camel_case referenceable.name}}.{{#if (eq referenceable.hash_type "ActionHash")}}actionHash{{else}}entryHash{{/if}}, Array.from(collectionOutput.keys())[0]); 34 | }); 35 | }); 36 | 37 | -------------------------------------------------------------------------------- /templates/zome/collection/ui/src/elements/{{kebab_case collection_name}}.ts.hbs: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from 'lit'; 2 | import { state, customElement, property } from 'lit/decorators.js'; 3 | import { AgentPubKey, EntryHash, ActionHash, Record } from '@holochain/client'; 4 | import { SignalWatcher } from '@holochain-open-dev/signals'; 5 | import { consume } from '@lit/context'; 6 | import { localized, msg } from '@lit/localize'; 7 | import { hashProperty, sharedStyles, wrapPathInSvg } from '@holochain-open-dev/elements'; 8 | import { mdiInformationOutline } from '@mdi/js'; 9 | 10 | import '@holochain-open-dev/elements/dist/elements/display-error.js'; 11 | import '@shoelace-style/shoelace/dist/components/spinner/spinner.js'; 12 | import '@shoelace-style/shoelace/dist/components/icon/icon.js'; 13 | 14 | import './{{kebab_case referenceable.name}}-summary.js'; 15 | import { {{pascal_case coordinator_zome_manifest.name}}Store } from '../{{kebab_case coordinator_zome_manifest.name}}-store.js'; 16 | import { {{camel_case coordinator_zome_manifest.name}}StoreContext } from '../context.js'; 17 | 18 | /** 19 | * @element {{kebab_case collection_name}} 20 | */ 21 | @localized() 22 | @customElement('{{kebab_case collection_name}}') 23 | export class {{pascal_case collection_name}} extends SignalWatcher(LitElement) { 24 | {{#if (eq collection_type.type "ByAuthor")}} 25 | 26 | /** 27 | * REQUIRED. The author for which the {{plural (pascal_case referenceable.name)}} should be fetched 28 | */ 29 | @property(hashProperty('author')) 30 | author!: AgentPubKey; 31 | {{/if}} 32 | 33 | /** 34 | * @internal 35 | */ 36 | @consume({ context: {{camel_case coordinator_zome_manifest.name}}StoreContext, subscribe: true }) 37 | {{camel_case coordinator_zome_manifest.name}}Store!: {{pascal_case coordinator_zome_manifest.name}}Store; 38 | 39 | {{#if (eq collection_type.type "ByAuthor")}} 40 | firstUpdated() { 41 | if (this.author === undefined) { 42 | throw new Error(`The author property is required for the {{pascal_case collection_name}} element`); 43 | } 44 | } 45 | {{/if}} 46 | 47 | renderList(hashes: Array<{{referenceable.hash_type}}>) { 48 | if (hashes.length === 0) 49 | return html`
50 | 55 | ${msg("No {{lower_case (plural referenceable.name)}} found")} 56 |
`; 57 | 58 | return html` 59 |
60 | ${hashes.map(hash => 61 | html`<{{kebab_case referenceable.name}}-summary .{{camel_case referenceable.name}}Hash=${hash}>` 62 | )} 63 |
64 | `; 65 | } 66 | 67 | render() { 68 | const map = this.{{camel_case coordinator_zome_manifest.name}}Store.{{camel_case collection_name}}{{#if (eq collection_type.type "ByAuthor")}}.get(this.author){{/if}}.get(); 69 | 70 | switch (map.status) { 71 | case 'pending': 72 | return html`
73 | 74 |
`; 75 | case 'error': 76 | return html``; 80 | case 'completed': 81 | return this.renderList(Array.from(map.value.keys())); 82 | } 83 | } 84 | 85 | static styles = [sharedStyles]; 86 | } 87 | -------------------------------------------------------------------------------- /templates/zome/collection/ui/src/mocks.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope (concat "export class " (pascal_case coordinator_zome_manifest.name) "ZomeMock extends ZomeMock implements AppClient {" ) }} 3 | {{previous_scope_content}} 4 | 5 | {{#if (eq collection_type.type "ByAuthor")}} 6 | async get_{{snake_case collection_name}}(author: AgentPubKey): Promise> { 7 | const records: Record[] = Array.from(this.{{camel_case (plural referenceable.name)}}.values()).map(r => r.revisions[r.revisions.length - 1]).filter(r => r.signed_action.hashed.content.author.toString() === author.toString()); 8 | const base = await fakeEntryHash(); 9 | return Promise.all(records.map(async record => ({ 10 | base, 11 | target: {{#if (eq referenceable.hash_type "EntryHash")}}(record.signed_action.hashed.content as NewEntryAction).entry_hash{{else}}record.signed_action.hashed.hash{{/if}}, 12 | author: record.signed_action.hashed.content.author, 13 | timestamp: record.signed_action.hashed.content.timestamp, 14 | zome_index: 0, 15 | link_type: 0, 16 | tag: new Uint8Array(), 17 | create_link_hash: await fakeActionHash() 18 | }))); 19 | } 20 | 21 | {{else}} 22 | async get_{{snake_case collection_name}}(): Promise> { 23 | const records: Record[] = Array.from(this.{{camel_case (plural referenceable.name)}}.values()).map(r => r.revisions[r.revisions.length - 1]); 24 | const base = await fakeEntryHash(); 25 | return Promise.all(records.map(async record => ({ 26 | base, 27 | target: {{#if (eq referenceable.hash_type "EntryHash")}}(record.signed_action.hashed.content as NewEntryAction).entry_hash{{else}}record.signed_action.hashed.hash{{/if}}, 28 | author: record.signed_action.hashed.content.author, 29 | timestamp: record.signed_action.hashed.content.timestamp, 30 | zome_index: 0, 31 | link_type: 0, 32 | tag: new Uint8Array(), 33 | create_link_hash: await fakeActionHash() 34 | }))); 35 | } 36 | {{/if}} 37 | 38 | {{/match_scope}} 39 | {{/merge}} 40 | 41 | -------------------------------------------------------------------------------- /templates/zome/collection/ui/src/{{kebab_case coordinator_zome_manifest.name}}-client.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope (concat "export class " (pascal_case coordinator_zome_manifest.name) "Client extends ZomeClient<" (pascal_case coordinator_zome_manifest.name) "Signal> {" ) }} 3 | {{previous_scope_content}} 4 | 5 | /** {{title_case collection_name}} */ 6 | 7 | async get{{pascal_case collection_name}}({{#if (eq collection_type.type "ByAuthor")}}author: AgentPubKey{{else}}{{/if}}): Promise> { 8 | return this.callZome('get_{{snake_case collection_name}}', {{#if (eq collection_type.type "ByAuthor")}}author{{else}}undefined{{/if}}); 9 | } 10 | 11 | {{/match_scope}} 12 | {{/merge}} 13 | -------------------------------------------------------------------------------- /templates/zome/collection/ui/src/{{kebab_case coordinator_zome_manifest.name}}-store.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope (concat "export class " (pascal_case coordinator_zome_manifest.name) "Store {" ) }} 3 | {{previous_scope_content}} 4 | 5 | /** {{title_case collection_name}} */ 6 | 7 | {{#if (eq collection_type.type "ByAuthor")}} 8 | {{camel_case collection_name}} = new LazyHoloHashMap((author: AgentPubKey) => 9 | pipe( 10 | collectionSignal( 11 | this.client, 12 | () => this.client.get{{pascal_case collection_name}}(author), 13 | '{{pascal_case collection_name}}' 14 | ), 15 | {{camel_case collection_name}} => slice(this.{{camel_case (plural referenceable.name)}}, {{camel_case collection_name}}.map(l => l.target)) 16 | ) 17 | ); 18 | {{else}} 19 | {{camel_case collection_name}} = pipe( 20 | collectionSignal( 21 | this.client, 22 | () => this.client.get{{pascal_case collection_name}}(), 23 | '{{pascal_case collection_name}}' 24 | ), 25 | {{camel_case collection_name}} => slice(this.{{camel_case (plural referenceable.name)}}, {{camel_case collection_name}}.map(l => l.target)) 26 | ); 27 | {{/if}} 28 | {{/match_scope}} 29 | {{/merge}} 30 | -------------------------------------------------------------------------------- /templates/zome/collection/zomes/coordinator/{{snake_case coordinator_zome_manifest.name}}/tests/{{snake_case collection_name}}.rs.hbs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_variables)] 3 | #![allow(unused_imports)] 4 | 5 | use std::time::Duration; 6 | use hdk::prelude::*; 7 | use holochain::{conductor::config::ConductorConfig, sweettest::*}; 8 | 9 | mod common; 10 | use common::{create_{{snake_case referenceable.name}}, sample_{{snake_case referenceable.name}}_1}; 11 | 12 | #[tokio::test(flavor = "multi_thread")] 13 | async fn create_a_{{snake_case referenceable.name}}_and_get_{{snake_case collection_name}}() { 14 | // Use prebuilt dna file 15 | let dna_path = std::env::current_dir() 16 | .unwrap() 17 | .join(std::env::var("DNA_PATH").expect("DNA_PATH not set, must be run using nix flake check")); 18 | 19 | let dna = SweetDnaFile::from_bundle(&dna_path).await.unwrap(); 20 | 21 | // Set up conductors 22 | let mut conductors = SweetConductorBatch::from_config(2, ConductorConfig::default()).await; 23 | let apps = conductors.setup_app("{{dna_role_name}}", &[dna]).await.unwrap(); 24 | conductors.exchange_peer_info().await; 25 | 26 | let ((alice,), (bobbo,)) = apps.into_tuples(); 27 | 28 | let alice_zome = alice.zome("{{snake_case coordinator_zome_manifest.name}}"); 29 | let bob_zome = bobbo.zome("{{snake_case coordinator_zome_manifest.name}}"); 30 | 31 | let sample = sample_{{snake_case referenceable.name}}_1(&conductors[0], &alice_zome).await; 32 | 33 | // Alice creates a {{pascal_case referenceable.name}} 34 | let record: Record = create_{{snake_case referenceable.name}}(&conductors[0], &alice_zome, sample.clone()).await; 35 | 36 | await_consistency(Duration::from_secs(60), [&alice, &bobbo]) 37 | .await 38 | .expect("Timed out waiting for consistency"); 39 | 40 | let links: Vec = conductors[1] 41 | .call(&bob_zome, "get_{{snake_case collection_name}}", {{#if (eq collection_type.type "Global")}}(){{else}}alice_zome.cell_id().agent_pubkey().clone(){{/if}}) 42 | .await; 43 | 44 | assert_eq!(links.len(), 1); 45 | {{#if (eq referenceable.hash_type "EntryHash")}} 46 | assert_eq!(links[0].target.clone().into_entry_hash().unwrap(), record.action().entry_hash().unwrap().clone()); 47 | {{else}} 48 | assert_eq!(links[0].target.clone().into_action_hash().unwrap(), record.signed_action.hashed.hash); 49 | {{/if}} 50 | } 51 | -------------------------------------------------------------------------------- /templates/zome/coordinator-zome.instructions.hbs: -------------------------------------------------------------------------------- 1 | We recommended that you format your UI files with: 2 | 3 | npm run format -w ui 4 | -------------------------------------------------------------------------------- /templates/zome/coordinator-zome/dnas/{{dna_role_name}}/zomes/coordinator/{{zome_manifest.name}}/Cargo.toml.hbs: -------------------------------------------------------------------------------- 1 | {{previous_file_content}} 2 | 3 | [dev-dependencies] 4 | fixt = "0.3.1" 5 | futures = { version = "0.3.1", default-features = false } 6 | hdk = { workspace = true, features = ["encoding", "test_utils"] } 7 | holochain = { workspace = true } 8 | tokio = { version = "1.3", features = ["full"] } 9 | 10 | -------------------------------------------------------------------------------- /templates/zome/coordinator-zome/dnas/{{dna_role_name}}/zomes/coordinator/{{zome_manifest.name}}/tests/common/mod.rs.hbs: -------------------------------------------------------------------------------- 1 | use hdk::prelude::*; 2 | use holochain::sweettest::*; 3 | 4 | use {{snake_case zome_manifest.name}}_integrity::*; 5 | 6 | -------------------------------------------------------------------------------- /templates/zome/coordinator-zome/ui/demo/index.html.hbs: -------------------------------------------------------------------------------- 1 | import { {{camel_case zome_manifest.name}}StoreContext } from './{{dna_role_name}}/{{kebab_case zome_manifest.name}}/context.js'; 2 | import { {{pascal_case zome_manifest.name}}Client } from './{{dna_role_name}}/{{kebab_case zome_manifest.name}}/{{kebab_case zome_manifest.name}}-client.js'; 3 | import { {{pascal_case zome_manifest.name}}Store } from './{{dna_role_name}}/{{kebab_case zome_manifest.name}}/{{kebab_case zome_manifest.name}}-store.js'; 4 | 5 | {{#merge previous_file_content}} 6 | {{#match_scope "export class DemoApp extends SignalWatcher(LitElement) {"}} 7 | 8 | {{#merge previous_scope_content}} 9 | {{#match_scope "async initStores(appClient) {"}} 10 | {{previous_scope_content}} 11 | this._{{camel_case zome_manifest.name}}Store = new ContextProvider(this, {{camel_case zome_manifest.name}}StoreContext, new {{pascal_case zome_manifest.name}}Store(new {{pascal_case zome_manifest.name}}Client(appClient, '{{dna_role_name}}'))); 12 | {{/match_scope}} 13 | {{/merge}} 14 | {{/match_scope}} 15 | {{/merge}} 16 | -------------------------------------------------------------------------------- /templates/zome/dna.instructions.hbs: -------------------------------------------------------------------------------- 1 | If you want the UI to work, you must include the profiles zome in one of your DNAs. 2 | 3 | Scaffold the profiles zome in this DNA by running these commands, if you haven't already: 4 | 5 | hc-scaffold zome profiles --coordinator dnas/{{dna_name}}/zomes/coordinator --integrity dnas/{{dna_name}}/zomes/integrity 6 | cargo add -p profiles hc_zome_profiles_coordinator 7 | echo "extern crate hc_zome_profiles_coordinator;" > dnas/{{dna_name}}/zomes/coordinator/profiles/src/lib.rs 8 | cargo add -p profiles_integrity hc_zome_profiles_integrity 9 | echo "extern crate hc_zome_profiles_integrity;" > dnas/{{dna_name}}/zomes/integrity/profiles/src/lib.rs 10 | -------------------------------------------------------------------------------- /templates/zome/entry-type/ui/src/elements/{{kebab_case entry_type.name}}-summary.ts.hbs: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from 'lit'; 2 | import { state, property, customElement } from 'lit/decorators.js'; 3 | import { EntryHash, Record, ActionHash } from '@holochain/client'; 4 | import { EntryRecord } from '@holochain-open-dev/utils'; 5 | import { SignalWatcher } from '@holochain-open-dev/signals'; 6 | import { hashProperty, sharedStyles } from '@holochain-open-dev/elements'; 7 | import { consume } from '@lit/context'; 8 | 9 | import { localized, msg } from '@lit/localize'; 10 | 11 | {{#uniq_lines}} 12 | import '@shoelace-style/shoelace/dist/components/card/card.js'; 13 | import '@shoelace-style/shoelace/dist/components/spinner/spinner.js'; 14 | import '@holochain-open-dev/elements/dist/elements/display-error.js'; 15 | {{#each entry_type.fields}} 16 | {{#if widget}} 17 | {{> (concat field_type.type "/" widget "/detail/imports") }} 18 | 19 | {{/if}} 20 | {{/each}} 21 | {{/uniq_lines}} 22 | 23 | import { {{pascal_case coordinator_zome_manifest.name}}Store } from '../{{kebab_case coordinator_zome_manifest.name}}-store.js'; 24 | import { {{camel_case coordinator_zome_manifest.name}}StoreContext } from '../context.js'; 25 | import { {{pascal_case entry_type.name}}{{#each entry_type.fields}}{{#if (eq field_type.type "Enum")}}, {{field_type.label}}{{/if}}{{/each}} } from '../types.js'; 26 | 27 | /** 28 | * @element {{kebab_case entry_type.name}}-summary 29 | * @fires {{kebab_case entry_type.name}}-selected: detail will contain { {{camel_case entry_type.name}}Hash } 30 | */ 31 | @localized() 32 | @customElement('{{kebab_case entry_type.name}}-summary') 33 | export class {{pascal_case entry_type.name}}Summary extends SignalWatcher(LitElement) { 34 | 35 | /** 36 | * REQUIRED. The hash of the {{pascal_case entry_type.name}} to show 37 | */ 38 | @property(hashProperty('{{kebab_case entry_type.name}}-hash')) 39 | {{camel_case entry_type.name}}Hash!: {{#if entry_type.reference_entry_hash}}EntryHash{{else}}ActionHash{{/if}}; 40 | 41 | /** 42 | * @internal 43 | */ 44 | @consume({ context: {{camel_case coordinator_zome_manifest.name}}StoreContext, subscribe: true }) 45 | {{camel_case coordinator_zome_manifest.name}}Store!: {{pascal_case coordinator_zome_manifest.name}}Store; 46 | 47 | renderSummary(entryRecord: EntryRecord<{{pascal_case entry_type.name}}>) { 48 | return html` 49 |
50 | 51 | {{#each entry_type.fields}} 52 | {{#if widget}} 53 |
54 | ${msg("{{title_case field_name}}")} 55 | {{#if (not (eq cardinality "vector") )}} 56 | {{> (concat field_type.type "/" widget "/detail/render") field_name=field_name variable_to_read=(concat "entryRecord.entry." (snake_case field_name) ) }} 57 | {{else}} 58 | {{> Vec/detail/render variable_to_read=(concat "entryRecord.entry." (snake_case field_name) ) field_name=field_name field_type=field_type widget=widget }} 59 | {{/if}} 60 |
61 | 62 | {{/if}} 63 | {{/each}} 64 |
65 | `; 66 | } 67 | 68 | render{{pascal_case entry_type.name}}() { 69 | const {{camel_case entry_type.name}} = this.{{camel_case coordinator_zome_manifest.name}}Store.{{camel_case (plural entry_type.name)}}.get(this.{{camel_case entry_type.name}}Hash).{{#if crud.update}}latestVersion{{else}}entry{{/if}}.get(); 70 | 71 | switch ({{camel_case entry_type.name}}.status) { 72 | case 'pending': 73 | return html`
75 | 76 |
`; 77 | case 'error': 78 | return html``; 82 | case 'completed': 83 | return this.renderSummary({{camel_case entry_type.name}}.value); 84 | } 85 | } 86 | 87 | render() { 88 | return html` this.dispatchEvent(new CustomEvent('{{kebab_case entry_type.name}}-selected', { 89 | composed: true, 90 | bubbles: true, 91 | detail: { 92 | {{camel_case entry_type.name}}Hash: this.{{camel_case entry_type.name}}Hash 93 | } 94 | }))}> 95 | ${this.render{{pascal_case entry_type.name}}()} 96 | `; 97 | } 98 | 99 | 100 | static styles = [sharedStyles]; 101 | } 102 | -------------------------------------------------------------------------------- /templates/zome/entry-type/ui/src/types.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#if (includes previous_file_content "export type EntryTypes = never;")}} 2 | {{replace previous_file_content "export type EntryTypes = never;" (concat "export type EntryTypes = ({ type: '" (pascal_case entry_type.name) "'; } & " (pascal_case entry_type.name) ");")}} 3 | {{else}} 4 | {{replace previous_file_content "export type EntryTypes =" (concat "export type EntryTypes = ({ type: '" (pascal_case entry_type.name) "'; } & " (pascal_case entry_type.name) ") | ")}} 5 | {{/if}} 6 | 7 | {{#each entry_type.fields}} 8 | {{#if (eq field_type.type "Enum")}} 9 | export interface {{field_type.label}} { 10 | type: 11 | {{#each field_type.variants}} 12 | | '{{this}}' 13 | {{/each}}; 14 | } 15 | {{/if}} 16 | {{/each}} 17 | 18 | export interface {{pascal_case entry_type.name}} { {{#each entry_type.fields}} 19 | {{#if (not (eq cardinality "vector" ) )}} 20 | {{snake_case field_name}}: {{> (concat field_type.type "/type") }}{{#if (eq cardinality "option")}} | undefined{{/if}}; 21 | {{else}} 22 | {{snake_case field_name}}: Array<{{> (concat field_type.type "/type") }}>; 23 | {{/if}} 24 | {{/each}} 25 | } 26 | 27 | -------------------------------------------------------------------------------- /templates/zome/field-types/ActionHash/sample-js.hbs: -------------------------------------------------------------------------------- 1 | (await fakeActionHash()) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/ActionHash/sample1.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(ActionHash) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/ActionHash/sample2.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(ActionHash) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/ActionHash/type.hbs: -------------------------------------------------------------------------------- 1 | ActionHash 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/AgentPubKey/SearchAgent/detail/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@holochain-open-dev/profiles/dist/elements/agent-avatar.js'; 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/AgentPubKey/SearchAgent/detail/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/AgentPubKey/SearchAgent/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@holochain-open-dev/profiles/dist/elements/search-agent.js'; 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/AgentPubKey/SearchAgent/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/zome/field-types/AgentPubKey/SearchAgent/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/AgentPubKey/sample-js.hbs: -------------------------------------------------------------------------------- 1 | (await fakeAgentPubKey()) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/AgentPubKey/sample1.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(AgentPubKey) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/AgentPubKey/sample2.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(AgentPubKey) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/AgentPubKey/type.hbs: -------------------------------------------------------------------------------- 1 | AgentPubKey 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/DnaHash/sample-js.hbs: -------------------------------------------------------------------------------- 1 | (await fakeDnaHash()) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/DnaHash/sample1.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(DnaHash) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/DnaHash/sample2.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(DnaHash) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/DnaHash/type.hbs: -------------------------------------------------------------------------------- 1 | DnaHash 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/EntryHash/sample-js.hbs: -------------------------------------------------------------------------------- 1 | (await fakeEntryHash()) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/EntryHash/sample1.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(EntryHash) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/EntryHash/sample2.hbs: -------------------------------------------------------------------------------- 1 | ::fixt::fixt!(EntryHash) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/EntryHash/type.hbs: -------------------------------------------------------------------------------- 1 | EntryHash 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Enum/Select/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{#each field_type.variants}}{{#unless @last}} {{../variable_to_read}}.type === '{{pascal_case this}}' ?{{/unless}} msg("{{title_case this}}"){{#unless @last}} :{{/unless}} {{/each}} } 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Enum/Select/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/select/select.js'; 2 | import '@shoelace-style/shoelace/dist/components/option/option.js'; 3 | -------------------------------------------------------------------------------- /templates/zome/field-types/Enum/Select/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | {{#each field_type.variants}} 3 | ${msg("{{title_case this}}")} 4 | {{/each}} 5 | 6 | -------------------------------------------------------------------------------- /templates/zome/field-types/Enum/Select/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Enum/sample-js.hbs: -------------------------------------------------------------------------------- 1 | { type: '{{lookup field_type.variants 0}}' } 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Enum/sample1.hbs: -------------------------------------------------------------------------------- 1 | {{field_type.label}}::{{lookup field_type.variants 0}} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Enum/sample2.hbs: -------------------------------------------------------------------------------- 1 | {{#if (eq (len field_type.variants) 1)}} 2 | {{field_type.label}}::{{lookup field_type.variants 0}} 3 | {{else}} 4 | {{field_type.label}}::{{lookup field_type.variants 1}} 5 | {{/if}} 6 | -------------------------------------------------------------------------------- /templates/zome/field-types/Enum/type.hbs: -------------------------------------------------------------------------------- 1 | {{pascal_case field_type.label}} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/TextArea/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/TextArea/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/textarea/textarea.js'; 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/TextArea/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/TextArea/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/TextField/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/TextField/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/input/input.js'; 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/TextField/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/TextField/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/sample-js.hbs: -------------------------------------------------------------------------------- 1 | "Lorem ipsum 2" 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/sample1.hbs: -------------------------------------------------------------------------------- 1 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit.".to_string() 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/sample2.hbs: -------------------------------------------------------------------------------- 1 | "Lorem ipsum 2".to_string() 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/String/type.hbs: -------------------------------------------------------------------------------- 1 | string 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Timestamp/DateTimePicker/detail/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/format-date/format-date.js'; 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Timestamp/DateTimePicker/detail/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Timestamp/DateTimePicker/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/input/input.js'; 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Timestamp/DateTimePicker/edit/render.hbs: -------------------------------------------------------------------------------- 1 | e.preventDefault()} {{#if required}} required{{/if}}{{#if edit}} .defaultValue=${new Date({{default_value}}/1000).toLocaleString()} {{/if}}> 2 | 3 | -------------------------------------------------------------------------------- /templates/zome/field-types/Timestamp/DateTimePicker/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | new Date({{variable_to_read}}).valueOf() * 1000 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Timestamp/sample-js.hbs: -------------------------------------------------------------------------------- 1 | Date.now() * 1000 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Timestamp/sample1.hbs: -------------------------------------------------------------------------------- 1 | Timestamp::from_micros(1674053334548000) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Timestamp/sample2.hbs: -------------------------------------------------------------------------------- 1 | Timestamp::from_micros(1674059334548000) 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Timestamp/type.hbs: -------------------------------------------------------------------------------- 1 | number 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Vec/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}}.map(el => html`{{> (concat field_type.type "/" widget "/detail/render") variable_to_read="el"}}`)} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/Vec/edit/render.hbs: -------------------------------------------------------------------------------- 1 |
2 | ${msg("{{title_case field_name}}")} 3 | 4 | ${repeat(this._{{camel_case field_name}}Fields, i => i, index => html`
{{> (concat field_type.type "/" widget "/edit/render") edit=edit label="" field_name=field_name default_value=(concat "currentRecord.entry." (snake_case field_name) "[index]") }} { this._{{camel_case field_name}}Fields = this._{{camel_case field_name}}Fields.filter(i => i !== index) } }>
`)} 5 | { this._{{camel_case field_name}}Fields = [...this._{{camel_case field_name}}Fields, Math.max(...this._{{camel_case field_name}}Fields) + 1]; } }>${msg("Add {{title_case field_name}}")} 6 |
7 | -------------------------------------------------------------------------------- /templates/zome/field-types/Vec/type.hbs: -------------------------------------------------------------------------------- 1 | Array<{{field_type.type}}> 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/Checkbox/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} ? 'Yes' : 'No' } 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/Checkbox/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js'; 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/Checkbox/edit/render.hbs: -------------------------------------------------------------------------------- 1 | ${msg("{{title_case field_name}}")} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/Checkbox/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} === 'on' 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/Switch/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} ? 'Yes' : 'No' } 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/Switch/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/switch/switch.js'; 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/Switch/edit/render.hbs: -------------------------------------------------------------------------------- 1 | ${msg("{{title_case field_name}}")} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/Switch/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} === 'on' 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/sample-js.hbs: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/sample1.hbs: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/sample2.hbs: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/bool/type.hbs: -------------------------------------------------------------------------------- 1 | boolean 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/f32/Slider/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/f32/Slider/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/range/range.js' 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/f32/Slider/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/zome/field-types/f32/Slider/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/f32/sample-js.hbs: -------------------------------------------------------------------------------- 1 | 1.5 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/f32/sample1.hbs: -------------------------------------------------------------------------------- 1 | 0.5 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/f32/sample2.hbs: -------------------------------------------------------------------------------- 1 | 1.5 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/f32/type.hbs: -------------------------------------------------------------------------------- 1 | number 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/i32/Slider/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/i32/Slider/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/range/range.js' 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/i32/Slider/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/zome/field-types/i32/Slider/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/i32/sample-js.hbs: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/i32/sample1.hbs: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/i32/sample2.hbs: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/i32/type.hbs: -------------------------------------------------------------------------------- 1 | number 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/u32/Slider/detail/render.hbs: -------------------------------------------------------------------------------- 1 | ${ {{variable_to_read}} } 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/u32/Slider/edit/imports.hbs: -------------------------------------------------------------------------------- 1 | import '@shoelace-style/shoelace/dist/components/range/range.js' 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/u32/Slider/edit/render.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/zome/field-types/u32/Slider/edit/to-value.hbs: -------------------------------------------------------------------------------- 1 | {{variable_to_read}} 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/u32/sample-js.hbs: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/u32/sample1.hbs: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/u32/sample2.hbs: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /templates/zome/field-types/u32/type.hbs: -------------------------------------------------------------------------------- 1 | number 2 | -------------------------------------------------------------------------------- /templates/zome/link-type/ui/src/mocks.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope (concat "export class " (pascal_case coordinator_zome_manifest.name) "ZomeMock extends ZomeMock implements AppClient {" ) }} 3 | {{previous_scope_content}} 4 | 5 | /** {{title_case (plural to_referenceable.name)}} for {{title_case from_referenceable.name}} */ 6 | {{camel_case from_referenceable.name}}To{{pascal_case to_referenceable.name}} = new HoloHashMap<{{from_referenceable.hash_type}}, Link[]>(); 7 | {{#if bidirectional}} 8 | {{camel_case to_referenceable.name}}To{{pascal_case from_referenceable.name}} = new HoloHashMap<{{to_referenceable.hash_type}}, Link[]>(); 9 | {{/if}} 10 | 11 | async get_{{snake_case (plural to_referenceable.name)}}_for_{{snake_case from_referenceable.name}}({{camel_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}): Promise> { 12 | return this.{{camel_case from_referenceable.name}}To{{pascal_case to_referenceable.name}}.get({{camel_case from_referenceable.singular_arg}}) || []; 13 | } 14 | 15 | async add_{{snake_case to_referenceable.name}}_for_{{snake_case from_referenceable.name}}(input: { {{snake_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}; {{snake_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}} }): Promise { 16 | const existing = this.{{camel_case from_referenceable.name}}To{{pascal_case to_referenceable.name}}.get(input.{{snake_case from_referenceable.singular_arg}}) || []; 17 | this.{{camel_case from_referenceable.name}}To{{pascal_case to_referenceable.name}}.set(input.{{snake_case from_referenceable.singular_arg}}, [...existing, { 18 | base: input.{{snake_case from_referenceable.singular_arg}}, 19 | target: input.{{snake_case to_referenceable.singular_arg}}, 20 | author: this.myPubKey, 21 | timestamp: Date.now() * 1000, 22 | zome_index: 0, 23 | link_type: 0, 24 | tag: new Uint8Array(), 25 | create_link_hash: await fakeActionHash() 26 | }]); 27 | {{#if bidirectional}} 28 | const existingReverse = this.{{camel_case to_referenceable.name}}To{{pascal_case from_referenceable.name}}.get(input.{{snake_case to_referenceable.singular_arg}}) || []; 29 | this.{{camel_case to_referenceable.name}}To{{pascal_case from_referenceable.name}}.set(input.{{snake_case to_referenceable.singular_arg}}, [...existingReverse, { 30 | base: input.{{snake_case to_referenceable.singular_arg}}, 31 | target: input.{{snake_case from_referenceable.singular_arg}}, 32 | author: this.myPubKey, 33 | timestamp: Date.now() * 1000, 34 | zome_index: 0, 35 | link_type: 0, 36 | tag: new Uint8Array(), 37 | create_link_hash: await fakeActionHash() 38 | }]); 39 | {{/if}} 40 | } 41 | 42 | {{#if bidirectional}} 43 | async get_{{snake_case (plural from_referenceable.name)}}_for_{{snake_case to_referenceable.name}}({{camel_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}}): Promise> { 44 | return this.{{camel_case to_referenceable.name}}To{{pascal_case from_referenceable.name}}.get({{camel_case to_referenceable.singular_arg}}) || []; 45 | } 46 | {{/if}} 47 | 48 | {{/match_scope}} 49 | {{/merge}} 50 | -------------------------------------------------------------------------------- /templates/zome/link-type/ui/src/{{kebab_case coordinator_zome_manifest.name}}-client.ts.hbs: -------------------------------------------------------------------------------- 1 | {{#merge previous_file_content}} 2 | {{#match_scope (concat "export class " (pascal_case coordinator_zome_manifest.name) "Client extends ZomeClient<" (pascal_case coordinator_zome_manifest.name) "Signal> {" ) }} 3 | {{previous_scope_content}} 4 | 5 | /** {{title_case (plural to_referenceable.name)}} for {{title_case from_referenceable.name}} */ 6 | 7 | async get{{pascal_case (plural to_referenceable.name)}}For{{pascal_case from_referenceable.name}}({{camel_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}): Promise> { 8 | return this.callZome('get_{{snake_case (plural to_referenceable.name)}}_for_{{snake_case from_referenceable.name}}', {{camel_case from_referenceable.singular_arg}}); 9 | } 10 | {{#if delete}} 11 | 12 | async getDeleted{{pascal_case (plural to_referenceable.name)}}For{{pascal_case from_referenceable.name}}({{camel_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}): Promise, SignedActionHashed[]]>> { 13 | return this.callZome('get_deleted_{{snake_case (plural to_referenceable.name)}}_for_{{snake_case from_referenceable.name}}', {{camel_case from_referenceable.singular_arg}}); 14 | } 15 | {{/if}} 16 | 17 | add{{pascal_case to_referenceable.name}}For{{pascal_case from_referenceable.name}}({{camel_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}, {{camel_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}}): Promise { 18 | return this.callZome('add_{{snake_case to_referenceable.name}}_for_{{snake_case from_referenceable.name}}', { 19 | base_{{snake_case from_referenceable.singular_arg}}: {{camel_case from_referenceable.singular_arg}}, 20 | target_{{snake_case to_referenceable.singular_arg}}: {{camel_case to_referenceable.singular_arg}}, 21 | }); 22 | } 23 | 24 | {{#if delete}} 25 | remove{{pascal_case to_referenceable.name}}For{{pascal_case from_referenceable.name}}({{camel_case from_referenceable.singular_arg}}: {{from_referenceable.hash_type}}, {{camel_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}}): Promise { 26 | return this.callZome('remove_{{snake_case to_referenceable.name}}_for_{{snake_case from_referenceable.name}}', { 27 | base_{{snake_case from_referenceable.singular_arg}}: {{camel_case from_referenceable.singular_arg}}, 28 | target_{{snake_case to_referenceable.singular_arg}}: {{camel_case to_referenceable.singular_arg}}, 29 | }); 30 | } 31 | 32 | {{/if}} 33 | {{#if bidirectional}} 34 | async get{{pascal_case (plural from_referenceable.name)}}For{{pascal_case to_referenceable.name}}({{camel_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}}): Promise> { 35 | return this.callZome('get_{{snake_case (plural from_referenceable.name)}}_for_{{snake_case to_referenceable.name}}', {{camel_case to_referenceable.singular_arg}}); 36 | } 37 | {{#if delete}} 38 | 39 | async getDeleted{{pascal_case (plural from_referenceable.name)}}For{{pascal_case to_referenceable.name}}({{camel_case to_referenceable.singular_arg}}: {{to_referenceable.hash_type}}): Promise, SignedActionHashed[]]>> { 40 | return this.callZome('get_deleted_{{snake_case (plural from_referenceable.name)}}_for_{{snake_case to_referenceable.name}}', {{camel_case to_referenceable.singular_arg}}); 41 | } 42 | {{/if}} 43 | {{/if}} 44 | {{/match_scope}} 45 | {{/merge}} 46 | -------------------------------------------------------------------------------- /templates/zome/web-app.instructions.hbs: -------------------------------------------------------------------------------- 1 | Notice that because you are using the zome template, this template only works with a single dna and zome, which are already scaffolded for you. It also comes with the profiles module pre-installed. 2 | 3 | So **don't run "hc scaffold dna" or "hc scaffold zome"**. 4 | 5 | Set up your development environment with: 6 | 7 | cd {{kebab_case app_name}} 8 | nix develop 9 | pnpm install 10 | 11 | In order to be able to revert any next scaffolding steps, it is also recommended to commit the skeleton now: 12 | 13 | git add . 14 | git commit -m "module skeleton scaffolded" 15 | 16 | To continue scaffolding your zome, add new entry types: 17 | 18 | hc scaffold entry-type 19 | 20 | After that, at any point in time you can start the demo with: 21 | 22 | pnpm start 23 | 24 | You can run the tests with: 25 | 26 | pnpm test 27 | -------------------------------------------------------------------------------- /templates/zome/web-app/.envrc.hbs: -------------------------------------------------------------------------------- 1 | use flake . 2 | -------------------------------------------------------------------------------- /templates/zome/web-app/.github/actions/extend-space/action.yaml.hbs: -------------------------------------------------------------------------------- 1 | # Extends disk space on github hosted runners 2 | 3 | 4 | name: "Extend space" 5 | description: "Teases out as much free space as possible" 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Create mountpoint for extended space 11 | shell: "bash" 12 | run: sudo mkdir /mnt/extended 13 | 14 | - name: Maximize build space 15 | uses: easimon/maximize-build-space@v6 16 | # uses: AdityaGarg8/remove-unwanted-software@v1 17 | with: 18 | remove-dotnet: true 19 | remove-android: true 20 | remove-haskell: true 21 | build-mount-path: /mnt/extended 22 | root-reserve-mb: 512 23 | swap-size-mb: 1024 24 | 25 | # after we've got the extended space mounted, we can use overlayfs and bind 26 | # mounts as appropriate to make this space available to all paths that are 27 | # expected to be written to. 28 | # 29 | # for new directories (such as /nix), a bind mount is enough for existing 30 | # directories (such as $HOME and /tmp), we use an overlay to preserve access 31 | # to the existing files, while redirecting changes to the extended space. 32 | - name: Use extended space for /nix, /tmp, /var/tmp, and $HOME 33 | shell: "bash" 34 | run: | 35 | export EXTENDED_PATH=/mnt/extended 36 | sudo mkdir $EXTENDED_PATH/{tmp,nix,home} 37 | sudo mkdir $EXTENDED_PATH/tmp/{upper,work} 38 | sudo mv /tmp{,_lower} 39 | sudo mkdir /tmp 40 | sudo mount -t overlay overlay \ 41 | -o lowerdir=/tmp_lower,upperdir=$EXTENDED_PATH/tmp/upper,workdir=$EXTENDED_PATH/tmp/work \ 42 | /tmp 43 | sudo chown $(id -u):$(id -g) /tmp 44 | sudo mkdir -p $EXTENDED_PATH/var/tmp/{upper,work} 45 | sudo mv /var/tmp{,_lower} 46 | sudo mkdir -p /var/tmp 47 | sudo mount -t overlay overlay \ 48 | -o lowerdir=/var/tmp_lower,upperdir=$EXTENDED_PATH/var/tmp/upper,workdir=$EXTENDED_PATH/var/tmp/work \ 49 | /var/tmp 50 | sudo chown $(id -u):$(id -g) /var/tmp 51 | sudo mkdir /nix 52 | sudo mount -o bind $EXTENDED_PATH/nix /nix 53 | sudo mkdir $EXTENDED_PATH/home/{upper,work} 54 | sudo mv ${HOME} ${HOME}_lower 55 | sudo mkdir ${HOME} 56 | sudo chown $(id -u):$(id -g) $HOME 57 | sudo mount -t overlay overlay \ 58 | -o lowerdir=${HOME}_lower,upperdir=$EXTENDED_PATH/home/upper,workdir=$EXTENDED_PATH/home/work \ 59 | $HOME 60 | sudo chown $(id -u):$(id -g) $HOME 61 | df -h 62 | -------------------------------------------------------------------------------- /templates/zome/web-app/.github/workflows/build-and-cache-zomes.yaml.hbs: -------------------------------------------------------------------------------- 1 | name: "Build and cache zomes" 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | branches: [ main ] 7 | 8 | jobs: 9 | build-and-cache-zomes: 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, macos-latest] 13 | 14 | runs-on: $\{{ matrix.os }} 15 | steps: 16 | - uses: actions/checkout@v3 17 | 18 | - uses: cachix/install-nix-action@v27 19 | with: 20 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 21 | nix_path: nixpkgs=channel:nixos-24.05 22 | 23 | - uses: cachix/cachix-action@v15 24 | with: 25 | name: holochain-ci 26 | 27 | - uses: cachix/cachix-action@v15 28 | with: 29 | name: holochain-open-dev 30 | 31 | - name: Install and test 32 | run: | 33 | nix develop --no-update-lock-file --accept-flake-config --command bash -c "pnpm i && pnpm t && pnpm -F @holochain-open-dev/{{kebab_case app_name}} build" 34 | 35 | - name: 'Setup jq' 36 | uses: dcarbone/install-jq-action@v2 37 | 38 | - name: Build debug zomes 39 | env: 40 | CACHIX_AUTH_TOKEN: "$\{{ secrets.CACHIX_TOKEN_HOLOCHAIN_OPEN_DEV }}" 41 | run: | 42 | cachix watch-exec holochain-open-dev -- nix build --no-update-lock-file --accept-flake-config -L .#{{snake_case app_name}}_integrity 43 | cachix push holochain-open-dev $(nix path-info --json --accept-flake-config --no-warn-dirty .#{{snake_case app_name}}_integrity | jq -- -r 'keys[0]') 44 | 45 | cachix watch-exec holochain-open-dev -- nix build --no-update-lock-file --accept-flake-config -L .#{{snake_case app_name}} 46 | cachix push holochain-open-dev $(nix path-info --json --accept-flake-config --no-warn-dirty .#{{snake_case app_name}} | jq -- -r 'keys[0]') 47 | 48 | - name: Pin debug zomes 49 | if: github.event_name != 'pull_request' 50 | env: 51 | CACHIX_AUTH_TOKEN: "$\{{ secrets.CACHIX_TOKEN_HOLOCHAIN_OPEN_DEV }}" 52 | run: | 53 | cachix pin holochain-open-dev {{snake_case app_name}}_integrity_debug $(nix path-info --json --accept-flake-config --no-warn-dirty .#{{snake_case app_name}}_integrity | jq -- -r 'keys[0]') 54 | cachix pin holochain-open-dev {{snake_case app_name}}_debug $(nix path-info --json --accept-flake-config --no-warn-dirty .#{{snake_case app_name}} | jq -- -r 'keys[0]') 55 | 56 | - name: Build release zomes 57 | if: matrix.os == 'ubuntu-latest' 58 | env: 59 | CACHIX_AUTH_TOKEN: "$\{{ secrets.CACHIX_TOKEN_HOLOCHAIN_OPEN_DEV }}" 60 | run: | 61 | cachix watch-exec holochain-open-dev -- nix build --no-update-lock-file --accept-flake-config -L .#{{snake_case app_name}}_integrity.meta.release 62 | cachix push holochain-open-dev $(nix path-info --json --accept-flake-config --no-warn-dirty .#{{snake_case app_name}}_integrity.meta.release | jq -- -r 'keys[0]') 63 | 64 | cachix watch-exec holochain-open-dev -- nix build --no-update-lock-file --accept-flake-config -L .#{{snake_case app_name}}.meta.release 65 | cachix push holochain-open-dev $(nix path-info --json --accept-flake-config --no-warn-dirty .#{{snake_case app_name}}.meta.release | jq -- -r 'keys[0]') 66 | 67 | - name: Pin release zomes 68 | if: github.event_name != 'pull_request' && matrix.os == 'ubuntu-latest' 69 | env: 70 | CACHIX_AUTH_TOKEN: "$\{{ secrets.CACHIX_TOKEN_HOLOCHAIN_OPEN_DEV }}" 71 | run: | 72 | cachix pin holochain-open-dev {{snake_case app_name}}_integrity_debug $(nix path-info --json --accept-flake-config --no-warn-dirty .#{{snake_case app_name}}_integrity | jq -- -r 'keys[0]') 73 | cachix pin holochain-open-dev {{snake_case app_name}}_debug $(nix path-info --json --accept-flake-config --no-warn-dirty .#{{snake_case app_name}} | jq -- -r 'keys[0]') 74 | 75 | -------------------------------------------------------------------------------- /templates/zome/web-app/.github/workflows/publish-docs.yml.hbs: -------------------------------------------------------------------------------- 1 | name: Publish docs 2 | on: 3 | push: 4 | branches: [ main ] 5 | permissions: 6 | contents: write 7 | jobs: 8 | build-and-deploy: 9 | concurrency: ci-$\{{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession. 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 🛎️ 13 | uses: actions/checkout@v3 14 | 15 | - uses: cachix/install-nix-action@v27 16 | with: 17 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 18 | nix_path: nixpkgs=channel:nixos-24.05 19 | 20 | - uses: cachix/cachix-action@v15 21 | with: 22 | name: holochain-ci 23 | 24 | - uses: cachix/cachix-action@v15 25 | with: 26 | name: holochain-open-dev 27 | 28 | - name: Install and Build 🔧 29 | run: | 30 | nix develop --no-update-lock-file --accept-flake-config --command bash -c "pnpm i && pnpm -F docs build" 31 | 32 | - name: Deploy 🚀 33 | uses: JamesIves/github-pages-deploy-action@v4.3.3 34 | with: 35 | branch: gh-pages # The branch the action should deploy to. 36 | folder: docs/.vitepress/dist # The folder the action should deploy. 37 | -------------------------------------------------------------------------------- /templates/zome/web-app/.github/workflows/test.yaml.hbs: -------------------------------------------------------------------------------- 1 | name: "test" 2 | on: 3 | # Trigger the workflow on push or pull request, 4 | # but only for the main branch 5 | push: 6 | branches: [ main, develop ] 7 | pull_request: 8 | branches: [ main, develop ] 9 | 10 | jobs: 11 | test-and-build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - uses: cachix/install-nix-action@v27 17 | with: 18 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 19 | nix_path: nixpkgs=channel:nixos-24.05 20 | 21 | - uses: cachix/cachix-action@v15 22 | with: 23 | name: holochain-ci 24 | 25 | - uses: cachix/cachix-action@v15 26 | with: 27 | name: holochain-open-dev 28 | 29 | - name: Install and test 30 | run: | 31 | nix develop --no-update-lock-file --accept-flake-config --command bash -c "pnpm i && pnpm t && pnpm -F @holochain-open-dev/{{kebab_case app_name}} build" 32 | -------------------------------------------------------------------------------- /templates/zome/web-app/.github/workflows/update-flake-inputs.yaml.hbs: -------------------------------------------------------------------------------- 1 | name: Update flake inputs 2 | on: 3 | schedule: 4 | - cron: "0 7 * * *" # Run every day at 7AM 5 | 6 | permissions: 7 | pull-requests: write 8 | contents: write 9 | 10 | jobs: 11 | automerge: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - uses: cachix/install-nix-action@v27 17 | with: 18 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 19 | nix_path: nixpkgs=channel:nixos-24.05 20 | 21 | - uses: cachix/cachix-action@v15 22 | with: 23 | name: holochain-ci 24 | 25 | - uses: cachix/cachix-action@v15 26 | with: 27 | name: holochain-open-dev 28 | 29 | - name: Update Flake 30 | run: | 31 | nix flake update 32 | nix develop --accept-flake-config --command bash -c "echo \"This is here so sync-npm-git-dependencies-with-git is triggered\"" 33 | 34 | - uses: dorny/paths-filter@v3 35 | id: changes 36 | with: 37 | filters: | 38 | flake_lock: 39 | - 'flake.lock' 40 | package_json: 41 | - '**/*/package.json' 42 | 43 | - name: Create Pull Request 44 | id: cpr 45 | if: steps.changes.outputs.flake_lock == 'true' || steps.changes.outputs.package_json == 'true' 46 | uses: peter-evans/create-pull-request@v3 47 | with: 48 | token: $\{{ secrets.GITHUB_TOKEN }} 49 | 50 | - name: Enable Pull Request Automerge 51 | if: (steps.changes.outputs.flake_lock == 'true' || steps.changes.outputs.package_json == 'true') && steps.cpr.outputs.pull-request-operation == 'created' 52 | uses: peter-evans/enable-pull-request-automerge@v3 53 | with: 54 | token: $\{{ secrets.GITHUB_TOKEN }} 55 | pull-request-number: $\{{ steps.cpr.outputs.pull-request-number }} 56 | merge-method: squash 57 | -------------------------------------------------------------------------------- /templates/zome/web-app/.gitignore.hbs: -------------------------------------------------------------------------------- 1 | ## editors 2 | /.idea 3 | /.vscode 4 | 5 | ## system files 6 | .DS_Store 7 | 8 | ## npm 9 | node_modules/ 10 | /npm-debug.log 11 | 12 | ## testing 13 | /coverage/ 14 | 15 | ## temp folders 16 | /.tmp/ 17 | 18 | # build 19 | /_site/ 20 | dist/ 21 | /out-tsc/ 22 | 23 | storybook-static 24 | *.tsbuildinfo 25 | 26 | # holochain 27 | *.happ 28 | *.webhapp 29 | *.dna 30 | 31 | # cargo 32 | /target/ 33 | /.cargo/ 34 | 35 | /.direnv/ 36 | -------------------------------------------------------------------------------- /templates/zome/web-app/.prettierrc.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "arrowParens": "avoid", 4 | "useTabs": true, 5 | "plugins": ["@trivago/prettier-plugin-sort-imports"], 6 | "importOrder": ["", "^[./]"], 7 | "importOrderSeparation": true, 8 | "importOrderSortSpecifiers": true, 9 | "importOrderParserPlugins": ["typescript", "decorators-legacy"] 10 | } 11 | -------------------------------------------------------------------------------- /templates/zome/web-app/Cargo.toml.hbs: -------------------------------------------------------------------------------- 1 | [profile.dev] 2 | opt-level = "z" 3 | 4 | [profile.release] 5 | opt-level = "z" 6 | 7 | [workspace] 8 | members = ["zomes/coordinator/*", "zomes/integrity/*"] 9 | resolver = "2" 10 | 11 | [workspace.dependencies] 12 | holochain = { version = "{{holochain_version}}", default-features = false, features = [ 13 | "test_utils", 14 | ] } 15 | hdi = "{{hdi_version}}" 16 | hdk = "{{hdk_version}}" 17 | serde = "1" 18 | 19 | -------------------------------------------------------------------------------- /templates/zome/web-app/README.md.hbs: -------------------------------------------------------------------------------- 1 | # {{title_case app_name}} 2 | 3 | ## Environment Setup 4 | 5 | > PREREQUISITE: set up the [holochain development environment](https://developer.holochain.org/docs/install/). 6 | 7 | Enter the nix shell by running this in the root folder of the repository: 8 | 9 | ```bash 10 | nix develop 11 | npm install 12 | ``` 13 | 14 | **Run all the other instructions in this README from inside this nix shell, otherwise they won't work**. 15 | 16 | ## Running 2 agents 17 | 18 | ```bash 19 | npm start 20 | ``` 21 | 22 | This will create a network of 2 nodes connected to each other and their respective UIs. 23 | It will also bring up the Holochain Playground for advanced introspection of the conductors. 24 | 25 | ## Running the backend tests 26 | 27 | ```bash 28 | npm test 29 | ``` 30 | 31 | ## Bootstrapping a network 32 | 33 | Create a custom network of nodes connected to each other and their respective UIs with: 34 | 35 | ```bash 36 | npm run network 3 37 | ``` 38 | 39 | Substitute the "3" for the number of nodes that you want to bootstrap in your network. 40 | This will also bring up the Holochain Playground for advanced introspection of the conductors. 41 | 42 | ## Packaging 43 | 44 | To package the web happ: 45 | ``` bash 46 | npm run package 47 | ``` 48 | 49 | You'll have the `{{app_name}}.webhapp` in `workdir`. This is what you should distribute so that the Holochain Launcher can install it. 50 | You will also have its subcomponent `{{app_name}}.happ` in the same folder`. 51 | 52 | ## Documentation 53 | 54 | This repository is using these tools: 55 | - [NPM Workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces/): npm v7's built-in monorepo capabilities. 56 | - [hc](https://github.com/holochain/holochain/tree/develop/crates/hc): Holochain CLI to easily manage Holochain development instances. 57 | - [@holochain/tryorama](https://www.npmjs.com/package/@holochain/tryorama): test framework. 58 | - [@holochain/client](https://www.npmjs.com/package/@holochain/client): client library to connect to Holochain from the UI. 59 | - [@holochain-playground/cli](https://www.npmjs.com/package/@holochain-playground/cli): introspection tooling to understand what's going on in the Holochain nodes. 60 | -------------------------------------------------------------------------------- /templates/zome/web-app/docs/.gitignore.hbs: -------------------------------------------------------------------------------- 1 | .vitepress/cache 2 | .vitepress/dist 3 | public/backend 4 | -------------------------------------------------------------------------------- /templates/zome/web-app/docs/.vitepress/config.mts.hbs: -------------------------------------------------------------------------------- 1 | import { withMermaid } from 'vitepress-plugin-mermaid'; 2 | import fs from 'fs'; 3 | import { defineConfig } from 'vitepress'; 4 | 5 | // https://vitepress.dev/reference/site-config 6 | // Uncomment this to enable mermaid inside your site when this is solved: https://github.com/mermaid-js/mermaid/issues/4320 7 | // export default withMermaid({ 8 | export default defineConfig({ 9 | vue: { 10 | template: { 11 | compilerOptions: { 12 | // treat all tags with a dash as custom elements 13 | isCustomElement: (tag) => tag.includes("-"), 14 | }, 15 | }, 16 | }, 17 | vite: { 18 | optimizeDeps: { 19 | include: [ 20 | 'mermaid' 21 | ] 22 | } 23 | }, 24 | base: "/{{kebab_case app_name}}", 25 | title: "@holochain-open-dev/{{kebab_case app_name}}", 26 | description: "{{title_case app_name}} zome for holochain apps", 27 | themeConfig: { 28 | // https://vitepress.dev/reference/default-theme-config 29 | 30 | sidebar: [ 31 | { 32 | text: "Setup", 33 | link: "/setup.md", 34 | }, 35 | { 36 | text: "API Reference", 37 | items: [ 38 | { 39 | text: "Integrity Zome", 40 | link: "/backend/doc/{{snake_case app_name}}_integrity/index.html", 41 | target: "_blank", 42 | }, 43 | { 44 | text: "Coordinator Zome", 45 | link: "/backend/doc/{{snake_case app_name}}/index.html", 46 | target: "_blank", 47 | }, 48 | { 49 | text: "Frontend", 50 | items: [ 51 | { 52 | text: "{{pascal_case app_name}}Store", 53 | link: "/{{kebab_case app_name}}-store.md", 54 | }, 55 | { 56 | text: "Elements", 57 | items: fs.readdirSync("./elements").filter(file => file.endsWith('.md')).map(el => 58 | ({ 59 | text: el.split('.md')[0], 60 | link: `/elements/${el}`, 61 | }), 62 | ), 63 | }, 64 | ], 65 | }, 66 | ], 67 | }, 68 | ], 69 | 70 | socialLinks: [ 71 | { 72 | icon: "github", 73 | link: "https://github.com/holochain-open-dev/{{kebab_case app_name}}", 74 | }, 75 | ], 76 | search: { 77 | provider: 'local' 78 | } 79 | }, 80 | head: [ 81 | [ 82 | 'script', 83 | {}, 84 | // Synchronize the vitepress dark/light theme with the shoelace mode 85 | ` 86 | function syncTheme() { 87 | const isDark = document.documentElement.classList.contains('dark'); 88 | const isShoelaceDark = document.documentElement.classList.contains('sl-theme-dark'); 89 | if (isDark && !isShoelaceDark) document.documentElement.classList = "dark sl-theme-dark"; 90 | if (!isDark && isShoelaceDark) document.documentElement.classList = ""; 91 | } 92 | const attrObserver = new MutationObserver((mutations) => { 93 | mutations.forEach(mu => { 94 | if (mu.type !== "attributes" && mu.attributeName !== "class") return; 95 | syncTheme(); 96 | }); 97 | }); 98 | attrObserver.observe(document.documentElement, {attributes: true}); 99 | syncTheme(); 100 | ` 101 | ] 102 | ], 103 | }); 104 | -------------------------------------------------------------------------------- /templates/zome/web-app/docs/.vitepress/theme/custom.css.hbs: -------------------------------------------------------------------------------- 1 | api-demo::part(header) { 2 | display: none; 3 | } 4 | api-docs::part(header) { 5 | display: none; 6 | } 7 | api-demo, api-docs { 8 | font-family: inherit; 9 | } 10 | 11 | .sl-theme-dark api-demo::part(tab-panel) { 12 | background: unset !important; 13 | } 14 | 15 | .sl-theme-dark api-demo { 16 | --ave-item-color: rgba(255, 255, 255, 0.87); 17 | --ave-tab-color: rgba(255, 255, 255, 0.54); 18 | --ave-secondary-color: rgba(255, 255, 255, 0.54); 19 | } 20 | .sl-theme-dark api-docs { 21 | --ave-item-color: rgba(255, 255, 255, 0.87); 22 | --ave-tab-color: rgba(255, 255, 255, 0.54); 23 | --ave-secondary-color: rgba(255, 255, 255, 0.54); 24 | } 25 | -------------------------------------------------------------------------------- /templates/zome/web-app/docs/.vitepress/theme/index.js.hbs: -------------------------------------------------------------------------------- 1 | // .vitepress/theme/index.js 2 | import DefaultTheme from 'vitepress/theme' 3 | import '@shoelace-style/shoelace/dist/themes/light.css'; 4 | import '@shoelace-style/shoelace/dist/themes/dark.css'; 5 | import './custom.css' 6 | 7 | export default DefaultTheme; 8 | -------------------------------------------------------------------------------- /templates/zome/web-app/docs/index.md.hbs: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "@holochain-open-dev/{{kebab_case app_name}}" 7 | text: "{{title_case app_name}} zome for holochain apps" 8 | tagline: Plug-and-play {{lower_case app_name}} management for your hApps 9 | actions: 10 | - theme: brand 11 | text: Setup 12 | link: /setup.md 13 | - theme: alt 14 | text: Integrity Zome API 15 | link: "/backend/doc/{{snake_case app_name}}_integrity/index.html" 16 | target: "_blank" 17 | - theme: alt 18 | text: Coordinator Zome API 19 | link: "/backend/doc/{{snake_case app_name}}/index.html" 20 | target: "_blank" 21 | - theme: alt 22 | text: Frontend API 23 | link: "/{{kebab_case app_name}}-store.md" 24 | 25 | features: 26 | - title: UI+Backend Module 27 | details: Following the holochain-open-dev guidelines 28 | link: https://holochain-open-dev.github.io 29 | --- 30 | -------------------------------------------------------------------------------- /templates/zome/web-app/docs/introduction.md.hbs: -------------------------------------------------------------------------------- 1 | # @holochain-open-dev/{{kebab_case app_name}} 2 | 3 | This module follows the [holochain-open-dev](https://github.com/holochain-open-dev/) pattern of developing holochain modules. Read [its documentation](https://holochain-open-dev.github.io) to understand more about its motivation and the big picture. 4 | 5 | To integrate this module into your application: 6 | 7 | - If you are starting a new project from scratch, it is very recommended to follow the [Template Setup](?path=/docs/template-setup--docs). 8 | - If not, you can visit [Setting up the Backend](?path=/docs/backend-setting-up-the-zomes--docs) and [Setting up the Frontend](?path=/docs/frontend-setting-up-the-frontend--docs). 9 | -------------------------------------------------------------------------------- /templates/zome/web-app/docs/package.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.1", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "start": "pnpm dev", 8 | "dev": "pnpm setup:docs && vitepress dev", 9 | "build": "pnpm setup:docs && vitepress build", 10 | "setup:docs": "pnpm setup:custom-elements && pnpm setup:cargo", 11 | "setup:custom-elements": "pnpm -F @holochain-open-dev/{{kebab_case app_name}} analyze && mkdir -p public/elements && cp ../ui/custom-elements.json ./public/elements/custom-elements.json", 12 | "setup:cargo": "cargo doc --release --no-deps --target-dir public/backend", 13 | "preview": "vitepress preview" 14 | }, 15 | "devDependencies": { 16 | "@api-viewer/demo": "1.0.0-pre.10", 17 | "@api-viewer/docs": "1.0.0-pre.10", 18 | "@holochain/client": "^0.17.1", 19 | "@holochain-open-dev/profiles": "github:holochain-open-dev/profiles#nixify&path:ui", 20 | "@holochain-open-dev/{{kebab_case app_name}}": "workspace:*", 21 | "@shoelace-style/shoelace": "^2.11.0", 22 | "api-viewer-element": "^1.0.0-pre.10", 23 | "lit": "^3.0.0", 24 | "vitepress": "^1.0.1", 25 | "vitepress-plugin-mermaid": "^2.0.16" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /templates/zome/web-app/docs/{{kebab_case app_name}}-store.md.hbs: -------------------------------------------------------------------------------- 1 | # {{pascal_case app_name}}Store 2 | 3 | The `{{pascal_case app_name}}Store` is a typescript class that contains [async signals](https://www.npmjs.com/package/async-signals), which you can watch to get reactive updates in your elements. 4 | 5 | ```js 6 | import { {{pascal_case app_name}}Store, {{pascal_case app_name}}Client } from "@holochain-open-dev/{{kebab_case app_name}}"; 7 | const store = new {{pascal_case app_name}}Store(new {{pascal_case app_name}}Client(appClient, 'my-role-name')); 8 | ``` 9 | 10 | > Learn how to setup the `AppClient` object [here](https://www.npmjs.com/package/@holochain/client). 11 | 12 | Learn more about the stores and how to integrate them in different frameworks [here](https://holochain-open-dev.github.io/reusable-modules/frontend/using/#stores). 13 | -------------------------------------------------------------------------------- /templates/zome/web-app/eslint.config.js.hbs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | import eslint from '@eslint/js'; 4 | import tseslint from 'typescript-eslint'; 5 | import eslintConfigPrettier from "eslint-config-prettier"; 6 | 7 | export default tseslint.config( 8 | eslint.configs.recommended, 9 | ...tseslint.configs.recommended, 10 | eslintConfigPrettier, 11 | { 12 | rules: { 13 | '@typescript-eslint/no-unused-vars': "warn" 14 | } 15 | } 16 | ); 17 | -------------------------------------------------------------------------------- /templates/zome/web-app/flake.nix.hbs: -------------------------------------------------------------------------------- 1 | { 2 | description = "Template for Holochain app development"; 3 | 4 | inputs = { 5 | holonix.url = "github:holochain/holonix/main-0.3"; 6 | 7 | nixpkgs.follows = "holonix/nixpkgs"; 8 | flake-parts.follows = "holonix/flake-parts"; 9 | 10 | hc-infra.url = "github:holochain-open-dev/infrastructure"; 11 | scaffolding.url = "github:holochain-open-dev/templates"; 12 | playground.url = "github:darksoil-studio/holochain-playground"; 13 | 14 | profiles.url = "github:holochain-open-dev/profiles/nixify"; 15 | }; 16 | 17 | nixConfig = { 18 | extra-substituters = [ 19 | "https://holochain-open-dev.cachix.org" 20 | ]; 21 | extra-trusted-public-keys = [ 22 | "holochain-open-dev.cachix.org-1:3Tr+9in6uo44Ga7qiuRIfOTFXog+2+YbyhwI/Z6Cp4U=" 23 | ]; 24 | }; 25 | 26 | 27 | outputs = inputs: 28 | inputs.flake-parts.lib.mkFlake 29 | { 30 | inherit inputs; 31 | } 32 | { 33 | imports = [ 34 | ./zomes/integrity/{{snake_case app_name}}/zome.nix 35 | ./zomes/coordinator/{{snake_case app_name}}/zome.nix 36 | # Just for testing purposes 37 | ./workdir/dna.nix 38 | ./workdir/happ.nix 39 | ]; 40 | 41 | systems = builtins.attrNames inputs.holonix.devShells; 42 | perSystem = 43 | { inputs' 44 | , config 45 | , pkgs 46 | , system 47 | , ... 48 | }: { 49 | devShells.default = pkgs.mkShell { 50 | inputsFrom = [ 51 | inputs'.hc-infra.devShells.synchronized-pnpm 52 | inputs'.holonix.devShells.default 53 | ]; 54 | 55 | packages = [ 56 | inputs'.scaffolding.packages.hc-scaffold-zome-template 57 | inputs'.playground.packages.hc-playground 58 | ]; 59 | }; 60 | 61 | packages.scaffold = pkgs.symlinkJoin { 62 | name = "scaffold-remote-zome"; 63 | paths = [ inputs'.hc-infra.packages.scaffold-remote-zome ]; 64 | buildInputs = [ pkgs.makeWrapper ]; 65 | postBuild = '' 66 | wrapProgram $out/bin/scaffold-remote-zome \ 67 | --add-flags "{{kebab_case app_name}} \ 68 | --integrity-zome-name {{snake_case app_name}}_integrity \ 69 | --coordinator-zome-name {{snake_case app_name}} \ 70 | --remote-zome-git-url github:holochain-open-dev/{{kebab_case app_name}} \ 71 | --remote-npm-package-name {{kebab_case app_name}} \ 72 | --remote-npm-package-path ui \ 73 | --remote-zome-git-branch main" 74 | ''; 75 | }; 76 | }; 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /templates/zome/web-app/package.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{app_name}}-dev", 3 | "private": true, 4 | "scripts": { 5 | "start": "AGENTS=2 pnpm network", 6 | "network": "hc s clean && pnpm build:happ && BOOTSTRAP_PORT=$(port) SIGNAL_PORT=$(port) UI_PORT=8888 concurrently -k \"pnpm -F @holochain-open-dev/{{kebab_case app_name}} start\" \"pnpm launch\" \"hc playground\" \"pnpm local-services\"", 7 | "launch": "echo pass | RUST_LOG=warn hc launch --piped -n $AGENTS workdir/{{app_name}}_test.happ --ui-port $UI_PORT network --bootstrap http://127.0.0.1:$BOOTSTRAP_PORT webrtc ws://127.0.0.1:$SIGNAL_PORT", 8 | "local-services": "hc run-local-services --bootstrap-port $BOOTSTRAP_PORT --signal-port $SIGNAL_PORT", 9 | "test": "pnpm build:happ && nix flake check -L && pnpm -F tests test", 10 | "build:happ": "nix build -L .#{{snake_case app_name}}_test_happ -o workdir/{{app_name}}_test.happ" 11 | }, 12 | "devDependencies": { 13 | "@trivago/prettier-plugin-sort-imports": "^4.3.0", 14 | "@eslint/js": "^9.0.0", 15 | "concurrently": "^6.2.1", 16 | "eslint": "^9.0.0", 17 | "eslint-config-prettier": "^9.1.0", 18 | "new-port-cli": "^1.0.0", 19 | "prettier": "^3.2.5", 20 | "typescript-eslint": "^8.0.0", 21 | "typescript": "^5.4.5" 22 | }, 23 | "type": "module", 24 | "engines": { 25 | "pnpm": ">=9.0.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /templates/zome/web-app/pnpm-workspace.yaml.hbs: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'ui' 3 | - 'tests' 4 | - 'docs' 5 | -------------------------------------------------------------------------------- /templates/zome/web-app/tests/package.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tests", 3 | "private": true, 4 | "scripts": { 5 | "test": "vitest run" 6 | }, 7 | "dependencies": { 8 | "@msgpack/msgpack": "^2.8.0", 9 | "@holochain/client": "^0.17.1", 10 | "@holochain/tryorama": "{{tryorama_version}}", 11 | "@holochain-open-dev/signals": "^0.300.6", 12 | "@holochain-open-dev/utils": "^0.300.2", 13 | "typescript": "^5.4.5", 14 | "vitest": "^1.4.0" 15 | }, 16 | "type": "module" 17 | } 18 | -------------------------------------------------------------------------------- /templates/zome/web-app/tests/src/setup.ts.hbs: -------------------------------------------------------------------------------- 1 | import { 2 | AgentPubKey, 3 | EntryHash, 4 | NewEntryAction, 5 | ActionHash, 6 | Record, 7 | AppBundleSource, 8 | fakeActionHash, 9 | fakeAgentPubKey, 10 | fakeEntryHash, 11 | fakeDnaHash, 12 | AppCallZomeRequest, 13 | AppWebsocket, 14 | encodeHashToBase64 15 | } from '@holochain/client'; 16 | import { encode } from '@msgpack/msgpack'; 17 | import { Scenario } from '@holochain/tryorama'; 18 | import { EntryRecord } from '@holochain-open-dev/utils'; 19 | import { dirname } from 'path'; 20 | import { fileURLToPath } from 'url'; 21 | import { {{pascal_case app_name}}Client } from '../../ui/src/{{kebab_case app_name}}-client.js'; 22 | import { {{pascal_case app_name}}Store } from '../../ui/src/{{kebab_case app_name}}-store.js'; 23 | 24 | export async function setup(scenario: Scenario) { 25 | const testHappUrl = 26 | dirname(fileURLToPath(import.meta.url)) + '/../../workdir/{{app_name}}_test.happ'; 27 | 28 | // Add 2 players with the test hApp to the Scenario. The returned players 29 | // can be destructured. 30 | const [alice, bob] = await scenario.addPlayersWithApps([ 31 | { appBundleSource: { path: testHappUrl } }, 32 | { appBundleSource: { path: testHappUrl } }, 33 | ]); 34 | 35 | // Shortcut peer discovery through gossip and register all agents in every 36 | // conductor of the scenario. 37 | await scenario.shareAllAgents(); 38 | 39 | const aliceStore = new {{pascal_case app_name}}Store( 40 | new {{pascal_case app_name}}Client(alice.appWs as any, '{{snake_case app_name}}_test', '{{snake_case app_name}}') 41 | ); 42 | 43 | const bobStore = new {{pascal_case app_name}}Store( 44 | new {{pascal_case app_name}}Client(bob.appWs as any, '{{snake_case app_name}}_test', '{{snake_case app_name}}') 45 | ); 46 | 47 | // Shortcut peer discovery through gossip and register all agents in every 48 | // conductor of the scenario. 49 | await scenario.shareAllAgents(); 50 | 51 | return { 52 | alice: { 53 | player: alice, 54 | store: aliceStore, 55 | }, 56 | bob: { 57 | player: bob, 58 | store: bobStore, 59 | }, 60 | }; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /templates/zome/web-app/tests/tsconfig.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /templates/zome/web-app/tests/vitest.config.ts.hbs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | //@ts-ignore 3 | import pkg from "./package.json"; 4 | 5 | export default defineConfig({ 6 | test: { 7 | poolOptions: { 8 | threads: { 9 | singleThread: true, 10 | }, 11 | }, 12 | testTimeout: 60 * 1000 * 3, // 3 mins 13 | deps: { 14 | optimizer: { 15 | ssr: { 16 | enabled: true, 17 | //@ts-ignore 18 | include: Object.keys(pkg.dependencies), 19 | exclude: ["@holochain/client"], 20 | }, 21 | }, 22 | }, 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /templates/zome/web-app/tsconfig.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "noEmitOnError": true, 7 | "lib": ["es2018", "dom"], 8 | "strict": true, 9 | "esModuleInterop": false, 10 | "allowSyntheticDefaultImports": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "sourceMap": true, 14 | "declaration": true, 15 | "inlineSources": true, 16 | "incremental": true, 17 | "skipLibCheck": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/.gitignore.hbs: -------------------------------------------------------------------------------- 1 | ## editors 2 | /.idea 3 | /.vscode 4 | 5 | ## system files 6 | .DS_Store 7 | 8 | ## npm 9 | /node_modules/ 10 | /npm-debug.log 11 | 12 | ## testing 13 | /coverage/ 14 | 15 | ## temp folders 16 | /.tmp/ 17 | 18 | # build 19 | /_site/ 20 | /dist/ 21 | /out-tsc/ 22 | 23 | locales/ 24 | .rollup.cache 25 | *.tsbuildinfo 26 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/demo/index.html.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 28 | {{title_case app_name}} 29 | 30 | 31 | 32 | 33 | 34 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/lit-localize.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/lit/lit/main/packages/localize-tools/config.schema.json", 3 | "sourceLocale": "en", 4 | "targetLocales": [], 5 | "tsConfig": "./tsconfig.json", 6 | "output": { 7 | "mode": "runtime", 8 | "outputDir": "./locales", 9 | "localeCodesModule": "./locales/locales.js", 10 | "language": "js" 11 | }, 12 | "interchange": { 13 | "format": "xliff", 14 | "xliffDir": "./xliff/" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/package.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@holochain-open-dev/{{kebab_case app_name}}", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "start": "vite --clearScreen false --port $UI_PORT", 6 | "build": "pnpm lint && tsc && pnpm analyze", 7 | "lint": "eslint src && prettier \"src/**/*.ts\" --check --ignore-path .gitignore", 8 | "format": "eslint src --fix && prettier \"src/**/*.ts\" --write --ignore-path .gitignore", 9 | "localize:extract": "lit-localize extract", 10 | "localize:build": "lit-localize build", 11 | "analyze": "cem analyze --litelement --exclude dist", 12 | "prepare": "tsc && pnpm localize:build && pnpm analyze" 13 | }, 14 | "main": "dist/index.js", 15 | "module": "dist/index.js", 16 | "types": "dist/index.d.ts", 17 | "files": ["dist", "locales", "src"], 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/holochain-open-dev/{{kebab_case app_name}}.git" 21 | }, 22 | "exports": { 23 | ".": "./dist/index.js", 24 | "./dist/*": "./dist/*", 25 | "./locales/*": "./locales/*" 26 | }, 27 | "dependencies": { 28 | "@holochain/client": "^0.17.1", 29 | "@holochain-open-dev/elements": "^0.300.1", 30 | "@holochain-open-dev/profiles": "github:holochain-open-dev/profiles#nixify&path:ui", 31 | "@holochain-open-dev/signals": "^0.300.6", 32 | "@holochain-open-dev/utils": "^0.300.2", 33 | "@lit/context": "^1.0.0", 34 | "@lit/localize": "^0.12.0", 35 | "@mdi/js": "^7.2.0", 36 | "@msgpack/msgpack": "^2.8.0", 37 | "@shoelace-style/shoelace": "^2.11.0", 38 | "lit": "^3.0.0" 39 | }, 40 | "devDependencies": { 41 | "@custom-elements-manifest/analyzer": "^0.9.4", 42 | "@lit/localize-tools": "^0.6.3", 43 | "typescript": "^5.4.5", 44 | "tslib": "^2.6.2", 45 | "vite": "^4.0.0" 46 | }, 47 | "publishConfig": { 48 | "access": "public" 49 | }, 50 | "customElements": "custom-elements.json", 51 | "type": "module" 52 | } 53 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/src/context.ts.hbs: -------------------------------------------------------------------------------- 1 | import { createContext } from '@lit/context'; 2 | import { {{pascal_case app_name}}Store } from './{{kebab_case app_name}}-store.js'; 3 | 4 | export const {{camel_case app_name}}StoreContext = createContext<{{pascal_case app_name}}Store>( 5 | '{{snake_case app_name}}/store' 6 | ); 7 | 8 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/src/elements/{{kebab_case app_name}}-context.ts.hbs: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { provide } from '@lit/context'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | 5 | import { {{camel_case app_name}}StoreContext } from '../context.js'; 6 | import { {{pascal_case app_name}}Store } from '../{{kebab_case app_name}}-store.js'; 7 | 8 | @customElement('{{kebab_case app_name}}-context') 9 | export class {{pascal_case app_name}}Context extends LitElement { 10 | @provide({ context: {{camel_case app_name}}StoreContext }) 11 | @property({ type: Object }) 12 | store!: {{pascal_case app_name}}Store; 13 | 14 | render() { 15 | return html``; 16 | } 17 | 18 | static styles = css` 19 | :host { 20 | display: contents; 21 | } 22 | `; 23 | } 24 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/src/index.ts.hbs: -------------------------------------------------------------------------------- 1 | export * from './{{kebab_case app_name}}-store.js'; 2 | export * from './{{kebab_case app_name}}-client.js'; 3 | export * from './context.js'; 4 | export * from './types.js'; 5 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/src/mocks.ts.hbs: -------------------------------------------------------------------------------- 1 | import { 2 | AgentPubKeyMap, 3 | decodeEntry, 4 | fakeEntry, 5 | fakeCreateAction, 6 | fakeUpdateEntry, 7 | fakeDeleteEntry, 8 | fakeRecord, 9 | pickBy, 10 | ZomeMock, 11 | RecordBag, 12 | entryState, 13 | HoloHashMap, 14 | HashType, 15 | hash 16 | } from "@holochain-open-dev/utils"; 17 | import { 18 | decodeHashFromBase64, 19 | NewEntryAction, 20 | AgentPubKey, 21 | ActionHash, 22 | EntryHash, 23 | Delete, 24 | AppClient, 25 | fakeAgentPubKey, 26 | fakeDnaHash, 27 | Link, 28 | fakeActionHash, 29 | SignedActionHashed, 30 | fakeEntryHash, 31 | Record, 32 | } from "@holochain/client"; 33 | import { {{pascal_case app_name}}Client } from './{{kebab_case app_name}}-client.js' 34 | 35 | export class {{pascal_case app_name}}ZomeMock extends ZomeMock implements AppClient { 36 | constructor( 37 | myPubKey?: AgentPubKey 38 | ) { 39 | super("{{snake_case app_name}}_test", "{{snake_case app_name}}", myPubKey); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/src/types.ts.hbs: -------------------------------------------------------------------------------- 1 | import { 2 | Record, 3 | ActionHash, 4 | DnaHash, 5 | SignedActionHashed, 6 | EntryHash, 7 | AgentPubKey, 8 | Create, 9 | Update, 10 | Delete, 11 | CreateLink, 12 | DeleteLink 13 | } from '@holochain/client'; 14 | import { ActionCommittedSignal } from '@holochain-open-dev/utils'; 15 | 16 | export type {{pascal_case app_name}}Signal = ActionCommittedSignal; 17 | 18 | export type EntryTypes = never; 19 | 20 | export type LinkTypes = string; 21 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/src/{{kebab_case app_name}}-client.ts.hbs: -------------------------------------------------------------------------------- 1 | import { 2 | SignedActionHashed, 3 | CreateLink, 4 | Link, 5 | DeleteLink, 6 | Delete, 7 | AppClient, 8 | Record, 9 | ActionHash, 10 | EntryHash, 11 | AgentPubKey, 12 | } from '@holochain/client'; 13 | import { EntryRecord, ZomeClient } from '@holochain-open-dev/utils'; 14 | 15 | import { {{pascal_case app_name}}Signal } from './types.js'; 16 | 17 | export class {{pascal_case app_name}}Client extends ZomeClient<{{pascal_case app_name}}Signal> { 18 | 19 | constructor(public client: AppClient, public roleName: string, public zomeName = '{{snake_case app_name}}') { 20 | super(client, roleName, zomeName); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/src/{{kebab_case app_name}}-store.ts.hbs: -------------------------------------------------------------------------------- 1 | import { 2 | collectionSignal, 3 | liveLinksSignal, 4 | deletedLinksSignal, 5 | allRevisionsOfEntrySignal, 6 | latestVersionOfEntrySignal, 7 | immutableEntrySignal, 8 | deletesForEntrySignal, 9 | AsyncComputed, 10 | pipe, 11 | } from "@holochain-open-dev/signals"; 12 | import { slice, HashType, retype, EntryRecord, LazyHoloHashMap } from "@holochain-open-dev/utils"; 13 | import { NewEntryAction, Record, ActionHash, EntryHash, AgentPubKey } from '@holochain/client'; 14 | 15 | import { {{pascal_case app_name}}Client } from './{{kebab_case app_name}}-client.js'; 16 | 17 | export class {{pascal_case app_name}}Store { 18 | 19 | constructor(public client: {{pascal_case app_name}}Client) {} 20 | 21 | } 22 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/tsconfig.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /templates/zome/web-app/ui/vite.config.ts.hbs: -------------------------------------------------------------------------------- 1 | // vite.config.js 2 | import { defineConfig } from "vite"; 3 | 4 | export default defineConfig({ 5 | root: './demo', 6 | }); 7 | 8 | -------------------------------------------------------------------------------- /templates/zome/web-app/workdir/dna.nix.hbs: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | 3 | { 4 | perSystem = 5 | { inputs' 6 | , self' 7 | , lib 8 | , system 9 | , ... 10 | }: { 11 | packages.{{snake_case app_name}}_test_dna = inputs.hc-infra.outputs.builders.${system}.dna { 12 | dnaManifest = ./dna.yaml; 13 | zomes = { 14 | # Include here the zome packages for this DNA, e.g.: 15 | profiles_integrity = inputs'.profiles.packages.profiles_integrity; 16 | profiles = inputs'.profiles.packages.profiles; 17 | # This overrides all the "bundled" properties for the DNA manifest 18 | {{snake_case app_name}}_integrity = self'.packages.{{snake_case app_name}}_integrity; 19 | {{snake_case app_name}} = self'.packages.{{snake_case app_name}}; 20 | }; 21 | }; 22 | }; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /templates/zome/web-app/workdir/dna.yaml.hbs: -------------------------------------------------------------------------------- 1 | --- 2 | manifest_version: "1" 3 | name: {{snake_case app_name}}_test 4 | integrity: 5 | network_seed: ~ 6 | properties: ~ 7 | origin_time: 1676140846503210 8 | zomes: 9 | - name: {{snake_case app_name}}_integrity 10 | hash: ~ 11 | bundled: "../target/wasm32-unknown-unknown/release/{{snake_case app_name}}_integrity.wasm" 12 | dependencies: ~ 13 | - name: profiles_integrity 14 | hash: ~ 15 | bundled: 16 | dependencies: ~ 17 | coordinator: 18 | zomes: 19 | - name: {{snake_case app_name}} 20 | hash: ~ 21 | bundled: "../target/wasm32-unknown-unknown/release/{{snake_case app_name}}.wasm" 22 | dependencies: 23 | - name: {{snake_case app_name}}_integrity 24 | - name: profiles 25 | hash: ~ 26 | bundled: 27 | dependencies: 28 | - name: profiles_integrity 29 | -------------------------------------------------------------------------------- /templates/zome/web-app/workdir/happ.nix.hbs: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | 3 | { 4 | perSystem = 5 | { inputs' 6 | , lib 7 | , self' 8 | , system 9 | , ... 10 | }: { 11 | packages.{{snake_case app_name}}_test_happ = inputs.hc-infra.outputs.builders.${system}.happ { 12 | happManifest = ./happ.yaml; 13 | 14 | dnas = { 15 | # Include here the DNA packages for this hApp, e.g.: 16 | # my_dna = inputs'.some_input.packages.my_dna; 17 | # This overrides all the "bundled" properties for the hApp manifest 18 | {{snake_case app_name}}_test = self'.packages.{{snake_case app_name}}_test_dna; 19 | }; 20 | }; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /templates/zome/web-app/workdir/happ.yaml.hbs: -------------------------------------------------------------------------------- 1 | --- 2 | manifest_version: "1" 3 | name: {{app_name}}_test 4 | description: ~ 5 | roles: 6 | - name: {{snake_case app_name}}_test 7 | provisioning: 8 | strategy: create 9 | deferred: false 10 | dna: 11 | bundled: "./{{snake_case app_name}}_test.dna" 12 | modifiers: 13 | network_seed: ~ 14 | properties: ~ 15 | origin_time: ~ 16 | version: ~ 17 | clone_limit: 0 18 | -------------------------------------------------------------------------------- /templates/zome/web-app/zomes/coordinator/{{snake_case app_name}}/Cargo.toml.hbs: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{snake_case app_name}}" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib", "rlib"] 8 | name = "{{snake_case app_name}}" 9 | 10 | [dependencies] 11 | hdk = { workspace = true } 12 | 13 | serde = { workspace = true } 14 | 15 | {{snake_case app_name}}_integrity = { path = "../../integrity/{{snake_case app_name}}", package = "{{snake_case app_name}}_integrity" } 16 | 17 | [dev-dependencies] 18 | fixt = "0.3.1" 19 | futures = { version = "0.3.1", default-features = false } 20 | hdk = { workspace = true, features = ["encoding", "test_utils"] } 21 | holochain = { workspace = true } 22 | tokio = { version = "1.3", features = ["full"] } 23 | 24 | -------------------------------------------------------------------------------- /templates/zome/web-app/zomes/coordinator/{{snake_case app_name}}/src/lib.rs.hbs: -------------------------------------------------------------------------------- 1 | use hdk::prelude::*; 2 | use {{snake_case app_name}}_integrity::*; 3 | 4 | #[hdk_extern] 5 | pub fn init(_: ()) -> ExternResult { 6 | Ok(InitCallbackResult::Pass) 7 | } 8 | 9 | #[derive(Serialize, Deserialize, Debug)] 10 | #[serde(tag = "type")] 11 | pub enum Signal {} 12 | 13 | #[hdk_extern(infallible)] 14 | pub fn post_commit(committed_actions: Vec) { 15 | for action in committed_actions { 16 | if let Err(err) = signal_action(action) { 17 | error!("Error signaling new action: {:?}", err); 18 | } 19 | } 20 | } 21 | fn signal_action(action: SignedActionHashed) -> ExternResult<()> { 22 | Ok(()) 23 | } 24 | -------------------------------------------------------------------------------- /templates/zome/web-app/zomes/coordinator/{{snake_case app_name}}/tests/common/mod.rs.hbs: -------------------------------------------------------------------------------- 1 | use hdk::prelude::*; 2 | use holochain::sweettest::*; 3 | 4 | use {{snake_case app_name}}_integrity::*; 5 | 6 | -------------------------------------------------------------------------------- /templates/zome/web-app/zomes/coordinator/{{snake_case app_name}}/zome.nix.hbs: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | 3 | { 4 | perSystem = 5 | { inputs' 6 | , system 7 | , self' 8 | , ... 9 | }: rec { 10 | packages.{{snake_case app_name}} = inputs.hc-infra.outputs.builders.${system}.rustZome { 11 | workspacePath = inputs.self.outPath; 12 | crateCargoToml = ./Cargo.toml; 13 | cargoArtifacts = inputs'.hc-infra.packages.zomeCargoArtifacts; 14 | }; 15 | 16 | # Test only this zome and its integrity in isolation 17 | checks.{{snake_case app_name}} = inputs.hc-infra.outputs.builders.${system}.sweettest { 18 | workspacePath = inputs.self.outPath; 19 | dna = (inputs.hc-infra.outputs.builders.${system}.dna { 20 | dnaManifest = builtins.toFile "dna.yaml" '' 21 | --- 22 | manifest_version: "1" 23 | name: test_dna 24 | integrity: 25 | network_seed: ~ 26 | properties: ~ 27 | origin_time: 1709638576394039 28 | zomes: 29 | - name: {{snake_case app_name}}_integrity 30 | coordinator: 31 | zomes: 32 | - name: {{snake_case app_name}} 33 | hash: ~ 34 | dependencies: 35 | - name: {{snake_case app_name}}_integrity 36 | dylib: ~ 37 | ''; 38 | zomes = { 39 | {{snake_case app_name}}_integrity = self'.packages.{{snake_case app_name}}_integrity; 40 | {{snake_case app_name}} = self'.packages.{{snake_case app_name}}; 41 | }; 42 | }).meta.debug; 43 | crateCargoToml = ./Cargo.toml; 44 | cargoArtifacts = inputs'.hc-infra.packages.holochainCargoArtifacts; 45 | }; 46 | 47 | }; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /templates/zome/web-app/zomes/integrity/{{snake_case app_name}}/Cargo.toml.hbs: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{snake_case app_name}}_integrity" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib", "rlib"] 8 | name = "{{snake_case app_name}}_integrity" 9 | 10 | [dependencies] 11 | hdi = { workspace = true } 12 | 13 | serde = { workspace = true } 14 | -------------------------------------------------------------------------------- /templates/zome/web-app/zomes/integrity/{{snake_case app_name}}/zome.nix.hbs: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | 3 | { 4 | perSystem = 5 | { inputs' 6 | , system 7 | , ... 8 | }: { 9 | packages.{{snake_case app_name}}_integrity = inputs.hc-infra.outputs.builders.${system}.rustZome { 10 | workspacePath = inputs.self.outPath; 11 | crateCargoToml = ./Cargo.toml; 12 | cargoArtifacts = inputs'.hc-infra.packages.zomeCargoArtifacts; 13 | }; 14 | }; 15 | } 16 | 17 | --------------------------------------------------------------------------------