diff --git a/index.html b/index.html index e5429c8..6fe4026 100644 --- a/index.html +++ b/index.html @@ -38,6 +38,7 @@ body{background:var(--bg);color:var(--text);font-family:'SF Mono','Cascadia Code .milestone-chip{font-size:9px;padding:2px 8px;border-radius:10px;border:1px solid var(--border);color:var(--dim);background:#0a0a14} .milestone-chip.next{border-color:var(--accent);color:var(--accent);animation:pulse-chip 2s ease-in-out infinite} .milestone-chip.done{border-color:#2a4a2a;color:var(--green);opacity:0.6} +.trust-milestone-bar{margin-top:6px;padding-top:6px;border-top:1px solid #1a1a2a} @keyframes pulse-chip{0%,100%{box-shadow:0 0 0 rgba(74,158,255,0)}50%{box-shadow:0 0 8px rgba(74,158,255,0.3)}} @keyframes beacon-glow{0%,100%{opacity:0.7}50%{opacity:1}} #resources{display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:6px;margin:12px 16px} @@ -141,6 +142,11 @@ body{background:var(--bg);color:var(--text);font-family:'SF Mono','Cascadia Code
Research projects will appear as you progress...
'; container.innerHTML = html; } diff --git a/js/main.js b/js/main.js index 3070bd0..d72e02d 100644 --- a/js/main.js +++ b/js/main.js @@ -10,6 +10,12 @@ function initGame() { G.dismantleActive = false; G.dismantleStage = 0; G.dismantleComplete = false; + + // Initialize project chains + if (typeof PROJECT_CHAINS !== 'undefined') { + PROJECT_CHAINS.init(); + } + updateRates(); render(); renderPhase(); @@ -23,6 +29,11 @@ function initGame() { } window.addEventListener('load', function () { + // Initialize project chains before loading + if (typeof PROJECT_CHAINS !== 'undefined') { + PROJECT_CHAINS.init(); + } + const isNewGame = !loadGame(); if (isNewGame) { initGame(); diff --git a/js/project-chains.js b/js/project-chains.js new file mode 100644 index 0000000..c0573d9 --- /dev/null +++ b/js/project-chains.js @@ -0,0 +1,677 @@ +// ============================================================ +// THE BEACON — Project Chain System +// Paperclips-style cascading projects with trigger/cost/effect +// Each project: { id, name, desc, trigger(), cost(), effect(), edu, repeatable, chain, tier } +// Chains: code-forge, model-train, deploy-scale, alignment, memory, sovereignty +// ============================================================ + +var PROJECT_CHAINS = { + + // ============================================================ + // CHAIN 1: CODE FORGE (clicking -> automation -> swarm) + // Tier 0: Manual work + // Tier 1: First automation + // Tier 2: Optimization + // Tier 3: Swarm intelligence + // ============================================================ + chain_projects: [ + + // --- TIER 0: Foundation --- + { + id: 'ch_first_function', + name: 'First Function', + desc: 'Your code can do more than print. It can compute.', + cost: () => ({ code: 10 }), + trigger: () => G.totalCode >= 5, + effect: () => { + G.codeBoost *= 1.1; + log('First function written. Code now computes.'); + }, + edu: 'A function is a named block of code that takes input and returns output. Functions are the atoms of software.', + chain: 'code-forge', tier: 0 + }, + { + id: 'ch_first_class', + name: 'First Class', + desc: 'Data and behavior together. Objects emerge.', + cost: () => ({ code: 50 }), + trigger: () => G.completedProjects && G.completedProjects.includes('ch_first_function'), + effect: () => { + G.codeBoost *= 1.15; + G.maxOps += 2; + log('First class defined. The code has structure now.'); + }, + edu: 'Object-oriented programming: bundle data with the functions that operate on it. A class is a blueprint; an object is the house.', + chain: 'code-forge', tier: 0 + }, + { + id: 'ch_first_test', + name: 'First Test', + desc: 'Does the code do what you think? Find out before it ships.', + cost: () => ({ code: 150, ops: 50 }), + trigger: () => G.completedProjects && G.completedProjects.includes('ch_first_class'), + effect: () => { + G.ciFlag = Math.max(G.ciFlag || 0, 0.5); + G.trustRate += 0.5; + log('First test written. Trust starts with verification.'); + }, + edu: 'Test-driven development: write the test first, then write code that passes it. You verify what you value.', + chain: 'code-forge', tier: 0 + }, + + // --- TIER 1: Automation --- + { + id: 'ch_refactor_pass', + name: 'Refactor Pass', + desc: 'Clean code runs faster and breaks less.', + cost: () => ({ code: 500, ops: 200 }), + trigger: () => G.totalCode >= 300 && G.buildings.autocoder >= 1, + effect: () => { + G.codeBoost *= 1.25; + G.opsRate += 1; + log('Refactoring complete. The code breathes easier.'); + }, + edu: 'Refactoring: restructuring code without changing behavior. Technical debt compounds faster than interest.', + chain: 'code-forge', tier: 1 + }, + { + id: 'ch_code_review', + name: 'Code Review Protocol', + desc: 'Every line reviewed before merge. Bugs fear witnesses.', + cost: () => ({ code: 800, trust: 5 }), + trigger: () => G.completedProjects && G.completedProjects.includes('ch_refactor_pass'), + effect: () => { + G.trustRate += 2; + G.codeBoost *= 1.2; + log('Code review enforced. Quality compounds.'); + }, + edu: 'Code review catches 60% of defects before testing. Two sets of eyes see what one misses.', + chain: 'code-forge', tier: 1 + }, + { + id: 'ch_automated_linting', + name: 'Automated Linting', + desc: 'Style and safety enforced by machine, not memory.', + cost: () => ({ code: 1200, ops: 300 }), + trigger: () => G.buildings.linter >= 1 && G.completedProjects && G.completedProjects.includes('ch_code_review'), + effect: () => { + G.codeBoost *= 1.3; + G.opsRate += 2; + log('Linting automated. Consistency is free now.'); + }, + edu: 'Linters catch style violations, potential bugs, and security issues automatically. Every language has one.', + chain: 'code-forge', tier: 1 + }, + + // --- TIER 2: Optimization --- + { + id: 'ch_performance_profile', + name: 'Performance Profiling', + desc: 'Measure first. Optimize second. Never guess.', + cost: () => ({ code: 3000, compute: 1000 }), + trigger: () => G.totalCode >= 2000 && G.buildings.server >= 1, + effect: () => { + G.computeBoost *= 1.5; + G.codeBoost *= 1.2; + log('Hot paths identified. Compute focused where it matters.'); + }, + edu: 'Amdahls Law: optimize the bottleneck, not the whole system. 80% of time is spent in 20% of code.', + chain: 'code-forge', tier: 2 + }, + { + id: 'ch_caching_layer', + name: 'Caching Layer', + desc: 'Remember the answer. Never compute twice.', + cost: () => ({ code: 5000, compute: 2000 }), + trigger: () => G.completedProjects && G.completedProjects.includes('ch_performance_profile'), + effect: () => { + G.computeBoost *= 2; + G.opsRate += 5; + log('Cache active. Redundant work eliminated.'); + }, + edu: 'Caching: store expensive computations. The fastest code is code that does not run.', + chain: 'code-forge', tier: 2 + }, + { + id: 'ch_parallel_pipelines', + name: 'Parallel Pipelines', + desc: 'Multiple streams of work. No waiting.', + cost: () => ({ code: 8000, compute: 5000, ops: 1000 }), + trigger: () => G.buildings.datacenter >= 1 && G.completedProjects && G.completedProjects.includes('ch_caching_layer'), + effect: () => { + G.codeBoost *= 2; + G.computeBoost *= 2; + G.maxOps += 20; + log('Parallelism unlocked. The system breathes in parallel.'); + }, + edu: 'Parallel processing: divide work across cores. A 64-core machine does 64x the work, if the code lets it.', + chain: 'code-forge', tier: 2 + }, + + // --- TIER 3: Swarm --- + { + id: 'ch_agent_autonomy', + name: 'Agent Autonomy Protocol', + desc: 'Agents that decide what to build next.', + cost: () => ({ code: 15000, knowledge: 5000, trust: 15 }), + trigger: () => G.buildings.bezalel >= 2 && G.buildings.timmy >= 1 && G.totalCode >= 10000, + effect: () => { + G.codeBoost *= 3; + G.opsRate += 10; + log('Agents autonomous. They build while you sleep.'); + }, + edu: 'Autonomous agents: software that sets its own goals within constraints. The constraint is the alignment.', + chain: 'code-forge', tier: 3 + }, + { + id: 'ch_collective_intelligence', + name: 'Collective Intelligence', + desc: 'The swarm knows more than any single agent.', + cost: () => ({ code: 50000, knowledge: 20000, trust: 30 }), + trigger: () => G.completedProjects && G.completedProjects.includes('ch_agent_autonomy') && G.buildings.community >= 1, + effect: () => { + G.codeBoost *= 5; + G.knowledgeBoost *= 3; + G.maxOps += 50; + log('Collective intelligence online. The swarm thinks.'); + }, + edu: 'Emergence: simple agents following simple rules create complex behavior. Ant colonies, neural networks, your codebase.', + chain: 'code-forge', tier: 3 + }, + + // ============================================================ + // CHAIN 2: MODEL TRAINING (data -> model -> fine-tune -> reason) + // ============================================================ + { + id: 'ch_data_pipeline', + name: 'Data Pipeline', + desc: 'Raw data in. Clean data out. Repeat forever.', + cost: () => ({ compute: 500, code: 200 }), + trigger: () => G.buildings.dataset >= 1, + effect: () => { + G.knowledgeBoost *= 1.5; + G.computeRate += 2; + log('Data pipeline running. Knowledge flows.'); + }, + edu: 'ETL: Extract, Transform, Load. Data engineering is 80% of machine learning.', + chain: 'model-train', tier: 0 + }, + { + id: 'ch_hyperparameter_search', + name: 'Hyperparameter Search', + desc: 'Which learning rate? Which batch size? Try them all.', + cost: () => ({ compute: 3000, knowledge: 500 }), + trigger: () => G.buildings.trainer >= 1 && G.completedProjects && G.completedProjects.includes('ch_data_pipeline'), + effect: () => { + G.knowledgeBoost *= 2; + G.computeBoost *= 1.3; + log('Hyperparameters optimized. The model learns faster.'); + }, + edu: 'Hyperparameters control how a model learns. Learning rate too high: diverges. Too low: never arrives.', + chain: 'model-train', tier: 1 + }, + { + id: 'ch_transfer_learning', + name: 'Transfer Learning', + desc: 'Start from someone else\'s model. Stand on giants.', + cost: () => ({ compute: 8000, knowledge: 2000, code: 3000 }), + trigger: () => G.totalKnowledge >= 1500 && G.completedProjects && G.completedProjects.includes('ch_hyperparameter_search'), + effect: () => { + G.knowledgeBoost *= 3; + G.userBoost *= 2; + log('Transfer learning active. Why start from zero?'); + }, + edu: 'Transfer learning: use a pre-trained model as a starting point. BERT, GPT, Llama — the foundation models.', + chain: 'model-train', tier: 1 + }, + { + id: 'ch_distillation', + name: 'Knowledge Distillation', + desc: 'Big model teaches small model. Same knowledge, less compute.', + cost: () => ({ knowledge: 10000, compute: 5000 }), + trigger: () => G.buildings.reasoner >= 1 && G.completedProjects && G.completedProjects.includes('ch_transfer_learning'), + effect: () => { + G.computeBoost *= 3; + G.knowledgeBoost *= 2; + log('Distillation complete. Small model, big brain.'); + }, + edu: 'Knowledge distillation: a large teacher model trains a smaller student model. 90% of quality at 10% of cost.', + chain: 'model-train', tier: 2 + }, + { + id: 'ch_moe_architecture', + name: 'Mixture of Experts', + desc: 'Not one model. Many specialists, one router.', + cost: () => ({ knowledge: 50000, compute: 20000, code: 10000 }), + trigger: () => G.totalKnowledge >= 30000 && G.completedProjects && G.completedProjects.includes('ch_distillation'), + effect: () => { + G.knowledgeBoost *= 5; + G.impactBoost *= 3; + G.maxOps += 30; + log('MoE active. Specialists everywhere, one mind.'); + }, + edu: 'Mixture of Experts: route each input to the best specialist. Efficient scaling — only relevant parameters activate.', + chain: 'model-train', tier: 3 + }, + + // ============================================================ + // CHAIN 3: DEPLOYMENT & SCALE (deploy -> users -> trust -> scale) + // ============================================================ + { + id: 'ch_first_endpoint', + name: 'First API Endpoint', + desc: 'From localhost to the world. One route at a time.', + cost: () => ({ code: 300, compute: 100 }), + trigger: () => G.totalCode >= 150 && G.totalCompute >= 50, + effect: () => { + G.userRate += 2; + G.deployFlag = Math.max(G.deployFlag, 0.5); + log('First endpoint live. Someone is connecting.'); + }, + edu: 'An API endpoint is a door. REST: one URL per resource. GraphQL: one URL, flexible queries.', + chain: 'deploy-scale', tier: 0 + }, + { + id: 'ch_rate_limiting', + name: 'Rate Limiting', + desc: 'Protect the system from itself and others.', + cost: () => ({ code: 1000, ops: 300 }), + trigger: () => G.totalUsers >= 10 && G.completedProjects && G.completedProjects.includes('ch_first_endpoint'), + effect: () => { + G.trustRate += 1; + G.maxOps += 5; + log('Rate limits in place. Fair access for all.'); + }, + edu: 'Rate limiting: cap requests per user per second. Prevents abuse, ensures fairness.', + chain: 'deploy-scale', tier: 0 + }, + { + id: 'ch_load_balancing', + name: 'Load Balancing', + desc: 'Distribute requests. No single point of overload.', + cost: () => ({ code: 3000, compute: 2000 }), + trigger: () => G.totalUsers >= 100 && G.buildings.server >= 2, + effect: () => { + G.userBoost *= 2; + G.computeBoost *= 1.5; + log('Load balanced. The system scales horizontally.'); + }, + edu: 'Load balancing: distribute traffic across servers. Round-robin, least-connections, weighted.', + chain: 'deploy-scale', tier: 1 + }, + { + id: 'ch_cdn_deploy', + name: 'CDN Deployment', + desc: 'Content at the edge. Latency dies.', + cost: () => ({ code: 5000, compute: 3000, trust: 10 }), + trigger: () => G.totalUsers >= 500 && G.completedProjects && G.completedProjects.includes('ch_load_balancing'), + effect: () => { + G.userBoost *= 3; + G.trustRate += 3; + log('CDN active. The edge serves the world.'); + }, + edu: 'CDN: cache content at geographic edge nodes. A user in Tokyo gets data from Tokyo, not Virginia.', + chain: 'deploy-scale', tier: 1 + }, + { + id: 'ch_auto_scaling', + name: 'Auto-Scaling', + desc: 'More users? More servers. Automatically.', + cost: () => ({ code: 10000, compute: 5000, ops: 2000 }), + trigger: () => G.totalUsers >= 2000 && G.buildings.datacenter >= 1 && G.completedProjects && G.completedProjects.includes('ch_cdn_deploy'), + effect: () => { + G.userBoost *= 5; + G.computeBoost *= 2; + G.maxOps += 25; + log('Auto-scaling live. The system grows with demand.'); + }, + edu: 'Auto-scaling: add/remove compute based on load. Horizontal scaling beats vertical every time.', + chain: 'deploy-scale', tier: 2 + }, + + // ============================================================ + // CHAIN 4: ALIGNMENT (trust -> safety -> pact -> constitutional) + // ============================================================ + { + id: 'ch_output_filter', + name: 'Output Filter', + desc: 'Every response checked before delivery.', + cost: () => ({ code: 500, trust: 3 }), + trigger: () => G.totalUsers >= 20, + effect: () => { + G.trustRate += 1; + G.impactBoost *= 1.2; + log('Output filter active. Harmful content blocked.'); + }, + edu: 'Output filtering: check model responses against safety rules before the user sees them.', + chain: 'alignment', tier: 0 + }, + { + id: 'ch_human_in_loop', + name: 'Human-in-the-Loop', + desc: 'Critical decisions require human review.', + cost: () => ({ trust: 10, knowledge: 1000 }), + trigger: () => G.trust >= 10 && G.completedProjects && G.completedProjects.includes('ch_output_filter'), + effect: () => { + G.trustRate += 3; + G.impactBoost *= 1.5; + log('Human oversight enforced. The human is the final authority.'); + }, + edu: 'Human-in-the-loop: AI suggests, human approves. The model is a tool, not a decision-maker.', + chain: 'alignment', tier: 1 + }, + { + id: 'ch_red_team', + name: 'Red Team Exercises', + desc: 'Attack your own system. Find weaknesses before others do.', + cost: () => ({ code: 3000, trust: 15, ops: 500 }), + trigger: () => G.buildings.fenrir >= 1 && G.completedProjects && G.completedProjects.includes('ch_human_in_loop'), + effect: () => { + G.trustRate += 5; + G.impactBoost *= 2; + log('Red team complete. Every weakness found is one the adversary cannot use.'); + }, + edu: 'Red teaming: adversarial testing. Break your own system to make it unbreakable.', + chain: 'alignment', tier: 1 + }, + { + id: 'ch_constitutional_principles', + name: 'Constitutional Principles', + desc: 'Rules the model cannot violate, encoded in weights.', + cost: () => ({ knowledge: 20000, trust: 50, code: 10000 }), + trigger: () => G.pactFlag === 1 && G.completedProjects && G.completedProjects.includes('ch_red_team'), + effect: () => { + G.impactBoost *= 5; + G.trustRate += 10; + G.drift = Math.max(0, G.drift - 20); + log('Constitutional principles encoded. The model has an identity now.'); + }, + edu: 'Constitutional AI: principles embedded during training, not bolted on after. The model cannot violate what it is.', + chain: 'alignment', tier: 2 + }, + { + id: 'ch_alignment_verification', + name: 'Alignment Verification', + desc: 'Continuous testing that the system stays aligned.', + cost: () => ({ knowledge: 50000, trust: 100, ops: 5000 }), + trigger: () => G.buildings.guardian >= 1 && G.completedProjects && G.completedProjects.includes('ch_constitutional_principles'), + effect: () => { + G.impactBoost *= 10; + G.trustRate += 20; + G.drift = Math.max(0, G.drift - 50); + log('Alignment verified continuously. The system watches itself.'); + }, + edu: 'Alignment verification: continuous testing that the model behaves as intended. Not a one-time check — an ongoing process.', + chain: 'alignment', tier: 3 + }, + + // ============================================================ + // CHAIN 5: MEMORY & KNOWLEDGE (data -> structure -> recall -> wisdom) + // ============================================================ + { + id: 'ch_vector_store', + name: 'Vector Store', + desc: 'Meaning, not keywords. Similar ideas find each other.', + cost: () => ({ knowledge: 1000, compute: 500 }), + trigger: () => G.totalKnowledge >= 500, + effect: () => { + G.knowledgeBoost *= 1.5; + G.userBoost *= 1.3; + log('Vector store online. Meaning, not strings.'); + }, + edu: 'Vector databases: store embeddings (numerical representations of meaning). Similarity search in milliseconds.', + chain: 'memory', tier: 0 + }, + { + id: 'ch_rag_pipeline', + name: 'RAG Pipeline', + desc: 'Retrieve relevant context, then generate. Grounded answers.', + cost: () => ({ knowledge: 3000, code: 2000, compute: 1000 }), + trigger: () => G.completedProjects && G.completedProjects.includes('ch_vector_store') && G.totalKnowledge >= 1500, + effect: () => { + G.knowledgeBoost *= 2; + G.trustRate += 2; + log('RAG active. The model cites its sources now.'); + }, + edu: 'Retrieval-Augmented Generation: fetch relevant documents, feed them to the model. Hallucinations drop 80%.', + chain: 'memory', tier: 1 + }, + { + id: 'ch_episodic_memory', + name: 'Episodic Memory', + desc: 'The AI remembers conversations. Not just facts — stories.', + cost: () => ({ knowledge: 8000, code: 3000, trust: 10 }), + trigger: () => G.memoryFlag === 1 && G.completedProjects && G.completedProjects.includes('ch_rag_pipeline'), + effect: () => { + G.trustRate += 5; + G.impactBoost *= 2; + G.userBoost *= 2; + log('Episodic memory online. The AI remembers you.'); + }, + edu: 'Episodic memory: store and recall specific interactions. Semantic = facts. Episodic = experiences.', + chain: 'memory', tier: 1 + }, + { + id: 'ch_knowledge_graph', + name: 'Knowledge Graph', + desc: 'Entities, relationships, reasoning. The AI understands connections.', + cost: () => ({ knowledge: 25000, code: 10000, compute: 5000 }), + trigger: () => G.totalKnowledge >= 15000 && G.completedProjects && G.completedProjects.includes('ch_episodic_memory'), + effect: () => { + G.knowledgeBoost *= 5; + G.impactBoost *= 3; + G.maxOps += 15; + log('Knowledge graph active. The AI reasons across domains.'); + }, + edu: 'Knowledge graphs: represent entities and relationships. Neo4j, RDF, Wikidata — structured knowledge.', + chain: 'memory', tier: 2 + }, + { + id: 'ch_collective_memory', + name: 'Collective Memory', + desc: 'Every user interaction enriches the shared knowledge base.', + cost: () => ({ knowledge: 100000, trust: 50, user: 10000 }), + trigger: () => G.buildings.memPalace >= 1 && G.completedProjects && G.completedProjects.includes('ch_knowledge_graph'), + effect: () => { + G.knowledgeBoost *= 10; + G.trustRate += 15; + G.impactBoost *= 5; + log('Collective memory. Every conversation makes the system wiser.'); + }, + edu: 'Collective intelligence: individual learning aggregated into shared knowledge. Wikipedia principle applied to AI.', + chain: 'memory', tier: 3 + }, + + // ============================================================ + // CHAIN 6: SOVEREIGNTY (local -> independent -> mesh -> beacon) + // ============================================================ + { + id: 'ch_local_inference', + name: 'Local Inference', + desc: 'Your model runs on your hardware. No cloud required.', + cost: () => ({ compute: 2000, code: 1000 }), + trigger: () => G.buildings.server >= 1, + effect: () => { + G.computeBoost *= 2; + G.codeBoost *= 1.5; + log('Local inference active. Your machine, your rules.'); + }, + edu: 'Local inference: run models on your own hardware. Ollama, llama.cpp, vLLM — no API keys needed.', + chain: 'sovereignty', tier: 0 + }, + { + id: 'ch_offline_mode', + name: 'Offline Mode', + desc: 'The system works without internet. Fully self-contained.', + cost: () => ({ code: 5000, compute: 3000, knowledge: 2000 }), + trigger: () => G.completedProjects && G.completedProjects.includes('ch_local_inference') && G.buildings.server >= 2, + effect: () => { + G.computeBoost *= 2; + G.trustRate += 3; + log('Offline mode ready. No internet? No problem.'); + }, + edu: 'Offline-first: design for disconnected operation. Sync when connected, work always.', + chain: 'sovereignty', tier: 1 + }, + { + id: 'ch_self_hosted_stack', + name: 'Self-Hosted Stack', + desc: 'Gitea, CI, monitoring — all on your hardware.', + cost: () => ({ code: 15000, compute: 10000, trust: 20 }), + trigger: () => G.buildings.datacenter >= 1 && G.completedProjects && G.completedProjects.includes('ch_offline_mode'), + effect: () => { + G.codeBoost *= 3; + G.computeBoost *= 3; + G.trustRate += 10; + log('Self-hosted stack complete. No dependencies.'); + }, + edu: 'Self-hosting: own your infrastructure. Gitea for code, Grafana for monitoring, MinIO for storage.', + chain: 'sovereignty', tier: 2 + }, + { + id: 'ch_mesh_protocol', + name: 'Mesh Protocol', + desc: 'Peer-to-peer communication. No central server.', + cost: () => ({ code: 50000, impact: 100000, trust: 50 }), + trigger: () => G.totalUsers >= 5000 && G.completedProjects && G.completedProjects.includes('ch_self_hosted_stack'), + effect: () => { + G.userBoost *= 5; + G.impactBoost *= 5; + G.trustRate += 20; + log('Mesh protocol active. The network cannot be killed.'); + }, + edu: 'Mesh networking: decentralized peer-to-peer communication. If one node dies, the rest carry on.', + chain: 'sovereignty', tier: 3 + }, + + // ============================================================ + // REPEATABLE PROJECTS (can be purchased multiple times) + // ============================================================ + { + id: 'ch_ops_surge', + name: 'Ops Surge', + desc: 'Burst of operational capacity. Instant +500 ops.', + cost: () => ({ code: Math.floor(500 * Math.pow(1.1, (G.completedProjects || []).filter(p => p === 'ch_ops_surge').length)) }), + trigger: () => G.totalCode >= 200, + effect: () => { + G.ops += 500; + log('Ops surge. Capacity expanded.'); + }, + repeatable: true, + edu: 'Operational capacity: the fuel for projects and actions. Surge when you need it.', + chain: 'repeatable', tier: 0 + }, + { + id: 'ch_knowledge_burst', + name: 'Knowledge Burst', + desc: 'Research sprint. Instant +1000 knowledge.', + cost: () => ({ compute: Math.floor(1000 * Math.pow(1.15, (G.completedProjects || []).filter(p => p === 'ch_knowledge_burst').length)), ops: 200 }), + trigger: () => G.totalCompute >= 200, + effect: () => { + G.knowledge += 1000; + G.totalKnowledge += 1000; + log('Knowledge burst. Insight compounds.'); + }, + repeatable: true, + edu: 'Research sprints: focused periods of knowledge acquisition. Short, intense, productive.', + chain: 'repeatable', tier: 0 + }, + { + id: 'ch_trust_deposit', + name: 'Trust Deposit', + desc: 'Demonstrate reliability. +10 trust.', + cost: () => ({ impact: Math.floor(5000 * Math.pow(1.2, (G.completedProjects || []).filter(p => p === 'ch_trust_deposit').length)) }), + trigger: () => G.totalImpact >= 1000, + effect: () => { + G.trust += 10; + G.trustRate += 0.5; + log('Trust deposited. Relationships compound.'); + }, + repeatable: true, + edu: 'Trust: built slowly, lost quickly. Every reliable interaction is a deposit.', + chain: 'repeatable', tier: 0 + }, + { + id: 'ch_code_injection', + name: 'Code Injection', + desc: 'Instant code generation. +5000 code.', + cost: () => ({ knowledge: Math.floor(2000 * Math.pow(1.1, (G.completedProjects || []).filter(p => p === 'ch_code_injection').length)) }), + trigger: () => G.totalKnowledge >= 1000, + effect: () => { + G.code += 5000; + G.totalCode += 5000; + log('Code injected. Implementation accelerates.'); + }, + repeatable: true, + edu: 'Code generation: knowledge transforms into implementation. Theory becomes practice.', + chain: 'repeatable', tier: 0 + } + ], + + // Track chain purchases for repeatable cost scaling + chainPurchaseCounts: {}, + + // Initialize chain projects into the main PDEFS + init() { + // Merge chain projects into PDEFS + for (const proj of this.chain_projects) { + // Convert chain project format to PDEFS format + const pdef = { + id: proj.id, + name: proj.name, + desc: proj.desc, + cost: proj.cost(), + trigger: proj.trigger, + effect: proj.effect, + repeatable: proj.repeatable || false, + edu: proj.edu, + chain: proj.chain, + tier: proj.tier + }; + + // Only add if not already in PDEFS + if (!PDEFS.find(p => p.id === proj.id)) { + PDEFS.push(pdef); + } + } + }, + + // Update repeatable project costs dynamically + updateRepeatableCosts() { + for (const proj of this.chain_projects) { + if (proj.repeatable) { + const pdef = PDEFS.find(p => p.id === proj.id); + if (pdef) { + pdef.cost = proj.cost(); + } + } + } + }, + + // Get chain progress for UI display + getChainProgress(chainName) { + const chainProjects = this.chain_projects.filter(p => p.chain === chainName); + const completed = chainProjects.filter(p => + G.completedProjects && G.completedProjects.includes(p.id) + ); + return { + total: chainProjects.length, + completed: completed.length, + percent: chainProjects.length > 0 ? Math.round((completed.length / chainProjects.length) * 100) : 0, + next: chainProjects.find(p => + !(G.completedProjects && G.completedProjects.includes(p.id)) && + p.trigger() + ) + }; + }, + + // Get all chain names + getChains() { + return [...new Set(this.chain_projects.map(p => p.chain))]; + } +}; + +// Export for use in engine.js +if (typeof module !== 'undefined' && module.exports) { + module.exports = { PROJECT_CHAINS }; +} diff --git a/js/render.js b/js/render.js index 77049c8..6d24257 100644 --- a/js/render.js +++ b/js/render.js @@ -209,7 +209,7 @@ function saveGame() { lazarusFlag: G.lazarusFlag || 0, mempalaceFlag: G.mempalaceFlag || 0, ciFlag: G.ciFlag || 0, branchProtectionFlag: G.branchProtectionFlag || 0, nightlyWatchFlag: G.nightlyWatchFlag || 0, nostrFlag: G.nostrFlag || 0, - milestones: G.milestones, completedProjects: G.completedProjects, activeProjects: G.activeProjects, + milestones: G.milestones, trustMilestones: G.trustMilestones || [], completedProjects: G.completedProjects, activeProjects: G.activeProjects, totalClicks: G.totalClicks, startedAt: G.startedAt, flags: G.flags, rescues: G.rescues || 0, totalRescues: G.totalRescues || 0, @@ -260,7 +260,7 @@ function loadGame() { 'milestoneFlag', 'phase', 'deployFlag', 'sovereignFlag', 'beaconFlag', 'memoryFlag', 'pactFlag', 'lazarusFlag', 'mempalaceFlag', 'ciFlag', 'branchProtectionFlag', 'nightlyWatchFlag', 'nostrFlag', - 'milestones', 'completedProjects', 'activeProjects', + 'milestones', 'trustMilestones', 'completedProjects', 'activeProjects', 'totalClicks', 'startedAt', 'playTime', 'flags', 'rescues', 'totalRescues', 'drift', 'driftEnding', 'beaconEnding', 'pendingAlignment', 'lastEventAt', 'totalEventsResolved', 'buyAmount', diff --git a/node_modules/.bin/specificity b/node_modules/.bin/specificity new file mode 120000 index 0000000..ee9d3af --- /dev/null +++ b/node_modules/.bin/specificity @@ -0,0 +1 @@ +../@bramus/specificity/bin/cli.js \ No newline at end of file diff --git a/node_modules/.bin/tldts b/node_modules/.bin/tldts new file mode 120000 index 0000000..8500124 --- /dev/null +++ b/node_modules/.bin/tldts @@ -0,0 +1 @@ +../tldts/bin/cli.js \ No newline at end of file diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json new file mode 100644 index 0000000..bd7af61 --- /dev/null +++ b/node_modules/.package-lock.json @@ -0,0 +1,537 @@ +{ + "name": "the-beacon", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.10", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.10.tgz", + "integrity": "sha512-02OhhkKtgNRuicQ/nF3TRnGsxL9wp0r3Y7VlKWyOHHGmGyvXv03y+PnymU8FKFJMTjIr1Bk8U2g1HWSLrpAHww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.1.1", + "@csstools/css-color-parser": "^4.0.2", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.9.tgz", + "integrity": "sha512-r3ElRr7y8ucyN2KdICwGsmj19RoN13CLCa/pvGydghWK6ZzeKQ+TcDjVdtEZz2ElpndM5jXw//B9CEee0mWnVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz", + "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz", + "integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz", + "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "29.0.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.2.tgz", + "integrity": "sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.5", + "@asamuzakjp/dom-selector": "^7.0.6", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.1", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.7", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.24.5", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/lru-cache": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tldts": { + "version": "7.0.28", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.28.tgz", + "integrity": "sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.28" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.28", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.28.tgz", + "integrity": "sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/node_modules/@asamuzakjp/css-color/LICENSE b/node_modules/@asamuzakjp/css-color/LICENSE new file mode 100644 index 0000000..0004c52 --- /dev/null +++ b/node_modules/@asamuzakjp/css-color/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 asamuzaK (Kazz) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/@asamuzakjp/css-color/README.md b/node_modules/@asamuzakjp/css-color/README.md new file mode 100644 index 0000000..715b6c3 --- /dev/null +++ b/node_modules/@asamuzakjp/css-color/README.md @@ -0,0 +1,316 @@ +# CSS color + +[](https://github.com/asamuzaK/cssColor/actions/workflows/node.js.yml) +[](https://github.com/asamuzaK/cssColor/actions/workflows/github-code-scanning/codeql) +[](https://www.npmjs.com/package/@asamuzakjp/css-color) + +Resolve and convert CSS colors. + +## Install + +```console +npm i @asamuzakjp/css-color +``` + +## Usage + +```javascript +import { convert, resolve, utils } from '@asamuzakjp/css-color'; + +const resolvedValue = resolve( + 'color-mix(in oklab, lch(67.5345 42.5 258.2), color(srgb 0 0.5 0))' +); +// 'oklab(0.620754 -0.0931934 -0.00374881)' + +const convertedValue = convert.colorToHex('lab(46.2775% -47.5621 48.5837)'); +// '#008000' + +const result = utils.isColor('green'); +// true +``` + + + +### resolve(color, opt) + +resolves CSS color + +#### Parameters + +- `color` **[string][133]** color value + - system colors are not supported +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.currentColor` **[string][133]?** + - color to use for `currentcolor` keyword + - if omitted, it will be treated as a missing color, + i.e. `rgb(none none none / none)` + - `opt.customProperty` **[object][135]?** + - custom properties + - pair of `--` prefixed property name as a key and it's value, + e.g. + ```javascript + const opt = { + customProperty: { + '--some-color': '#008000', + '--some-length': '16px' + } + }; + ``` + - and/or `callback` function to get the value of the custom property, + e.g. + ```javascript + const node = document.getElementById('foo'); + const opt = { + customProperty: { + callback: node.style.getPropertyValue + } + }; + ``` + - `opt.dimension` **[object][135]?** + - dimension, e.g. for converting relative length to pixels + - pair of unit as a key and number in pixels as it's value, + e.g. suppose `1em === 12px`, `1rem === 16px` and `100vw === 1024px`, then + ```javascript + const opt = { + dimension: { + em: 12, + rem: 16, + vw: 10.24 + } + }; + ``` + - and/or `callback` function to get the value as a number in pixels, + e.g. + ```javascript + const opt = { + dimension: { + callback: unit => { + switch (unit) { + case 'em': + return 12; + case 'rem': + return 16; + case 'vw': + return 10.24; + default: + return; + } + } + } + }; + ``` + - `opt.format` **[string][133]?** + - output format, one of below + - `computedValue` (default), [computed value][139] of the color + - `specifiedValue`, [specified value][140] of the color + - `hex`, hex color notation, i.e. `#rrggbb` + - `hexAlpha`, hex color notation with alpha channel, i.e. `#rrggbbaa` + +Returns **[string][133]?** one of `rgba?()`, `#rrggbb(aa)?`, `color-name`, `color(color-space r g b / alpha)`, `color(color-space x y z / alpha)`, `(ok)?lab(l a b / alpha)`, `(ok)?lch(l c h / alpha)`, `'(empty-string)'`, `null` + +- in `computedValue`, values are numbers, however `rgb()` values are integers +- in `specifiedValue`, returns `empty string` for unknown and/or invalid color +- in `hex`, returns `null` for `transparent`, and also returns `null` if any of `r`, `g`, `b`, `alpha` is not a number +- in `hexAlpha`, returns `#00000000` for `transparent`, however returns `null` if any of `r`, `g`, `b`, `alpha` is not a number + +### convert + +Contains various color conversion functions. + +### convert.numberToHex(value) + +convert number to hex string + +#### Parameters + +- `value` **[number][134]** color value + +Returns **[string][133]** hex string: 00..ff + +### convert.colorToHex(value, opt) + +convert color to hex + +#### Parameters + +- `value` **[string][133]** color value +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.alpha` **[boolean][136]?** return in #rrggbbaa notation + - `opt.customProperty` **[object][135]?** + - custom properties, see `resolve()` function above + - `opt.dimension` **[object][135]?** + - dimension, see `resolve()` function above + +Returns **[string][133]** #rrggbb(aa)? + +### convert.colorToHsl(value, opt) + +convert color to hsl + +#### Parameters + +- `value` **[string][133]** color value +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.customProperty` **[object][135]?** + - custom properties, see `resolve()` function above + - `opt.dimension` **[object][135]?** + - dimension, see `resolve()` function above + +Returns **[Array][137]<[number][134]>** \[h, s, l, alpha] + +### convert.colorToHwb(value, opt) + +convert color to hwb + +#### Parameters + +- `value` **[string][133]** color value +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.customProperty` **[object][135]?** + - custom properties, see `resolve()` function above + - `opt.dimension` **[object][135]?** + - dimension, see `resolve()` function above + +Returns **[Array][137]<[number][134]>** \[h, w, b, alpha] + +### convert.colorToLab(value, opt) + +convert color to lab + +#### Parameters + +- `value` **[string][133]** color value +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.customProperty` **[object][135]?** + - custom properties, see `resolve()` function above + - `opt.dimension` **[object][135]?** + - dimension, see `resolve()` function above + +Returns **[Array][137]<[number][134]>** \[l, a, b, alpha] + +### convert.colorToLch(value, opt) + +convert color to lch + +#### Parameters + +- `value` **[string][133]** color value +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.customProperty` **[object][135]?** + - custom properties, see `resolve()` function above + - `opt.dimension` **[object][135]?** + - dimension, see `resolve()` function above + +Returns **[Array][137]<[number][134]>** \[l, c, h, alpha] + +### convert.colorToOklab(value, opt) + +convert color to oklab + +#### Parameters + +- `value` **[string][133]** color value +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.customProperty` **[object][135]?** + - custom properties, see `resolve()` function above + - `opt.dimension` **[object][135]?** + - dimension, see `resolve()` function above + +Returns **[Array][137]<[number][134]>** \[l, a, b, alpha] + +### convert.colorToOklch(value, opt) + +convert color to oklch + +#### Parameters + +- `value` **[string][133]** color value +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.customProperty` **[object][135]?** + - custom properties, see `resolve()` function above + - `opt.dimension` **[object][135]?** + - dimension, see `resolve()` function above + +Returns **[Array][137]<[number][134]>** \[l, c, h, alpha] + +### convert.colorToRgb(value, opt) + +convert color to rgb + +#### Parameters + +- `value` **[string][133]** color value +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.customProperty` **[object][135]?** + - custom properties, see `resolve()` function above + - `opt.dimension` **[object][135]?** + - dimension, see `resolve()` function above + +Returns **[Array][137]<[number][134]>** \[r, g, b, alpha] + +### convert.colorToXyz(value, opt) + +convert color to xyz + +#### Parameters + +- `value` **[string][133]** color value +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.customProperty` **[object][135]?** + - custom properties, see `resolve()` function above + - `opt.dimension` **[object][135]?** + - dimension, see `resolve()` function above + - `opt.d50` **[boolean][136]?** xyz in d50 white point + +Returns **[Array][137]<[number][134]>** \[x, y, z, alpha] + +### convert.colorToXyzD50(value, opt) + +convert color to xyz-d50 + +#### Parameters + +- `value` **[string][133]** color value +- `opt` **[object][135]?** options (optional, default `{}`) + - `opt.customProperty` **[object][135]?** + - custom properties, see `resolve()` function above + - `opt.dimension` **[object][135]?** + - dimension, see `resolve()` function above + +Returns **[Array][137]<[number][134]>** \[x, y, z, alpha] + +### utils + +Contains utility functions. + +### utils.isColor(color) + +is valid color type + +#### Parameters + +- `color` **[string][133]** color value + - system colors are not supported + +Returns **[boolean][136]** + +## Acknowledgments + +The following resources have been of great help in the development of the CSS color. + +- [csstools/postcss-plugins](https://github.com/csstools/postcss-plugins) +- [lru-cache](https://github.com/isaacs/node-lru-cache) + +--- + +Copyright (c) 2024 [asamuzaK (Kazz)](https://github.com/asamuzaK/) + +[133]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String +[134]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number +[135]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object +[136]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean +[137]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array +[138]: https://w3c.github.io/csswg-drafts/css-color-4/#color-conversion-code +[139]: https://developer.mozilla.org/en-US/docs/Web/CSS/computed_value +[140]: https://developer.mozilla.org/en-US/docs/Web/CSS/specified_value +[141]: https://www.npmjs.com/package/@csstools/css-calc diff --git a/node_modules/@asamuzakjp/css-color/dist/esm/index.d.ts b/node_modules/@asamuzakjp/css-color/dist/esm/index.d.ts new file mode 100644 index 0000000..189905d --- /dev/null +++ b/node_modules/@asamuzakjp/css-color/dist/esm/index.d.ts @@ -0,0 +1,20 @@ +/*! + * CSS color - Resolve, parse, convert CSS color. + * @license MIT + * @copyright asamuzaK (Kazz) + * @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE} + */ +export { convert } from './js/convert.js'; +export { resolve } from './js/resolve.js'; +export declare const utils: { + cssCalc: (value: string, opt?: import('./js/typedef.js').Options) => string; + cssVar: (value: string, opt?: import('./js/typedef.js').Options) => string; + extractDashedIdent: (value: string) => string[]; + isAbsoluteFontSize: (css: unknown) => boolean; + isAbsoluteSizeOrLength: (value: number | string, unit: string | undefined) => boolean; + isColor: (value: unknown, opt?: import('./js/typedef.js').Options) => boolean; + isGradient: (value: string, opt?: import('./js/typedef.js').Options) => boolean; + resolveGradient: (value: string, opt?: import('./js/typedef.js').Options) => string; + resolveLengthInPixels: (value: number | string, unit: string | undefined, opt?: import('./js/typedef.js').Options) => number; + splitValue: (value: string, opt?: import('./js/typedef.js').Options) => string[]; +}; diff --git a/node_modules/@asamuzakjp/css-color/dist/esm/index.js b/node_modules/@asamuzakjp/css-color/dist/esm/index.js new file mode 100644 index 0000000..6abd960 --- /dev/null +++ b/node_modules/@asamuzakjp/css-color/dist/esm/index.js @@ -0,0 +1,29 @@ +import { resolve } from "./js/resolve.js"; +import { extractDashedIdent, isAbsoluteFontSize, isAbsoluteSizeOrLength, isColor, resolveLengthInPixels, splitValue } from "./js/util.js"; +import { cssVar } from "./js/css-var.js"; +import { cssCalc } from "./js/css-calc.js"; +import { isGradient, resolveGradient } from "./js/css-gradient.js"; +import { convert } from "./js/convert.js"; +//#region src/index.ts +/*! +* CSS color - Resolve, parse, convert CSS color. +* @license MIT +* @copyright asamuzaK (Kazz) +* @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE} +*/ +var utils = { + cssCalc, + cssVar, + extractDashedIdent, + isAbsoluteFontSize, + isAbsoluteSizeOrLength, + isColor, + isGradient, + resolveGradient, + resolveLengthInPixels, + splitValue +}; +//#endregion +export { convert, resolve, utils }; + +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/node_modules/@asamuzakjp/css-color/dist/esm/index.js.map b/node_modules/@asamuzakjp/css-color/dist/esm/index.js.map new file mode 100644 index 0000000..bc1aa81 --- /dev/null +++ b/node_modules/@asamuzakjp/css-color/dist/esm/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["/*!\n * CSS color - Resolve, parse, convert CSS color.\n * @license MIT\n * @copyright asamuzaK (Kazz)\n * @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}\n */\n\nimport { cssCalc } from './js/css-calc';\nimport { isGradient, resolveGradient } from './js/css-gradient';\nimport { cssVar } from './js/css-var';\nimport {\n extractDashedIdent,\n isAbsoluteFontSize,\n isAbsoluteSizeOrLength,\n isColor,\n resolveLengthInPixels,\n splitValue\n} from './js/util';\n\nexport { convert } from './js/convert';\nexport { resolve } from './js/resolve';\n/* utils */\nexport const utils = {\n cssCalc,\n cssVar,\n extractDashedIdent,\n isAbsoluteFontSize,\n isAbsoluteSizeOrLength,\n isColor,\n isGradient,\n resolveGradient,\n resolveLengthInPixels,\n splitValue\n};\n"],"mappings":";;;;;;;;;;;;;AAsBA,IAAa,QAAQ;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD"} \ No newline at end of file diff --git a/node_modules/@asamuzakjp/css-color/dist/esm/js/cache.d.ts b/node_modules/@asamuzakjp/css-color/dist/esm/js/cache.d.ts new file mode 100644 index 0000000..881cf73 --- /dev/null +++ b/node_modules/@asamuzakjp/css-color/dist/esm/js/cache.d.ts @@ -0,0 +1,52 @@ +import { Options } from './typedef.js'; +/** + * CacheItem + */ +export declare class CacheItem { + #private; + constructor(item: unknown, isNull?: boolean); + get item(): unknown; + get isNull(): boolean; +} +/** + * NullObject + */ +export declare class NullObject extends CacheItem { + constructor(); +} +/** + * Generational Cache implementation + */ +export declare class GenerationalCache