hecks 3.10.0 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/examples/pizzas/bluebook/pizzas.bluebook +29 -0
- data/examples/pizzas/bluebook/pizzas.hecksagon +7 -0
- data/exe/hecks +4 -4
- data/lib/hecks/adapters/driven/d1.rb +2 -10
- data/lib/hecks/adapters/driven/heki.adapter +1 -1
- data/lib/hecks/adapters/driven/heki.rb +2 -12
- data/lib/hecks/adapters/driven/lambda.adapter +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +3 -18
- data/lib/hecks/adapters/driven/postgres.rb +4 -11
- data/lib/hecks/adapters/driven/sqlite.adapter +1 -1
- data/lib/hecks/adapters/driven/sqlite.rb +2 -10
- data/lib/hecks/adapters/driving/admission.rb +46 -0
- data/lib/hecks/adapters/driving/cli/nesting.rb +36 -0
- data/lib/hecks/adapters/driving/cli/shorthand.rb +88 -0
- data/lib/hecks/adapters/driving/cli.rb +127 -0
- data/lib/hecks/adapters/driving/cli_runner/answers.rb +160 -0
- data/lib/hecks/adapters/driving/cli_runner/resolution.rb +155 -0
- data/lib/hecks/adapters/driving/cli_runner/settling.rb +120 -0
- data/lib/hecks/adapters/driving/cli_runner/streaming.rb +93 -0
- data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +47 -0
- data/lib/hecks/adapters/driving/cli_runner.rb +69 -0
- data/lib/hecks/adapters/driving/command_request.rb +121 -0
- data/lib/hecks/adapters/driving/handle.rb +214 -0
- data/lib/hecks/adapters/driving/json.rb +136 -0
- data/lib/hecks/adapters/driving/launcher_options.rb +267 -0
- data/lib/hecks/adapters/driving/mcp/properties.rb +49 -0
- data/lib/hecks/adapters/driving/mcp/tool_calls.rb +116 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/auditing.rb +53 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/calling.rb +71 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/reading.rb +99 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions.rb +18 -0
- data/lib/hecks/adapters/driving/mcp.rb +159 -0
- data/lib/hecks/adapters/driving/mcp_guide_cache.rb +78 -0
- data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +97 -0
- data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +69 -0
- data/lib/hecks/adapters/driving/mcp_scope/presenter.rb +83 -0
- data/lib/hecks/adapters/driving/mcp_scope.rb +213 -0
- data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
- data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
- data/lib/hecks/adapters/driving/ruby.rb +83 -0
- data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
- data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
- data/lib/hecks/behaviors/expectations/boot.rb +1 -1
- data/lib/hecks/bench/ruby_runner.rb +1 -1
- data/lib/hecks/bluebook/ask_resolution.rb +130 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
- data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
- data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
- data/lib/hecks/bluebook/assembly/marks.rb +1 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/command.rb +13 -8
- data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
- data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
- data/lib/hecks/bluebook/capabilities.rb +91 -9
- data/lib/hecks/bluebook/domain_port.rb +16 -4
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
- data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
- data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
- data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
- data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
- data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
- data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
- data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
- data/lib/hecks/bluebook/expression/projection.json +36 -0
- data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
- data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
- data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
- data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
- data/lib/hecks/bluebook/expression/resolver.rb +20 -1
- data/lib/hecks/bluebook/hecksagon.rb +23 -3
- data/lib/hecks/bluebook/lifecycle.rb +22 -3
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
- data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/readings.rb +18 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
- data/lib/hecks/bluebook/meta_validator.rb +12 -12
- data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
- data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
- data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
- data/lib/hecks/bluebook/model_check.rb +8 -2
- data/lib/hecks/bluebook/policy.rb +4 -2
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- data/lib/hecks/bluebook/synthesizer.rb +2 -0
- data/lib/hecks/bluebook.rb +1 -0
- data/lib/hecks/cli/command_table.rb +1 -1
- data/lib/hecks/cli/console.rb +2 -1
- data/lib/hecks/cli/document.rb +1 -1
- data/lib/hecks/cli/launcher_words.rb +4 -4
- data/lib/hecks/cli/mcp.rb +4 -4
- data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
- data/lib/hecks/cli/project_cli/templates.rb +5 -5
- data/lib/hecks/cli/project_cli.rb +9 -6
- data/lib/hecks/cli/run.rb +2 -2
- data/lib/hecks/cli/smoke_http/checks.rb +4 -1
- data/lib/hecks/cli/smoke_http/transport.rb +3 -3
- data/lib/hecks/cli/smoke_http.rb +3 -0
- data/lib/hecks/codemod.rb +5 -2
- data/lib/hecks/corpus/routes.rb +7 -0
- data/lib/hecks/corpus/rust.rb +3 -2
- data/lib/hecks/deploy/bluebook/deploy.bluebook +18 -2
- data/lib/hecks/doc/reference/rendering.rb +6 -3
- data/lib/hecks/forms/app/commands.rb +8 -7
- data/lib/hecks/forms/app/queries.rb +2 -1
- data/lib/hecks/forms/app.rb +1 -1
- data/lib/hecks/forms/record_renderer.rb +1 -1
- data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
- data/lib/hecks/fuzzing/failure_triage.rb +2 -1
- data/lib/hecks/fuzzing/mutation/run.rb +11 -4
- data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
- data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
- data/lib/hecks/fuzzing/properties/guards.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency.rb +6 -2
- data/lib/hecks/fuzzing/shrinker.rb +3 -1
- data/lib/hecks/fuzzing/value_generator.rb +19 -5
- data/lib/hecks/gate/stages.yml +4 -10
- data/lib/hecks/grammar/evolve.rb +1 -1
- data/lib/hecks/grammar/expression_operators.json +372 -0
- data/lib/hecks/grammar/operators.rb +3 -1
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
- data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
- data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
- data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
- data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
- data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
- data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
- data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
- data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
- data/lib/hecks/hecks/adapters/local_files.rb +2 -2
- data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
- data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
- data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
- data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +129 -0
- data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
- data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
- data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
- data/lib/hecks/hecks/adapters/terminal.rb +13 -11
- data/lib/hecks/hecks/codebase.behaviors +13 -1
- data/lib/hecks/hecks/codebase.bluebook +239 -5
- data/lib/hecks/hecks/custodian.behaviors +24 -6
- data/lib/hecks/hecks/custodian.bluebook +6 -1227
- data/lib/hecks/hecks/deploy.behaviors +1 -1
- data/lib/hecks/hecks/deploy.world +1 -3
- data/lib/hecks/hecks/hecks.behaviors +1 -1
- data/lib/hecks/hecks/hecks.hecksagon +53 -18
- data/lib/hecks/hecks/hecks.world +10 -10
- data/lib/hecks/hecks/rehearsal.bluebook +477 -0
- data/lib/hecks/hecks/site.behaviors +1 -1
- data/lib/hecks/hecks/site.world +0 -1
- data/lib/hecks/hecks/tooling.bluebook +1238 -0
- data/lib/hecks/indifferent_key.rb +20 -0
- data/lib/hecks/language/adapter.bluebook +13 -3
- data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
- data/lib/hecks/language/bluebook/entity.bluebook +18 -0
- data/lib/hecks/language/bluebook/policy.bluebook +9 -1
- data/lib/hecks/language/bluebook/query.bluebook +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +11 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
- data/lib/hecks/language/oidc.json +10 -0
- data/lib/hecks/language/port.bluebook +4 -4
- data/lib/hecks/literal.rb +12 -3
- data/lib/hecks/ports/key_vault.rb +7 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
- data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +29 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/shared.rb +5 -2
- data/lib/hecks/projections/model/deviations.rb +2 -2
- data/lib/hecks/projections/model.rb +2 -2
- data/lib/hecks/projections/oidc.rb +13 -1
- data/lib/hecks/projections/shape.rb +5 -2
- data/lib/hecks/projections/site/cms_editor/attributes.rb +26 -6
- data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
- data/lib/hecks/projections/site/cms_editor/checks.rb +14 -1
- data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
- data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
- data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
- data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
- data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
- data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
- data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
- data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
- data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
- data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
- data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
- data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
- data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
- data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
- data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
- data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
- data/lib/hecks/projections/site/cms_editor/schema.rb +96 -19
- data/lib/hecks/projections/site/cms_editor/setting.rb +52 -9
- data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +18 -1
- data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +251 -57
- data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +32 -24
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +78 -24
- data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +6 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +19 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +31 -8
- data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +131 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +8 -5
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +229 -34
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +40 -81
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +29 -12
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +53 -17
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +178 -88
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
- data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +2 -1
- data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
- data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
- data/lib/hecks/projections/site/cms_editor.rb +49 -23
- data/lib/hecks/projections/site/role_probe.rb +2 -0
- data/lib/hecks/projector/cli_audience.rb +2 -1
- data/lib/hecks/projector/cli_projector/listing.rb +1 -1
- data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
- data/lib/hecks/projector/docs_projector/closing.rb +1 -1
- data/lib/hecks/projector/docs_projector.rb +1 -1
- data/lib/hecks/projector/exporter/bindings.rb +40 -10
- data/lib/hecks/projector/exporter/capability_values.rb +55 -0
- data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
- data/lib/hecks/projector/target.rb +11 -0
- data/lib/hecks/projector.rb +32 -5
- data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
- data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
- data/lib/hecks/quality_control/quality_control.bluebook +6 -2
- data/lib/hecks/release/approval.rb +64 -0
- data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
- data/lib/hecks/router/namespace_installer.rb +16 -16
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/best_effort.rb +22 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
- data/lib/hecks/runtime/command_interpreter.rb +3 -2
- data/lib/hecks/runtime/command_rules/emission.rb +1 -1
- data/lib/hecks/runtime/dispatcher.rb +4 -4
- data/lib/hecks/runtime/entity_element/location.rb +1 -1
- data/lib/hecks/runtime/event.rb +5 -0
- data/lib/hecks/runtime/identity.rb +7 -6
- data/lib/hecks/runtime/interpreting.rb +2 -1
- data/lib/hecks/runtime/invocation_builder.rb +1 -1
- data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
- data/lib/hecks/runtime/loader.rb +10 -10
- data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
- data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
- data/lib/hecks/runtime/policy_interpreter.rb +6 -6
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
- data/lib/hecks/runtime/reaction_invocation.rb +1 -1
- data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
- data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
- data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
- data/lib/hecks/runtime/registry/merging.rb +1 -1
- data/lib/hecks/runtime/registry/providers.rb +16 -0
- data/lib/hecks/runtime/registry/read_repository.rb +6 -4
- data/lib/hecks/runtime/registry/verification.rb +4 -1
- data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
- data/lib/hecks/runtime/registry.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
- data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter.rb +3 -3
- data/lib/hecks/runtime/tenant_check.rb +5 -4
- data/lib/hecks/runtime/value/coercion.rb +2 -2
- data/lib/hecks/runtime/value/validation.rb +16 -4
- data/lib/hecks/runtime.rb +9 -9
- data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
- data/lib/hecks/rust_build/wasm.rb +1 -1
- data/lib/hecks/rust_build/wasm_browser.rb +1 -1
- data/lib/hecks/rust_build.rb +11 -0
- data/lib/hecks/served_options.rb +53 -0
- data/lib/hecks/site/bluebook/site.bluebook +1 -0
- data/lib/hecks/storehouse/audit.rb +1 -1
- data/lib/hecks/storehouse/dispatching.rb +8 -6
- data/lib/hecks/storehouse/introspection.rb +2 -2
- data/lib/hecks/storehouse/querying.rb +6 -4
- data/lib/hecks/storehouse.rb +4 -4
- data/lib/hecks/tools/comment_style/source_file.rb +4 -1
- data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
- data/lib/hecks/tools/lanes/rows.rb +6 -0
- data/lib/hecks/tools/oidc_manifests.rb +4 -5
- data/lib/hecks/tools/regeneration_run.rb +11 -4
- data/lib/hecks/tools/site_routes/editor_files.rb +12 -5
- data/lib/hecks/tools/tenant_provisioning.rb +2 -2
- data/lib/hecks/tools/tools_doc.rb +4 -4
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +4 -4
- data/lib/hecks.rb +15 -15
- data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
- data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
- data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
- data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
- data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
- data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
- data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
- data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
- data/prebuilt/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
- data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
- data/prebuilt/verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json +1 -0
- data/rust/.gitignore +6 -0
- data/rust/Cargo.toml +6 -0
- data/rust/build/src/binding_pass.rs +103 -0
- data/rust/build/src/cargo_sync.rs +6 -0
- data/rust/build/src/lineage_pass.rs +3 -3
- data/rust/build/src/main.rs +17 -5
- data/rust/build/src/pipeline.rs +48 -5
- data/rust/build/src/seams_pass.rs +123 -0
- data/rust/build/src/translation_pass.rs +167 -0
- data/rust/codegen/src/asks.rs +103 -0
- data/rust/codegen/src/commands.rs +13 -8
- data/rust/codegen/src/constraints.rs +25 -0
- data/rust/codegen/src/domain_generator.rs +2 -0
- data/rust/codegen/src/expr_emitter.rs +13 -0
- data/rust/codegen/src/json_codec.rs +54 -15
- data/rust/codegen/src/main.rs +1 -0
- data/rust/codegen/src/mutations.rs +128 -3
- data/rust/codegen/src/queries.rs +11 -2
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/types.rs +166 -8
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +4 -4
- data/rust/host/src/bin/validate_harness.rs +43 -0
- data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
- data/rust/host/src/boundary_fuzz/ir.rs +1 -0
- data/rust/host/src/boundary_fuzz/server.rs +1 -1
- data/rust/host/src/checkout.rs +35 -19
- data/rust/host/src/commerce_ir.rs +408 -2
- data/rust/host/src/expr_json.rs +118 -0
- data/rust/host/src/mint.rs +1 -1
- data/rust/host/src/payments/tests.rs +27 -1
- data/rust/host/src/payments.rs +12 -4
- data/rust/host/src/query_step.rs +22 -4
- data/rust/host/src/reference_validate.rs +653 -108
- data/rust/host/src/resend.rs +2 -0
- data/rust/host/src/server.rs +19 -6
- data/rust/host/src/ui_schema.rs +132 -5
- data/rust/host/src/web/newsletter.rs +7 -8
- data/rust/host/src/web/newsletter_send.rs +38 -13
- data/rust/host/src/web/registrations.rs +229 -26
- data/rust/host/src/web.rs +3 -3
- data/rust/parser/src/emit.rs +38 -6
- data/rust/parser/src/expr/ast_json.rs +7 -1
- data/rust/parser/src/expr/resolver.rs +171 -6
- data/rust/parser/src/ir.rs +7 -0
- data/rust/parser/src/keywords.rs +24 -0
- data/rust/parser/src/main.rs +3 -0
- data/rust/parser/src/parse/aggregate.rs +2 -1
- data/rust/parser/src/parse/chapter.rs +2 -2
- data/rust/parser/src/parse/command.rs +2 -0
- data/rust/parser/src/parse/hecksagon.rs +5 -2
- data/rust/parser/src/parse/lifecycle.rs +81 -0
- data/rust/parser/src/parse/mod.rs +15 -1
- data/rust/parser/src/parse/policy.rs +13 -2
- data/rust/parser/src/parse/value_object.rs +13 -8
- data/rust/parser/src/ruby_value.rs +1 -1
- data/rust/src/exemplar/commands.rs +4 -4
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/json.rs +43 -1
- data/rust/src/kernel/cli.rs +7 -5
- data/rust/src/kernel/dispatch.rs +2 -2
- data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
- data/rust/src/kernel/expr.rs +66 -3
- data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
- data/rust/src/kernel/expression_operators/text.rs +26 -2
- data/rust/src/kernel/json.rs +15 -0
- data/rust/src/kernel/mod.rs +1 -1
- data/rust/src/kernel/orchestrate.rs +54 -5
- data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
- data/rust/src/kernel/pattern.rs +60 -0
- data/rust/src/kernel/reference_lookup.rs +90 -7
- metadata +140 -38
- data/lib/hecks/doors/cli_door/nesting.rb +0 -34
- data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
- data/lib/hecks/doors/cli_door.rb +0 -124
- data/lib/hecks/doors/cli_runner/answers.rb +0 -138
- data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
- data/lib/hecks/doors/cli_runner/settling.rb +0 -115
- data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
- data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
- data/lib/hecks/doors/cli_runner.rb +0 -66
- data/lib/hecks/doors/command_request.rb +0 -119
- data/lib/hecks/doors/handle.rb +0 -208
- data/lib/hecks/doors/json_door.rb +0 -132
- data/lib/hecks/doors/launcher_options.rb +0 -247
- data/lib/hecks/doors/mcp_door/properties.rb +0 -47
- data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
- data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
- data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
- data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
- data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
- data/lib/hecks/doors/mcp_door.rb +0 -156
- data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
- data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
- data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
- data/lib/hecks/doors/mcp_door_scope.rb +0 -201
- data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
- data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
- data/lib/hecks/doors/ruby_door.rb +0 -80
- data/lib/hecks/doors/usage_cache.rb +0 -139
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_widget.js.tmpl +0 -282
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/media_picker.js.tmpl +0 -109
- data/prebuilt/c28d6d9db14eea3e17161e9d9c262efee30b256cf97693a7cfd552bce24b2f7f.marshal +0 -0
- data/prebuilt/cb375cba61ca0dbe8ddb9739c306510fe05e5f4b7a9b6955caff8224c63ea0e0.marshal +0 -0
- data/prebuilt/verdicts-d6d584ad43b5f3cebf88aafbbdbf024c38574acf18e06adffbc555f7786b1c2f.json +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
module Site
|
|
6
|
+
module CmsEditor
|
|
7
|
+
# Which commands the editor asks a person to confirm before it runs them, and what colour
|
|
8
|
+
# each lifecycle state's badge has.
|
|
9
|
+
#
|
|
10
|
+
# A command is destructive when its first word is one of `WORDS` (`WithdrawOffer`,
|
|
11
|
+
# `DiscardDraft`), or when it is a lifecycle move into a state named for one of `STATES`.
|
|
12
|
+
# The test reads the bluebook's own words and structure, never a list of aggregates, so a
|
|
13
|
+
# domain with a different vocabulary confirms nothing it did not name that way. A move that
|
|
14
|
+
# can be undone, such as an archive with a restore, is not destructive by that alone.
|
|
15
|
+
#
|
|
16
|
+
# A state's tone is structural: the starting state is `neutral`, a state named for a
|
|
17
|
+
# destructive word is `danger`, and the others alternate `ok` and `info` in the order the
|
|
18
|
+
# moves reach them. The badge always carries the state's name; tone is never the only cue.
|
|
19
|
+
module Destructive
|
|
20
|
+
# The first words of a command that undoes or ends something.
|
|
21
|
+
WORDS = %w[withdraw retire discard delete cancel remove].freeze
|
|
22
|
+
|
|
23
|
+
# The states those commands move into.
|
|
24
|
+
STATES = %w[withdrawn retired discarded deleted cancelled canceled removed].freeze
|
|
25
|
+
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
# @param name [String] the command's name, such as `"DiscardDraft"`
|
|
29
|
+
# @param lifecycle [Hash{String => Object}, nil] the lifecycle as `Schema` reads it
|
|
30
|
+
# @return [Boolean] whether the editor confirms the command first
|
|
31
|
+
def command?(name, lifecycle)
|
|
32
|
+
return true if WORDS.include?(words(name).first)
|
|
33
|
+
|
|
34
|
+
move = lifecycle && lifecycle["transitions"].find { |transition| transition["verb"] == name }
|
|
35
|
+
!move.nil? && STATES.include?(move["to"].downcase)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @param lifecycle [Hash{String => Object}, nil] the lifecycle as `Schema` reads it
|
|
39
|
+
# @return [Hash{String => String}] each state's tone, empty without a lifecycle
|
|
40
|
+
def tones(lifecycle)
|
|
41
|
+
return {} unless lifecycle
|
|
42
|
+
|
|
43
|
+
states = [lifecycle["default"], *lifecycle["transitions"].flat_map { |move| [*move["from"], move["to"]] }].uniq
|
|
44
|
+
cycle = %w[ok info].cycle
|
|
45
|
+
states.to_h { |state| [state, tone(state, lifecycle["default"], cycle)] }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [String] the tone of one state; `cycle` hands out `ok` and `info` in turn
|
|
49
|
+
def tone(state, default, cycle)
|
|
50
|
+
return "neutral" if state == default
|
|
51
|
+
|
|
52
|
+
STATES.include?(state.downcase) ? "danger" : cycle.next
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @return [Array<String>] the lower-case words of a CamelCase or snake_case name
|
|
56
|
+
def words(name) = name.to_s.scan(/[A-Z][a-z0-9]*|[a-z0-9]+/).map(&:downcase)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "clearing"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Projections
|
|
7
|
+
module Site
|
|
8
|
+
module CmsEditor
|
|
9
|
+
# An aggregate that keeps a draft of its content beside the live one: the editor writes the
|
|
10
|
+
# draft as the person types and publishes it only when asked.
|
|
11
|
+
#
|
|
12
|
+
# A command on the aggregate whose every argument is `draft_<x>` (the aggregate holds
|
|
13
|
+
# both `draft_<x>` and `<x>`, of one type, and some `<x>` is a rich-text body, a block list
|
|
14
|
+
# or a value object, alone or in a list) saves the draft. A command with a
|
|
15
|
+
# `sets <x>, to: state(:draft_<x>)` mutation publishes it; one that only empties
|
|
16
|
+
# `draft_<x>` (see `Clearing`) discards it. A missing publishing or discarding command is
|
|
17
|
+
# left out.
|
|
18
|
+
module Drafts
|
|
19
|
+
# What a draft attribute's name starts with.
|
|
20
|
+
PREFIX = "draft_"
|
|
21
|
+
|
|
22
|
+
module_function
|
|
23
|
+
|
|
24
|
+
# @param agg [Bluebook::Aggregate] the aggregate
|
|
25
|
+
# @param attributes [Array<Hash{String => Object}>] its attributes as `Schema` shapes
|
|
26
|
+
# them
|
|
27
|
+
# @return [Hash{String => String}, nil] `attribute` (the draft), `live`, `save`, and
|
|
28
|
+
# `publish` and `discard` when the aggregate has them; nil when it keeps no drafts
|
|
29
|
+
def of(agg, attributes)
|
|
30
|
+
found = agg.commands.lazy.filter_map { |command| saving(command, agg, attributes) }.first
|
|
31
|
+
return nil unless found
|
|
32
|
+
|
|
33
|
+
found.merge("publish" => publishing(agg, found), "discard" => discarding(agg, found)).compact
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [Hash{String => String}, nil] the draft and live attributes `command` saves
|
|
37
|
+
def saving(command, agg, attributes)
|
|
38
|
+
return nil unless acts_on?(command, agg) && !command.attributes.empty?
|
|
39
|
+
|
|
40
|
+
lives = command.attributes.map { |argument| live_of(argument, agg) }
|
|
41
|
+
live = lives.all? ? lives.find { |name| draftable?(name, attributes) } : nil
|
|
42
|
+
live && { "attribute" => "#{PREFIX}#{live}", "live" => live, "save" => command.hecks_name }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [Boolean] whether the command acts on an existing instance of the aggregate
|
|
46
|
+
def acts_on?(command, agg) = !command.creates? && command.references.to_s == agg.hecks_name
|
|
47
|
+
|
|
48
|
+
# @return [String, nil] the live attribute `argument` is the draft of, when it is one
|
|
49
|
+
def live_of(argument, agg)
|
|
50
|
+
name = argument.name.to_s
|
|
51
|
+
live = name.delete_prefix(PREFIX)
|
|
52
|
+
return nil unless name.start_with?(PREFIX) && same_type?(agg, name, live) && agg.attributes.any? do |held|
|
|
53
|
+
held.name.to_s == name
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
live
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @return [Boolean] whether the aggregate holds both attributes, of one type
|
|
60
|
+
def same_type?(agg, draft, live)
|
|
61
|
+
types = [draft, live].map { |name| agg.attributes.find { |held| held.name.to_s == name }&.type.to_s }
|
|
62
|
+
types.none?(&:empty?) && types.uniq.size == 1
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @return [Boolean] whether the named attribute is a rich-text body, a block list or a
|
|
66
|
+
# value object (a page's fixed slots), alone or in a list
|
|
67
|
+
def draftable?(name, attributes)
|
|
68
|
+
attributes.any? do |attr|
|
|
69
|
+
attr["name"] == name && (attr["widget"] == "body" || attr.key?("blockList") || attr["kind"] == "object")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @return [String, nil] the command that moves the draft into the live attribute
|
|
74
|
+
def publishing(agg, found)
|
|
75
|
+
agg.commands.find { |command| acts_on?(command, agg) && promotes?(command, found) }&.hecks_name
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @return [String, nil] the command that empties the draft and changes nothing live
|
|
79
|
+
def discarding(agg, found)
|
|
80
|
+
agg.commands.find { |command| acts_on?(command, agg) && empties?(command, agg, found) }&.hecks_name
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def promotes?(command, found)
|
|
84
|
+
command.mutations.any? do |mutation|
|
|
85
|
+
mutation.target.to_s == found["live"] && mutation.source.is_a?(Hecks::StateRef) &&
|
|
86
|
+
mutation.source.name.to_s == found["attribute"]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def empties?(command, agg, found)
|
|
91
|
+
return false if command.hecks_name == found["save"] || targets_of(command) != [found["attribute"]]
|
|
92
|
+
|
|
93
|
+
clears = Clearing.of(command, agg).map { |argument| argument.name.to_s }
|
|
94
|
+
command.mutations.all? { |mutation| clears.include?(mutation.source.to_s) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def targets_of(command) = command.mutations.map { |mutation| mutation.target.to_s }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "key_kinds"
|
|
4
|
+
require_relative "moment"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module Projections
|
|
8
|
+
module Site
|
|
9
|
+
module CmsEditor
|
|
10
|
+
# What one attribute's names, pattern and closed set say about how a value of it is
|
|
11
|
+
# entered: a moment (see `Moment`), a `<kind>:<slug>` key (see `KeyKinds`) or one of a
|
|
12
|
+
# closed set's members. A value object with a single plain part is entered as that part,
|
|
13
|
+
# so the part's pattern and the value object's names count; a value object of several
|
|
14
|
+
# parts has parts of its own, each read the same way.
|
|
15
|
+
class Hints
|
|
16
|
+
# @param objects [Hash{String => Bluebook::ValueObject}] the value objects by name
|
|
17
|
+
def initialize(objects)
|
|
18
|
+
@objects = objects
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @param attribute [Bluebook::Attribute] a declared attribute
|
|
22
|
+
# @return [Hash{String => Object}] `widget`, `keys` and `options` when the attribute has
|
|
23
|
+
# them
|
|
24
|
+
def of(attribute)
|
|
25
|
+
leaf, names = leaf(attribute)
|
|
26
|
+
return {} unless leaf
|
|
27
|
+
|
|
28
|
+
{ **moment(leaf, names), **keys(leaf), **options(attribute, leaf) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# @return [Array(Bluebook::Attribute, Array<String>), nil] the attribute that holds the
|
|
34
|
+
# value and the names it goes by, or nil for a reference or a value object of parts
|
|
35
|
+
def leaf(attribute)
|
|
36
|
+
return nil if attribute.type.is_a?(Bluebook::Reference)
|
|
37
|
+
|
|
38
|
+
object = @objects[attribute.type.to_s]
|
|
39
|
+
return [attribute, [attribute.name.to_s]] unless object
|
|
40
|
+
|
|
41
|
+
part = plain_part(object)
|
|
42
|
+
part && [part, [attribute.name.to_s, attribute.type.to_s, part.name.to_s]]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [Bluebook::Attribute, nil] the one part of a value object that is a plain value
|
|
46
|
+
def plain_part(object)
|
|
47
|
+
part = object.attributes.first
|
|
48
|
+
part if object.attributes.size == 1 && !part.list? && !@objects.key?(part.type.to_s)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def moment(leaf, names)
|
|
52
|
+
widget = Moment.widget(names) if leaf.type.to_s == "Integer"
|
|
53
|
+
widget ? { "widget" => widget } : {}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# A value object declared a closed set (`one_of`) is entered by choosing one of its
|
|
57
|
+
# members.
|
|
58
|
+
def options(attribute, leaf)
|
|
59
|
+
object = @objects[attribute.type.to_s]
|
|
60
|
+
return {} unless object&.closed_set?
|
|
61
|
+
|
|
62
|
+
members = object.members.filter_map { |member| member[leaf.name] }.uniq
|
|
63
|
+
members.empty? ? {} : { "options" => members.map(&:to_s) }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def keys(leaf)
|
|
67
|
+
kinds = KeyKinds.of(leaf.pattern)
|
|
68
|
+
kinds.empty? ? {} : { "keys" => kinds }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
module Site
|
|
6
|
+
module CmsEditor
|
|
7
|
+
# The editor's icons, drawn once here and emitted as one vector sprite
|
|
8
|
+
# (`src/ui/icons.ts`). Pages and the browser code refer to an icon by name with
|
|
9
|
+
# `<svg><use href="#i-name"/></svg>`, so there is no icon font, no request and no external
|
|
10
|
+
# file. Each is a 24 by 24 outline drawn with the current text colour.
|
|
11
|
+
module Icons
|
|
12
|
+
# Each icon's name and the drawing elements inside its symbol.
|
|
13
|
+
DRAWINGS = {
|
|
14
|
+
"menu" => '<path d="M4 7h16M4 12h16M4 17h16"/>',
|
|
15
|
+
"close" => '<path d="M6 6l12 12M18 6L6 18"/>',
|
|
16
|
+
"sun" => '<circle cx="12" cy="12" r="4"/>' \
|
|
17
|
+
'<path d="M12 3v2M12 19v2M3 12h2M19 12h2M5.6 5.6L7 7M17 17l1.4 1.4M5.6 18.4L7 17M17 7l1.4-1.4"/>',
|
|
18
|
+
"moon" => '<path d="M20 14.5A8 8 0 019.5 4 8 8 0 1020 14.5z"/>',
|
|
19
|
+
"sign-out" => '<path d="M10 4H5v16h5M15 8l4 4-4 4M19 12H9"/>',
|
|
20
|
+
"search" => '<circle cx="11" cy="11" r="6"/><path d="M16 16l4 4"/>',
|
|
21
|
+
"plus" => '<path d="M12 5v14M5 12h14"/>',
|
|
22
|
+
"copy" => '<rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V6a2 2 0 012-2h8"/>',
|
|
23
|
+
"check" => '<path d="M5 12.5l4.5 4.5L19 7.5"/>',
|
|
24
|
+
"chevron-up" => '<path d="M6 14l6-6 6 6"/>',
|
|
25
|
+
"chevron-down" => '<path d="M6 10l6 6 6-6"/>',
|
|
26
|
+
"chevron-right" => '<path d="M10 6l6 6-6 6"/>',
|
|
27
|
+
"arrow-up" => '<path d="M12 19V5M6 11l6-6 6 6"/>',
|
|
28
|
+
"arrow-down" => '<path d="M12 5v14M6 13l6 6 6-6"/>',
|
|
29
|
+
"sort" => '<path d="M8 9l4-4 4 4M8 15l4 4 4-4"/>',
|
|
30
|
+
"trash" => '<path d="M5 7h14M10 7V4h4v3M7 7l1 13h8l1-13M10 11v6M14 11v6"/>',
|
|
31
|
+
"pencil" => '<path d="M5 19l1-4L16 5l3 3L9 18l-4 1zM14 7l3 3"/>',
|
|
32
|
+
"bold" => '<path d="M7 5h6a3.5 3.5 0 010 7H7zM7 12h7a3.5 3.5 0 010 7H7z"/>',
|
|
33
|
+
"italic" => '<path d="M10 5h8M6 19h8M14 5l-4 14"/>',
|
|
34
|
+
"underline" => '<path d="M7 5v7a5 5 0 0010 0V5M5 20h14"/>',
|
|
35
|
+
"strike" => '<path d="M4 12h16M16 7a4 3 0 00-4-2c-2.5 0-4 1.2-4 3s1.5 2.5 4 4 4 2 4 4-1.7 3-4.2 3' \
|
|
36
|
+
'a5 4 0 01-4.3-2"/>',
|
|
37
|
+
"code" => '<path d="M9 7l-5 5 5 5M15 7l5 5-5 5"/>',
|
|
38
|
+
"link" => '<path d="M10 14a4 4 0 005.7 0l3-3a4 4 0 00-5.7-5.7l-1 1' \
|
|
39
|
+
'M14 10a4 4 0 00-5.7 0l-3 3A4 4 0 0011 18.7l1-1"/>',
|
|
40
|
+
"unlink" => '<path d="M10 14a4 4 0 005.7 0l3-3a4 4 0 00-5.7-5.7l-1 1' \
|
|
41
|
+
'M14 10a4 4 0 00-5.7 0l-3 3A4 4 0 0011 18.7l1-1M4 4l16 16"/>',
|
|
42
|
+
"list-bullets" => '<path d="M9 6h11M9 12h11M9 18h11"/><circle cx="4.5" cy="6" r="1"/>' \
|
|
43
|
+
'<circle cx="4.5" cy="12" r="1"/><circle cx="4.5" cy="18" r="1"/>',
|
|
44
|
+
"list-numbers" => '<path d="M10 6h10M10 12h10M10 18h10M4 5l1.5-1v4M4 11.5h3l-3 3h3M4 17.5h3v1.5H5M7 19v1.5H4"/>',
|
|
45
|
+
"indent" => '<path d="M4 5h16M12 10h8M12 15h8M4 20h16M4 9l3 3-3 3"/>',
|
|
46
|
+
"outdent" => '<path d="M4 5h16M12 10h8M12 15h8M4 20h16M7 9l-3 3 3 3"/>',
|
|
47
|
+
"align-left" => '<path d="M4 6h16M4 10h10M4 14h16M4 18h10"/>',
|
|
48
|
+
"align-center" => '<path d="M4 6h16M7 10h10M4 14h16M7 18h10"/>',
|
|
49
|
+
"align-right" => '<path d="M4 6h16M10 10h10M4 14h16M10 18h10"/>',
|
|
50
|
+
"align-justify" => '<path d="M4 6h16M4 10h16M4 14h16M4 18h16"/>',
|
|
51
|
+
"image" => '<rect x="4" y="5" width="16" height="14" rx="2"/><circle cx="9" cy="10" r="1.5"/>' \
|
|
52
|
+
'<path d="M5 17l5-5 4 4 2-2 3 3"/>',
|
|
53
|
+
"divider" => '<path d="M4 12h16M8 7h8M8 17h8" stroke-dasharray="0"/>',
|
|
54
|
+
"quote" => '<path d="M5 17v-4a4 4 0 014-4M13 17v-4a4 4 0 014-4M5 17h4v-4H5M13 17h4v-4h-4"/>',
|
|
55
|
+
"line-break" => '<path d="M5 6v6a3 3 0 003 3h10M14 11l4 4-4 4"/>',
|
|
56
|
+
"undo" => '<path d="M9 7L4 12l5 5M4 12h10a5 5 0 010 10"/>',
|
|
57
|
+
"redo" => '<path d="M15 7l5 5-5 5M20 12H10a5 5 0 000 10"/>',
|
|
58
|
+
"upload" => '<path d="M12 16V5M7 10l5-5 5 5M5 19h14"/>',
|
|
59
|
+
"alert" => '<path d="M12 4l9 16H3zM12 10v4M12 17v.5"/>',
|
|
60
|
+
"info" => '<circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 8v.5"/>',
|
|
61
|
+
"success" => '<circle cx="12" cy="12" r="9"/><path d="M8 12.5l3 3 5-6"/>',
|
|
62
|
+
"clock" => '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
|
|
63
|
+
"dot" => '<circle cx="12" cy="12" r="4"/>',
|
|
64
|
+
"inbox" => '<path d="M4 13l2-8h12l2 8M4 13v6h16v-6M4 13h5l1 2h4l1-2h5"/>',
|
|
65
|
+
"eye" => '<path d="M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12z"/>' \
|
|
66
|
+
'<circle cx="12" cy="12" r="3"/>',
|
|
67
|
+
"refresh" => '<path d="M19 6v5h-5M5 18v-5h5M18.2 10A7 7 0 006 7.5M5.8 14A7 7 0 0018 16.5"/>',
|
|
68
|
+
"external" => '<path d="M14 4h6v6M20 4l-9 9M18 14v5H5V6h5"/>',
|
|
69
|
+
"phone" => '<rect x="7" y="3" width="10" height="18" rx="2"/><path d="M11 18h2"/>',
|
|
70
|
+
"tablet" => '<rect x="5" y="3" width="14" height="18" rx="2"/><path d="M11 18h2"/>',
|
|
71
|
+
"desktop" => '<rect x="3" y="4" width="18" height="12" rx="2"/><path d="M9 20h6M12 16v4"/>',
|
|
72
|
+
"calendar" => '<rect x="4" y="5" width="16" height="15" rx="2"/><path d="M4 10h16M9 3v4M15 3v4"/>',
|
|
73
|
+
"arrow-left" => '<path d="M19 12H5M11 6l-6 6 6 6"/>'
|
|
74
|
+
}.freeze
|
|
75
|
+
|
|
76
|
+
module_function
|
|
77
|
+
|
|
78
|
+
# @return [String] the symbols as one hidden sprite's body, each with an id `i-<name>`
|
|
79
|
+
def sprite
|
|
80
|
+
DRAWINGS.map { |name, drawing| %(<symbol id="i-#{name}" viewBox="0 0 24 24">#{drawing}</symbol>) }.join("\n")
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
module Site
|
|
6
|
+
module CmsEditor
|
|
7
|
+
# The `<kind>:<slug>` key convention, read from an attribute's own `pattern:`.
|
|
8
|
+
#
|
|
9
|
+
# A key such as `post:hello` says what it names in its first part. The bluebook states the
|
|
10
|
+
# convention only when the attribute's pattern does: it must start `^kind:` or
|
|
11
|
+
# `^(kind|kind):`, with lower-case words of letters, digits and underscores. Anything
|
|
12
|
+
# else is not a convention the editor assumes.
|
|
13
|
+
module KeyKinds
|
|
14
|
+
# `^` then one word, or a group of words separated by `|`, then the `:`.
|
|
15
|
+
LEAD = /\A\^(?:\((?:\?:)?(?<kinds>\w+(?:\|\w+)*)\)|(?<kinds>\w+)):/
|
|
16
|
+
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# @param pattern [String, nil] the attribute's declared pattern
|
|
20
|
+
# @return [Array<String>] the kinds the pattern allows before the colon; empty when none
|
|
21
|
+
def of(pattern)
|
|
22
|
+
found = LEAD.match(pattern.to_s)
|
|
23
|
+
return [] unless found
|
|
24
|
+
|
|
25
|
+
found[:kinds].split("|").grep(/\A[a-z][a-z0-9_]*\z/).uniq
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
module Site
|
|
6
|
+
module CmsEditor
|
|
7
|
+
# What a holder's rules say about how much an attribute may hold: a list's least and most
|
|
8
|
+
# rows (`chapters.size >= 1`, `parts.size <= 12`) and a text's most characters
|
|
9
|
+
# (`summary.to_s.size <= 600`). The rules are the domain's own, so the editor shows the
|
|
10
|
+
# limit it will be held to, and a text that may be long is entered in a writing box.
|
|
11
|
+
#
|
|
12
|
+
# The holder is a value object (its invariants), an aggregate (its invariants) or a command
|
|
13
|
+
# (its `given`s). A rule is read only when it is the whole of the invariant, or the whole
|
|
14
|
+
# of it after an `x.unset? ||` guard on the same part.
|
|
15
|
+
module Limits
|
|
16
|
+
# A list's size compared with a number.
|
|
17
|
+
LIST = /\A(\w+)\.size (<=|>=) (\d+)\z/
|
|
18
|
+
|
|
19
|
+
# A text's length bounded above (`to_s.size`, `size` or `length`), maybe after a guard.
|
|
20
|
+
TEXT = /\A(?:(\w+)\.unset\? \|\| )?(\w+)\.(?:to_s\.)?(?:size|length) <= (\d+)\z/
|
|
21
|
+
|
|
22
|
+
# A text that may be longer than this many characters is entered in a writing box.
|
|
23
|
+
MULTILINE_FROM = 200
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
# @param attributes [Array<Hash{String => Object}>] the holder's attributes as `Schema`
|
|
28
|
+
# shapes them
|
|
29
|
+
# @param rules [Array<#canonical>] the holder's invariants or `given`s
|
|
30
|
+
# @return [Array<Hash{String => Object}>] the attributes, each with `min` and `max` when
|
|
31
|
+
# it is a list a rule bounds, and `maxLength` (with `multiline` above `MULTILINE_FROM`)
|
|
32
|
+
# when it is a text a rule bounds
|
|
33
|
+
def apply(attributes, rules)
|
|
34
|
+
texts = rules.map { |rule| rule.canonical.to_s }
|
|
35
|
+
lists = sizes(texts)
|
|
36
|
+
bound = lengths(texts)
|
|
37
|
+
attributes.map { |attr| attr.merge(listed(attr, lists), texted(attr, bound)) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @return [Hash{String => Hash{String => Integer}}] each list's `min` and `max`; where a
|
|
41
|
+
# holder says several, the tightest wins
|
|
42
|
+
def sizes(texts)
|
|
43
|
+
texts.filter_map { |text| LIST.match(text) }.each_with_object({}) do |found, bounds|
|
|
44
|
+
held = (bounds[found[1]] ||= {})
|
|
45
|
+
kind = found[2] == "<=" ? "max" : "min"
|
|
46
|
+
held[kind] = [held[kind], found[3].to_i].compact.public_send(kind == "max" ? :min : :max)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @return [Hash{String => Integer}] each text's most characters
|
|
51
|
+
def lengths(texts)
|
|
52
|
+
texts.filter_map { |text| TEXT.match(text) }
|
|
53
|
+
.select { |found| found[1].nil? || found[1] == found[2] }
|
|
54
|
+
.to_h { |found| [found[2], found[3].to_i] }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def listed(attr, lists)
|
|
58
|
+
found = attr["list"] ? lists[attr["name"]] : nil
|
|
59
|
+
found ? found.reject { |kind, size| kind == "min" && size < 1 } : {}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def texted(attr, bound)
|
|
63
|
+
most = attr["type"] == "String" && !attr["list"] ? bound[attr["name"]] : nil
|
|
64
|
+
return {} unless most
|
|
65
|
+
|
|
66
|
+
{ "maxLength" => most, **(most > MULTILINE_FROM ? { "multiline" => true } : {}) }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -75,7 +75,7 @@ module Hecks
|
|
|
75
75
|
# @return [Hash{String => Object}] the picture aggregate as the editor's schema names it
|
|
76
76
|
def describe(agg, command, found)
|
|
77
77
|
listing = agg["queries"].find { |query| query["attributes"].empty? }
|
|
78
|
-
{ "aggregate" => agg["name"], "command" => command["name"], "fields" => found,
|
|
78
|
+
{ "aggregate" => agg["name"], **agg.slice("chapter"), "command" => command["name"], "fields" => found,
|
|
79
79
|
"listing" => listing && listing["name"] }
|
|
80
80
|
end
|
|
81
81
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
module Site
|
|
6
|
+
module CmsEditor
|
|
7
|
+
# Which integer attributes are moments in time, read from the names the bluebook gave them.
|
|
8
|
+
#
|
|
9
|
+
# An integer is a moment when one of the names it goes by (the attribute's, its value
|
|
10
|
+
# object's, the value object's part's) ends in `_at` or `At` (a date and a time), ends in
|
|
11
|
+
# `_on` or `On` (a date), or has `epoch` in it (seconds since 1970, so a date and a time).
|
|
12
|
+
# The value is always whole seconds since 1970 UTC; the editor shows it in the viewer's own
|
|
13
|
+
# time zone. A name is the only evidence the bluebook gives, so the rule is this narrow:
|
|
14
|
+
# an integer named `duration` or `Season` is never a date.
|
|
15
|
+
module Moment
|
|
16
|
+
# Names that end in `at` or `on` as a word, so `created_at` and `CreatedAt` match and
|
|
17
|
+
# `format`, `Season` and `Combat` do not.
|
|
18
|
+
AT = /(?:_at|[a-z0-9]At)\z/
|
|
19
|
+
ON = /(?:_on|[a-z0-9]On)\z/
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# @param names [Array<String>] the names the integer goes by
|
|
24
|
+
# @return [String, nil] `"datetime"`, `"date"`, or nil when the integer is not a moment
|
|
25
|
+
def widget(names)
|
|
26
|
+
return "datetime" if names.any? { |name| AT.match?(name) || name.match?(/epoch/i) }
|
|
27
|
+
|
|
28
|
+
"date" if names.any? { |name| ON.match?(name) }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "color"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Projections
|
|
7
|
+
module Site
|
|
8
|
+
module CmsEditor
|
|
9
|
+
# The editor's colours, light and dark, derived from one accent colour, as the custom
|
|
10
|
+
# properties of a daisyUI theme (`--color-base-100`, `--color-primary`, ...) and the few
|
|
11
|
+
# extras the pages use (`--ed-muted`, `--ed-edge`). Every text and fill pair is moved
|
|
12
|
+
# until it reaches the accessibility standard's level for it (4.5:1 for text, 3:1 for the
|
|
13
|
+
# edge of a control), so a brand colour that would fail cannot make the editor unreadable.
|
|
14
|
+
#
|
|
15
|
+
# The neutrals (ground, page, ink, muted ink, rule) are fixed when the row names no
|
|
16
|
+
# accent, and take the accent's hue at low saturation when it does, so an accent retints
|
|
17
|
+
# the whole theme. Primary buttons are filled with ink (daisyUI's `neutral`); the accent
|
|
18
|
+
# (`primary`) marks selection, focus, links, the active navigation item and the saved or
|
|
19
|
+
# published state.
|
|
20
|
+
class Palette
|
|
21
|
+
# The accent a row that names none gets: a deep verdigris.
|
|
22
|
+
DEFAULT_ACCENT = "#1f7a6d"
|
|
23
|
+
|
|
24
|
+
# The neutrals a row that names no accent gets, per theme.
|
|
25
|
+
FIXED = {
|
|
26
|
+
light: { ground: "#f4f6f8", surface: "#ffffff", ink: "#1a222c", muted: "#5a6675", rule: "#dce1e7" },
|
|
27
|
+
dark: { ground: "#11161b", surface: "#181f26", ink: "#e7ebef", muted: "#9aa6b3", rule: "#28313b" }
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
# Hues of the status colours, in degrees.
|
|
31
|
+
TONES = { ok: 150.0, warn: 38.0, danger: 4.0 }.freeze
|
|
32
|
+
|
|
33
|
+
# The daisyUI variable each status tone fills.
|
|
34
|
+
STATUS = { ok: "success", warn: "warning", danger: "error" }.freeze
|
|
35
|
+
|
|
36
|
+
# @param accent [String, nil] a six-digit hex colour, or nil for the default
|
|
37
|
+
def initialize(accent = nil)
|
|
38
|
+
@custom = !accent.to_s.empty?
|
|
39
|
+
@accent = (accent.to_s.empty? ? DEFAULT_ACCENT : accent).downcase
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [Hash{Symbol => Hash{String => String}}] each theme's properties, name to value
|
|
43
|
+
def themes = { light: properties(:light), dark: properties(:dark) }
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def properties(theme)
|
|
48
|
+
base = neutrals(theme)
|
|
49
|
+
found = base.merge(accents(theme, base)).merge(tones(theme, base))
|
|
50
|
+
{ **daisy(found), **extras(found) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def lighter(theme) = theme == :light ? :darker : :lighter
|
|
54
|
+
|
|
55
|
+
def neutrals(theme)
|
|
56
|
+
fixed = @custom ? tinted(FIXED.fetch(theme)) : FIXED.fetch(theme)
|
|
57
|
+
backs = [fixed[:surface], fixed[:ground]]
|
|
58
|
+
edge = Color.reach(Color.mix(fixed[:rule], fixed[:muted], 0.5), backs, 3.0, lighter(theme))
|
|
59
|
+
{ **fixed, muted: Color.reach(fixed[:muted], backs, 4.5, lighter(theme)), edge: edge }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The fixed neutrals with the accent's hue at the saturation each already has.
|
|
63
|
+
def tinted(fixed)
|
|
64
|
+
hue = Color.hsl(@accent).first
|
|
65
|
+
fixed.transform_values do |hex|
|
|
66
|
+
_, sat, light = Color.hsl(hex)
|
|
67
|
+
hex == "#ffffff" ? hex : Color.from_hsl(hue, sat, light)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def accents(theme, base)
|
|
72
|
+
backs = [base[:surface], base[:ground]]
|
|
73
|
+
fill = theme == :light ? @accent : Color.reach(@accent, backs, 4.5, :lighter)
|
|
74
|
+
soft = Color.mix(base[:surface], fill, theme == :light ? 0.1 : 0.18)
|
|
75
|
+
{ primary: Color.reach(fill, [*backs, soft], 4.5, lighter(theme)), accent_soft: soft }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def tones(theme, base)
|
|
79
|
+
TONES.each_with_object({}) do |(name, hue), found|
|
|
80
|
+
back = theme == :light ? Color.from_hsl(hue, 0.5, 0.94) : Color.from_hsl(hue, 0.28, 0.18)
|
|
81
|
+
start = Color.from_hsl(hue, 0.55, theme == :light ? 0.3 : 0.72)
|
|
82
|
+
found[:"#{name}_bg"] = back
|
|
83
|
+
found[:"#{name}_fg"] = Color.reach(start, [back, base[:surface]], 4.5, lighter(theme))
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The first of white, a near-black and black that reads on `fill`.
|
|
88
|
+
def on(fill) = ["#ffffff", "#0f151a", "#000000"].find { |hex| Color.contrast(hex, fill) >= 4.5 }
|
|
89
|
+
|
|
90
|
+
def daisy(found)
|
|
91
|
+
colours = surfaces(found).merge(fills(found))
|
|
92
|
+
named = colours.slice("primary", "secondary", "accent", "info", *STATUS.values)
|
|
93
|
+
contents = named.to_h { |name, value| ["#{name}-content", on(value)] }
|
|
94
|
+
colours.merge(contents).transform_keys { |name| "--color-#{name}" }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def surfaces(found)
|
|
98
|
+
{ "base-100" => found[:surface], "base-200" => found[:ground], "base-300" => found[:rule],
|
|
99
|
+
"base-content" => found[:ink], "neutral" => found[:ink], "neutral-content" => found[:surface] }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def fills(found)
|
|
103
|
+
{ "primary" => found[:primary], "secondary" => found[:muted], "accent" => found[:primary], "info" => found[:primary],
|
|
104
|
+
**STATUS.to_h { |tone, name| [name, found[:"#{tone}_fg"]] } }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def extras(found)
|
|
108
|
+
{ "--ed-muted" => found[:muted], "--ed-edge" => found[:edge], "--ed-soft" => found[:accent_soft],
|
|
109
|
+
"--ed-ok-bg" => found[:ok_bg], "--ed-warn-bg" => found[:warn_bg], "--ed-danger-bg" => found[:danger_bg] }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|