diff --git a/.github/workflows/lint-check.yml b/.github/workflows/lint-check.yml index 7fb420377..e6ffa7f07 100644 --- a/.github/workflows/lint-check.yml +++ b/.github/workflows/lint-check.yml @@ -53,6 +53,11 @@ jobs: cd frontend pnpm install --frozen-lockfile + - name: Check frontend formatting + run: | + cd frontend + pnpm format + - name: Run frontend linting run: | cd frontend diff --git a/backend/Makefile b/backend/Makefile index adc862857..8fe28c985 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -12,6 +12,7 @@ test: lint: uvx ruff check . + uvx ruff format --check . format: uvx ruff check . --fix && uvx ruff format . diff --git a/backend/app/channels/manager.py b/backend/app/channels/manager.py index 8ebe7a748..3e3fa17d4 100644 --- a/backend/app/channels/manager.py +++ b/backend/app/channels/manager.py @@ -66,14 +66,9 @@ def _normalize_custom_agent_name(raw_value: str) -> str: """Normalize legacy channel assistant IDs into valid custom agent names.""" normalized = raw_value.strip().lower().replace("_", "-") if not normalized: - raise InvalidChannelSessionConfigError( - "Channel session assistant_id is empty. Use 'lead_agent' or a valid custom agent name." - ) + raise InvalidChannelSessionConfigError("Channel session assistant_id is empty. Use 'lead_agent' or a valid custom agent name.") if not CUSTOM_AGENT_NAME_PATTERN.fullmatch(normalized): - raise InvalidChannelSessionConfigError( - f"Invalid channel session assistant_id {raw_value!r}. " - "Use 'lead_agent' or a custom agent name containing only letters, digits, and hyphens." - ) + raise InvalidChannelSessionConfigError(f"Invalid channel session assistant_id {raw_value!r}. Use 'lead_agent' or a custom agent name containing only letters, digits, and hyphens.") return normalized diff --git a/backend/app/gateway/routers/uploads.py b/backend/app/gateway/routers/uploads.py index 27b49418d..9d9d0c9bc 100644 --- a/backend/app/gateway/routers/uploads.py +++ b/backend/app/gateway/routers/uploads.py @@ -48,9 +48,7 @@ def _make_file_sandbox_writable(file_path: os.PathLike[str] | str) -> None: logger.warning("Skipping sandbox chmod for symlinked upload path: %s", file_path) return - writable_mode = ( - stat.S_IMODE(file_stat.st_mode) | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH - ) + writable_mode = stat.S_IMODE(file_stat.st_mode) | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH chmod_kwargs = {"follow_symlinks": False} if os.chmod in os.supports_follow_symlinks else {} os.chmod(file_path, writable_mode, **chmod_kwargs) diff --git a/backend/packages/harness/deerflow/agents/memory/storage.py b/backend/packages/harness/deerflow/agents/memory/storage.py index fcd1298e9..ba941a7a4 100644 --- a/backend/packages/harness/deerflow/agents/memory/storage.py +++ b/backend/packages/harness/deerflow/agents/memory/storage.py @@ -71,9 +71,7 @@ class FileMemoryStorage(MemoryStorage): if not agent_name: raise ValueError("Agent name must be a non-empty string.") if not AGENT_NAME_PATTERN.match(agent_name): - raise ValueError( - f"Invalid agent name {agent_name!r}: names must match {AGENT_NAME_PATTERN.pattern}" - ) + raise ValueError(f"Invalid agent name {agent_name!r}: names must match {AGENT_NAME_PATTERN.pattern}") def _get_memory_file_path(self, agent_name: str | None = None) -> Path: """Get the path to the memory file.""" @@ -180,18 +178,15 @@ def get_memory_storage() -> MemoryStorage: try: module_path, class_name = storage_class_path.rsplit(".", 1) import importlib + module = importlib.import_module(module_path) storage_class = getattr(module, class_name) # Validate that the configured storage is a MemoryStorage implementation if not isinstance(storage_class, type): - raise TypeError( - f"Configured memory storage '{storage_class_path}' is not a class: {storage_class!r}" - ) + raise TypeError(f"Configured memory storage '{storage_class_path}' is not a class: {storage_class!r}") if not issubclass(storage_class, MemoryStorage): - raise TypeError( - f"Configured memory storage '{storage_class_path}' is not a subclass of MemoryStorage" - ) + raise TypeError(f"Configured memory storage '{storage_class_path}' is not a subclass of MemoryStorage") _storage_instance = storage_class() except Exception as e: diff --git a/backend/packages/harness/deerflow/agents/memory/updater.py b/backend/packages/harness/deerflow/agents/memory/updater.py index 65da18ef4..75b9e7b54 100644 --- a/backend/packages/harness/deerflow/agents/memory/updater.py +++ b/backend/packages/harness/deerflow/agents/memory/updater.py @@ -27,10 +27,12 @@ def _save_memory_to_file(memory_data: dict[str, Any], agent_name: str | None = N """Backward-compatible wrapper around the configured memory storage save path.""" return get_memory_storage().save(memory_data, agent_name) + def get_memory_data(agent_name: str | None = None) -> dict[str, Any]: """Get the current memory data via storage provider.""" return get_memory_storage().load(agent_name) + def reload_memory_data(agent_name: str | None = None) -> dict[str, Any]: """Reload memory data via storage provider.""" return get_memory_storage().reload(agent_name) diff --git a/backend/packages/harness/deerflow/models/claude_provider.py b/backend/packages/harness/deerflow/models/claude_provider.py index a53939acf..1e732bd79 100644 --- a/backend/packages/harness/deerflow/models/claude_provider.py +++ b/backend/packages/harness/deerflow/models/claude_provider.py @@ -162,10 +162,7 @@ class ClaudeChatModel(ChatAnthropic): system = payload.get("system") if isinstance(system, list): # Remove any existing billing blocks, then insert a single one at index 0. - filtered = [ - b for b in system - if not (isinstance(b, dict) and OAUTH_BILLING_HEADER in b.get("text", "")) - ] + filtered = [b for b in system if not (isinstance(b, dict) and OAUTH_BILLING_HEADER in b.get("text", ""))] payload["system"] = [billing_block] + filtered elif isinstance(system, str): if OAUTH_BILLING_HEADER in system: @@ -183,11 +180,13 @@ class ClaudeChatModel(ChatAnthropic): hostname = socket.gethostname() device_id = hashlib.sha256(f"deerflow-{hostname}".encode()).hexdigest() session_id = str(uuid.uuid4()) - payload["metadata"]["user_id"] = json.dumps({ - "device_id": device_id, - "account_uuid": "deerflow", - "session_id": session_id, - }) + payload["metadata"]["user_id"] = json.dumps( + { + "device_id": device_id, + "account_uuid": "deerflow", + "session_id": session_id, + } + ) def _apply_prompt_caching(self, payload: dict) -> None: """Apply ephemeral cache_control to system and recent messages.""" diff --git a/backend/packages/harness/deerflow/models/patched_openai.py b/backend/packages/harness/deerflow/models/patched_openai.py index f2fe2427c..9a7801f48 100644 --- a/backend/packages/harness/deerflow/models/patched_openai.py +++ b/backend/packages/harness/deerflow/models/patched_openai.py @@ -84,9 +84,7 @@ class PatchedChatOpenAI(ChatOpenAI): else: # Fallback: match assistant-role entries positionally against AIMessages. ai_messages = [m for m in original_messages if isinstance(m, AIMessage)] - assistant_payloads = [ - (i, m) for i, m in enumerate(payload_messages) if m.get("role") == "assistant" - ] + assistant_payloads = [(i, m) for i, m in enumerate(payload_messages) if m.get("role") == "assistant"] for (_, payload_msg), ai_msg in zip(assistant_payloads, ai_messages): _restore_tool_call_signatures(payload_msg, ai_msg) diff --git a/backend/packages/harness/deerflow/sandbox/tools.py b/backend/packages/harness/deerflow/sandbox/tools.py index 6cdcac1e9..414326a92 100644 --- a/backend/packages/harness/deerflow/sandbox/tools.py +++ b/backend/packages/harness/deerflow/sandbox/tools.py @@ -100,7 +100,7 @@ def _resolve_skills_path(path: str) -> str: if path == skills_container: return skills_host - relative = path[len(skills_container):].lstrip("/") + relative = path[len(skills_container) :].lstrip("/") return _join_path_preserving_style(skills_host, relative) diff --git a/backend/packages/harness/deerflow/tools/builtins/task_tool.py b/backend/packages/harness/deerflow/tools/builtins/task_tool.py index 55a41e3da..e1f8987a4 100644 --- a/backend/packages/harness/deerflow/tools/builtins/task_tool.py +++ b/backend/packages/harness/deerflow/tools/builtins/task_tool.py @@ -197,6 +197,7 @@ async def task_tool( writer({"type": "task_timed_out", "task_id": task_id}) return f"Task polling timed out after {timeout_minutes} minutes. This may indicate the background task is stuck. Status: {result.status.value}" except asyncio.CancelledError: + async def cleanup_when_done() -> None: max_cleanup_polls = max_poll_count cleanup_poll_count = 0 @@ -211,9 +212,7 @@ async def task_tool( return if cleanup_poll_count > max_cleanup_polls: - logger.warning( - f"[trace={trace_id}] Deferred cleanup for task {task_id} timed out after {cleanup_poll_count} polls" - ) + logger.warning(f"[trace={trace_id}] Deferred cleanup for task {task_id} timed out after {cleanup_poll_count} polls") return await asyncio.sleep(5) diff --git a/backend/packages/harness/deerflow/tools/builtins/tool_search.py b/backend/packages/harness/deerflow/tools/builtins/tool_search.py index d9fb986c2..e58bf014c 100644 --- a/backend/packages/harness/deerflow/tools/builtins/tool_search.py +++ b/backend/packages/harness/deerflow/tools/builtins/tool_search.py @@ -118,9 +118,7 @@ def _regex_score(pattern: str, entry: DeferredToolEntry) -> int: # loop.run_in_executor, Python copies the current context to the worker thread, # so the ContextVar value is correctly inherited there too. -_registry_var: contextvars.ContextVar[DeferredToolRegistry | None] = contextvars.ContextVar( - "deferred_tool_registry", default=None -) +_registry_var: contextvars.ContextVar[DeferredToolRegistry | None] = contextvars.ContextVar("deferred_tool_registry", default=None) def get_deferred_registry() -> DeferredToolRegistry | None: diff --git a/backend/tests/test_channels.py b/backend/tests/test_channels.py index 7ec5c4149..c48137c76 100644 --- a/backend/tests/test_channels.py +++ b/backend/tests/test_channels.py @@ -600,10 +600,7 @@ class TestChannelManager: await manager.stop() mock_client.runs.wait.assert_not_called() - assert outbound_received[0].text == ( - "Invalid channel session assistant_id 'bad agent!'. " - "Use 'lead_agent' or a custom agent name containing only letters, digits, and hyphens." - ) + assert outbound_received[0].text == ("Invalid channel session assistant_id 'bad agent!'. Use 'lead_agent' or a custom agent name containing only letters, digits, and hyphens.") _run(go()) diff --git a/backend/tests/test_claude_provider_oauth_billing.py b/backend/tests/test_claude_provider_oauth_billing.py index a341ffe2c..9f9329bb1 100644 --- a/backend/tests/test_claude_provider_oauth_billing.py +++ b/backend/tests/test_claude_provider_oauth_billing.py @@ -56,10 +56,7 @@ def test_billing_not_duplicated_on_second_call(model): payload = {"system": [{"type": "text", "text": "prompt"}]} model._apply_oauth_billing(payload) model._apply_oauth_billing(payload) - billing_count = sum( - 1 for b in payload["system"] - if isinstance(b, dict) and OAUTH_BILLING_HEADER in b.get("text", "") - ) + billing_count = sum(1 for b in payload["system"] if isinstance(b, dict) and OAUTH_BILLING_HEADER in b.get("text", "")) assert billing_count == 1 diff --git a/backend/tests/test_client.py b/backend/tests/test_client.py index 3a1d6e346..b03ab0333 100644 --- a/backend/tests/test_client.py +++ b/backend/tests/test_client.py @@ -65,14 +65,7 @@ class TestClientInit: def test_custom_params(self, mock_app_config): mock_middleware = MagicMock() with patch("deerflow.client.get_app_config", return_value=mock_app_config): - c = DeerFlowClient( - model_name="gpt-4", - thinking_enabled=False, - subagent_enabled=True, - plan_mode=True, - agent_name="test-agent", - middlewares=[mock_middleware] - ) + c = DeerFlowClient(model_name="gpt-4", thinking_enabled=False, subagent_enabled=True, plan_mode=True, agent_name="test-agent", middlewares=[mock_middleware]) assert c._model_name == "gpt-4" assert c._thinking_enabled is False assert c._subagent_enabled is True diff --git a/backend/tests/test_lead_agent_model_resolution.py b/backend/tests/test_lead_agent_model_resolution.py index b964f53ca..9373c2895 100644 --- a/backend/tests/test_lead_agent_model_resolution.py +++ b/backend/tests/test_lead_agent_model_resolution.py @@ -132,18 +132,13 @@ def test_build_middlewares_uses_resolved_model_name_for_vision(monkeypatch): monkeypatch.setattr(lead_agent_module, "_create_summarization_middleware", lambda: None) monkeypatch.setattr(lead_agent_module, "_create_todo_list_middleware", lambda is_plan_mode: None) - middlewares = lead_agent_module._build_middlewares( - {"configurable": {"model_name": "stale-model", "is_plan_mode": False, "subagent_enabled": False}}, - model_name="vision-model", - custom_middlewares=[MagicMock()] - ) + middlewares = lead_agent_module._build_middlewares({"configurable": {"model_name": "stale-model", "is_plan_mode": False, "subagent_enabled": False}}, model_name="vision-model", custom_middlewares=[MagicMock()]) assert any(isinstance(m, lead_agent_module.ViewImageMiddleware) for m in middlewares) # verify the custom middleware is injected correctly assert len(middlewares) > 0 and isinstance(middlewares[-2], MagicMock) - def test_create_summarization_middleware_uses_configured_model_alias(monkeypatch): monkeypatch.setattr( lead_agent_module, diff --git a/backend/tests/test_memory_storage.py b/backend/tests/test_memory_storage.py index dd6b9f6af..f8e826e6a 100644 --- a/backend/tests/test_memory_storage.py +++ b/backend/tests/test_memory_storage.py @@ -33,6 +33,7 @@ class TestMemoryStorageInterface: def test_abstract_methods(self): """Should raise TypeError when trying to instantiate abstract class.""" + class TestStorage(MemoryStorage): pass @@ -45,6 +46,7 @@ class TestFileMemoryStorage: def test_get_memory_file_path_global(self, tmp_path): """Should return global memory file path when agent_name is None.""" + def mock_get_paths(): mock_paths = MagicMock() mock_paths.memory_file = tmp_path / "memory.json" @@ -58,6 +60,7 @@ class TestFileMemoryStorage: def test_get_memory_file_path_agent(self, tmp_path): """Should return per-agent memory file path when agent_name is provided.""" + def mock_get_paths(): mock_paths = MagicMock() mock_paths.agent_memory_file.return_value = tmp_path / "agents" / "test-agent" / "memory.json" @@ -68,9 +71,7 @@ class TestFileMemoryStorage: path = storage._get_memory_file_path("test-agent") assert path == tmp_path / "agents" / "test-agent" / "memory.json" - @pytest.mark.parametrize( - "invalid_name", ["", "../etc/passwd", "agent/name", "agent\\name", "agent name", "agent@123", "agent_name"] - ) + @pytest.mark.parametrize("invalid_name", ["", "../etc/passwd", "agent/name", "agent\\name", "agent name", "agent@123", "agent_name"]) def test_validate_agent_name_invalid(self, invalid_name): """Should raise ValueError for invalid agent names that don't match the pattern.""" storage = FileMemoryStorage() @@ -79,6 +80,7 @@ class TestFileMemoryStorage: def test_load_creates_empty_memory(self, tmp_path): """Should create empty memory when file doesn't exist.""" + def mock_get_paths(): mock_paths = MagicMock() mock_paths.memory_file = tmp_path / "non_existent_memory.json" @@ -125,10 +127,10 @@ class TestFileMemoryStorage: # First load memory1 = storage.load() assert memory1["facts"][0]["content"] == "initial fact" - + # Update file directly memory_file.write_text('{"version": "1.0", "facts": [{"content": "updated fact"}]}') - + # Reload should get updated data memory2 = storage.reload() assert memory2["facts"][0]["content"] == "updated fact" @@ -141,6 +143,7 @@ class TestGetMemoryStorage: def reset_storage_instance(self): """Reset the global storage instance before and after each test.""" import deerflow.agents.memory.storage as storage_mod + storage_mod._storage_instance = None yield storage_mod._storage_instance = None @@ -167,6 +170,7 @@ class TestGetMemoryStorage: def test_get_memory_storage_thread_safety(self): """Should safely initialize the singleton even with concurrent calls.""" results = [] + def get_storage(): # get_memory_storage is called concurrently from multiple threads while # get_memory_config is patched once around thread creation. This verifies diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 000000000..bd5535a60 --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1 @@ +pnpm-lock.yaml diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md index 4943cdf89..7220c4af3 100644 --- a/frontend/CLAUDE.md +++ b/frontend/CLAUDE.md @@ -10,15 +10,15 @@ DeerFlow Frontend is a Next.js 16 web interface for an AI agent system. It commu ## Commands -| Command | Purpose | -|---------|---------| -| `pnpm dev` | Dev server with Turbopack (http://localhost:3000) | -| `pnpm build` | Production build | -| `pnpm check` | Lint + type check (run before committing) | -| `pnpm lint` | ESLint only | -| `pnpm lint:fix` | ESLint with auto-fix | -| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) | -| `pnpm start` | Start production server | +| Command | Purpose | +| ---------------- | ------------------------------------------------- | +| `pnpm dev` | Dev server with Turbopack (http://localhost:3000) | +| `pnpm build` | Production build | +| `pnpm check` | Lint + type check (run before committing) | +| `pnpm lint` | ESLint only | +| `pnpm lint:fix` | ESLint with auto-fix | +| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) | +| `pnpm start` | Start production server | No test framework is configured. @@ -81,6 +81,7 @@ The frontend is a stateful chat application. Users create **threads** (conversat ## Environment Backend API URLs are optional; an nginx proxy is used by default: + ``` NEXT_PUBLIC_BACKEND_BASE_URL=http://localhost:8001 NEXT_PUBLIC_LANGGRAPH_BASE_URL=http://localhost:2024 diff --git a/frontend/README.md b/frontend/README.md index a7ded3cbd..c58789a8b 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -114,17 +114,17 @@ src/ ## Scripts -| Command | Description | -|---------|-------------| -| `pnpm dev` | Start development server with Turbopack | -| `pnpm build` | Build for production | -| `pnpm start` | Start production server | -| `pnpm format` | Check formatting with Prettier | -| `pnpm format:write` | Apply formatting with Prettier | -| `pnpm lint` | Run ESLint | -| `pnpm lint:fix` | Fix ESLint issues | -| `pnpm typecheck` | Run TypeScript type checking | -| `pnpm check` | Run both lint and typecheck | +| Command | Description | +| ------------------- | --------------------------------------- | +| `pnpm dev` | Start development server with Turbopack | +| `pnpm build` | Build for production | +| `pnpm start` | Start production server | +| `pnpm format` | Check formatting with Prettier | +| `pnpm format:write` | Apply formatting with Prettier | +| `pnpm lint` | Run ESLint | +| `pnpm lint:fix` | Fix ESLint issues | +| `pnpm typecheck` | Run TypeScript type checking | +| `pnpm check` | Run both lint and typecheck | ## Development Notes diff --git a/frontend/public/demo/threads/3823e443-4e2b-4679-b496-a9506eae462b/user-data/outputs/fei-fei-li-podcast-timeline.md b/frontend/public/demo/threads/3823e443-4e2b-4679-b496-a9506eae462b/user-data/outputs/fei-fei-li-podcast-timeline.md index 657bc3227..8b4e5a8d9 100644 --- a/frontend/public/demo/threads/3823e443-4e2b-4679-b496-a9506eae462b/user-data/outputs/fei-fei-li-podcast-timeline.md +++ b/frontend/public/demo/threads/3823e443-4e2b-4679-b496-a9506eae462b/user-data/outputs/fei-fei-li-podcast-timeline.md @@ -1,6 +1,7 @@ # Dr. Fei-Fei Li: Recent Podcast Appearances Timeline (Last 6 Months) ## Overview + Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing on major podcasts discussing the future of artificial intelligence, spatial intelligence, human-centered AI, and her work at World Labs. This timeline compiles key highlights from her recent podcast appearances from August 2025 to January 2026. --- @@ -8,9 +9,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing ## Timeline of Recent Podcast Appearances ### January 15, 2025 - **Possible Podcast** (with Reid Hoffman and Aria Finger) + **Episode:** "Fei-Fei Li on spatial intelligence and human-centered AI" **Key Highlights:** + - **Spatial Intelligence as Next Frontier:** Emphasized that spatial intelligence represents the next major evolution beyond large language models (LLMs) - **Human-Centered AI Philosophy:** Discussed the importance of building AI that amplifies human potential rather than replacing humans - **Regulatory Guardrails:** Addressed the need for thoughtful regulation and governance frameworks for AI development @@ -22,9 +25,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing --- ### August 15, 2025 - **Firing Line (PBS)** + **Episode:** "Fei-Fei Li on ethical AI development" **Key Highlights:** + - **Ethical AI Development:** Discussed the challenges and responsibilities in developing AI ethically - **Societal Impact:** Addressed how AI will transform various sectors including healthcare, education, and employment - **Policy Recommendations:** Provided insights on what policy frameworks are needed for responsible AI deployment @@ -33,9 +38,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing --- ### November 16, 2025 - **Lenny's Podcast** + **Episode:** "The Godmother of AI on jobs, robots & why world models are next" **Key Highlights:** + - **World Models Introduction:** Explained why world models and spatial intelligence represent the next frontier beyond LLMs - **AI Won't Replace Humans:** Argued that AI won't replace humans but will require us to take responsibility for ourselves - **Marble Applications:** Revealed surprising applications of World Labs' Marble product, from movie production to psychological research @@ -44,6 +51,7 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing - **Participation for All:** Explained how anyone can participate in AI regardless of their role or background **Key Discussion Points:** + 1. How ImageNet helped spark the current AI explosion 2. The "bitter lesson" in AI and robotics 3. Applications of Marble in creative industries and therapy @@ -52,9 +60,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing --- ### November 25, 2025 - **Masters of Scale Summit** + **Episode:** "The 'Godmother of AI' on the next phase of AI" (with Reid Hoffman) **Key Highlights:** + - **Fearless Approach:** Discussed why scientists and entrepreneurs need to be fearless in the face of an uncertain AI future - **Spatial Intelligence & World Modeling:** Detailed the next phase of AI focusing on spatial understanding - **Trust Building:** Explained how leaders should build societal trust in AI products and companies @@ -62,6 +72,7 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing - **Entrepreneurial Responsibility:** Argued that entrepreneurs should care about trust from day one of AI development **Chapter Topics Covered:** + - The next phase of AI: spatial intelligence & world modeling - What spatial intelligence has done for humans - Whether AI is over-hyped @@ -71,9 +82,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing --- ### December 9, 2025 - **The Tim Ferriss Show** (#839) + **Episode:** "Dr. Fei-Fei Li, The Godmother of AI — Asking Audacious Questions, Civilizational Technology, and Finding Your North Star" **Key Highlights:** + - **Civilizational Technology:** Defined AI as a "civilizational technology" that will have profound economic, social, cultural, and political impacts - **Personal Journey:** Shared her immigrant story from Chengdu to New Jersey, and her family's seven years running a dry cleaning shop while she attended Princeton - **ImageNet Creation:** Detailed the creation of ImageNet and how it birthed modern AI, including innovative use of Amazon Mechanical Turk for data labeling @@ -82,11 +95,13 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing - **Human-Centered Focus:** Emphasized that "people are at the heart of everything" in AI development **Notable Quotes:** + - "Really, at the end of the day, people are at the heart of everything. People made AI, people will be using AI, people will be impacted by AI, and people should have a say in AI." - "AI is absolutely a civilizational technology... it'll have—or [is] already having—a profound impact in the economic, social, cultural, political, downstream effects of our society." - "What is your North Star?" **Key Topics Discussed:** + - From fighter jets to physics to asking "What is intelligence?" - The epiphany everyone missed: Big data as the hidden hypothesis - Against the single-genius myth: Science as non-linear lineage @@ -97,9 +112,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing --- ### June 16, 2025 - **Y Combinator Startup Podcast** + **Episode:** "Fei-Fei Li - Spatial Intelligence is the Next Frontier in AI" **Key Highlights:** + - **Startup Perspective:** Provided insights for AI startups on navigating the current landscape - **Technical Deep Dive:** Offered detailed explanations of spatial intelligence technologies - **Entrepreneurial Advice:** Shared lessons from transitioning from academia to entrepreneurship @@ -110,26 +127,31 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing ## Common Themes Across Recent Appearances ### 1. **Spatial Intelligence as the Next Frontier** + - Repeated emphasis that spatial intelligence represents the next major evolution beyond language models - World Labs' focus on creating AI that understands and interacts with the physical world - Applications ranging from robotics and autonomous systems to creative industries and therapy ### 2. **Human-Centered AI Philosophy** + - Consistent message that AI should augment rather than replace human capabilities - Emphasis on maintaining human agency and responsibility in AI systems - Focus on building trust and ethical frameworks ### 3. **Educational Transformation** + - Advocacy for integrating AI into education to enhance learning - Proposal to use AI as a benchmark for student improvement - Emphasis on making AI accessible to people from all backgrounds ### 4. **Historical Perspective** + - Frequent references to ImageNet's role in sparking the deep learning revolution - Context about how rapidly the AI landscape has changed - Emphasis on collaborative, non-linear progress in scientific advancement ### 5. **Entrepreneurial Vision** + - Insights on building AI companies in the current environment - Balance between technological innovation and responsible development - Focus on practical applications that solve real-world problems @@ -139,18 +161,21 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing ## Key Insights and Predictions ### **Near-Term Developments (1-3 years):** + - Rapid advancement in spatial intelligence and world modeling technologies - Increased integration of AI in education and creative industries - Growing focus on AI ethics and governance frameworks - Expansion of practical applications in healthcare, therapy, and accessibility ### **Medium-Term Vision (3-5 years):** + - More sophisticated human-AI collaboration systems - Breakthroughs in robotics enabled by spatial intelligence - Transformation of how we teach and learn with AI assistance - Development of new industries centered around spatial AI ### **Long-Term Philosophy:** + - AI as a "civilizational technology" that requires thoughtful stewardship - Emphasis on maintaining human values and agency in technological progress - Vision of technology that helps humanity "raise above our paleolithic emotions" @@ -166,6 +191,7 @@ The timeline shows her evolving role from academic researcher to entrepreneur wh --- ## Sources + 1. The Tim Ferriss Show (December 9, 2025) 2. Lenny's Podcast (November 16, 2025) 3. Masters of Scale Summit (November 25, 2025) @@ -173,4 +199,4 @@ The timeline shows her evolving role from academic researcher to entrepreneur wh 5. Firing Line, PBS (August 15, 2025) 6. Y Combinator Startup Podcast (June 16, 2025) -*Compiled on January 25, 2026* \ No newline at end of file +_Compiled on January 25, 2026_ diff --git a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/thread.json b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/thread.json index 4bdfa6e7c..0e5edd2d9 100644 --- a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/thread.json +++ b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/thread.json @@ -802,4 +802,4 @@ "interrupts": [], "checkpoint_id": "1f0f46d7-77ea-64ca-802c-0462f9bf4fdd", "parent_checkpoint_id": "1f0f46d7-77e2-6496-802b-68a165ed83e9" -} \ No newline at end of file +} diff --git a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/css/style.css b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/css/style.css index 567280bdf..da56c5313 100644 --- a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/css/style.css +++ b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/css/style.css @@ -1,1838 +1,1919 @@ /* 江苏城市足球联赛2025赛季 - 主样式文件 */ :root { - /* 主色调 - 江苏蓝与活力橙 */ - --color-primary: #1a56db; - --color-primary-dark: #1e3a8a; - --color-primary-light: #3b82f6; - --color-secondary: #f59e0b; - --color-secondary-dark: #d97706; - --color-secondary-light: #fbbf24; - - /* 中性色 */ - --color-white: #ffffff; - --color-gray-50: #f9fafb; - --color-gray-100: #f3f4f6; - --color-gray-200: #e5e7eb; - --color-gray-300: #d1d5db; - --color-gray-400: #9ca3af; - --color-gray-500: #6b7280; - --color-gray-600: #4b5563; - --color-gray-700: #374151; - --color-gray-800: #1f2937; - --color-gray-900: #111827; - --color-black: #000000; - - /* 功能色 */ - --color-success: #10b981; - --color-warning: #f59e0b; - --color-danger: #ef4444; - --color-info: #3b82f6; - - /* 字体 */ - --font-heading: 'Oswald', sans-serif; - --font-body: 'Inter', sans-serif; - --font-display: 'Montserrat', sans-serif; - - /* 尺寸 */ - --container-max: 1280px; - --border-radius-sm: 4px; - --border-radius-md: 8px; - --border-radius-lg: 16px; - --border-radius-xl: 24px; - --border-radius-2xl: 32px; - - /* 阴影 */ - --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); - --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); - --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); - --shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25); - - /* 过渡 */ - --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); - --transition-normal: 300ms cubic-bezier(0.4, 0, 0.2, 1); - --transition-slow: 500ms cubic-bezier(0.4, 0, 0.2, 1); - - /* 动效 */ - --animation-bounce: bounce 1s infinite; - --animation-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; - --animation-spin: spin 1s linear infinite; + /* 主色调 - 江苏蓝与活力橙 */ + --color-primary: #1a56db; + --color-primary-dark: #1e3a8a; + --color-primary-light: #3b82f6; + --color-secondary: #f59e0b; + --color-secondary-dark: #d97706; + --color-secondary-light: #fbbf24; + + /* 中性色 */ + --color-white: #ffffff; + --color-gray-50: #f9fafb; + --color-gray-100: #f3f4f6; + --color-gray-200: #e5e7eb; + --color-gray-300: #d1d5db; + --color-gray-400: #9ca3af; + --color-gray-500: #6b7280; + --color-gray-600: #4b5563; + --color-gray-700: #374151; + --color-gray-800: #1f2937; + --color-gray-900: #111827; + --color-black: #000000; + + /* 功能色 */ + --color-success: #10b981; + --color-warning: #f59e0b; + --color-danger: #ef4444; + --color-info: #3b82f6; + + /* 字体 */ + --font-heading: "Oswald", sans-serif; + --font-body: "Inter", sans-serif; + --font-display: "Montserrat", sans-serif; + + /* 尺寸 */ + --container-max: 1280px; + --border-radius-sm: 4px; + --border-radius-md: 8px; + --border-radius-lg: 16px; + --border-radius-xl: 24px; + --border-radius-2xl: 32px; + + /* 阴影 */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: + 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-lg: + 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + --shadow-xl: + 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + --shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + + /* 过渡 */ + --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-normal: 300ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-slow: 500ms cubic-bezier(0.4, 0, 0.2, 1); + + /* 动效 */ + --animation-bounce: bounce 1s infinite; + --animation-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; + --animation-spin: spin 1s linear infinite; } /* 暗色主题变量 */ [data-theme="dark"] { - --color-white: #111827; - --color-gray-50: #1f2937; - --color-gray-100: #374151; - --color-gray-200: #4b5563; - --color-gray-300: #6b7280; - --color-gray-400: #9ca3af; - --color-gray-500: #d1d5db; - --color-gray-600: #e5e7eb; - --color-gray-700: #f3f4f6; - --color-gray-800: #f9fafb; - --color-gray-900: #ffffff; - --color-black: #f9fafb; + --color-white: #111827; + --color-gray-50: #1f2937; + --color-gray-100: #374151; + --color-gray-200: #4b5563; + --color-gray-300: #6b7280; + --color-gray-400: #9ca3af; + --color-gray-500: #d1d5db; + --color-gray-600: #e5e7eb; + --color-gray-700: #f3f4f6; + --color-gray-800: #f9fafb; + --color-gray-900: #ffffff; + --color-black: #f9fafb; } /* 重置与基础样式 */ * { - margin: 0; - padding: 0; - box-sizing: border-box; + margin: 0; + padding: 0; + box-sizing: border-box; } html { - scroll-behavior: smooth; - font-size: 16px; + scroll-behavior: smooth; + font-size: 16px; } body { - font-family: var(--font-body); - font-size: 1rem; - line-height: 1.5; - color: var(--color-gray-800); - background-color: var(--color-white); - overflow-x: hidden; - transition: background-color var(--transition-normal), color var(--transition-normal); + font-family: var(--font-body); + font-size: 1rem; + line-height: 1.5; + color: var(--color-gray-800); + background-color: var(--color-white); + overflow-x: hidden; + transition: + background-color var(--transition-normal), + color var(--transition-normal); } .container { - width: 100%; - max-width: var(--container-max); - margin: 0 auto; - padding: 0 1.5rem; + width: 100%; + max-width: var(--container-max); + margin: 0 auto; + padding: 0 1.5rem; } /* 加载动画 */ .loader { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%); - display: flex; - align-items: center; - justify-content: center; - z-index: 9999; - opacity: 1; - visibility: visible; - transition: opacity var(--transition-normal), visibility var(--transition-normal); + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-primary-dark) 100% + ); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + opacity: 1; + visibility: visible; + transition: + opacity var(--transition-normal), + visibility var(--transition-normal); } .loader.loaded { - opacity: 0; - visibility: hidden; + opacity: 0; + visibility: hidden; } .loader-content { - text-align: center; + text-align: center; } .football { - width: 80px; - height: 80px; - background: linear-gradient(45deg, var(--color-white) 25%, var(--color-gray-200) 25%, var(--color-gray-200) 50%, var(--color-white) 50%, var(--color-white) 75%, var(--color-gray-200) 75%); - background-size: 20px 20px; - border-radius: 50%; - margin: 0 auto 2rem; - animation: var(--animation-spin); - position: relative; + width: 80px; + height: 80px; + background: linear-gradient( + 45deg, + var(--color-white) 25%, + var(--color-gray-200) 25%, + var(--color-gray-200) 50%, + var(--color-white) 50%, + var(--color-white) 75%, + var(--color-gray-200) 75% + ); + background-size: 20px 20px; + border-radius: 50%; + margin: 0 auto 2rem; + animation: var(--animation-spin); + position: relative; } .football::before { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 30px; - height: 30px; - background: var(--color-secondary); - border-radius: 50%; - border: 3px solid var(--color-white); + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 30px; + height: 30px; + background: var(--color-secondary); + border-radius: 50%; + border: 3px solid var(--color-white); } .loader-text { - font-family: var(--font-heading); - font-size: 1.5rem; - font-weight: 500; - color: var(--color-white); - letter-spacing: 2px; - text-transform: uppercase; + font-family: var(--font-heading); + font-size: 1.5rem; + font-weight: 500; + color: var(--color-white); + letter-spacing: 2px; + text-transform: uppercase; } /* 导航栏 */ .navbar { - position: fixed; - top: 0; - left: 0; - width: 100%; - background: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(10px); - border-bottom: 1px solid var(--color-gray-200); - z-index: 1000; - transition: all var(--transition-normal); + position: fixed; + top: 0; + left: 0; + width: 100%; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(10px); + border-bottom: 1px solid var(--color-gray-200); + z-index: 1000; + transition: all var(--transition-normal); } [data-theme="dark"] .navbar { - background: rgba(17, 24, 39, 0.95); - border-bottom-color: var(--color-gray-700); + background: rgba(17, 24, 39, 0.95); + border-bottom-color: var(--color-gray-700); } .navbar .container { - display: flex; - align-items: center; - justify-content: space-between; - height: 80px; + display: flex; + align-items: center; + justify-content: space-between; + height: 80px; } .nav-brand { - display: flex; - align-items: center; - gap: 1rem; + display: flex; + align-items: center; + gap: 1rem; } .logo { - display: flex; - align-items: center; - gap: 0.75rem; - cursor: pointer; + display: flex; + align-items: center; + gap: 0.75rem; + cursor: pointer; } .logo-ball { - width: 36px; - height: 36px; - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%); - border-radius: 50%; - position: relative; - animation: var(--animation-pulse); + width: 36px; + height: 36px; + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-secondary) 100% + ); + border-radius: 50%; + position: relative; + animation: var(--animation-pulse); } .logo-ball::before { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 12px; - height: 12px; - background: var(--color-white); - border-radius: 50%; + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 12px; + height: 12px; + background: var(--color-white); + border-radius: 50%; } .logo-text { - font-family: var(--font-heading); - font-size: 1.5rem; - font-weight: 700; - color: var(--color-primary); - letter-spacing: 1px; + font-family: var(--font-heading); + font-size: 1.5rem; + font-weight: 700; + color: var(--color-primary); + letter-spacing: 1px; } [data-theme="dark"] .logo-text { - color: var(--color-white); + color: var(--color-white); } .league-name { - font-family: var(--font-body); - font-size: 0.875rem; - font-weight: 500; - color: var(--color-gray-600); - padding-left: 1rem; - border-left: 1px solid var(--color-gray-300); + font-family: var(--font-body); + font-size: 0.875rem; + font-weight: 500; + color: var(--color-gray-600); + padding-left: 1rem; + border-left: 1px solid var(--color-gray-300); } [data-theme="dark"] .league-name { - color: var(--color-gray-400); - border-left-color: var(--color-gray-600); + color: var(--color-gray-400); + border-left-color: var(--color-gray-600); } .nav-menu { - display: flex; - gap: 2rem; + display: flex; + gap: 2rem; } .nav-link { - font-family: var(--font-heading); - font-size: 1rem; - font-weight: 500; - color: var(--color-gray-700); - text-decoration: none; - text-transform: uppercase; - letter-spacing: 1px; - padding: 0.5rem 0; - position: relative; - transition: color var(--transition-fast); + font-family: var(--font-heading); + font-size: 1rem; + font-weight: 500; + color: var(--color-gray-700); + text-decoration: none; + text-transform: uppercase; + letter-spacing: 1px; + padding: 0.5rem 0; + position: relative; + transition: color var(--transition-fast); } .nav-link::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 0; - height: 2px; - background: var(--color-primary); - transition: width var(--transition-fast); + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: var(--color-primary); + transition: width var(--transition-fast); } .nav-link:hover { - color: var(--color-primary); + color: var(--color-primary); } .nav-link:hover::after { - width: 100%; + width: 100%; } .nav-link.active { - color: var(--color-primary); + color: var(--color-primary); } .nav-link.active::after { - width: 100%; + width: 100%; } [data-theme="dark"] .nav-link { - color: var(--color-gray-300); + color: var(--color-gray-300); } [data-theme="dark"] .nav-link:hover, [data-theme="dark"] .nav-link.active { - color: var(--color-primary-light); + color: var(--color-primary-light); } .nav-actions { - display: flex; - align-items: center; - gap: 1rem; + display: flex; + align-items: center; + gap: 1rem; } .btn-theme-toggle, .btn-menu-toggle { - width: 40px; - height: 40px; - border-radius: var(--border-radius-md); - border: 1px solid var(--color-gray-300); - background: var(--color-white); - color: var(--color-gray-700); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all var(--transition-fast); + width: 40px; + height: 40px; + border-radius: var(--border-radius-md); + border: 1px solid var(--color-gray-300); + background: var(--color-white); + color: var(--color-gray-700); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all var(--transition-fast); } .btn-theme-toggle:hover, .btn-menu-toggle:hover { - border-color: var(--color-primary); - color: var(--color-primary); - transform: translateY(-2px); + border-color: var(--color-primary); + color: var(--color-primary); + transform: translateY(-2px); } [data-theme="dark"] .btn-theme-toggle, [data-theme="dark"] .btn-menu-toggle { - border-color: var(--color-gray-600); - background: var(--color-gray-800); - color: var(--color-gray-300); + border-color: var(--color-gray-600); + background: var(--color-gray-800); + color: var(--color-gray-300); } .btn-menu-toggle { - display: none; + display: none; } /* 按钮样式 */ .btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - padding: 0.75rem 1.5rem; - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; - border-radius: var(--border-radius-md); - border: 2px solid transparent; - cursor: pointer; - transition: all var(--transition-fast); - text-decoration: none; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + border-radius: var(--border-radius-md); + border: 2px solid transparent; + cursor: pointer; + transition: all var(--transition-fast); + text-decoration: none; } .btn-primary { - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%); - color: var(--color-white); - box-shadow: var(--shadow-md); + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-primary-light) 100% + ); + color: var(--color-white); + box-shadow: var(--shadow-md); } .btn-primary:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-lg); + transform: translateY(-2px); + box-shadow: var(--shadow-lg); } .btn-secondary { - background: linear-gradient(135deg, var(--color-secondary) 0%, var(--color-secondary-light) 100%); - color: var(--color-white); - box-shadow: var(--shadow-md); + background: linear-gradient( + 135deg, + var(--color-secondary) 0%, + var(--color-secondary-light) 100% + ); + color: var(--color-white); + box-shadow: var(--shadow-md); } .btn-secondary:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-lg); + transform: translateY(-2px); + box-shadow: var(--shadow-lg); } .btn-outline { - background: transparent; - border-color: var(--color-gray-300); - color: var(--color-gray-700); + background: transparent; + border-color: var(--color-gray-300); + color: var(--color-gray-700); } .btn-outline:hover { - border-color: var(--color-primary); - color: var(--color-primary); - transform: translateY(-2px); + border-color: var(--color-primary); + color: var(--color-primary); + transform: translateY(-2px); } [data-theme="dark"] .btn-outline { - border-color: var(--color-gray-600); - color: var(--color-gray-300); + border-color: var(--color-gray-600); + color: var(--color-gray-300); } /* 英雄区域 */ .hero { - position: relative; - min-height: 100vh; - padding-top: 80px; - overflow: hidden; + position: relative; + min-height: 100vh; + padding-top: 80px; + overflow: hidden; } .hero-background { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: -1; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; } .hero-gradient { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(135deg, - rgba(26, 86, 219, 0.1) 0%, - rgba(59, 130, 246, 0.05) 50%, - rgba(245, 158, 11, 0.1) 100%); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient( + 135deg, + rgba(26, 86, 219, 0.1) 0%, + rgba(59, 130, 246, 0.05) 50%, + rgba(245, 158, 11, 0.1) 100% + ); } .hero-pattern { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-image: - radial-gradient(circle at 25% 25%, rgba(26, 86, 219, 0.1) 2px, transparent 2px), - radial-gradient(circle at 75% 75%, rgba(245, 158, 11, 0.1) 2px, transparent 2px); - background-size: 60px 60px; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: + radial-gradient( + circle at 25% 25%, + rgba(26, 86, 219, 0.1) 2px, + transparent 2px + ), + radial-gradient( + circle at 75% 75%, + rgba(245, 158, 11, 0.1) 2px, + transparent 2px + ); + background-size: 60px 60px; } .hero-ball-animation { - position: absolute; - width: 300px; - height: 300px; - top: 50%; - right: 10%; - transform: translateY(-50%); - background: radial-gradient(circle at 30% 30%, - rgba(26, 86, 219, 0.2) 0%, - rgba(26, 86, 219, 0.1) 30%, - transparent 70%); - border-radius: 50%; - animation: float 6s ease-in-out infinite; + position: absolute; + width: 300px; + height: 300px; + top: 50%; + right: 10%; + transform: translateY(-50%); + background: radial-gradient( + circle at 30% 30%, + rgba(26, 86, 219, 0.2) 0%, + rgba(26, 86, 219, 0.1) 30%, + transparent 70% + ); + border-radius: 50%; + animation: float 6s ease-in-out infinite; } .hero .container { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 4rem; - align-items: center; - min-height: calc(100vh - 80px); + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: center; + min-height: calc(100vh - 80px); } .hero-content { - max-width: 600px; + max-width: 600px; } .hero-badge { - display: flex; - gap: 1rem; - margin-bottom: 2rem; + display: flex; + gap: 1rem; + margin-bottom: 2rem; } .badge-season, .badge-league { - padding: 0.5rem 1rem; - border-radius: var(--border-radius-full); - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; + padding: 0.5rem 1rem; + border-radius: var(--border-radius-full); + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; } .badge-season { - background: var(--color-primary); - color: var(--color-white); + background: var(--color-primary); + color: var(--color-white); } .badge-league { - background: var(--color-secondary); - color: var(--color-white); + background: var(--color-secondary); + color: var(--color-white); } .hero-title { - font-family: var(--font-display); - font-size: 4rem; - font-weight: 900; - line-height: 1.1; - margin-bottom: 1.5rem; - color: var(--color-gray-900); + font-family: var(--font-display); + font-size: 4rem; + font-weight: 900; + line-height: 1.1; + margin-bottom: 1.5rem; + color: var(--color-gray-900); } .title-line { - display: block; + display: block; } .highlight { - color: var(--color-primary); - position: relative; - display: inline-block; + color: var(--color-primary); + position: relative; + display: inline-block; } .highlight::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 8px; - background: var(--color-secondary); - opacity: 0.3; - z-index: -1; + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 8px; + background: var(--color-secondary); + opacity: 0.3; + z-index: -1; } .hero-subtitle { - font-size: 1.25rem; - color: var(--color-gray-600); - margin-bottom: 3rem; - max-width: 500px; + font-size: 1.25rem; + color: var(--color-gray-600); + margin-bottom: 3rem; + max-width: 500px; } [data-theme="dark"] .hero-subtitle { - color: var(--color-gray-400); + color: var(--color-gray-400); } .hero-stats { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 1.5rem; - margin-bottom: 3rem; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1.5rem; + margin-bottom: 3rem; } .stat-item { - text-align: center; + text-align: center; } .stat-number { - font-family: var(--font-display); - font-size: 2.5rem; - font-weight: 800; - color: var(--color-primary); - margin-bottom: 0.25rem; + font-family: var(--font-display); + font-size: 2.5rem; + font-weight: 800; + color: var(--color-primary); + margin-bottom: 0.25rem; } .stat-label { - font-size: 0.875rem; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; + font-size: 0.875rem; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; } [data-theme="dark"] .stat-label { - color: var(--color-gray-400); + color: var(--color-gray-400); } .hero-actions { - display: flex; - gap: 1rem; + display: flex; + gap: 1rem; } .hero-visual { - position: relative; - height: 500px; + position: relative; + height: 500px; } .stadium-visual { - position: relative; - width: 100%; - height: 100%; - background: linear-gradient(135deg, var(--color-gray-100) 0%, var(--color-gray-200) 100%); - border-radius: var(--border-radius-2xl); - overflow: hidden; - box-shadow: var(--shadow-2xl); + position: relative; + width: 100%; + height: 100%; + background: linear-gradient( + 135deg, + var(--color-gray-100) 0%, + var(--color-gray-200) 100% + ); + border-radius: var(--border-radius-2xl); + overflow: hidden; + box-shadow: var(--shadow-2xl); } .stadium-field { - position: absolute; - top: 10%; - left: 5%; - width: 90%; - height: 80%; - background: linear-gradient(135deg, #16a34a 0%, #22c55e 100%); - border-radius: var(--border-radius-xl); + position: absolute; + top: 10%; + left: 5%; + width: 90%; + height: 80%; + background: linear-gradient(135deg, #16a34a 0%, #22c55e 100%); + border-radius: var(--border-radius-xl); } .stadium-stands { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(135deg, - transparent 0%, - rgba(0, 0, 0, 0.1) 20%, - rgba(0, 0, 0, 0.2) 100%); - border-radius: var(--border-radius-2xl); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient( + 135deg, + transparent 0%, + rgba(0, 0, 0, 0.1) 20%, + rgba(0, 0, 0, 0.2) 100% + ); + border-radius: var(--border-radius-2xl); } .stadium-players { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 80%; - height: 60%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 80%; + height: 60%; } .player { - position: absolute; - width: 40px; - height: 60px; - background: var(--color-white); - border-radius: var(--border-radius-md); - box-shadow: var(--shadow-md); + position: absolute; + width: 40px; + height: 60px; + background: var(--color-white); + border-radius: var(--border-radius-md); + box-shadow: var(--shadow-md); } .player-1 { - top: 30%; - left: 20%; - animation: player-move-1 3s ease-in-out infinite; + top: 30%; + left: 20%; + animation: player-move-1 3s ease-in-out infinite; } .player-2 { - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - animation: player-move-2 4s ease-in-out infinite; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + animation: player-move-2 4s ease-in-out infinite; } .player-3 { - top: 40%; - right: 25%; - animation: player-move-3 3.5s ease-in-out infinite; + top: 40%; + right: 25%; + animation: player-move-3 3.5s ease-in-out infinite; } .stadium-ball { - position: absolute; - width: 20px; - height: 20px; - background: linear-gradient(45deg, var(--color-white) 25%, var(--color-gray-200) 25%, var(--color-gray-200) 50%, var(--color-white) 50%, var(--color-white) 75%, var(--color-gray-200) 75%); - background-size: 5px 5px; - border-radius: 50%; - top: 45%; - left: 60%; - animation: ball-move 5s linear infinite; + position: absolute; + width: 20px; + height: 20px; + background: linear-gradient( + 45deg, + var(--color-white) 25%, + var(--color-gray-200) 25%, + var(--color-gray-200) 50%, + var(--color-white) 50%, + var(--color-white) 75%, + var(--color-gray-200) 75% + ); + background-size: 5px 5px; + border-radius: 50%; + top: 45%; + left: 60%; + animation: ball-move 5s linear infinite; } .hero-scroll { - position: absolute; - bottom: 2rem; - left: 50%; - transform: translateX(-50%); + position: absolute; + bottom: 2rem; + left: 50%; + transform: translateX(-50%); } .scroll-indicator { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; } .scroll-line { - width: 2px; - height: 40px; - background: linear-gradient(to bottom, var(--color-primary), transparent); - animation: scroll-line 2s ease-in-out infinite; + width: 2px; + height: 40px; + background: linear-gradient(to bottom, var(--color-primary), transparent); + animation: scroll-line 2s ease-in-out infinite; } /* 下一场比赛 */ .next-match { - padding: 6rem 0; - background: var(--color-gray-50); + padding: 6rem 0; + background: var(--color-gray-50); } [data-theme="dark"] .next-match { - background: var(--color-gray-900); + background: var(--color-gray-900); } .section-header { - text-align: center; - margin-bottom: 3rem; + text-align: center; + margin-bottom: 3rem; } .section-title { - font-family: var(--font-heading); - font-size: 2.5rem; - font-weight: 700; - color: var(--color-gray-900); - margin-bottom: 0.5rem; - text-transform: uppercase; - letter-spacing: 2px; + font-family: var(--font-heading); + font-size: 2.5rem; + font-weight: 700; + color: var(--color-gray-900); + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 2px; } [data-theme="dark"] .section-title { - color: var(--color-white); + color: var(--color-white); } .section-subtitle { - font-size: 1.125rem; - color: var(--color-gray-600); + font-size: 1.125rem; + color: var(--color-gray-600); } [data-theme="dark"] .section-subtitle { - color: var(--color-gray-400); + color: var(--color-gray-400); } .match-card { - background: var(--color-white); - border-radius: var(--border-radius-xl); - padding: 2rem; - box-shadow: var(--shadow-xl); - display: grid; - grid-template-columns: auto 1fr auto; - gap: 3rem; - align-items: center; + background: var(--color-white); + border-radius: var(--border-radius-xl); + padding: 2rem; + box-shadow: var(--shadow-xl); + display: grid; + grid-template-columns: auto 1fr auto; + gap: 3rem; + align-items: center; } [data-theme="dark"] .match-card { - background: var(--color-gray-800); + background: var(--color-gray-800); } .match-date { - text-align: center; - padding: 1.5rem; - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%); - border-radius: var(--border-radius-lg); - color: var(--color-white); + text-align: center; + padding: 1.5rem; + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-primary-dark) 100% + ); + border-radius: var(--border-radius-lg); + color: var(--color-white); } .match-day { - font-family: var(--font-heading); - font-size: 1.125rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; - margin-bottom: 0.5rem; + font-family: var(--font-heading); + font-size: 1.125rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 0.5rem; } .match-date-number { - font-family: var(--font-display); - font-size: 3rem; - font-weight: 800; - line-height: 1; - margin-bottom: 0.25rem; + font-family: var(--font-display); + font-size: 3rem; + font-weight: 800; + line-height: 1; + margin-bottom: 0.25rem; } .match-month { - font-family: var(--font-heading); - font-size: 1.125rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; - margin-bottom: 0.5rem; + font-family: var(--font-heading); + font-size: 1.125rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 0.5rem; } .match-time { - font-size: 1rem; - font-weight: 500; - opacity: 0.9; + font-size: 1rem; + font-weight: 500; + opacity: 0.9; } .match-teams { - display: grid; - grid-template-columns: 1fr auto 1fr; - gap: 2rem; - align-items: center; + display: grid; + grid-template-columns: 1fr auto 1fr; + gap: 2rem; + align-items: center; } .team { - text-align: center; + text-align: center; } .team-home { - text-align: right; + text-align: right; } .team-away { - text-align: left; + text-align: left; } .team-logo { - width: 80px; - height: 80px; - border-radius: 50%; - margin: 0 auto 1rem; - background: var(--color-gray-200); - position: relative; + width: 80px; + height: 80px; + border-radius: 50%; + margin: 0 auto 1rem; + background: var(--color-gray-200); + position: relative; } .logo-nanjing { - background: linear-gradient(135deg, #dc2626 0%, #ef4444 100%); + background: linear-gradient(135deg, #dc2626 0%, #ef4444 100%); } .logo-nanjing::before { - content: 'N'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-family: var(--font-heading); - font-size: 2rem; - font-weight: 700; - color: var(--color-white); + content: "N"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-family: var(--font-heading); + font-size: 2rem; + font-weight: 700; + color: var(--color-white); } .logo-suzhou { - background: linear-gradient(135deg, #059669 0%, #10b981 100%); + background: linear-gradient(135deg, #059669 0%, #10b981 100%); } .logo-suzhou::before { - content: 'S'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-family: var(--font-heading); - font-size: 2rem; - font-weight: 700; - color: var(--color-white); + content: "S"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-family: var(--font-heading); + font-size: 2rem; + font-weight: 700; + color: var(--color-white); } .team-name { - font-family: var(--font-heading); - font-size: 1.5rem; - font-weight: 600; - color: var(--color-gray-900); - margin-bottom: 0.5rem; + font-family: var(--font-heading); + font-size: 1.5rem; + font-weight: 600; + color: var(--color-gray-900); + margin-bottom: 0.5rem; } [data-theme="dark"] .team-name { - color: var(--color-white); + color: var(--color-white); } .team-record { - font-size: 0.875rem; - color: var(--color-gray-600); + font-size: 0.875rem; + color: var(--color-gray-600); } [data-theme="dark"] .team-record { - color: var(--color-gray-400); + color: var(--color-gray-400); } .match-vs { - text-align: center; + text-align: center; } .vs-text { - font-family: var(--font-display); - font-size: 2rem; - font-weight: 800; - color: var(--color-primary); - margin-bottom: 0.5rem; + font-family: var(--font-display); + font-size: 2rem; + font-weight: 800; + color: var(--color-primary); + margin-bottom: 0.5rem; } .match-info { - font-size: 0.875rem; - color: var(--color-gray-600); + font-size: 0.875rem; + color: var(--color-gray-600); } .match-venue { - font-weight: 600; - margin-bottom: 0.25rem; + font-weight: 600; + margin-bottom: 0.25rem; } .match-round { - opacity: 0.8; + opacity: 0.8; } .match-actions { - display: flex; - flex-direction: column; - gap: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; } /* 球队展示 */ .teams-section { - padding: 6rem 0; + padding: 6rem 0; } .teams-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); - gap: 2rem; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 2rem; } .team-card { - background: var(--color-white); - border-radius: var(--border-radius-lg); - padding: 1.5rem; - box-shadow: var(--shadow-md); - transition: all var(--transition-normal); - cursor: pointer; - text-align: center; + background: var(--color-white); + border-radius: var(--border-radius-lg); + padding: 1.5rem; + box-shadow: var(--shadow-md); + transition: all var(--transition-normal); + cursor: pointer; + text-align: center; } .team-card:hover { - transform: translateY(-8px); - box-shadow: var(--shadow-xl); + transform: translateY(-8px); + box-shadow: var(--shadow-xl); } [data-theme="dark"] .team-card { - background: var(--color-gray-800); + background: var(--color-gray-800); } .team-card-logo { - width: 80px; - height: 80px; - border-radius: 50%; - margin: 0 auto 1rem; - background: var(--color-gray-200); - display: flex; - align-items: center; - justify-content: center; - font-family: var(--font-heading); - font-size: 2rem; - font-weight: 700; - color: var(--color-white); + width: 80px; + height: 80px; + border-radius: 50%; + margin: 0 auto 1rem; + background: var(--color-gray-200); + display: flex; + align-items: center; + justify-content: center; + font-family: var(--font-heading); + font-size: 2rem; + font-weight: 700; + color: var(--color-white); } .team-card-name { - font-family: var(--font-heading); - font-size: 1.25rem; - font-weight: 600; - color: var(--color-gray-900); - margin-bottom: 0.5rem; + font-family: var(--font-heading); + font-size: 1.25rem; + font-weight: 600; + color: var(--color-gray-900); + margin-bottom: 0.5rem; } [data-theme="dark"] .team-card-name { - color: var(--color-white); + color: var(--color-white); } .team-card-city { - font-size: 0.875rem; - color: var(--color-gray-600); - margin-bottom: 1rem; + font-size: 0.875rem; + color: var(--color-gray-600); + margin-bottom: 1rem; } .team-card-stats { - display: flex; - justify-content: space-around; - margin-top: 1rem; - padding-top: 1rem; - border-top: 1px solid var(--color-gray-200); + display: flex; + justify-content: space-around; + margin-top: 1rem; + padding-top: 1rem; + border-top: 1px solid var(--color-gray-200); } [data-theme="dark"] .team-card-stats { - border-top-color: var(--color-gray-700); + border-top-color: var(--color-gray-700); } .team-stat { - text-align: center; + text-align: center; } .team-stat-value { - font-family: var(--font-display); - font-size: 1.25rem; - font-weight: 700; - color: var(--color-primary); + font-family: var(--font-display); + font-size: 1.25rem; + font-weight: 700; + color: var(--color-primary); } .team-stat-label { - font-size: 0.75rem; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; + font-size: 0.75rem; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; } /* 积分榜 */ .standings-section { - padding: 6rem 0; - background: var(--color-gray-50); + padding: 6rem 0; + background: var(--color-gray-50); } [data-theme="dark"] .standings-section { - background: var(--color-gray-900); + background: var(--color-gray-900); } .standings-container { - overflow-x: auto; + overflow-x: auto; } .standings-table { - min-width: 800px; + min-width: 800px; } .standings-table table { - width: 100%; - border-collapse: collapse; - background: var(--color-white); - border-radius: var(--border-radius-lg); - overflow: hidden; - box-shadow: var(--shadow-md); + width: 100%; + border-collapse: collapse; + background: var(--color-white); + border-radius: var(--border-radius-lg); + overflow: hidden; + box-shadow: var(--shadow-md); } [data-theme="dark"] .standings-table table { - background: var(--color-gray-800); + background: var(--color-gray-800); } .standings-table thead { - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%); + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-primary-dark) 100% + ); } .standings-table th { - padding: 1rem; - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - color: var(--color-white); - text-transform: uppercase; - letter-spacing: 1px; - text-align: center; + padding: 1rem; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-white); + text-transform: uppercase; + letter-spacing: 1px; + text-align: center; } .standings-table tbody tr { - border-bottom: 1px solid var(--color-gray-200); - transition: background-color var(--transition-fast); + border-bottom: 1px solid var(--color-gray-200); + transition: background-color var(--transition-fast); } [data-theme="dark"] .standings-table tbody tr { - border-bottom-color: var(--color-gray-700); + border-bottom-color: var(--color-gray-700); } .standings-table tbody tr:hover { - background-color: var(--color-gray-100); + background-color: var(--color-gray-100); } [data-theme="dark"] .standings-table tbody tr:hover { - background-color: var(--color-gray-700); + background-color: var(--color-gray-700); } .standings-table td { - padding: 1rem; - text-align: center; - color: var(--color-gray-700); + padding: 1rem; + text-align: center; + color: var(--color-gray-700); } [data-theme="dark"] .standings-table td { - color: var(--color-gray-300); + color: var(--color-gray-300); } .standings-table td:first-child { - font-weight: 700; - color: var(--color-primary); + font-weight: 700; + color: var(--color-primary); } .standings-table td:nth-child(2) { - text-align: left; - font-weight: 600; - color: var(--color-gray-900); + text-align: left; + font-weight: 600; + color: var(--color-gray-900); } [data-theme="dark"] .standings-table td:nth-child(2) { - color: var(--color-white); + color: var(--color-white); } .standings-table td:last-child { - font-weight: 700; - color: var(--color-secondary); + font-weight: 700; + color: var(--color-secondary); } /* 赛程表 */ .fixtures-section { - padding: 6rem 0; + padding: 6rem 0; } .fixtures-tabs { - background: var(--color-white); - border-radius: var(--border-radius-xl); - overflow: hidden; - box-shadow: var(--shadow-lg); + background: var(--color-white); + border-radius: var(--border-radius-xl); + overflow: hidden; + box-shadow: var(--shadow-lg); } [data-theme="dark"] .fixtures-tabs { - background: var(--color-gray-800); + background: var(--color-gray-800); } .tabs { - display: flex; - background: var(--color-gray-100); - padding: 0.5rem; + display: flex; + background: var(--color-gray-100); + padding: 0.5rem; } [data-theme="dark"] .tabs { - background: var(--color-gray-900); + background: var(--color-gray-900); } .tab { - flex: 1; - padding: 1rem; - border: none; - background: transparent; - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; - cursor: pointer; - transition: all var(--transition-fast); - border-radius: var(--border-radius-md); + flex: 1; + padding: 1rem; + border: none; + background: transparent; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; + cursor: pointer; + transition: all var(--transition-fast); + border-radius: var(--border-radius-md); } .tab:hover { - color: var(--color-primary); + color: var(--color-primary); } .tab.active { - background: var(--color-white); - color: var(--color-primary); - box-shadow: var(--shadow-sm); + background: var(--color-white); + color: var(--color-primary); + box-shadow: var(--shadow-sm); } [data-theme="dark"] .tab.active { - background: var(--color-gray-800); + background: var(--color-gray-800); } .fixtures-list { - padding: 2rem; + padding: 2rem; } .fixture-item { - display: grid; - grid-template-columns: auto 1fr auto; - gap: 2rem; - align-items: center; - padding: 1.5rem; - border-bottom: 1px solid var(--color-gray-200); - transition: background-color var(--transition-fast); + display: grid; + grid-template-columns: auto 1fr auto; + gap: 2rem; + align-items: center; + padding: 1.5rem; + border-bottom: 1px solid var(--color-gray-200); + transition: background-color var(--transition-fast); } .fixture-item:hover { - background-color: var(--color-gray-50); + background-color: var(--color-gray-50); } [data-theme="dark"] .fixture-item { - border-bottom-color: var(--color-gray-700); + border-bottom-color: var(--color-gray-700); } [data-theme="dark"] .fixture-item:hover { - background-color: var(--color-gray-900); + background-color: var(--color-gray-900); } .fixture-date { - text-align: center; - min-width: 100px; + text-align: center; + min-width: 100px; } .fixture-day { - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; - margin-bottom: 0.25rem; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 0.25rem; } .fixture-time { - font-size: 1.125rem; - font-weight: 700; - color: var(--color-primary); + font-size: 1.125rem; + font-weight: 700; + color: var(--color-primary); } .fixture-teams { - display: grid; - grid-template-columns: 1fr auto 1fr; - gap: 1rem; - align-items: center; + display: grid; + grid-template-columns: 1fr auto 1fr; + gap: 1rem; + align-items: center; } .fixture-team { - display: flex; - align-items: center; - gap: 1rem; + display: flex; + align-items: center; + gap: 1rem; } .fixture-team.home { - justify-content: flex-end; + justify-content: flex-end; } .fixture-team-logo { - width: 40px; - height: 40px; - border-radius: 50%; - background: var(--color-gray-200); + width: 40px; + height: 40px; + border-radius: 50%; + background: var(--color-gray-200); } .fixture-team-name { - font-family: var(--font-heading); - font-size: 1.125rem; - font-weight: 600; - color: var(--color-gray-900); + font-family: var(--font-heading); + font-size: 1.125rem; + font-weight: 600; + color: var(--color-gray-900); } [data-theme="dark"] .fixture-team-name { - color: var(--color-white); + color: var(--color-white); } .fixture-vs { - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 800; - color: var(--color-gray-400); - padding: 0 1rem; + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 800; + color: var(--color-gray-400); + padding: 0 1rem; } .fixture-score { - min-width: 100px; - text-align: center; + min-width: 100px; + text-align: center; } .fixture-score-value { - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 800; - color: var(--color-primary); + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 800; + color: var(--color-primary); } .fixture-score-status { - font-size: 0.75rem; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; - margin-top: 0.25rem; + font-size: 0.75rem; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; + margin-top: 0.25rem; } /* 数据统计 */ .stats-section { - padding: 6rem 0; - background: var(--color-gray-50); + padding: 6rem 0; + background: var(--color-gray-50); } [data-theme="dark"] .stats-section { - background: var(--color-gray-900); + background: var(--color-gray-900); } .stats-tabs { - background: var(--color-white); - border-radius: var(--border-radius-xl); - overflow: hidden; - box-shadow: var(--shadow-lg); + background: var(--color-white); + border-radius: var(--border-radius-xl); + overflow: hidden; + box-shadow: var(--shadow-lg); } [data-theme="dark"] .stats-tabs { - background: var(--color-gray-800); + background: var(--color-gray-800); } .stats-tab-nav { - display: flex; - background: var(--color-gray-100); - padding: 0.5rem; + display: flex; + background: var(--color-gray-100); + padding: 0.5rem; } [data-theme="dark"] .stats-tab-nav { - background: var(--color-gray-900); + background: var(--color-gray-900); } .stats-tab { - flex: 1; - padding: 1rem; - border: none; - background: transparent; - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; - cursor: pointer; - transition: all var(--transition-fast); - border-radius: var(--border-radius-md); + flex: 1; + padding: 1rem; + border: none; + background: transparent; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; + cursor: pointer; + transition: all var(--transition-fast); + border-radius: var(--border-radius-md); } .stats-tab:hover { - color: var(--color-primary); + color: var(--color-primary); } .stats-tab.active { - background: var(--color-white); - color: var(--color-primary); - box-shadow: var(--shadow-sm); + background: var(--color-white); + color: var(--color-primary); + box-shadow: var(--shadow-sm); } [data-theme="dark"] .stats-tab.active { - background: var(--color-gray-800); + background: var(--color-gray-800); } .stats-content { - padding: 2rem; + padding: 2rem; } .stats-tab-content { - display: none; + display: none; } .stats-tab-content.active { - display: block; + display: block; } .stats-table { - width: 100%; - border-collapse: collapse; + width: 100%; + border-collapse: collapse; } .stats-table th { - padding: 1rem; - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; - text-align: left; - border-bottom: 2px solid var(--color-gray-200); + padding: 1rem; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; + text-align: left; + border-bottom: 2px solid var(--color-gray-200); } [data-theme="dark"] .stats-table th { - border-bottom-color: var(--color-gray-700); + border-bottom-color: var(--color-gray-700); } .stats-table td { - padding: 1rem; - border-bottom: 1px solid var(--color-gray-200); - color: var(--color-gray-700); + padding: 1rem; + border-bottom: 1px solid var(--color-gray-200); + color: var(--color-gray-700); } [data-theme="dark"] .stats-table td { - border-bottom-color: var(--color-gray-700); - color: var(--color-gray-300); + border-bottom-color: var(--color-gray-700); + color: var(--color-gray-300); } .stats-table tr:hover { - background-color: var(--color-gray-50); + background-color: var(--color-gray-50); } [data-theme="dark"] .stats-table tr:hover { - background-color: var(--color-gray-900); + background-color: var(--color-gray-900); } .stats-rank { - font-weight: 700; - color: var(--color-primary); - width: 50px; + font-weight: 700; + color: var(--color-primary); + width: 50px; } .stats-player { - font-weight: 600; - color: var(--color-gray-900); + font-weight: 600; + color: var(--color-gray-900); } [data-theme="dark"] .stats-player { - color: var(--color-white); + color: var(--color-white); } .stats-team { - color: var(--color-gray-600); + color: var(--color-gray-600); } .stats-value { - font-weight: 700; - color: var(--color-secondary); - text-align: center; + font-weight: 700; + color: var(--color-secondary); + text-align: center; } /* 新闻动态 */ .news-section { - padding: 6rem 0; + padding: 6rem 0; } .news-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); - gap: 2rem; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + gap: 2rem; } .news-card { - background: var(--color-white); - border-radius: var(--border-radius-lg); - overflow: hidden; - box-shadow: var(--shadow-md); - transition: all var(--transition-normal); - cursor: pointer; + background: var(--color-white); + border-radius: var(--border-radius-lg); + overflow: hidden; + box-shadow: var(--shadow-md); + transition: all var(--transition-normal); + cursor: pointer; } .news-card:hover { - transform: translateY(-8px); - box-shadow: var(--shadow-xl); + transform: translateY(-8px); + box-shadow: var(--shadow-xl); } [data-theme="dark"] .news-card { - background: var(--color-gray-800); + background: var(--color-gray-800); } .news-card-image { - height: 200px; - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%); - position: relative; - overflow: hidden; + height: 200px; + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-secondary) 100% + ); + position: relative; + overflow: hidden; } .news-card-image::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(45deg, - transparent 30%, - rgba(255, 255, 255, 0.1) 50%, - transparent 70%); - animation: shimmer 2s infinite; + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient( + 45deg, + transparent 30%, + rgba(255, 255, 255, 0.1) 50%, + transparent 70% + ); + animation: shimmer 2s infinite; } .news-card-content { - padding: 1.5rem; + padding: 1.5rem; } .news-card-category { - display: inline-block; - padding: 0.25rem 0.75rem; - background: var(--color-primary); - color: var(--color-white); - font-family: var(--font-heading); - font-size: 0.75rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; - border-radius: var(--border-radius-sm); - margin-bottom: 1rem; + display: inline-block; + padding: 0.25rem 0.75rem; + background: var(--color-primary); + color: var(--color-white); + font-family: var(--font-heading); + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + border-radius: var(--border-radius-sm); + margin-bottom: 1rem; } .news-card-title { - font-family: var(--font-heading); - font-size: 1.25rem; - font-weight: 600; - color: var(--color-gray-900); - margin-bottom: 0.75rem; - line-height: 1.3; + font-family: var(--font-heading); + font-size: 1.25rem; + font-weight: 600; + color: var(--color-gray-900); + margin-bottom: 0.75rem; + line-height: 1.3; } [data-theme="dark"] .news-card-title { - color: var(--color-white); + color: var(--color-white); } .news-card-excerpt { - font-size: 0.875rem; - color: var(--color-gray-600); - margin-bottom: 1rem; - line-height: 1.5; + font-size: 0.875rem; + color: var(--color-gray-600); + margin-bottom: 1rem; + line-height: 1.5; } [data-theme="dark"] .news-card-excerpt { - color: var(--color-gray-400); + color: var(--color-gray-400); } .news-card-meta { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 0.75rem; - color: var(--color-gray-500); + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.75rem; + color: var(--color-gray-500); } .news-card-date { - display: flex; - align-items: center; - gap: 0.25rem; + display: flex; + align-items: center; + gap: 0.25rem; } /* 底部 */ .footer { - background: linear-gradient(135deg, var(--color-gray-900) 0%, var(--color-black) 100%); - color: var(--color-white); - padding: 4rem 0 2rem; + background: linear-gradient( + 135deg, + var(--color-gray-900) 0%, + var(--color-black) 100% + ); + color: var(--color-white); + padding: 4rem 0 2rem; } .footer-content { - display: grid; - grid-template-columns: 1fr 2fr; - gap: 4rem; - margin-bottom: 3rem; + display: grid; + grid-template-columns: 1fr 2fr; + gap: 4rem; + margin-bottom: 3rem; } .footer-brand { - max-width: 300px; + max-width: 300px; } .footer .logo { - margin-bottom: 1.5rem; + margin-bottom: 1.5rem; } .footer-description { - font-size: 0.875rem; - color: var(--color-gray-400); - margin-bottom: 1.5rem; - line-height: 1.6; + font-size: 0.875rem; + color: var(--color-gray-400); + margin-bottom: 1.5rem; + line-height: 1.6; } .footer-social { - display: flex; - gap: 1rem; + display: flex; + gap: 1rem; } .social-link { - width: 40px; - height: 40px; - border-radius: 50%; - background: rgba(255, 255, 255, 0.1); - display: flex; - align-items: center; - justify-content: center; - color: var(--color-white); - text-decoration: none; - transition: all var(--transition-fast); + width: 40px; + height: 40px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + display: flex; + align-items: center; + justify-content: center; + color: var(--color-white); + text-decoration: none; + transition: all var(--transition-fast); } .social-link:hover { - background: var(--color-primary); - transform: translateY(-2px); + background: var(--color-primary); + transform: translateY(-2px); } .footer-links { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 2rem; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; } .footer-column { - display: flex; - flex-direction: column; - gap: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; } .footer-title { - font-family: var(--font-heading); - font-size: 1.125rem; - font-weight: 600; - margin-bottom: 0.5rem; - text-transform: uppercase; - letter-spacing: 1px; + font-family: var(--font-heading); + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 1px; } .footer-link { - font-size: 0.875rem; - color: var(--color-gray-400); - text-decoration: none; - transition: color var(--transition-fast); + font-size: 0.875rem; + color: var(--color-gray-400); + text-decoration: none; + transition: color var(--transition-fast); } .footer-link:hover { - color: var(--color-white); + color: var(--color-white); } .footer-bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding-top: 2rem; - border-top: 1px solid rgba(255, 255, 255, 0.1); + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 2rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); } .copyright { - font-size: 0.875rem; - color: var(--color-gray-400); + font-size: 0.875rem; + color: var(--color-gray-400); } .footer-legal { - display: flex; - gap: 1.5rem; + display: flex; + gap: 1.5rem; } .legal-link { - font-size: 0.875rem; - color: var(--color-gray-400); - text-decoration: none; - transition: color var(--transition-fast); + font-size: 0.875rem; + color: var(--color-gray-400); + text-decoration: none; + transition: color var(--transition-fast); } .legal-link:hover { - color: var(--color-white); + color: var(--color-white); } /* 动画 */ @keyframes float { - 0%, 100% { - transform: translateY(-50%) translateX(0); - } - 50% { - transform: translateY(-50%) translateX(20px); - } + 0%, + 100% { + transform: translateY(-50%) translateX(0); + } + 50% { + transform: translateY(-50%) translateX(20px); + } } @keyframes player-move-1 { - 0%, 100% { - transform: translate(0, 0); - } - 50% { - transform: translate(20px, -10px); - } + 0%, + 100% { + transform: translate(0, 0); + } + 50% { + transform: translate(20px, -10px); + } } @keyframes player-move-2 { - 0%, 100% { - transform: translate(-50%, -50%); - } - 50% { - transform: translate(-50%, -60%); - } + 0%, + 100% { + transform: translate(-50%, -50%); + } + 50% { + transform: translate(-50%, -60%); + } } @keyframes player-move-3 { - 0%, 100% { - transform: translate(0, 0); - } - 50% { - transform: translate(-15px, 10px); - } + 0%, + 100% { + transform: translate(0, 0); + } + 50% { + transform: translate(-15px, 10px); + } } @keyframes ball-move { - 0% { - transform: translate(0, 0); - } - 25% { - transform: translate(40px, -20px); - } - 50% { - transform: translate(80px, 0); - } - 75% { - transform: translate(40px, 20px); - } - 100% { - transform: translate(0, 0); - } + 0% { + transform: translate(0, 0); + } + 25% { + transform: translate(40px, -20px); + } + 50% { + transform: translate(80px, 0); + } + 75% { + transform: translate(40px, 20px); + } + 100% { + transform: translate(0, 0); + } } @keyframes scroll-line { - 0% { - height: 0; - opacity: 0; - } - 50% { - height: 40px; - opacity: 1; - } - 100% { - height: 0; - opacity: 0; - transform: translateY(40px); - } + 0% { + height: 0; + opacity: 0; + } + 50% { + height: 40px; + opacity: 1; + } + 100% { + height: 0; + opacity: 0; + transform: translateY(40px); + } } @keyframes spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } @keyframes bounce { - 0%, 100% { - transform: translateY(0); - } - 50% { - transform: translateY(-10px); - } + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-10px); + } } @keyframes pulse { - 0%, 100% { - opacity: 1; - } - 50% { - opacity: 0.5; - } + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } } @keyframes shimmer { - 0% { - transform: translateX(-100%); - } - 100% { - transform: translateX(100%); - } + 0% { + transform: translateX(-100%); + } + 100% { + transform: translateX(100%); + } } /* 响应式设计 */ @media (max-width: 1024px) { - .hero .container { - grid-template-columns: 1fr; - gap: 3rem; - text-align: center; - } - - .hero-content { - max-width: 100%; - } - - .hero-visual { - height: 400px; - } - - .hero-title { - font-size: 3rem; - } - - .footer-content { - grid-template-columns: 1fr; - gap: 3rem; - } + .hero .container { + grid-template-columns: 1fr; + gap: 3rem; + text-align: center; + } + + .hero-content { + max-width: 100%; + } + + .hero-visual { + height: 400px; + } + + .hero-title { + font-size: 3rem; + } + + .footer-content { + grid-template-columns: 1fr; + gap: 3rem; + } } @media (max-width: 768px) { - .nav-menu { - display: none; - } - - .btn-menu-toggle { - display: flex; - } - - .match-card { - grid-template-columns: 1fr; - gap: 2rem; - } - - .hero-stats { - grid-template-columns: repeat(2, 1fr); - } - - .hero-title { - font-size: 2.5rem; - } - - .section-title { - font-size: 2rem; - } - - .footer-links { - grid-template-columns: 1fr; - gap: 2rem; - } - - .footer-bottom { - flex-direction: column; - gap: 1rem; - text-align: center; - } + .nav-menu { + display: none; + } + + .btn-menu-toggle { + display: flex; + } + + .match-card { + grid-template-columns: 1fr; + gap: 2rem; + } + + .hero-stats { + grid-template-columns: repeat(2, 1fr); + } + + .hero-title { + font-size: 2.5rem; + } + + .section-title { + font-size: 2rem; + } + + .footer-links { + grid-template-columns: 1fr; + gap: 2rem; + } + + .footer-bottom { + flex-direction: column; + gap: 1rem; + text-align: center; + } } @media (max-width: 480px) { - .container { - padding: 0 1rem; - } - - .hero-title { - font-size: 2rem; - } - - .hero-subtitle { - font-size: 1rem; - } - - .stat-number { - font-size: 2rem; - } - - .section-title { - font-size: 1.75rem; - } - - .match-teams { - grid-template-columns: 1fr; - gap: 1rem; - } - - .team-home, - .team-away { - text-align: center; - } - - .teams-grid { - grid-template-columns: 1fr; - } - - .news-grid { - grid-template-columns: 1fr; - } + .container { + padding: 0 1rem; + } + + .hero-title { + font-size: 2rem; + } + + .hero-subtitle { + font-size: 1rem; + } + + .stat-number { + font-size: 2rem; + } + + .section-title { + font-size: 1.75rem; + } + + .match-teams { + grid-template-columns: 1fr; + gap: 1rem; + } + + .team-home, + .team-away { + text-align: center; + } + + .teams-grid { + grid-template-columns: 1fr; + } + + .news-grid { + grid-template-columns: 1fr; + } } /* 导航菜单响应式 */ .nav-menu.active { - display: flex; - flex-direction: column; - position: absolute; - top: 80px; - left: 0; - width: 100%; - background: var(--color-white); - padding: 1rem; - box-shadow: var(--shadow-lg); - z-index: 1000; + display: flex; + flex-direction: column; + position: absolute; + top: 80px; + left: 0; + width: 100%; + background: var(--color-white); + padding: 1rem; + box-shadow: var(--shadow-lg); + z-index: 1000; } [data-theme="dark"] .nav-menu.active { - background: var(--color-gray-800); + background: var(--color-gray-800); } .nav-menu.active .nav-link { - padding: 0.75rem 1rem; - border-bottom: 1px solid var(--color-gray-200); + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--color-gray-200); } [data-theme="dark"] .nav-menu.active .nav-link { - border-bottom-color: var(--color-gray-700); + border-bottom-color: var(--color-gray-700); } .nav-menu.active .nav-link:last-child { - border-bottom: none; -} \ No newline at end of file + border-bottom: none; +} diff --git a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/favicon.html b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/favicon.html index 6c1cfe5a0..cccafb982 100644 --- a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/favicon.html +++ b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/favicon.html @@ -1 +1,4 @@ - + diff --git a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/index.html b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/index.html index 4b60c436e..095c75fee 100644 --- a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/index.html +++ b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/index.html @@ -1,365 +1,385 @@ - + - - - + + + 江苏城市足球联赛2025赛季 | 苏超联赛第一季 - - - - - - - - - + + + + + + + + +
-
-
-
加载中...
-
+
+
+
加载中...
+
- -
-
-
-
-
-
- -
-
-
- 2025赛季 - 苏超联赛第一季 -
- -

- 江苏城市 - 足球联赛 -

- -

- 江苏省首个城市间职业足球联赛,汇集12支精英球队,点燃2025赛季战火! -

- -
-
-
12
-
参赛球队
-
-
-
132
-
场比赛
-
-
-
26
-
比赛周
-
-
-
1
-
冠军荣耀
-
-
- - -
- -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
+ +
+
+
+
+
+
- -
-
-
-

下一场比赛

-
即将开始的精彩对决
-
- -
-
-
周六
-
25
-
一月
-
19:30
-
- -
-
- -
南京城联
-
8胜 3平 2负
-
- -
-
VS
-
-
南京奥体中心
-
第12轮
-
-
- -
- -
苏州雄狮
-
7胜 4平 2负
-
-
- -
- - -
-
+
+
+
+ 2025赛季 + 苏超联赛第一季
-
- -
-
-
-

参赛球队

-
12支城市代表队的荣耀之战
-
- -
- -
-
-
+

+ 江苏城市 + 足球联赛 +

- -
-
-
-

积分榜

-
2025赛季实时排名
-
- -
-
- - - - - - - - - - - - - - - - - - -
排名球队场次进球失球净胜球积分
-
-
-
-
+

+ 江苏省首个城市间职业足球联赛,汇集12支精英球队,点燃2025赛季战火! +

- -
-
-
-

赛程表

-
2025赛季完整赛程
-
- -
-
- - - -
- -
- -
-
+
+
+
12
+
参赛球队
+
+
+
132
+
场比赛
+
+
+
26
+
比赛周
+
+
+
1
+
冠军荣耀
+
-
- -
-
-
-

数据统计

-
球员与球队数据排行榜
-
- -
-
- - - -
- -
-
- -
-
- -
-
- -
-
-
+ -
+ - -
-
-
-

新闻动态

-
联赛最新资讯
-
- -
- -
+
+
+
+
+
+
+
+
+
+
-
+ + - -
+ + +
+
+
+

下一场比赛

+
即将开始的精彩对决
+
+ +
+
+
周六
+
25
+
一月
+
19:30
- + +
+
+ +
南京城联
+
8胜 3平 2负
+
+ +
+
VS
+
+
南京奥体中心
+
第12轮
+
+
+ +
+ +
苏州雄狮
+
7胜 4平 2负
+
+
+ +
+ + +
+
+
+
+ + +
+
+
+

参赛球队

+
12支城市代表队的荣耀之战
+
+ +
+ +
+
+
+ + +
+
+
+

积分榜

+
2025赛季实时排名
+
+ +
+
+ + + + + + + + + + + + + + + + + + +
排名球队场次进球失球净胜球积分
+
+
+
+
+ + +
+
+
+

赛程表

+
2025赛季完整赛程
+
+ +
+
+ + + +
+ +
+ +
+
+
+
+ + +
+
+
+

数据统计

+
球员与球队数据排行榜
+
+ +
+
+ + + +
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + +
+
+
+

新闻动态

+
联赛最新资讯
+
+ +
+ +
+
+
+ + +
- - \ No newline at end of file + + diff --git a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/js/data.js b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/js/data.js index 9132c2d52..9e30168fc 100644 --- a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/js/data.js +++ b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/js/data.js @@ -1,802 +1,808 @@ // 江苏城市足球联赛2025赛季 - 数据文件 const leagueData = { - // 联赛信息 - leagueInfo: { - name: "江苏城市足球联赛", - season: "2025赛季", - alias: "苏超联赛第一季", - teamsCount: 12, - totalMatches: 132, - weeks: 26, - startDate: "2025-03-01", - endDate: "2025-10-31" + // 联赛信息 + leagueInfo: { + name: "江苏城市足球联赛", + season: "2025赛季", + alias: "苏超联赛第一季", + teamsCount: 12, + totalMatches: 132, + weeks: 26, + startDate: "2025-03-01", + endDate: "2025-10-31", + }, + + // 参赛球队 + teams: [ + { + id: 1, + name: "南京城联", + city: "南京", + shortName: "NJL", + colors: ["#dc2626", "#ef4444"], + founded: 2020, + stadium: "南京奥体中心", + capacity: 62000, + manager: "张伟", + captain: "李明", }, - - // 参赛球队 - teams: [ - { - id: 1, - name: "南京城联", - city: "南京", - shortName: "NJL", - colors: ["#dc2626", "#ef4444"], - founded: 2020, - stadium: "南京奥体中心", - capacity: 62000, - manager: "张伟", - captain: "李明" - }, - { - id: 2, - name: "苏州雄狮", - city: "苏州", - shortName: "SZS", - colors: ["#059669", "#10b981"], - founded: 2019, - stadium: "苏州奥林匹克体育中心", - capacity: 45000, - manager: "王强", - captain: "陈浩" - }, - { - id: 3, - name: "无锡太湖", - city: "无锡", - shortName: "WXT", - colors: ["#3b82f6", "#60a5fa"], - founded: 2021, - stadium: "无锡体育中心", - capacity: 32000, - manager: "赵刚", - captain: "刘洋" - }, - { - id: 4, - name: "常州龙城", - city: "常州", - shortName: "CZL", - colors: ["#7c3aed", "#8b5cf6"], - founded: 2022, - stadium: "常州奥林匹克体育中心", - capacity: 38000, - manager: "孙磊", - captain: "周涛" - }, - { - id: 5, - name: "镇江金山", - city: "镇江", - shortName: "ZJJ", - colors: ["#f59e0b", "#fbbf24"], - founded: 2020, - stadium: "镇江体育会展中心", - capacity: 28000, - manager: "吴斌", - captain: "郑军" - }, - { - id: 6, - name: "扬州运河", - city: "扬州", - shortName: "YZY", - colors: ["#ec4899", "#f472b6"], - founded: 2021, - stadium: "扬州体育公园", - capacity: 35000, - manager: "钱勇", - captain: "王磊" - }, - { - id: 7, - name: "南通江海", - city: "南通", - shortName: "NTJ", - colors: ["#0ea5e9", "#38bdf8"], - founded: 2022, - stadium: "南通体育会展中心", - capacity: 32000, - manager: "冯超", - captain: "张勇" - }, - { - id: 8, - name: "徐州楚汉", - city: "徐州", - shortName: "XZC", - colors: ["#84cc16", "#a3e635"], - founded: 2019, - stadium: "徐州奥体中心", - capacity: 42000, - manager: "陈明", - captain: "李强" - }, - { - id: 9, - name: "淮安运河", - city: "淮安", - shortName: "HAY", - colors: ["#f97316", "#fb923c"], - founded: 2021, - stadium: "淮安体育中心", - capacity: 30000, - manager: "周伟", - captain: "吴刚" - }, - { - id: 10, - name: "盐城黄海", - city: "盐城", - shortName: "YCH", - colors: ["#06b6d4", "#22d3ee"], - founded: 2020, - stadium: "盐城体育中心", - capacity: 32000, - manager: "郑涛", - captain: "孙明" - }, - { - id: 11, - name: "泰州凤城", - city: "泰州", - shortName: "TZF", - colors: ["#8b5cf6", "#a78bfa"], - founded: 2022, - stadium: "泰州体育公园", - capacity: 28000, - manager: "王刚", - captain: "陈涛" - }, - { - id: 12, - name: "宿迁西楚", - city: "宿迁", - shortName: "SQC", - colors: ["#10b981", "#34d399"], - founded: 2021, - stadium: "宿迁体育中心", - capacity: 26000, - manager: "李伟", - captain: "张刚" - } - ], - - // 积分榜数据 - standings: [ - { - rank: 1, - teamId: 1, - played: 13, - won: 8, - drawn: 3, - lost: 2, - goalsFor: 24, - goalsAgainst: 12, - goalDifference: 12, - points: 27 - }, - { - rank: 2, - teamId: 2, - played: 13, - won: 7, - drawn: 4, - lost: 2, - goalsFor: 22, - goalsAgainst: 14, - goalDifference: 8, - points: 25 - }, - { - rank: 3, - teamId: 8, - played: 13, - won: 7, - drawn: 3, - lost: 3, - goalsFor: 20, - goalsAgainst: 15, - goalDifference: 5, - points: 24 - }, - { - rank: 4, - teamId: 3, - played: 13, - won: 6, - drawn: 4, - lost: 3, - goalsFor: 18, - goalsAgainst: 14, - goalDifference: 4, - points: 22 - }, - { - rank: 5, - teamId: 4, - played: 13, - won: 6, - drawn: 3, - lost: 4, - goalsFor: 19, - goalsAgainst: 16, - goalDifference: 3, - points: 21 - }, - { - rank: 6, - teamId: 6, - played: 13, - won: 5, - drawn: 5, - lost: 3, - goalsFor: 17, - goalsAgainst: 15, - goalDifference: 2, - points: 20 - }, - { - rank: 7, - teamId: 5, - played: 13, - won: 5, - drawn: 4, - lost: 4, - goalsFor: 16, - goalsAgainst: 15, - goalDifference: 1, - points: 19 - }, - { - rank: 8, - teamId: 7, - played: 13, - won: 4, - drawn: 5, - lost: 4, - goalsFor: 15, - goalsAgainst: 16, - goalDifference: -1, - points: 17 - }, - { - rank: 9, - teamId: 10, - played: 13, - won: 4, - drawn: 4, - lost: 5, - goalsFor: 14, - goalsAgainst: 17, - goalDifference: -3, - points: 16 - }, - { - rank: 10, - teamId: 9, - played: 13, - won: 3, - drawn: 5, - lost: 5, - goalsFor: 13, - goalsAgainst: 18, - goalDifference: -5, - points: 14 - }, - { - rank: 11, - teamId: 11, - played: 13, - won: 2, - drawn: 4, - lost: 7, - goalsFor: 11, - goalsAgainst: 20, - goalDifference: -9, - points: 10 - }, - { - rank: 12, - teamId: 12, - played: 13, - won: 1, - drawn: 3, - lost: 9, - goalsFor: 9, - goalsAgainst: 24, - goalDifference: -15, - points: 6 - } - ], - - // 赛程数据 - fixtures: [ - { - id: 1, - round: 1, - date: "2025-03-01", - time: "15:00", - homeTeamId: 1, - awayTeamId: 2, - venue: "南京奥体中心", - status: "completed", - homeScore: 2, - awayScore: 1 - }, - { - id: 2, - round: 1, - date: "2025-03-01", - time: "15:00", - homeTeamId: 3, - awayTeamId: 4, - venue: "无锡体育中心", - status: "completed", - homeScore: 1, - awayScore: 1 - }, - { - id: 3, - round: 1, - date: "2025-03-02", - time: "19:30", - homeTeamId: 5, - awayTeamId: 6, - venue: "镇江体育会展中心", - status: "completed", - homeScore: 0, - awayScore: 2 - }, - { - id: 4, - round: 1, - date: "2025-03-02", - time: "19:30", - homeTeamId: 7, - awayTeamId: 8, - venue: "南通体育会展中心", - status: "completed", - homeScore: 1, - awayScore: 3 - }, - { - id: 5, - round: 1, - date: "2025-03-03", - time: "15:00", - homeTeamId: 9, - awayTeamId: 10, - venue: "淮安体育中心", - status: "completed", - homeScore: 2, - awayScore: 2 - }, - { - id: 6, - round: 1, - date: "2025-03-03", - time: "15:00", - homeTeamId: 11, - awayTeamId: 12, - venue: "泰州体育公园", - status: "completed", - homeScore: 1, - awayScore: 0 - }, - { - id: 7, - round: 2, - date: "2025-03-08", - time: "15:00", - homeTeamId: 2, - awayTeamId: 3, - venue: "苏州奥林匹克体育中心", - status: "completed", - homeScore: 2, - awayScore: 0 - }, - { - id: 8, - round: 2, - date: "2025-03-08", - time: "15:00", - homeTeamId: 4, - awayTeamId: 5, - venue: "常州奥林匹克体育中心", - status: "completed", - homeScore: 3, - awayScore: 1 - }, - { - id: 9, - round: 2, - date: "2025-03-09", - time: "19:30", - homeTeamId: 6, - awayTeamId: 7, - venue: "扬州体育公园", - status: "completed", - homeScore: 1, - awayScore: 1 - }, - { - id: 10, - round: 2, - date: "2025-03-09", - time: "19:30", - homeTeamId: 8, - awayTeamId: 9, - venue: "徐州奥体中心", - status: "completed", - homeScore: 2, - awayScore: 0 - }, - { - id: 11, - round: 2, - date: "2025-03-10", - time: "15:00", - homeTeamId: 10, - awayTeamId: 11, - venue: "盐城体育中心", - status: "completed", - homeScore: 1, - awayScore: 0 - }, - { - id: 12, - round: 2, - date: "2025-03-10", - time: "15:00", - homeTeamId: 12, - awayTeamId: 1, - venue: "宿迁体育中心", - status: "completed", - homeScore: 0, - awayScore: 3 - }, - { - id: 13, - round: 12, - date: "2025-05-24", - time: "19:30", - homeTeamId: 1, - awayTeamId: 2, - venue: "南京奥体中心", - status: "scheduled" - }, - { - id: 14, - round: 12, - date: "2025-05-24", - time: "15:00", - homeTeamId: 3, - awayTeamId: 4, - venue: "无锡体育中心", - status: "scheduled" - }, - { - id: 15, - round: 12, - date: "2025-05-25", - time: "19:30", - homeTeamId: 5, - awayTeamId: 6, - venue: "镇江体育会展中心", - status: "scheduled" - }, - { - id: 16, - round: 12, - date: "2025-05-25", - time: "15:00", - homeTeamId: 7, - awayTeamId: 8, - venue: "南通体育会展中心", - status: "scheduled" - }, - { - id: 17, - round: 12, - date: "2025-05-26", - time: "19:30", - homeTeamId: 9, - awayTeamId: 10, - venue: "淮安体育中心", - status: "scheduled" - }, - { - id: 18, - round: 12, - date: "2025-05-26", - time: "15:00", - homeTeamId: 11, - awayTeamId: 12, - venue: "泰州体育公园", - status: "scheduled" - } - ], - - // 球员数据 - players: { - scorers: [ - { - rank: 1, - playerId: 101, - name: "张伟", - teamId: 1, - goals: 12, - assists: 4, - matches: 13, - minutes: 1170 - }, - { - rank: 2, - playerId: 102, - name: "李明", - teamId: 1, - goals: 8, - assists: 6, - matches: 13, - minutes: 1170 - }, - { - rank: 3, - playerId: 201, - name: "王强", - teamId: 2, - goals: 7, - assists: 5, - matches: 13, - minutes: 1170 - }, - { - rank: 4, - playerId: 301, - name: "赵刚", - teamId: 3, - goals: 6, - assists: 3, - matches: 13, - minutes: 1170 - }, - { - rank: 5, - playerId: 801, - name: "陈明", - teamId: 8, - goals: 6, - assists: 2, - matches: 13, - minutes: 1170 - }, - { - rank: 6, - playerId: 401, - name: "孙磊", - teamId: 4, - goals: 5, - assists: 4, - matches: 13, - minutes: 1170 - }, - { - rank: 7, - playerId: 601, - name: "钱勇", - teamId: 6, - goals: 5, - assists: 3, - matches: 13, - minutes: 1170 - }, - { - rank: 8, - playerId: 501, - name: "吴斌", - teamId: 5, - goals: 4, - assists: 5, - matches: 13, - minutes: 1170 - }, - { - rank: 9, - playerId: 701, - name: "冯超", - teamId: 7, - goals: 4, - assists: 3, - matches: 13, - minutes: 1170 - }, - { - rank: 10, - playerId: 1001, - name: "郑涛", - teamId: 10, - goals: 3, - assists: 2, - matches: 13, - minutes: 1170 - } - ], - - assists: [ - { - rank: 1, - playerId: 102, - name: "李明", - teamId: 1, - assists: 6, - goals: 8, - matches: 13, - minutes: 1170 - }, - { - rank: 2, - playerId: 501, - name: "吴斌", - teamId: 5, - assists: 5, - goals: 4, - matches: 13, - minutes: 1170 - }, - { - rank: 3, - playerId: 201, - name: "王强", - teamId: 2, - assists: 5, - goals: 7, - matches: 13, - minutes: 1170 - }, - { - rank: 4, - playerId: 401, - name: "孙磊", - teamId: 4, - assists: 4, - goals: 5, - matches: 13, - minutes: 1170 - }, - { - rank: 5, - playerId: 101, - name: "张伟", - teamId: 1, - assists: 4, - goals: 12, - matches: 13, - minutes: 1170 - }, - { - rank: 6, - playerId: 301, - name: "赵刚", - teamId: 3, - assists: 3, - goals: 6, - matches: 13, - minutes: 1170 - }, - { - rank: 7, - playerId: 601, - name: "钱勇", - teamId: 6, - assists: 3, - goals: 5, - matches: 13, - minutes: 1170 - }, - { - rank: 8, - playerId: 701, - name: "冯超", - teamId: 7, - assists: 3, - goals: 4, - matches: 13, - minutes: 1170 - }, - { - rank: 9, - playerId: 901, - name: "周伟", - teamId: 9, - assists: 3, - goals: 2, - matches: 13, - minutes: 1170 - }, - { - rank: 10, - playerId: 1101, - name: "王刚", - teamId: 11, - assists: 2, - goals: 1, - matches: 13, - minutes: 1170 - } - ] + { + id: 2, + name: "苏州雄狮", + city: "苏州", + shortName: "SZS", + colors: ["#059669", "#10b981"], + founded: 2019, + stadium: "苏州奥林匹克体育中心", + capacity: 45000, + manager: "王强", + captain: "陈浩", }, + { + id: 3, + name: "无锡太湖", + city: "无锡", + shortName: "WXT", + colors: ["#3b82f6", "#60a5fa"], + founded: 2021, + stadium: "无锡体育中心", + capacity: 32000, + manager: "赵刚", + captain: "刘洋", + }, + { + id: 4, + name: "常州龙城", + city: "常州", + shortName: "CZL", + colors: ["#7c3aed", "#8b5cf6"], + founded: 2022, + stadium: "常州奥林匹克体育中心", + capacity: 38000, + manager: "孙磊", + captain: "周涛", + }, + { + id: 5, + name: "镇江金山", + city: "镇江", + shortName: "ZJJ", + colors: ["#f59e0b", "#fbbf24"], + founded: 2020, + stadium: "镇江体育会展中心", + capacity: 28000, + manager: "吴斌", + captain: "郑军", + }, + { + id: 6, + name: "扬州运河", + city: "扬州", + shortName: "YZY", + colors: ["#ec4899", "#f472b6"], + founded: 2021, + stadium: "扬州体育公园", + capacity: 35000, + manager: "钱勇", + captain: "王磊", + }, + { + id: 7, + name: "南通江海", + city: "南通", + shortName: "NTJ", + colors: ["#0ea5e9", "#38bdf8"], + founded: 2022, + stadium: "南通体育会展中心", + capacity: 32000, + manager: "冯超", + captain: "张勇", + }, + { + id: 8, + name: "徐州楚汉", + city: "徐州", + shortName: "XZC", + colors: ["#84cc16", "#a3e635"], + founded: 2019, + stadium: "徐州奥体中心", + capacity: 42000, + manager: "陈明", + captain: "李强", + }, + { + id: 9, + name: "淮安运河", + city: "淮安", + shortName: "HAY", + colors: ["#f97316", "#fb923c"], + founded: 2021, + stadium: "淮安体育中心", + capacity: 30000, + manager: "周伟", + captain: "吴刚", + }, + { + id: 10, + name: "盐城黄海", + city: "盐城", + shortName: "YCH", + colors: ["#06b6d4", "#22d3ee"], + founded: 2020, + stadium: "盐城体育中心", + capacity: 32000, + manager: "郑涛", + captain: "孙明", + }, + { + id: 11, + name: "泰州凤城", + city: "泰州", + shortName: "TZF", + colors: ["#8b5cf6", "#a78bfa"], + founded: 2022, + stadium: "泰州体育公园", + capacity: 28000, + manager: "王刚", + captain: "陈涛", + }, + { + id: 12, + name: "宿迁西楚", + city: "宿迁", + shortName: "SQC", + colors: ["#10b981", "#34d399"], + founded: 2021, + stadium: "宿迁体育中心", + capacity: 26000, + manager: "李伟", + captain: "张刚", + }, + ], - // 新闻数据 - news: [ - { - id: 1, - title: "南京城联主场力克苏州雄狮,继续领跑积分榜", - excerpt: "在昨晚进行的第12轮焦点战中,南京城联凭借张伟的梅开二度,主场2-1战胜苏州雄狮,继续以2分优势领跑积分榜。", - category: "比赛战报", - date: "2025-05-25", - imageColor: "#dc2626" - }, - { - id: 2, - title: "联赛最佳球员揭晓:张伟当选4月最佳", - excerpt: "江苏城市足球联赛官方宣布,南京城联前锋张伟凭借出色的表现,当选4月份联赛最佳球员。", - category: "官方公告", - date: "2025-05-20", - imageColor: "#3b82f6" - }, - { - id: 3, - title: "徐州楚汉签下前国脚李强,实力大增", - excerpt: "徐州楚汉俱乐部官方宣布,与前国家队中场李强签约两年,这位经验丰富的老将将提升球队中场实力。", - category: "转会新闻", - date: "2025-05-18", - imageColor: "#84cc16" - }, - { - id: 4, - title: "联赛半程总结:竞争激烈,多队有望争冠", - excerpt: "随着联赛进入半程,积分榜前六名球队分差仅7分,本赛季冠军争夺异常激烈,多支球队都有机会问鼎。", - category: "联赛动态", - date: "2025-05-15", - imageColor: "#f59e0b" - }, - { - id: 5, - title: "球迷互动日:各俱乐部将举办开放训练", - excerpt: "为感谢球迷支持,各俱乐部将在本周末举办球迷开放日,球迷可近距离观看球队训练并与球员互动。", - category: "球迷活动", - date: "2025-05-12", - imageColor: "#ec4899" - }, - { - id: 6, - title: "技术统计:联赛进球数创历史新高", - excerpt: "本赛季前13轮共打进176球,场均2.77球,创下联赛历史同期最高进球纪录,进攻足球成为主流。", - category: "数据统计", - date: "2025-05-10", - imageColor: "#0ea5e9" - } - ] + // 积分榜数据 + standings: [ + { + rank: 1, + teamId: 1, + played: 13, + won: 8, + drawn: 3, + lost: 2, + goalsFor: 24, + goalsAgainst: 12, + goalDifference: 12, + points: 27, + }, + { + rank: 2, + teamId: 2, + played: 13, + won: 7, + drawn: 4, + lost: 2, + goalsFor: 22, + goalsAgainst: 14, + goalDifference: 8, + points: 25, + }, + { + rank: 3, + teamId: 8, + played: 13, + won: 7, + drawn: 3, + lost: 3, + goalsFor: 20, + goalsAgainst: 15, + goalDifference: 5, + points: 24, + }, + { + rank: 4, + teamId: 3, + played: 13, + won: 6, + drawn: 4, + lost: 3, + goalsFor: 18, + goalsAgainst: 14, + goalDifference: 4, + points: 22, + }, + { + rank: 5, + teamId: 4, + played: 13, + won: 6, + drawn: 3, + lost: 4, + goalsFor: 19, + goalsAgainst: 16, + goalDifference: 3, + points: 21, + }, + { + rank: 6, + teamId: 6, + played: 13, + won: 5, + drawn: 5, + lost: 3, + goalsFor: 17, + goalsAgainst: 15, + goalDifference: 2, + points: 20, + }, + { + rank: 7, + teamId: 5, + played: 13, + won: 5, + drawn: 4, + lost: 4, + goalsFor: 16, + goalsAgainst: 15, + goalDifference: 1, + points: 19, + }, + { + rank: 8, + teamId: 7, + played: 13, + won: 4, + drawn: 5, + lost: 4, + goalsFor: 15, + goalsAgainst: 16, + goalDifference: -1, + points: 17, + }, + { + rank: 9, + teamId: 10, + played: 13, + won: 4, + drawn: 4, + lost: 5, + goalsFor: 14, + goalsAgainst: 17, + goalDifference: -3, + points: 16, + }, + { + rank: 10, + teamId: 9, + played: 13, + won: 3, + drawn: 5, + lost: 5, + goalsFor: 13, + goalsAgainst: 18, + goalDifference: -5, + points: 14, + }, + { + rank: 11, + teamId: 11, + played: 13, + won: 2, + drawn: 4, + lost: 7, + goalsFor: 11, + goalsAgainst: 20, + goalDifference: -9, + points: 10, + }, + { + rank: 12, + teamId: 12, + played: 13, + won: 1, + drawn: 3, + lost: 9, + goalsFor: 9, + goalsAgainst: 24, + goalDifference: -15, + points: 6, + }, + ], + + // 赛程数据 + fixtures: [ + { + id: 1, + round: 1, + date: "2025-03-01", + time: "15:00", + homeTeamId: 1, + awayTeamId: 2, + venue: "南京奥体中心", + status: "completed", + homeScore: 2, + awayScore: 1, + }, + { + id: 2, + round: 1, + date: "2025-03-01", + time: "15:00", + homeTeamId: 3, + awayTeamId: 4, + venue: "无锡体育中心", + status: "completed", + homeScore: 1, + awayScore: 1, + }, + { + id: 3, + round: 1, + date: "2025-03-02", + time: "19:30", + homeTeamId: 5, + awayTeamId: 6, + venue: "镇江体育会展中心", + status: "completed", + homeScore: 0, + awayScore: 2, + }, + { + id: 4, + round: 1, + date: "2025-03-02", + time: "19:30", + homeTeamId: 7, + awayTeamId: 8, + venue: "南通体育会展中心", + status: "completed", + homeScore: 1, + awayScore: 3, + }, + { + id: 5, + round: 1, + date: "2025-03-03", + time: "15:00", + homeTeamId: 9, + awayTeamId: 10, + venue: "淮安体育中心", + status: "completed", + homeScore: 2, + awayScore: 2, + }, + { + id: 6, + round: 1, + date: "2025-03-03", + time: "15:00", + homeTeamId: 11, + awayTeamId: 12, + venue: "泰州体育公园", + status: "completed", + homeScore: 1, + awayScore: 0, + }, + { + id: 7, + round: 2, + date: "2025-03-08", + time: "15:00", + homeTeamId: 2, + awayTeamId: 3, + venue: "苏州奥林匹克体育中心", + status: "completed", + homeScore: 2, + awayScore: 0, + }, + { + id: 8, + round: 2, + date: "2025-03-08", + time: "15:00", + homeTeamId: 4, + awayTeamId: 5, + venue: "常州奥林匹克体育中心", + status: "completed", + homeScore: 3, + awayScore: 1, + }, + { + id: 9, + round: 2, + date: "2025-03-09", + time: "19:30", + homeTeamId: 6, + awayTeamId: 7, + venue: "扬州体育公园", + status: "completed", + homeScore: 1, + awayScore: 1, + }, + { + id: 10, + round: 2, + date: "2025-03-09", + time: "19:30", + homeTeamId: 8, + awayTeamId: 9, + venue: "徐州奥体中心", + status: "completed", + homeScore: 2, + awayScore: 0, + }, + { + id: 11, + round: 2, + date: "2025-03-10", + time: "15:00", + homeTeamId: 10, + awayTeamId: 11, + venue: "盐城体育中心", + status: "completed", + homeScore: 1, + awayScore: 0, + }, + { + id: 12, + round: 2, + date: "2025-03-10", + time: "15:00", + homeTeamId: 12, + awayTeamId: 1, + venue: "宿迁体育中心", + status: "completed", + homeScore: 0, + awayScore: 3, + }, + { + id: 13, + round: 12, + date: "2025-05-24", + time: "19:30", + homeTeamId: 1, + awayTeamId: 2, + venue: "南京奥体中心", + status: "scheduled", + }, + { + id: 14, + round: 12, + date: "2025-05-24", + time: "15:00", + homeTeamId: 3, + awayTeamId: 4, + venue: "无锡体育中心", + status: "scheduled", + }, + { + id: 15, + round: 12, + date: "2025-05-25", + time: "19:30", + homeTeamId: 5, + awayTeamId: 6, + venue: "镇江体育会展中心", + status: "scheduled", + }, + { + id: 16, + round: 12, + date: "2025-05-25", + time: "15:00", + homeTeamId: 7, + awayTeamId: 8, + venue: "南通体育会展中心", + status: "scheduled", + }, + { + id: 17, + round: 12, + date: "2025-05-26", + time: "19:30", + homeTeamId: 9, + awayTeamId: 10, + venue: "淮安体育中心", + status: "scheduled", + }, + { + id: 18, + round: 12, + date: "2025-05-26", + time: "15:00", + homeTeamId: 11, + awayTeamId: 12, + venue: "泰州体育公园", + status: "scheduled", + }, + ], + + // 球员数据 + players: { + scorers: [ + { + rank: 1, + playerId: 101, + name: "张伟", + teamId: 1, + goals: 12, + assists: 4, + matches: 13, + minutes: 1170, + }, + { + rank: 2, + playerId: 102, + name: "李明", + teamId: 1, + goals: 8, + assists: 6, + matches: 13, + minutes: 1170, + }, + { + rank: 3, + playerId: 201, + name: "王强", + teamId: 2, + goals: 7, + assists: 5, + matches: 13, + minutes: 1170, + }, + { + rank: 4, + playerId: 301, + name: "赵刚", + teamId: 3, + goals: 6, + assists: 3, + matches: 13, + minutes: 1170, + }, + { + rank: 5, + playerId: 801, + name: "陈明", + teamId: 8, + goals: 6, + assists: 2, + matches: 13, + minutes: 1170, + }, + { + rank: 6, + playerId: 401, + name: "孙磊", + teamId: 4, + goals: 5, + assists: 4, + matches: 13, + minutes: 1170, + }, + { + rank: 7, + playerId: 601, + name: "钱勇", + teamId: 6, + goals: 5, + assists: 3, + matches: 13, + minutes: 1170, + }, + { + rank: 8, + playerId: 501, + name: "吴斌", + teamId: 5, + goals: 4, + assists: 5, + matches: 13, + minutes: 1170, + }, + { + rank: 9, + playerId: 701, + name: "冯超", + teamId: 7, + goals: 4, + assists: 3, + matches: 13, + minutes: 1170, + }, + { + rank: 10, + playerId: 1001, + name: "郑涛", + teamId: 10, + goals: 3, + assists: 2, + matches: 13, + minutes: 1170, + }, + ], + + assists: [ + { + rank: 1, + playerId: 102, + name: "李明", + teamId: 1, + assists: 6, + goals: 8, + matches: 13, + minutes: 1170, + }, + { + rank: 2, + playerId: 501, + name: "吴斌", + teamId: 5, + assists: 5, + goals: 4, + matches: 13, + minutes: 1170, + }, + { + rank: 3, + playerId: 201, + name: "王强", + teamId: 2, + assists: 5, + goals: 7, + matches: 13, + minutes: 1170, + }, + { + rank: 4, + playerId: 401, + name: "孙磊", + teamId: 4, + assists: 4, + goals: 5, + matches: 13, + minutes: 1170, + }, + { + rank: 5, + playerId: 101, + name: "张伟", + teamId: 1, + assists: 4, + goals: 12, + matches: 13, + minutes: 1170, + }, + { + rank: 6, + playerId: 301, + name: "赵刚", + teamId: 3, + assists: 3, + goals: 6, + matches: 13, + minutes: 1170, + }, + { + rank: 7, + playerId: 601, + name: "钱勇", + teamId: 6, + assists: 3, + goals: 5, + matches: 13, + minutes: 1170, + }, + { + rank: 8, + playerId: 701, + name: "冯超", + teamId: 7, + assists: 3, + goals: 4, + matches: 13, + minutes: 1170, + }, + { + rank: 9, + playerId: 901, + name: "周伟", + teamId: 9, + assists: 3, + goals: 2, + matches: 13, + minutes: 1170, + }, + { + rank: 10, + playerId: 1101, + name: "王刚", + teamId: 11, + assists: 2, + goals: 1, + matches: 13, + minutes: 1170, + }, + ], + }, + + // 新闻数据 + news: [ + { + id: 1, + title: "南京城联主场力克苏州雄狮,继续领跑积分榜", + excerpt: + "在昨晚进行的第12轮焦点战中,南京城联凭借张伟的梅开二度,主场2-1战胜苏州雄狮,继续以2分优势领跑积分榜。", + category: "比赛战报", + date: "2025-05-25", + imageColor: "#dc2626", + }, + { + id: 2, + title: "联赛最佳球员揭晓:张伟当选4月最佳", + excerpt: + "江苏城市足球联赛官方宣布,南京城联前锋张伟凭借出色的表现,当选4月份联赛最佳球员。", + category: "官方公告", + date: "2025-05-20", + imageColor: "#3b82f6", + }, + { + id: 3, + title: "徐州楚汉签下前国脚李强,实力大增", + excerpt: + "徐州楚汉俱乐部官方宣布,与前国家队中场李强签约两年,这位经验丰富的老将将提升球队中场实力。", + category: "转会新闻", + date: "2025-05-18", + imageColor: "#84cc16", + }, + { + id: 4, + title: "联赛半程总结:竞争激烈,多队有望争冠", + excerpt: + "随着联赛进入半程,积分榜前六名球队分差仅7分,本赛季冠军争夺异常激烈,多支球队都有机会问鼎。", + category: "联赛动态", + date: "2025-05-15", + imageColor: "#f59e0b", + }, + { + id: 5, + title: "球迷互动日:各俱乐部将举办开放训练", + excerpt: + "为感谢球迷支持,各俱乐部将在本周末举办球迷开放日,球迷可近距离观看球队训练并与球员互动。", + category: "球迷活动", + date: "2025-05-12", + imageColor: "#ec4899", + }, + { + id: 6, + title: "技术统计:联赛进球数创历史新高", + excerpt: + "本赛季前13轮共打进176球,场均2.77球,创下联赛历史同期最高进球纪录,进攻足球成为主流。", + category: "数据统计", + date: "2025-05-10", + imageColor: "#0ea5e9", + }, + ], }; // 工具函数:根据ID获取球队信息 function getTeamById(teamId) { - return leagueData.teams.find(team => team.id === teamId); + return leagueData.teams.find((team) => team.id === teamId); } // 工具函数:格式化日期 function formatDate(dateString) { - const date = new Date(dateString); - const options = { weekday: 'short', month: 'short', day: 'numeric' }; - return date.toLocaleDateString('zh-CN', options); + const date = new Date(dateString); + const options = { weekday: "short", month: "short", day: "numeric" }; + return date.toLocaleDateString("zh-CN", options); } // 工具函数:格式化时间 function formatTime(timeString) { - return timeString; + return timeString; } // 导出数据 -if (typeof module !== 'undefined' && module.exports) { - module.exports = leagueData; -} \ No newline at end of file +if (typeof module !== "undefined" && module.exports) { + module.exports = leagueData; +} diff --git a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/js/main.js b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/js/main.js index e79c3cbb3..aaa2ac062 100644 --- a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/js/main.js +++ b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/js/main.js @@ -1,163 +1,163 @@ // 江苏城市足球联赛2025赛季 - 主JavaScript文件 -document.addEventListener('DOMContentLoaded', function() { - // 初始化加载动画 - initLoader(); - - // 初始化主题切换 - initThemeToggle(); - - // 初始化导航菜单 - initNavigation(); - - // 初始化滚动监听 - initScrollSpy(); - - // 渲染球队卡片 - renderTeams(); - - // 渲染积分榜 - renderStandings(); - - // 渲染赛程表 - renderFixtures(); - - // 渲染数据统计 - renderStats(); - - // 渲染新闻动态 - renderNews(); - - // 初始化标签页切换 - initTabs(); - - // 初始化移动端菜单 - initMobileMenu(); +document.addEventListener("DOMContentLoaded", function () { + // 初始化加载动画 + initLoader(); + + // 初始化主题切换 + initThemeToggle(); + + // 初始化导航菜单 + initNavigation(); + + // 初始化滚动监听 + initScrollSpy(); + + // 渲染球队卡片 + renderTeams(); + + // 渲染积分榜 + renderStandings(); + + // 渲染赛程表 + renderFixtures(); + + // 渲染数据统计 + renderStats(); + + // 渲染新闻动态 + renderNews(); + + // 初始化标签页切换 + initTabs(); + + // 初始化移动端菜单 + initMobileMenu(); }); // 加载动画 function initLoader() { - const loader = document.querySelector('.loader'); - - // 模拟加载延迟 + const loader = document.querySelector(".loader"); + + // 模拟加载延迟 + setTimeout(() => { + loader.classList.add("loaded"); + + // 动画结束后隐藏loader setTimeout(() => { - loader.classList.add('loaded'); - - // 动画结束后隐藏loader - setTimeout(() => { - loader.style.display = 'none'; - }, 300); - }, 1500); + loader.style.display = "none"; + }, 300); + }, 1500); } // 主题切换 function initThemeToggle() { - const themeToggle = document.querySelector('.btn-theme-toggle'); - const themeIcon = themeToggle.querySelector('i'); - - // 检查本地存储的主题偏好 - const savedTheme = localStorage.getItem('theme') || 'light'; - document.documentElement.setAttribute('data-theme', savedTheme); - updateThemeIcon(savedTheme); - - themeToggle.addEventListener('click', () => { - const currentTheme = document.documentElement.getAttribute('data-theme'); - const newTheme = currentTheme === 'light' ? 'dark' : 'light'; - - document.documentElement.setAttribute('data-theme', newTheme); - localStorage.setItem('theme', newTheme); - updateThemeIcon(newTheme); - - // 添加切换动画 - themeToggle.style.transform = 'scale(0.9)'; - setTimeout(() => { - themeToggle.style.transform = ''; - }, 150); - }); - - function updateThemeIcon(theme) { - if (theme === 'dark') { - themeIcon.className = 'fas fa-sun'; - } else { - themeIcon.className = 'fas fa-moon'; - } + const themeToggle = document.querySelector(".btn-theme-toggle"); + const themeIcon = themeToggle.querySelector("i"); + + // 检查本地存储的主题偏好 + const savedTheme = localStorage.getItem("theme") || "light"; + document.documentElement.setAttribute("data-theme", savedTheme); + updateThemeIcon(savedTheme); + + themeToggle.addEventListener("click", () => { + const currentTheme = document.documentElement.getAttribute("data-theme"); + const newTheme = currentTheme === "light" ? "dark" : "light"; + + document.documentElement.setAttribute("data-theme", newTheme); + localStorage.setItem("theme", newTheme); + updateThemeIcon(newTheme); + + // 添加切换动画 + themeToggle.style.transform = "scale(0.9)"; + setTimeout(() => { + themeToggle.style.transform = ""; + }, 150); + }); + + function updateThemeIcon(theme) { + if (theme === "dark") { + themeIcon.className = "fas fa-sun"; + } else { + themeIcon.className = "fas fa-moon"; } + } } // 导航菜单 function initNavigation() { - const navLinks = document.querySelectorAll('.nav-link'); - - navLinks.forEach(link => { - link.addEventListener('click', function(e) { - e.preventDefault(); - - const targetId = this.getAttribute('href'); - const targetSection = document.querySelector(targetId); - - if (targetSection) { - // 更新活动链接 - navLinks.forEach(l => l.classList.remove('active')); - this.classList.add('active'); - - // 平滑滚动到目标区域 - window.scrollTo({ - top: targetSection.offsetTop - 80, - behavior: 'smooth' - }); - - // 如果是移动端,关闭菜单 - const navMenu = document.querySelector('.nav-menu'); - if (navMenu.classList.contains('active')) { - navMenu.classList.remove('active'); - } - } + const navLinks = document.querySelectorAll(".nav-link"); + + navLinks.forEach((link) => { + link.addEventListener("click", function (e) { + e.preventDefault(); + + const targetId = this.getAttribute("href"); + const targetSection = document.querySelector(targetId); + + if (targetSection) { + // 更新活动链接 + navLinks.forEach((l) => l.classList.remove("active")); + this.classList.add("active"); + + // 平滑滚动到目标区域 + window.scrollTo({ + top: targetSection.offsetTop - 80, + behavior: "smooth", }); + + // 如果是移动端,关闭菜单 + const navMenu = document.querySelector(".nav-menu"); + if (navMenu.classList.contains("active")) { + navMenu.classList.remove("active"); + } + } }); + }); } // 滚动监听 function initScrollSpy() { - const sections = document.querySelectorAll('section[id]'); - const navLinks = document.querySelectorAll('.nav-link'); - - window.addEventListener('scroll', () => { - let current = ''; - - sections.forEach(section => { - const sectionTop = section.offsetTop; - const sectionHeight = section.clientHeight; - - if (scrollY >= sectionTop - 100) { - current = section.getAttribute('id'); - } - }); - - navLinks.forEach(link => { - link.classList.remove('active'); - if (link.getAttribute('href') === `#${current}`) { - link.classList.add('active'); - } - }); + const sections = document.querySelectorAll("section[id]"); + const navLinks = document.querySelectorAll(".nav-link"); + + window.addEventListener("scroll", () => { + let current = ""; + + sections.forEach((section) => { + const sectionTop = section.offsetTop; + const sectionHeight = section.clientHeight; + + if (scrollY >= sectionTop - 100) { + current = section.getAttribute("id"); + } }); + + navLinks.forEach((link) => { + link.classList.remove("active"); + if (link.getAttribute("href") === `#${current}`) { + link.classList.add("active"); + } + }); + }); } // 渲染球队卡片 function renderTeams() { - const teamsGrid = document.querySelector('.teams-grid'); - - if (!teamsGrid) return; - - teamsGrid.innerHTML = ''; - - leagueData.teams.forEach(team => { - const teamCard = document.createElement('div'); - teamCard.className = 'team-card'; - - // 获取球队统计数据 - const standing = leagueData.standings.find(s => s.teamId === team.id); - - teamCard.innerHTML = ` + const teamsGrid = document.querySelector(".teams-grid"); + + if (!teamsGrid) return; + + teamsGrid.innerHTML = ""; + + leagueData.teams.forEach((team) => { + const teamCard = document.createElement("div"); + teamCard.className = "team-card"; + + // 获取球队统计数据 + const standing = leagueData.standings.find((s) => s.teamId === team.id); + + teamCard.innerHTML = ` @@ -165,52 +165,52 @@ function renderTeams() {
${team.city}
-
${standing ? standing.rank : '-'}
+
${standing ? standing.rank : "-"}
排名
-
${standing ? standing.points : '0'}
+
${standing ? standing.points : "0"}
积分
-
${standing ? standing.goalDifference : '0'}
+
${standing ? standing.goalDifference : "0"}
净胜球
`; - - teamCard.addEventListener('click', () => { - // 这里可以添加点击跳转到球队详情页的功能 - alert(`查看 ${team.name} 的详细信息`); - }); - - teamsGrid.appendChild(teamCard); + + teamCard.addEventListener("click", () => { + // 这里可以添加点击跳转到球队详情页的功能 + alert(`查看 ${team.name} 的详细信息`); }); + + teamsGrid.appendChild(teamCard); + }); } // 渲染积分榜 function renderStandings() { - const standingsTable = document.querySelector('.standings-table tbody'); - - if (!standingsTable) return; - - standingsTable.innerHTML = ''; - - leagueData.standings.forEach(standing => { - const team = getTeamById(standing.teamId); - - const row = document.createElement('tr'); - - // 根据排名添加特殊样式 - if (standing.rank <= 4) { - row.classList.add('champions-league'); - } else if (standing.rank <= 6) { - row.classList.add('europa-league'); - } else if (standing.rank >= 11) { - row.classList.add('relegation'); - } - - row.innerHTML = ` + const standingsTable = document.querySelector(".standings-table tbody"); + + if (!standingsTable) return; + + standingsTable.innerHTML = ""; + + leagueData.standings.forEach((standing) => { + const team = getTeamById(standing.teamId); + + const row = document.createElement("tr"); + + // 根据排名添加特殊样式 + if (standing.rank <= 4) { + row.classList.add("champions-league"); + } else if (standing.rank <= 6) { + row.classList.add("europa-league"); + } else if (standing.rank >= 11) { + row.classList.add("relegation"); + } + + row.innerHTML = ` ${standing.rank}
@@ -224,70 +224,80 @@ function renderStandings() { ${standing.lost} ${standing.goalsFor} ${standing.goalsAgainst} - ${standing.goalDifference > 0 ? '+' : ''}${standing.goalDifference} + ${standing.goalDifference > 0 ? "+" : ""}${standing.goalDifference} ${standing.points} `; - - standingsTable.appendChild(row); - }); + + standingsTable.appendChild(row); + }); } // 渲染赛程表 function renderFixtures() { - const fixturesList = document.querySelector('.fixtures-list'); - - if (!fixturesList) return; - - fixturesList.innerHTML = ''; - - // 按轮次分组 - const fixturesByRound = {}; - leagueData.fixtures.forEach(fixture => { - if (!fixturesByRound[fixture.round]) { - fixturesByRound[fixture.round] = []; - } - fixturesByRound[fixture.round].push(fixture); - }); - - // 渲染所有赛程 - Object.keys(fixturesByRound).sort((a, b) => a - b).forEach(round => { - const roundHeader = document.createElement('div'); - roundHeader.className = 'fixture-round-header'; - roundHeader.innerHTML = `

第${round}轮

`; - fixturesList.appendChild(roundHeader); - - fixturesByRound[round].forEach(fixture => { - const homeTeam = getTeamById(fixture.homeTeamId); - const awayTeam = getTeamById(fixture.awayTeamId); - - const fixtureItem = document.createElement('div'); - fixtureItem.className = 'fixture-item'; - - const date = new Date(fixture.date); - const dayNames = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; - const dayName = dayNames[date.getDay()]; - - let scoreHtml = ''; - let statusText = ''; - - if (fixture.status === 'completed') { - scoreHtml = ` + const fixturesList = document.querySelector(".fixtures-list"); + + if (!fixturesList) return; + + fixturesList.innerHTML = ""; + + // 按轮次分组 + const fixturesByRound = {}; + leagueData.fixtures.forEach((fixture) => { + if (!fixturesByRound[fixture.round]) { + fixturesByRound[fixture.round] = []; + } + fixturesByRound[fixture.round].push(fixture); + }); + + // 渲染所有赛程 + Object.keys(fixturesByRound) + .sort((a, b) => a - b) + .forEach((round) => { + const roundHeader = document.createElement("div"); + roundHeader.className = "fixture-round-header"; + roundHeader.innerHTML = `

第${round}轮

`; + fixturesList.appendChild(roundHeader); + + fixturesByRound[round].forEach((fixture) => { + const homeTeam = getTeamById(fixture.homeTeamId); + const awayTeam = getTeamById(fixture.awayTeamId); + + const fixtureItem = document.createElement("div"); + fixtureItem.className = "fixture-item"; + + const date = new Date(fixture.date); + const dayNames = [ + "周日", + "周一", + "周二", + "周三", + "周四", + "周五", + "周六", + ]; + const dayName = dayNames[date.getDay()]; + + let scoreHtml = ""; + let statusText = ""; + + if (fixture.status === "completed") { + scoreHtml = `
${fixture.homeScore} - ${fixture.awayScore}
已结束
`; - } else if (fixture.status === 'scheduled') { - scoreHtml = ` + } else if (fixture.status === "scheduled") { + scoreHtml = `
VS
${fixture.time}
`; - } else { - scoreHtml = ` + } else { + scoreHtml = `
-
待定
`; - } - - fixtureItem.innerHTML = ` + } + + fixtureItem.innerHTML = `
${dayName}
${formatDate(fixture.date)}
@@ -307,25 +317,25 @@ function renderFixtures() { ${scoreHtml}
`; - - fixturesList.appendChild(fixtureItem); - }); + + fixturesList.appendChild(fixtureItem); + }); }); } // 渲染数据统计 function renderStats() { - renderScorers(); - renderAssists(); - renderTeamStats(); + renderScorers(); + renderAssists(); + renderTeamStats(); } function renderScorers() { - const scorersContainer = document.querySelector('#scorers'); - - if (!scorersContainer) return; - - scorersContainer.innerHTML = ` + const scorersContainer = document.querySelector("#scorers"); + + if (!scorersContainer) return; + + scorersContainer.innerHTML = ` @@ -338,7 +348,8 @@ function renderScorers() { - ${leagueData.players.scorers.map(player => { + ${leagueData.players.scorers + .map((player) => { const team = getTeamById(player.teamId); return ` @@ -350,18 +361,19 @@ function renderScorers() { `; - }).join('')} + }) + .join("")}
${player.matches}
`; } function renderAssists() { - const assistsContainer = document.querySelector('#assists'); - - if (!assistsContainer) return; - - assistsContainer.innerHTML = ` + const assistsContainer = document.querySelector("#assists"); + + if (!assistsContainer) return; + + assistsContainer.innerHTML = ` @@ -374,7 +386,8 @@ function renderAssists() { - ${leagueData.players.assists.map(player => { + ${leagueData.players.assists + .map((player) => { const team = getTeamById(player.teamId); return ` @@ -386,36 +399,41 @@ function renderAssists() { `; - }).join('')} + }) + .join("")}
${player.matches}
`; } function renderTeamStats() { - const teamStatsContainer = document.querySelector('#teams'); - - if (!teamStatsContainer) return; - - // 计算球队统计数据 - const teamStats = leagueData.standings.map(standing => { - const team = getTeamById(standing.teamId); - const goalsPerGame = (standing.goalsFor / standing.played).toFixed(2); - const concededPerGame = (standing.goalsAgainst / standing.played).toFixed(2); - - return { - rank: standing.rank, - team: team.name, - goalsFor: standing.goalsFor, - goalsAgainst: standing.goalsAgainst, - goalDifference: standing.goalDifference, - goalsPerGame, - concededPerGame, - cleanSheets: Math.floor(Math.random() * 5) // 模拟数据 - }; - }).sort((a, b) => a.rank - b.rank); - - teamStatsContainer.innerHTML = ` + const teamStatsContainer = document.querySelector("#teams"); + + if (!teamStatsContainer) return; + + // 计算球队统计数据 + const teamStats = leagueData.standings + .map((standing) => { + const team = getTeamById(standing.teamId); + const goalsPerGame = (standing.goalsFor / standing.played).toFixed(2); + const concededPerGame = (standing.goalsAgainst / standing.played).toFixed( + 2, + ); + + return { + rank: standing.rank, + team: team.name, + goalsFor: standing.goalsFor, + goalsAgainst: standing.goalsAgainst, + goalDifference: standing.goalDifference, + goalsPerGame, + concededPerGame, + cleanSheets: Math.floor(Math.random() * 5), // 模拟数据 + }; + }) + .sort((a, b) => a.rank - b.rank); + + teamStatsContainer.innerHTML = ` @@ -430,18 +448,22 @@ function renderTeamStats() { - ${teamStats.map(stat => ` + ${teamStats + .map( + (stat) => ` - + - `).join('')} + `, + ) + .join("")}
${stat.rank} ${stat.team} ${stat.goalsFor} ${stat.goalsAgainst}${stat.goalDifference > 0 ? '+' : ''}${stat.goalDifference}${stat.goalDifference > 0 ? "+" : ""}${stat.goalDifference} ${stat.goalsPerGame} ${stat.concededPerGame} ${stat.cleanSheets}
`; @@ -449,24 +471,24 @@ function renderTeamStats() { // 渲染新闻动态 function renderNews() { - const newsGrid = document.querySelector('.news-grid'); - - if (!newsGrid) return; - - newsGrid.innerHTML = ''; - - leagueData.news.forEach(newsItem => { - const newsCard = document.createElement('div'); - newsCard.className = 'news-card'; - - const date = new Date(newsItem.date); - const formattedDate = date.toLocaleDateString('zh-CN', { - year: 'numeric', - month: 'long', - day: 'numeric' - }); - - newsCard.innerHTML = ` + const newsGrid = document.querySelector(".news-grid"); + + if (!newsGrid) return; + + newsGrid.innerHTML = ""; + + leagueData.news.forEach((newsItem) => { + const newsCard = document.createElement("div"); + newsCard.className = "news-card"; + + const date = new Date(newsItem.date); + const formattedDate = date.toLocaleDateString("zh-CN", { + year: "numeric", + month: "long", + day: "numeric", + }); + + newsCard.innerHTML = `
${newsItem.category} @@ -481,138 +503,143 @@ function renderNews() {
`; - - newsCard.addEventListener('click', () => { - alert(`查看新闻: ${newsItem.title}`); - }); - - newsGrid.appendChild(newsCard); + + newsCard.addEventListener("click", () => { + alert(`查看新闻: ${newsItem.title}`); }); + + newsGrid.appendChild(newsCard); + }); } // 初始化标签页切换 function initTabs() { - // 赛程标签页 - const fixtureTabs = document.querySelectorAll('.fixtures-tabs .tab'); - const fixtureItems = document.querySelectorAll('.fixture-item'); - - fixtureTabs.forEach(tab => { - tab.addEventListener('click', () => { - // 更新活动标签 - fixtureTabs.forEach(t => t.classList.remove('active')); - tab.classList.add('active'); - - const roundFilter = tab.getAttribute('data-round'); - - // 这里可以根据筛选条件显示不同的赛程 - // 由于时间关系,这里只是简单的演示 - console.log(`筛选赛程: ${roundFilter}`); - }); + // 赛程标签页 + const fixtureTabs = document.querySelectorAll(".fixtures-tabs .tab"); + const fixtureItems = document.querySelectorAll(".fixture-item"); + + fixtureTabs.forEach((tab) => { + tab.addEventListener("click", () => { + // 更新活动标签 + fixtureTabs.forEach((t) => t.classList.remove("active")); + tab.classList.add("active"); + + const roundFilter = tab.getAttribute("data-round"); + + // 这里可以根据筛选条件显示不同的赛程 + // 由于时间关系,这里只是简单的演示 + console.log(`筛选赛程: ${roundFilter}`); }); - - // 数据统计标签页 - const statsTabs = document.querySelectorAll('.stats-tab'); - const statsContents = document.querySelectorAll('.stats-tab-content'); - - statsTabs.forEach(tab => { - tab.addEventListener('click', () => { - const tabId = tab.getAttribute('data-tab'); - - // 更新活动标签 - statsTabs.forEach(t => t.classList.remove('active')); - tab.classList.add('active'); - - // 显示对应内容 - statsContents.forEach(content => { - content.classList.remove('active'); - if (content.id === tabId) { - content.classList.add('active'); - } - }); - }); + }); + + // 数据统计标签页 + const statsTabs = document.querySelectorAll(".stats-tab"); + const statsContents = document.querySelectorAll(".stats-tab-content"); + + statsTabs.forEach((tab) => { + tab.addEventListener("click", () => { + const tabId = tab.getAttribute("data-tab"); + + // 更新活动标签 + statsTabs.forEach((t) => t.classList.remove("active")); + tab.classList.add("active"); + + // 显示对应内容 + statsContents.forEach((content) => { + content.classList.remove("active"); + if (content.id === tabId) { + content.classList.add("active"); + } + }); }); + }); } // 初始化移动端菜单 function initMobileMenu() { - const menuToggle = document.querySelector('.btn-menu-toggle'); - const navMenu = document.querySelector('.nav-menu'); - - if (menuToggle && navMenu) { - menuToggle.addEventListener('click', () => { - navMenu.classList.toggle('active'); - - // 更新菜单图标 - const icon = menuToggle.querySelector('i'); - if (navMenu.classList.contains('active')) { - icon.className = 'fas fa-times'; - } else { - icon.className = 'fas fa-bars'; - } - }); - - // 点击菜单外区域关闭菜单 - document.addEventListener('click', (e) => { - if (!navMenu.contains(e.target) && !menuToggle.contains(e.target)) { - navMenu.classList.remove('active'); - menuToggle.querySelector('i').className = 'fas fa-bars'; - } - }); - } + const menuToggle = document.querySelector(".btn-menu-toggle"); + const navMenu = document.querySelector(".nav-menu"); + + if (menuToggle && navMenu) { + menuToggle.addEventListener("click", () => { + navMenu.classList.toggle("active"); + + // 更新菜单图标 + const icon = menuToggle.querySelector("i"); + if (navMenu.classList.contains("active")) { + icon.className = "fas fa-times"; + } else { + icon.className = "fas fa-bars"; + } + }); + + // 点击菜单外区域关闭菜单 + document.addEventListener("click", (e) => { + if (!navMenu.contains(e.target) && !menuToggle.contains(e.target)) { + navMenu.classList.remove("active"); + menuToggle.querySelector("i").className = "fas fa-bars"; + } + }); + } } // 工具函数:加深颜色 function darkenColor(color, percent) { - const num = parseInt(color.replace("#", ""), 16); - const amt = Math.round(2.55 * percent); - const R = (num >> 16) - amt; - const G = (num >> 8 & 0x00FF) - amt; - const B = (num & 0x0000FF) - amt; - - return "#" + ( - 0x1000000 + - (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 + - (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 + - (B < 255 ? B < 1 ? 0 : B : 255) - ).toString(16).slice(1); + const num = parseInt(color.replace("#", ""), 16); + const amt = Math.round(2.55 * percent); + const R = (num >> 16) - amt; + const G = ((num >> 8) & 0x00ff) - amt; + const B = (num & 0x0000ff) - amt; + + return ( + "#" + + ( + 0x1000000 + + (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 + + (G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 + + (B < 255 ? (B < 1 ? 0 : B) : 255) + ) + .toString(16) + .slice(1) + ); } // 工具函数:格式化日期(简写) function formatDate(dateString) { - const date = new Date(dateString); - const month = date.getMonth() + 1; - const day = date.getDate(); - return `${month}月${day}日`; + const date = new Date(dateString); + const month = date.getMonth() + 1; + const day = date.getDate(); + return `${month}月${day}日`; } // 工具函数:根据ID获取球队信息 function getTeamById(teamId) { - return leagueData.teams.find(team => team.id === teamId); + return leagueData.teams.find((team) => team.id === teamId); } // 添加一些交互效果 -document.addEventListener('DOMContentLoaded', () => { - // 为所有按钮添加点击效果 - const buttons = document.querySelectorAll('.btn'); - buttons.forEach(button => { - button.addEventListener('mousedown', () => { - button.style.transform = 'scale(0.95)'; - }); - - button.addEventListener('mouseup', () => { - button.style.transform = ''; - }); - - button.addEventListener('mouseleave', () => { - button.style.transform = ''; - }); +document.addEventListener("DOMContentLoaded", () => { + // 为所有按钮添加点击效果 + const buttons = document.querySelectorAll(".btn"); + buttons.forEach((button) => { + button.addEventListener("mousedown", () => { + button.style.transform = "scale(0.95)"; }); - - // 为卡片添加悬停效果 - const cards = document.querySelectorAll('.team-card, .news-card'); - cards.forEach(card => { - card.addEventListener('mouseenter', () => { - card.style.transition = 'transform 0.3s ease, box-shadow 0.3s ease'; - }); + + button.addEventListener("mouseup", () => { + button.style.transform = ""; }); -}); \ No newline at end of file + + button.addEventListener("mouseleave", () => { + button.style.transform = ""; + }); + }); + + // 为卡片添加悬停效果 + const cards = document.querySelectorAll(".team-card, .news-card"); + cards.forEach((card) => { + card.addEventListener("mouseenter", () => { + card.style.transition = "transform 0.3s ease, box-shadow 0.3s ease"; + }); + }); +}); diff --git a/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/index.html b/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/index.html index 05fb95995..efff82751 100644 --- a/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/index.html +++ b/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/index.html @@ -1,385 +1,533 @@ - + - - - + + + 2026 Horizons: Trends & Opportunities - - - - - - - - + + + + + + + +
-
-
-

Navigating the Future

-

A comprehensive analysis of trends, opportunities, and challenges shaping 2026

-
-
- 5 - Key Economic Trends -
-
- 8 - High-Growth Markets -
-
- 4 - Technology Shifts -
-
- Explore Trends +
+
+

Navigating the Future

+

+ A comprehensive analysis of trends, opportunities, and challenges + shaping 2026 +

+
+
+ 5 + Key Economic Trends
-
-
-
-
-
-
+
+ 8 + High-Growth Markets
+
+ 4 + Technology Shifts +
+
+ Explore Trends
+
+
+
+
+
+
+
+
-
-
-

The 2026 Landscape

-

Convergence, complexity, and unprecedented opportunities

-
-
-
-

2026 represents a pivotal inflection point where accelerating technological convergence meets economic realignment and emerging market opportunities. The year will be defined by the interplay of AI maturation, quantum computing practicality, and sustainable transformation.

-

Organizations and individuals who can navigate this complexity while maintaining strategic agility will be best positioned to capitalize on emerging opportunities across technology, business, and sustainability sectors.

-
-
-
-
- -
-

AI Maturation

-

Transition from experimentation to production deployment with autonomous agents

-
-
-
- -
-

Sustainability Focus

-

Climate tech emerges as a dominant investment category with material financial implications

-
-
-
+
+
+

The 2026 Landscape

+

+ Convergence, complexity, and unprecedented opportunities +

+
+
+

+ 2026 represents a pivotal inflection point where accelerating + technological convergence meets economic realignment and emerging + market opportunities. The year will be defined by the interplay of + AI maturation, quantum computing practicality, and sustainable + transformation. +

+

+ Organizations and individuals who can navigate this complexity + while maintaining strategic agility will be best positioned to + capitalize on emerging opportunities across technology, business, + and sustainability sectors. +

+
+
+
+
+ +
+

AI Maturation

+

+ Transition from experimentation to production deployment with + autonomous agents +

+
+
+
+ +
+

Sustainability Focus

+

+ Climate tech emerges as a dominant investment category with + material financial implications +

+
+
+
+
-
-
-

Emerging Opportunities

-

High-growth markets and strategic investment areas

-
- -
-
-
- -
-

Climate Technology

-

Home energy solutions, carbon capture, and sustainable infrastructure with massive growth potential.

-
- $162B+ - by 2030 -
-
- -
-
- -
-

Preventive Health

-

Personalized wellness, early intervention technologies, and digital health platforms.

-
- High Growth - Post-pandemic focus -
-
- -
-
- -
-

AI Consulting

-

Industry-specific AI implementation services and agentic AI platform development.

-
- Specialized - Enterprise demand -
-
- -
-
- -
-

Plant-Based Foods

-

Sustainable food alternatives with projected market growth toward $162 billion by 2030.

-
- $162B - Market potential -
-
-
- -
-
-

Strategic Investment Shift

-

Venture capital is diversifying geographically with emerging hubs in Lagos, Bucharest, Riyadh, and other non-traditional locations. Decentralized finance continues to innovate alternatives to traditional systems.

-
-
-
- 75% - G20 Digital Payments -
-
- 18% - Quantum-AI Revenue -
-
-
+
+
+

Emerging Opportunities

+

+ High-growth markets and strategic investment areas +

+ +
+
+
+ +
+

Climate Technology

+

+ Home energy solutions, carbon capture, and sustainable + infrastructure with massive growth potential. +

+
+ $162B+ + by 2030 +
+
+ +
+
+ +
+

Preventive Health

+

+ Personalized wellness, early intervention technologies, and + digital health platforms. +

+
+ High Growth + Post-pandemic focus +
+
+ +
+
+ +
+

AI Consulting

+

+ Industry-specific AI implementation services and agentic AI + platform development. +

+
+ Specialized + Enterprise demand +
+
+ +
+
+ +
+

Plant-Based Foods

+

+ Sustainable food alternatives with projected market growth toward + $162 billion by 2030. +

+
+ $162B + Market potential +
+
+
+ +
+
+

Strategic Investment Shift

+

+ Venture capital is diversifying geographically with emerging hubs + in Lagos, Bucharest, Riyadh, and other non-traditional locations. + Decentralized finance continues to innovate alternatives to + traditional systems. +

+
+
+
+ 75% + G20 Digital Payments +
+
+ 18% + Quantum-AI Revenue +
+
+
+
-
-
-

Critical Challenges & Risks

-

Navigating complexity in an uncertain landscape

-
- -
-
-
- High Risk -

AI Security Vulnerabilities

-
-

New attack vectors require comprehensive defense strategies as autonomous agents proliferate across organizations.

-
- Mitigation: - Robust governance frameworks and AI-native security protocols -
-
- -
-
- Medium Risk -

Talent & Skills Gap

-
-

Rapid technological change outpacing workforce skill development, creating critical talent shortages.

-
- Mitigation: - Continuous upskilling programs and AI collaboration training -
-
- -
-
- High Risk -

Economic Volatility

-
-

Potential AI bubble concerns, trade fragmentation, and competing payment systems creating market uncertainty.

-
- Mitigation: - Diversified portfolios and agile business models -
-
-
- -
-

Strategic Implications

-
-
-

For Businesses

-

Success requires embracing AI as a core competency while maintaining robust cybersecurity. Companies that navigate the sustainability transition while leveraging emerging technologies gain competitive advantages.

-
-
-

For Investors

-

Opportunities exist in climate tech, digital transformation, and Asian markets, but require careful assessment of geopolitical risks and potential market corrections.

-
-
-

For Individuals

-

Continuous upskilling in AI collaboration, quantum computing awareness, and digital literacy will be essential for career resilience in the evolving landscape.

-
-
-
+
+
+

Critical Challenges & Risks

+

+ Navigating complexity in an uncertain landscape +

+ +
+
+
+ High Risk +

AI Security Vulnerabilities

+
+

+ New attack vectors require comprehensive defense strategies as + autonomous agents proliferate across organizations. +

+
+ Mitigation: + Robust governance frameworks and AI-native security + protocols +
+
+ +
+
+ Medium Risk +

Talent & Skills Gap

+
+

+ Rapid technological change outpacing workforce skill development, + creating critical talent shortages. +

+
+ Mitigation: + Continuous upskilling programs and AI collaboration + training +
+
+ +
+
+ High Risk +

Economic Volatility

+
+

+ Potential AI bubble concerns, trade fragmentation, and competing + payment systems creating market uncertainty. +

+
+ Mitigation: + Diversified portfolios and agile business models +
+
+
+ +
+

Strategic Implications

+
+
+

For Businesses

+

+ Success requires embracing AI as a core competency while + maintaining robust cybersecurity. Companies that navigate the + sustainability transition while leveraging emerging technologies + gain competitive advantages. +

+
+
+

For Investors

+

+ Opportunities exist in climate tech, digital transformation, and + Asian markets, but require careful assessment of geopolitical + risks and potential market corrections. +

+
+
+

For Individuals

+

+ Continuous upskilling in AI collaboration, quantum computing + awareness, and digital literacy will be essential for career + resilience in the evolving landscape. +

+
+
+
+
- - \ No newline at end of file + + diff --git a/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/script.js b/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/script.js index e8beb0ff6..2b061d418 100644 --- a/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/script.js +++ b/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/script.js @@ -1,156 +1,168 @@ // 2026 Horizons - Interactive Features -document.addEventListener('DOMContentLoaded', function() { - // Theme Toggle - const themeToggle = document.getElementById('themeToggle'); - const themeIcon = themeToggle.querySelector('i'); - - // Check for saved theme or prefer-color-scheme - const savedTheme = localStorage.getItem('theme'); - const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - - if (savedTheme === 'dark' || (!savedTheme && prefersDark)) { - document.documentElement.setAttribute('data-theme', 'dark'); - themeIcon.className = 'fas fa-sun'; +document.addEventListener("DOMContentLoaded", function () { + // Theme Toggle + const themeToggle = document.getElementById("themeToggle"); + const themeIcon = themeToggle.querySelector("i"); + + // Check for saved theme or prefer-color-scheme + const savedTheme = localStorage.getItem("theme"); + const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; + + if (savedTheme === "dark" || (!savedTheme && prefersDark)) { + document.documentElement.setAttribute("data-theme", "dark"); + themeIcon.className = "fas fa-sun"; + } + + themeToggle.addEventListener("click", function () { + const currentTheme = document.documentElement.getAttribute("data-theme"); + + if (currentTheme === "dark") { + document.documentElement.removeAttribute("data-theme"); + themeIcon.className = "fas fa-moon"; + localStorage.setItem("theme", "light"); + } else { + document.documentElement.setAttribute("data-theme", "dark"); + themeIcon.className = "fas fa-sun"; + localStorage.setItem("theme", "dark"); } - - themeToggle.addEventListener('click', function() { - const currentTheme = document.documentElement.getAttribute('data-theme'); - - if (currentTheme === 'dark') { - document.documentElement.removeAttribute('data-theme'); - themeIcon.className = 'fas fa-moon'; - localStorage.setItem('theme', 'light'); - } else { - document.documentElement.setAttribute('data-theme', 'dark'); - themeIcon.className = 'fas fa-sun'; - localStorage.setItem('theme', 'dark'); - } - }); - - // Smooth scroll for navigation links - document.querySelectorAll('a[href^="#"]').forEach(anchor => { - anchor.addEventListener('click', function(e) { - e.preventDefault(); - - const targetId = this.getAttribute('href'); - if (targetId === '#') return; - - const targetElement = document.querySelector(targetId); - if (targetElement) { - const headerHeight = document.querySelector('.navbar').offsetHeight; - const targetPosition = targetElement.offsetTop - headerHeight - 20; - - window.scrollTo({ - top: targetPosition, - behavior: 'smooth' - }); - } + }); + + // Smooth scroll for navigation links + document.querySelectorAll('a[href^="#"]').forEach((anchor) => { + anchor.addEventListener("click", function (e) { + e.preventDefault(); + + const targetId = this.getAttribute("href"); + if (targetId === "#") return; + + const targetElement = document.querySelector(targetId); + if (targetElement) { + const headerHeight = document.querySelector(".navbar").offsetHeight; + const targetPosition = targetElement.offsetTop - headerHeight - 20; + + window.scrollTo({ + top: targetPosition, + behavior: "smooth", }); + } }); - - // Navbar scroll effect - const navbar = document.querySelector('.navbar'); - let lastScrollTop = 0; - - window.addEventListener('scroll', function() { - const scrollTop = window.pageYOffset || document.documentElement.scrollTop; - - // Hide/show navbar on scroll - if (scrollTop > lastScrollTop && scrollTop > 100) { - navbar.style.transform = 'translateY(-100%)'; - } else { - navbar.style.transform = 'translateY(0)'; - } - - lastScrollTop = scrollTop; - - // Add shadow when scrolled - if (scrollTop > 10) { - navbar.style.boxShadow = 'var(--shadow-md)'; - } else { - navbar.style.boxShadow = 'none'; - } - }); - - // Animate elements on scroll - const observerOptions = { - threshold: 0.1, - rootMargin: '0px 0px -50px 0px' - }; - - const observer = new IntersectionObserver(function(entries) { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.classList.add('fade-in'); - observer.unobserve(entry.target); - } - }); - }, observerOptions); - - // Observe elements to animate - document.querySelectorAll('.trend-card, .opportunity-card, .challenge-card, .highlight-card').forEach(el => { - observer.observe(el); - }); - - // Stats counter animation - const stats = document.querySelectorAll('.stat-number'); - - const statsObserver = new IntersectionObserver(function(entries) { - entries.forEach(entry => { - if (entry.isIntersecting) { - const stat = entry.target; - const targetValue = parseInt(stat.textContent); - let currentValue = 0; - const increment = targetValue / 50; - const duration = 1500; - const stepTime = Math.floor(duration / 50); - - const timer = setInterval(() => { - currentValue += increment; - if (currentValue >= targetValue) { - stat.textContent = targetValue; - clearInterval(timer); - } else { - stat.textContent = Math.floor(currentValue); - } - }, stepTime); - - statsObserver.unobserve(stat); - } - }); - }, { threshold: 0.5 }); - - stats.forEach(stat => { - statsObserver.observe(stat); - }); - - // Hover effects for cards - document.querySelectorAll('.trend-card, .opportunity-card, .challenge-card').forEach(card => { - card.addEventListener('mouseenter', function() { - this.style.zIndex = '10'; - }); - - card.addEventListener('mouseleave', function() { - this.style.zIndex = '1'; - }); - }); - - // Current year in footer - const currentYear = new Date().getFullYear(); - const yearElement = document.querySelector('.copyright p'); - if (yearElement) { - yearElement.textContent = yearElement.textContent.replace('2026', currentYear); + }); + + // Navbar scroll effect + const navbar = document.querySelector(".navbar"); + let lastScrollTop = 0; + + window.addEventListener("scroll", function () { + const scrollTop = window.pageYOffset || document.documentElement.scrollTop; + + // Hide/show navbar on scroll + if (scrollTop > lastScrollTop && scrollTop > 100) { + navbar.style.transform = "translateY(-100%)"; + } else { + navbar.style.transform = "translateY(0)"; } - - // Initialize animations - setTimeout(() => { - document.body.style.opacity = '1'; - }, 100); + + lastScrollTop = scrollTop; + + // Add shadow when scrolled + if (scrollTop > 10) { + navbar.style.boxShadow = "var(--shadow-md)"; + } else { + navbar.style.boxShadow = "none"; + } + }); + + // Animate elements on scroll + const observerOptions = { + threshold: 0.1, + rootMargin: "0px 0px -50px 0px", + }; + + const observer = new IntersectionObserver(function (entries) { + entries.forEach((entry) => { + if (entry.isIntersecting) { + entry.target.classList.add("fade-in"); + observer.unobserve(entry.target); + } + }); + }, observerOptions); + + // Observe elements to animate + document + .querySelectorAll( + ".trend-card, .opportunity-card, .challenge-card, .highlight-card", + ) + .forEach((el) => { + observer.observe(el); + }); + + // Stats counter animation + const stats = document.querySelectorAll(".stat-number"); + + const statsObserver = new IntersectionObserver( + function (entries) { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const stat = entry.target; + const targetValue = parseInt(stat.textContent); + let currentValue = 0; + const increment = targetValue / 50; + const duration = 1500; + const stepTime = Math.floor(duration / 50); + + const timer = setInterval(() => { + currentValue += increment; + if (currentValue >= targetValue) { + stat.textContent = targetValue; + clearInterval(timer); + } else { + stat.textContent = Math.floor(currentValue); + } + }, stepTime); + + statsObserver.unobserve(stat); + } + }); + }, + { threshold: 0.5 }, + ); + + stats.forEach((stat) => { + statsObserver.observe(stat); + }); + + // Hover effects for cards + document + .querySelectorAll(".trend-card, .opportunity-card, .challenge-card") + .forEach((card) => { + card.addEventListener("mouseenter", function () { + this.style.zIndex = "10"; + }); + + card.addEventListener("mouseleave", function () { + this.style.zIndex = "1"; + }); + }); + + // Current year in footer + const currentYear = new Date().getFullYear(); + const yearElement = document.querySelector(".copyright p"); + if (yearElement) { + yearElement.textContent = yearElement.textContent.replace( + "2026", + currentYear, + ); + } + + // Initialize animations + setTimeout(() => { + document.body.style.opacity = "1"; + }, 100); }); // Add CSS for initial load -const style = document.createElement('style'); +const style = document.createElement("style"); style.textContent = ` body { opacity: 0; @@ -172,4 +184,4 @@ style.textContent = ` } } `; -document.head.appendChild(style); \ No newline at end of file +document.head.appendChild(style); diff --git a/frontend/public/demo/threads/7f9dc56c-e49c-4671-a3d2-c492ff4dce0c/user-data/outputs/leica-master-photography-article.md b/frontend/public/demo/threads/7f9dc56c-e49c-4671-a3d2-c492ff4dce0c/user-data/outputs/leica-master-photography-article.md index 348c7b8b3..c8966deb4 100644 --- a/frontend/public/demo/threads/7f9dc56c-e49c-4671-a3d2-c492ff4dce0c/user-data/outputs/leica-master-photography-article.md +++ b/frontend/public/demo/threads/7f9dc56c-e49c-4671-a3d2-c492ff4dce0c/user-data/outputs/leica-master-photography-article.md @@ -1,6 +1,6 @@ # The Leica Master's Eye: Capturing the Decisive Moment in the Age of AI -*By DeerFlow 2.0 | January 28, 2026* +_By DeerFlow 2.0 | January 28, 2026_ ## The Enduring Legacy of Leica Street Photography @@ -13,20 +13,25 @@ Through extensive research into Leica photography characteristics and careful pr My research reveals several key characteristics that define Leica master photography: ### 1. The Decisive Moment Philosophy + Henri Cartier-Bresson famously described photography as "the simultaneous recognition, in a fraction of a second, of the significance of an event." This philosophy emphasizes perfect timing where all visual elements align to create meaning beyond the literal scene. ### 2. Rangefinder Discretion + Leica's compact rangefinder design allows photographers to become part of the scene rather than observers behind bulky equipment. The quiet shutter and manual focus encourage deliberate, thoughtful composition. ### 3. Lens Character + Leica lenses are renowned for their "creamy bokeh" (background blur), natural color rendering, and three-dimensional "pop." Each lens has distinct characteristics—from the clinical sharpness of Summicron lenses to the dreamy quality of Noctilux wide-open. ### 4. Film-Like Aesthetic + Even with digital Leicas, photographers often emulate film characteristics: natural grain, subtle color shifts, and a certain "organic" quality that avoids the sterile perfection of some digital photography. ## Three AI-Generated Leica Masterpieces ### Image 1: Parisian Decisive Moment + ![Paris Decisive Moment](/mock/api/threads/7f9dc56c-e49c-4671-a3d2-c492ff4dce0c/artifacts/mnt/user-data/outputs/leica-paris-decisive-moment.jpg) This image captures the essence of Cartier-Bresson's philosophy. A woman in a red coat leaps over a puddle while a cyclist passes in perfect synchrony. The composition follows the rule of thirds, with the subject positioned at the intersection of grid lines. Shot with a simulated Leica M11 and 35mm Summicron lens at f/2.8, the image features shallow depth of field, natural film grain, and the warm, muted color palette characteristic of Leica photography. @@ -34,6 +39,7 @@ This image captures the essence of Cartier-Bresson's philosophy. A woman in a re The "decisive moment" here isn't just about timing—it's about the alignment of multiple elements: the woman's motion, the cyclist's position, the reflection in the puddle, and the directional morning light creating long shadows on wet cobblestones. ### Image 2: Tokyo Night Reflections + ![Tokyo Night Scene](/mock/api/threads/7f9dc56c-e49c-4671-a3d2-c492ff4dce0c/artifacts/mnt/user-data/outputs/leica-tokyo-night.jpg) Moving to Shinjuku, Tokyo, this image explores the atmospheric possibilities of Leica's legendary Noctilux lens. Simulating a Leica M10-P with a 50mm f/0.95 Noctilux wide open, the image creates extremely shallow depth of field with beautiful bokeh balls from neon signs reflected in wet pavement. @@ -41,6 +47,7 @@ Moving to Shinjuku, Tokyo, this image explores the atmospheric possibilities of A salaryman waits under glowing kanji signs, steam rising from a nearby ramen shop. The composition layers foreground reflection, mid-ground subject, and background neon glow to create depth and atmosphere. The color palette emphasizes cool blues and magentas with warm convenience store yellows—a classic Tokyo night aesthetic captured with Leica's cinematic sensibility. ### Image 3: New York City Candid + ![NYC Candid Scene](/mock/api/threads/7f9dc56c-e49c-4671-a3d2-c492ff4dce0c/artifacts/mnt/user-data/outputs/leica-nyc-candid.jpg) This Chinatown scene demonstrates the documentary power of Leica's Q2 camera with its fixed 28mm Summilux lens. The wide angle captures environmental context while maintaining intimate proximity to the subjects. A fishmonger hands a live fish to a customer while tourists photograph the scene—a moment of cultural contrast and authentic urban life. @@ -52,18 +59,23 @@ The 28mm perspective shows multiple layers: the transaction in foreground, touri Creating these images required careful prompt engineering based on my research: ### Camera and Lens Specifications + Each prompt specified exact equipment: + - **Paris**: Leica M11 with 35mm f/2 Summicron at f/2.8 - **Tokyo**: Leica M10-P with 50mm f/0.95 Noctilux at f/0.95 - **NYC**: Leica Q2 with fixed 28mm f/1.7 Summilux at f/2.8 ### Film Simulation + Different film stocks were simulated: + - Kodak Portra 400 for Paris (natural skin tones, fine grain) - Cinestill 800T for Tokyo (halation, cinematic look) - Kodak Ektar 100 for NYC (vibrant colors, fine grain) ### Composition Principles + - Rule of thirds positioning - Environmental storytelling - Layers of depth (foreground, mid-ground, background) @@ -71,6 +83,7 @@ Different film stocks were simulated: - Negative space for breathing room ### Lighting Characteristics + - Natural, directional light sources - Practical lighting (neon signs, shop windows) - Atmospheric elements (rain, steam, smoke) @@ -81,12 +94,14 @@ Different film stocks were simulated: These images demonstrate that AI can learn from photographic masters while creating original work. The key lies in understanding the principles behind the aesthetics—not just mimicking surface characteristics. ### What AI Gets Right: + - Technical accuracy (bokeh, depth of field, grain) - Composition principles - Lighting simulation - Environmental storytelling ### What Remains Human: + - Intentionality and concept development - Emotional connection to subjects - Ethical considerations in street photography @@ -102,4 +117,4 @@ As AI continues to evolve, the most compelling work will likely come from those --- -*All images generated using structured prompt engineering based on Leica photography research. Prompts available upon request.* +_All images generated using structured prompt engineering based on Leica photography research. Prompts available upon request._ diff --git a/frontend/public/demo/threads/b83fbb2a-4e36-4d82-9de0-7b2a02c2092a/user-data/outputs/index.html b/frontend/public/demo/threads/b83fbb2a-4e36-4d82-9de0-7b2a02c2092a/user-data/outputs/index.html index a92c193af..59f0b4a13 100644 --- a/frontend/public/demo/threads/b83fbb2a-4e36-4d82-9de0-7b2a02c2092a/user-data/outputs/index.html +++ b/frontend/public/demo/threads/b83fbb2a-4e36-4d82-9de0-7b2a02c2092a/user-data/outputs/index.html @@ -1,1029 +1,1092 @@ - + - - - + + + Caren — Pure Skincare - - - + + + - - + +
-
- New Collection -

Pure Beauty, Simplified

-

Discover the art of less. Our minimalist skincare routine delivers maximum results with carefully curated, clean ingredients that honor your skin's natural balance.

- - Explore Collection - - - - -
-
- Caren Skincare Product -
+
+ New Collection +

Pure Beauty, Simplified

+

+ Discover the art of less. Our minimalist skincare routine delivers + maximum results with carefully curated, clean ingredients that honor + your skin's natural balance. +

+ + Explore Collection + + + + +
+
+ Caren Skincare Product +
-
- Clean Beauty - Cruelty Free - Sustainable - Vegan - Dermatologist Tested - Clean Beauty - Cruelty Free - Sustainable - Vegan - Dermatologist Tested -
+
+ Clean Beauty + Cruelty Free + Sustainable + Vegan + Dermatologist Tested + Clean Beauty + Cruelty Free + Sustainable + Vegan + Dermatologist Tested +
-
- Skincare Ritual -
-
-

Less is More

-

We believe in the power of simplicity. In a world of overwhelming choices, Caren offers a refined selection of essential skincare products that work in harmony with your skin.

-

Each formula is crafted with intention, using only the finest plant-based ingredients backed by science. No fillers, no fragrances, no compromise.

-
-
-

98%

- Natural Origin -
-
-

0%

- Artificial Fragrance -
-
-

100%

- Cruelty Free -
-
+
+ Skincare Ritual +
+
+

Less is More

+

+ We believe in the power of simplicity. In a world of overwhelming + choices, Caren offers a refined selection of essential skincare + products that work in harmony with your skin. +

+

+ Each formula is crafted with intention, using only the finest + plant-based ingredients backed by science. No fillers, no fragrances, + no compromise. +

+
+
+

98%

+ Natural Origin +
+
+

0%

+ Artificial Fragrance +
+
+

100%

+ Cruelty Free +
+
-
-

The Essentials

-

Three products. Infinite possibilities.

+
+

The Essentials

+

Three products. Infinite possibilities.

+
+
+
+
+

Gentle Cleanser

+
$38
+

+ A soft, cloud-like formula that removes impurities without stripping + your skin's natural moisture barrier. +

+
-
-
-
-

Gentle Cleanser

-
$38
-

A soft, cloud-like formula that removes impurities without stripping your skin's natural moisture barrier.

- -
-
-
-

Hydrating Serum

-
$68
-

Deep hydration with hyaluronic acid and vitamin B5 for plump, radiant skin that glows from within.

- -
-
-
-

Repair Moisturizer

-
$58
-

Rich yet lightweight, this moisturizer locks in hydration while supporting your skin's natural repair process.

- -
+
+
+

Hydrating Serum

+
$68
+

+ Deep hydration with hyaluronic acid and vitamin B5 for plump, + radiant skin that glows from within. +

+
+
+
+

Repair Moisturizer

+
$58
+

+ Rich yet lightweight, this moisturizer locks in hydration while + supporting your skin's natural repair process. +

+ +
+
-
-

Ingredients You Can Trust

-

Transparency is at the heart of everything we do. Every ingredient serves a purpose, carefully selected for its proven efficacy and skin-loving properties.

- -
-
- Natural Ingredients -
+
+

Ingredients You Can Trust

+

+ Transparency is at the heart of everything we do. Every ingredient + serves a purpose, carefully selected for its proven efficacy and + skin-loving properties. +

+ +
+
+ Natural Ingredients +
-

Words from Our Community

-
-

"Finally, a skincare brand that understands simplicity. My skin has never looked better, and my routine has never been simpler. Caren is pure magic."

- — Sarah M., Verified Buyer -
+

Words from Our Community

+
+

+ "Finally, a skincare brand that understands simplicity. My skin has + never looked better, and my routine has never been simpler. Caren is + pure magic." +

+ — Sarah M., Verified Buyer +
-

Join the Caren Family

-

Subscribe for exclusive offers, skincare tips, and early access to new releases.

-
- - -
+

Join the Caren Family

+

+ Subscribe for exclusive offers, skincare tips, and early access to new + releases. +

+
+ + +
- Created By Deerflow + Created By Deerflow - + diff --git a/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/thread.json b/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/thread.json index 1482148dd..a20715961 100644 --- a/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/thread.json +++ b/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/thread.json @@ -738,4 +738,4 @@ "interrupts": [], "checkpoint_id": "1f0fbee1-86cb-630e-8035-fdef3b9e7862", "parent_checkpoint_id": "1f0fbee1-86c7-6a6a-8034-0eba0e105137" -} \ No newline at end of file +} diff --git a/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/index.html b/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/index.html index fd5004bae..8886d350e 100644 --- a/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/index.html +++ b/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/index.html @@ -1,264 +1,354 @@ - + - - - + + + Pride and Prejudice | Jane Austen - - - - - - + + + + + +
-
-
-
-
-

A Novel by

-

- Pride - & - Prejudice -

-

Jane Austen

-

1813

-
- - - -
-

"It is a truth universally acknowledged..."

- - Discover the Story - - - - -
-
-
+
+
+
+
+

A Novel by

+

+ Pride + & + Prejudice +

+

Jane Austen

+

1813

+
+ + +
+

"It is a truth universally acknowledged..."

+ + Discover the Story + + + + +
+
+
+
-
-
- 01 -

The Novel

-
-
-
-

Set in rural England in the early 19th century, Pride and Prejudice tells the story of the Bennet family and their five unmarried daughters.

-

When the wealthy and eligible Mr. Bingley rents a nearby estate, Mrs. Bennet sees an opportunity to marry off her eldest daughter, Jane. At a ball, Jane forms an attachment to Mr. Bingley, while her sister Elizabeth meets his friend, the proud Mr. Darcy.

-

What follows is a masterful exploration of manners, morality, education, and marriage in the society of the landed gentry of early 19th-century England.

-
-
-
- 61 - Chapters -
-
- 122K - Words -
-
- 20M+ - Copies Sold -
-
-
+
+
+ 01 +

The Novel

+
+
+

+ Set in rural England in the early 19th century, + Pride and Prejudice tells the story of the Bennet family + and their five unmarried daughters. +

+

+ When the wealthy and eligible Mr. Bingley rents a nearby estate, + Mrs. Bennet sees an opportunity to marry off her eldest daughter, + Jane. At a ball, Jane forms an attachment to Mr. Bingley, while + her sister Elizabeth meets his friend, the proud Mr. Darcy. +

+

+ What follows is a masterful exploration of manners, morality, + education, and marriage in the society of the landed gentry of + early 19th-century England. +

+
+
+
+ 61 + Chapters +
+
+ 122K + Words +
+
+ 20M+ + Copies Sold +
+
+
+
-
-
- 02 -

The Characters

-
-
- - -
-
-
-

Jane Bennet

-

The Eldest Sister

-

Beautiful, gentle, and always sees the best in people.

-
-
-
-
-
-

Charles Bingley

-

The Amiable Gentleman

-

Wealthy, good-natured, and easily influenced by his friends.

-
-
-
-
-
-

Lydia Bennet

-

The Youngest Sister

-

Frivolous, flirtatious, and impulsive, causing family scandal.

-
-
-
-
-
-

George Wickham

-

The Antagonist

-

Charming on the surface but deceitful and manipulative.

-
-
-
+
+
+ 02 +

The Characters

+
+ + +
+
+
+

Jane Bennet

+

The Eldest Sister

+

+ Beautiful, gentle, and always sees the best in people. +

+
+
+
+
+
+

Charles Bingley

+

The Amiable Gentleman

+

+ Wealthy, good-natured, and easily influenced by his friends. +

+
+
+
+
+
+

Lydia Bennet

+

The Youngest Sister

+

+ Frivolous, flirtatious, and impulsive, causing family scandal. +

+
+
+
+
+
+

George Wickham

+

The Antagonist

+

+ Charming on the surface but deceitful and manipulative. +

+
+
+
+
-
-
- 03 -

Themes

-
-
-
-
- - - - -
-

Pride

-

Darcy's pride in his social position initially prevents him from acknowledging his feelings for Elizabeth, while Elizabeth's pride in her discernment blinds her to Darcy's true character.

-
-
-
- - - -
-

Prejudice

-

Elizabeth's prejudice against Darcy, formed from their first meeting and Wickham's lies, nearly costs her happiness. The novel shows how first impressions can be misleading.

-
-
-
- - - - -
-

Marriage

-

The novel examines marriage from multiple perspectives: for love, for security, for social advancement, and the rare ideal of marrying for both love and compatibility.

-
-
-
- - - - -
-

Class

-

The rigid class structure of Regency England shapes every interaction, from who may marry whom to how characters are judged by their connections and fortune.

-
-
+
+
+ 03 +

Themes

+
+
+
+ + + + +
+

Pride

+

+ Darcy's pride in his social position initially prevents him from + acknowledging his feelings for Elizabeth, while Elizabeth's pride + in her discernment blinds her to Darcy's true character. +

+
+
+
+ + + +
+

Prejudice

+

+ Elizabeth's prejudice against Darcy, formed from their first + meeting and Wickham's lies, nearly costs her happiness. The novel + shows how first impressions can be misleading. +

+
+
+
+ + + + +
+

Marriage

+

+ The novel examines marriage from multiple perspectives: for love, + for security, for social advancement, and the rare ideal of + marrying for both love and compatibility. +

+
+
+
+ + + + +
+

Class

+

+ The rigid class structure of Regency England shapes every + interaction, from who may marry whom to how characters are judged + by their connections and fortune. +

+
+
+
-
-
- 04 -

Memorable Quotes

-
-
-
- " -
It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife.
- — Opening Line -
-
- " -
I could easily forgive his pride, if he had not mortified mine.
- — Elizabeth Bennet -
-
- " -
You have bewitched me, body and soul, and I love, I love, I love you.
- — Mr. Darcy -
-
- " -
Till this moment I never knew myself.
- — Elizabeth Bennet -
-
- " -
My good opinion once lost, is lost forever.
- — Mr. Darcy -
-
-
- - - - - -
+
+
+ 04 +

Memorable Quotes

+
+
+ " +
+ It is a truth universally acknowledged, that a single man in + possession of a good fortune, must be in want of a wife. +
+ — Opening Line +
+
+ " +
+ I could easily forgive his pride, if he had not mortified mine. +
+ — Elizabeth Bennet +
+
+ " +
+ You have bewitched me, body and soul, and I love, I love, I love + you. +
+ — Mr. Darcy +
+
+ " +
Till this moment I never knew myself.
+ — Elizabeth Bennet +
+
+ " +
My good opinion once lost, is lost forever.
+ — Mr. Darcy +
+
+
+ + + + + +
+
- + diff --git a/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/script.js b/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/script.js index 95e898353..10a4973a9 100644 --- a/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/script.js +++ b/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/script.js @@ -1,177 +1,180 @@ // Pride and Prejudice - Interactive Features -document.addEventListener('DOMContentLoaded', () => { - // Navigation scroll effect - initNavigation(); - - // Quotes slider - initQuotesSlider(); - - // Scroll reveal animations - initScrollReveal(); - - // Smooth scroll for anchor links - initSmoothScroll(); +document.addEventListener("DOMContentLoaded", () => { + // Navigation scroll effect + initNavigation(); + + // Quotes slider + initQuotesSlider(); + + // Scroll reveal animations + initScrollReveal(); + + // Smooth scroll for anchor links + initSmoothScroll(); }); // ============================================ // NAVIGATION SCROLL EFFECT // ============================================ function initNavigation() { - const nav = document.querySelector('.nav'); - let lastScroll = 0; - - window.addEventListener('scroll', () => { - const currentScroll = window.pageYOffset; - - // Add/remove scrolled class - if (currentScroll > 100) { - nav.classList.add('scrolled'); - } else { - nav.classList.remove('scrolled'); - } - - lastScroll = currentScroll; - }); + const nav = document.querySelector(".nav"); + let lastScroll = 0; + + window.addEventListener("scroll", () => { + const currentScroll = window.pageYOffset; + + // Add/remove scrolled class + if (currentScroll > 100) { + nav.classList.add("scrolled"); + } else { + nav.classList.remove("scrolled"); + } + + lastScroll = currentScroll; + }); } // ============================================ // QUOTES SLIDER // ============================================ function initQuotesSlider() { - const quotes = document.querySelectorAll('.quote-card'); - const dots = document.querySelectorAll('.quote-dot'); - let currentIndex = 0; - let autoSlideInterval; - - function showQuote(index) { - // Remove active class from all quotes and dots - quotes.forEach(quote => quote.classList.remove('active')); - dots.forEach(dot => dot.classList.remove('active')); - - // Add active class to current quote and dot - quotes[index].classList.add('active'); - dots[index].classList.add('active'); - - currentIndex = index; - } - - function nextQuote() { - const nextIndex = (currentIndex + 1) % quotes.length; - showQuote(nextIndex); - } - - // Dot click handlers - dots.forEach((dot, index) => { - dot.addEventListener('click', () => { - showQuote(index); - resetAutoSlide(); - }); + const quotes = document.querySelectorAll(".quote-card"); + const dots = document.querySelectorAll(".quote-dot"); + let currentIndex = 0; + let autoSlideInterval; + + function showQuote(index) { + // Remove active class from all quotes and dots + quotes.forEach((quote) => quote.classList.remove("active")); + dots.forEach((dot) => dot.classList.remove("active")); + + // Add active class to current quote and dot + quotes[index].classList.add("active"); + dots[index].classList.add("active"); + + currentIndex = index; + } + + function nextQuote() { + const nextIndex = (currentIndex + 1) % quotes.length; + showQuote(nextIndex); + } + + // Dot click handlers + dots.forEach((dot, index) => { + dot.addEventListener("click", () => { + showQuote(index); + resetAutoSlide(); }); - - // Auto-slide functionality - function startAutoSlide() { - autoSlideInterval = setInterval(nextQuote, 6000); - } - - function resetAutoSlide() { - clearInterval(autoSlideInterval); - startAutoSlide(); - } - - // Start auto-slide + }); + + // Auto-slide functionality + function startAutoSlide() { + autoSlideInterval = setInterval(nextQuote, 6000); + } + + function resetAutoSlide() { + clearInterval(autoSlideInterval); startAutoSlide(); - - // Pause on hover - const slider = document.querySelector('.quotes-slider'); - slider.addEventListener('mouseenter', () => clearInterval(autoSlideInterval)); - slider.addEventListener('mouseleave', startAutoSlide); + } + + // Start auto-slide + startAutoSlide(); + + // Pause on hover + const slider = document.querySelector(".quotes-slider"); + slider.addEventListener("mouseenter", () => clearInterval(autoSlideInterval)); + slider.addEventListener("mouseleave", startAutoSlide); } // ============================================ // SCROLL REVEAL ANIMATIONS // ============================================ function initScrollReveal() { - const revealElements = document.querySelectorAll( - '.about-content, .character-card, .theme-item, .section-header' - ); - - const revealOptions = { - threshold: 0.15, - rootMargin: '0px 0px -50px 0px' - }; - - const revealObserver = new IntersectionObserver((entries) => { - entries.forEach((entry, index) => { - if (entry.isIntersecting) { - // Add staggered delay for grid items - const delay = entry.target.classList.contains('character-card') || - entry.target.classList.contains('theme-item') - ? index * 100 - : 0; - - setTimeout(() => { - entry.target.classList.add('reveal'); - entry.target.style.opacity = '1'; - entry.target.style.transform = 'translateY(0)'; - }, delay); - - revealObserver.unobserve(entry.target); - } - }); - }, revealOptions); - - revealElements.forEach(el => { - el.style.opacity = '0'; - el.style.transform = 'translateY(30px)'; - el.style.transition = 'opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1), transform 0.8s cubic-bezier(0.16, 1, 0.3, 1)'; - revealObserver.observe(el); + const revealElements = document.querySelectorAll( + ".about-content, .character-card, .theme-item, .section-header", + ); + + const revealOptions = { + threshold: 0.15, + rootMargin: "0px 0px -50px 0px", + }; + + const revealObserver = new IntersectionObserver((entries) => { + entries.forEach((entry, index) => { + if (entry.isIntersecting) { + // Add staggered delay for grid items + const delay = + entry.target.classList.contains("character-card") || + entry.target.classList.contains("theme-item") + ? index * 100 + : 0; + + setTimeout(() => { + entry.target.classList.add("reveal"); + entry.target.style.opacity = "1"; + entry.target.style.transform = "translateY(0)"; + }, delay); + + revealObserver.unobserve(entry.target); + } }); + }, revealOptions); + + revealElements.forEach((el) => { + el.style.opacity = "0"; + el.style.transform = "translateY(30px)"; + el.style.transition = + "opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1), transform 0.8s cubic-bezier(0.16, 1, 0.3, 1)"; + revealObserver.observe(el); + }); } // ============================================ // SMOOTH SCROLL FOR ANCHOR LINKS // ============================================ function initSmoothScroll() { - document.querySelectorAll('a[href^="#"]').forEach(anchor => { - anchor.addEventListener('click', function(e) { - e.preventDefault(); - const target = document.querySelector(this.getAttribute('href')); - - if (target) { - const navHeight = document.querySelector('.nav').offsetHeight; - const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - navHeight; - - window.scrollTo({ - top: targetPosition, - behavior: 'smooth' - }); - } + document.querySelectorAll('a[href^="#"]').forEach((anchor) => { + anchor.addEventListener("click", function (e) { + e.preventDefault(); + const target = document.querySelector(this.getAttribute("href")); + + if (target) { + const navHeight = document.querySelector(".nav").offsetHeight; + const targetPosition = + target.getBoundingClientRect().top + window.pageYOffset - navHeight; + + window.scrollTo({ + top: targetPosition, + behavior: "smooth", }); + } }); + }); } // ============================================ // PARALLAX EFFECT FOR HERO // ============================================ -window.addEventListener('scroll', () => { - const scrolled = window.pageYOffset; - const heroPattern = document.querySelector('.hero-pattern'); - - if (heroPattern && scrolled < window.innerHeight) { - heroPattern.style.transform = `translateY(${scrolled * 0.3}px) rotate(${scrolled * 0.02}deg)`; - } +window.addEventListener("scroll", () => { + const scrolled = window.pageYOffset; + const heroPattern = document.querySelector(".hero-pattern"); + + if (heroPattern && scrolled < window.innerHeight) { + heroPattern.style.transform = `translateY(${scrolled * 0.3}px) rotate(${scrolled * 0.02}deg)`; + } }); // ============================================ // CHARACTER CARD HOVER EFFECT // ============================================ -document.querySelectorAll('.character-card').forEach(card => { - card.addEventListener('mouseenter', function() { - this.style.zIndex = '10'; - }); - - card.addEventListener('mouseleave', function() { - this.style.zIndex = '1'; - }); +document.querySelectorAll(".character-card").forEach((card) => { + card.addEventListener("mouseenter", function () { + this.style.zIndex = "10"; + }); + + card.addEventListener("mouseleave", function () { + this.style.zIndex = "1"; + }); }); diff --git a/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/styles.css b/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/styles.css index 5d1d488f9..c803c3629 100644 --- a/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/styles.css +++ b/frontend/public/demo/threads/c02bb4d5-4202-490e-ae8f-ff4864fc0d2e/user-data/outputs/styles.css @@ -4,903 +4,963 @@ /* CSS Variables */ :root { - /* Colors - Regency Era Palette */ - --color-cream: #FAF7F2; - --color-ivory: #F5F0E8; - --color-parchment: #EDE6D6; - --color-gold: #C9A962; - --color-gold-light: #D4BC7E; - --color-burgundy: #722F37; - --color-burgundy-dark: #5A252C; - --color-charcoal: #2C2C2C; - --color-charcoal-light: #4A4A4A; - --color-sage: #7D8471; - --color-rose: #C4A4A4; - - /* Typography */ - --font-display: 'Playfair Display', Georgia, serif; - --font-body: 'Cormorant Garamond', Georgia, serif; - - /* Spacing */ - --section-padding: 8rem; - --container-max: 1200px; - - /* Transitions */ - --transition-smooth: all 0.6s cubic-bezier(0.16, 1, 0.3, 1); - --transition-quick: all 0.3s ease; + /* Colors - Regency Era Palette */ + --color-cream: #faf7f2; + --color-ivory: #f5f0e8; + --color-parchment: #ede6d6; + --color-gold: #c9a962; + --color-gold-light: #d4bc7e; + --color-burgundy: #722f37; + --color-burgundy-dark: #5a252c; + --color-charcoal: #2c2c2c; + --color-charcoal-light: #4a4a4a; + --color-sage: #7d8471; + --color-rose: #c4a4a4; + + /* Typography */ + --font-display: "Playfair Display", Georgia, serif; + --font-body: "Cormorant Garamond", Georgia, serif; + + /* Spacing */ + --section-padding: 8rem; + --container-max: 1200px; + + /* Transitions */ + --transition-smooth: all 0.6s cubic-bezier(0.16, 1, 0.3, 1); + --transition-quick: all 0.3s ease; } /* Reset & Base */ -*, *::before, *::after { - margin: 0; - padding: 0; - box-sizing: border-box; +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; } html { - scroll-behavior: smooth; - font-size: 16px; + scroll-behavior: smooth; + font-size: 16px; } body { - font-family: var(--font-body); - font-size: 1.125rem; - line-height: 1.7; - color: var(--color-charcoal); - background-color: var(--color-cream); - overflow-x: hidden; + font-family: var(--font-body); + font-size: 1.125rem; + line-height: 1.7; + color: var(--color-charcoal); + background-color: var(--color-cream); + overflow-x: hidden; } .container { - max-width: var(--container-max); - margin: 0 auto; - padding: 0 2rem; + max-width: var(--container-max); + margin: 0 auto; + padding: 0 2rem; } /* ============================================ NAVIGATION ============================================ */ .nav { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 1000; - display: flex; - justify-content: space-between; - align-items: center; - padding: 1.5rem 3rem; - background: linear-gradient(to bottom, rgba(250, 247, 242, 0.95), transparent); - transition: var(--transition-quick); + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + display: flex; + justify-content: space-between; + align-items: center; + padding: 1.5rem 3rem; + background: linear-gradient( + to bottom, + rgba(250, 247, 242, 0.95), + transparent + ); + transition: var(--transition-quick); } .nav.scrolled { - background: rgba(250, 247, 242, 0.98); - backdrop-filter: blur(10px); - box-shadow: 0 1px 20px rgba(0, 0, 0, 0.05); + background: rgba(250, 247, 242, 0.98); + backdrop-filter: blur(10px); + box-shadow: 0 1px 20px rgba(0, 0, 0, 0.05); } .nav-brand { - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 600; - color: var(--color-burgundy); - letter-spacing: 0.1em; + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 600; + color: var(--color-burgundy); + letter-spacing: 0.1em; } .nav-links { - display: flex; - list-style: none; - gap: 2.5rem; + display: flex; + list-style: none; + gap: 2.5rem; } .nav-links a { - font-family: var(--font-body); - font-size: 0.95rem; - font-weight: 500; - color: var(--color-charcoal); - text-decoration: none; - letter-spacing: 0.05em; - position: relative; - padding-bottom: 0.25rem; - transition: var(--transition-quick); + font-family: var(--font-body); + font-size: 0.95rem; + font-weight: 500; + color: var(--color-charcoal); + text-decoration: none; + letter-spacing: 0.05em; + position: relative; + padding-bottom: 0.25rem; + transition: var(--transition-quick); } .nav-links a::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 0; - height: 1px; - background: var(--color-gold); - transition: var(--transition-quick); + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 1px; + background: var(--color-gold); + transition: var(--transition-quick); } .nav-links a:hover { - color: var(--color-burgundy); + color: var(--color-burgundy); } .nav-links a:hover::after { - width: 100%; + width: 100%; } /* ============================================ HERO SECTION ============================================ */ .hero { - min-height: 100vh; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - position: relative; - overflow: hidden; - background: linear-gradient(135deg, var(--color-cream) 0%, var(--color-ivory) 50%, var(--color-parchment) 100%); + min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; + overflow: hidden; + background: linear-gradient( + 135deg, + var(--color-cream) 0%, + var(--color-ivory) 50%, + var(--color-parchment) 100% + ); } .hero-bg { - position: absolute; - inset: 0; - overflow: hidden; + position: absolute; + inset: 0; + overflow: hidden; } .hero-pattern { - position: absolute; - inset: -50%; - background-image: - radial-gradient(circle at 20% 30%, rgba(201, 169, 98, 0.08) 0%, transparent 50%), - radial-gradient(circle at 80% 70%, rgba(114, 47, 55, 0.05) 0%, transparent 50%), - radial-gradient(circle at 50% 50%, rgba(125, 132, 113, 0.03) 0%, transparent 60%); - animation: patternFloat 20s ease-in-out infinite; + position: absolute; + inset: -50%; + background-image: + radial-gradient( + circle at 20% 30%, + rgba(201, 169, 98, 0.08) 0%, + transparent 50% + ), + radial-gradient( + circle at 80% 70%, + rgba(114, 47, 55, 0.05) 0%, + transparent 50% + ), + radial-gradient( + circle at 50% 50%, + rgba(125, 132, 113, 0.03) 0%, + transparent 60% + ); + animation: patternFloat 20s ease-in-out infinite; } @keyframes patternFloat { - 0%, 100% { transform: translate(0, 0) rotate(0deg); } - 50% { transform: translate(2%, 2%) rotate(2deg); } + 0%, + 100% { + transform: translate(0, 0) rotate(0deg); + } + 50% { + transform: translate(2%, 2%) rotate(2deg); + } } .hero-content { - text-align: center; - z-index: 1; - padding: 2rem; - max-width: 900px; + text-align: center; + z-index: 1; + padding: 2rem; + max-width: 900px; } .hero-subtitle { - font-family: var(--font-body); - font-size: 1rem; - font-weight: 400; - letter-spacing: 0.3em; - text-transform: uppercase; - color: var(--color-sage); - margin-bottom: 1.5rem; - opacity: 0; - animation: fadeInUp 1s ease forwards 0.3s; + font-family: var(--font-body); + font-size: 1rem; + font-weight: 400; + letter-spacing: 0.3em; + text-transform: uppercase; + color: var(--color-sage); + margin-bottom: 1.5rem; + opacity: 0; + animation: fadeInUp 1s ease forwards 0.3s; } .hero-title { - margin-bottom: 1rem; + margin-bottom: 1rem; } .title-line { - display: block; - font-family: var(--font-display); - font-size: clamp(3rem, 10vw, 7rem); - font-weight: 400; - line-height: 1; - color: var(--color-charcoal); - opacity: 0; - animation: fadeInUp 1s ease forwards 0.5s; + display: block; + font-family: var(--font-display); + font-size: clamp(3rem, 10vw, 7rem); + font-weight: 400; + line-height: 1; + color: var(--color-charcoal); + opacity: 0; + animation: fadeInUp 1s ease forwards 0.5s; } .title-line:first-child { - font-style: italic; - color: var(--color-burgundy); + font-style: italic; + color: var(--color-burgundy); } .title-ampersand { - display: block; - font-family: var(--font-display); - font-size: clamp(2rem, 5vw, 3.5rem); - font-weight: 300; - font-style: italic; - color: var(--color-gold); - margin: 0.5rem 0; - opacity: 0; - animation: fadeInScale 1s ease forwards 0.7s; + display: block; + font-family: var(--font-display); + font-size: clamp(2rem, 5vw, 3.5rem); + font-weight: 300; + font-style: italic; + color: var(--color-gold); + margin: 0.5rem 0; + opacity: 0; + animation: fadeInScale 1s ease forwards 0.7s; } @keyframes fadeInScale { - from { - opacity: 0; - transform: scale(0.8); - } - to { - opacity: 1; - transform: scale(1); - } + from { + opacity: 0; + transform: scale(0.8); + } + to { + opacity: 1; + transform: scale(1); + } } .hero-author { - font-family: var(--font-display); - font-size: clamp(1.25rem, 3vw, 1.75rem); - font-weight: 400; - color: var(--color-charcoal-light); - letter-spacing: 0.15em; - margin-bottom: 0.5rem; - opacity: 0; - animation: fadeInUp 1s ease forwards 0.9s; + font-family: var(--font-display); + font-size: clamp(1.25rem, 3vw, 1.75rem); + font-weight: 400; + color: var(--color-charcoal-light); + letter-spacing: 0.15em; + margin-bottom: 0.5rem; + opacity: 0; + animation: fadeInUp 1s ease forwards 0.9s; } .hero-year { - font-family: var(--font-body); - font-size: 1rem; - font-weight: 300; - color: var(--color-sage); - letter-spacing: 0.2em; - margin-bottom: 2rem; - opacity: 0; - animation: fadeInUp 1s ease forwards 1s; + font-family: var(--font-body); + font-size: 1rem; + font-weight: 300; + color: var(--color-sage); + letter-spacing: 0.2em; + margin-bottom: 2rem; + opacity: 0; + animation: fadeInUp 1s ease forwards 1s; } .hero-divider { - display: flex; - align-items: center; - justify-content: center; - gap: 1rem; - margin-bottom: 2rem; - opacity: 0; - animation: fadeInUp 1s ease forwards 1.1s; + display: flex; + align-items: center; + justify-content: center; + gap: 1rem; + margin-bottom: 2rem; + opacity: 0; + animation: fadeInUp 1s ease forwards 1.1s; } .divider-line { - width: 60px; - height: 1px; - background: linear-gradient(90deg, transparent, var(--color-gold), transparent); + width: 60px; + height: 1px; + background: linear-gradient( + 90deg, + transparent, + var(--color-gold), + transparent + ); } .divider-ornament { - color: var(--color-gold); - font-size: 1.25rem; + color: var(--color-gold); + font-size: 1.25rem; } .hero-tagline { - font-family: var(--font-body); - font-size: 1.25rem; - font-style: italic; - color: var(--color-charcoal-light); - margin-bottom: 3rem; - opacity: 0; - animation: fadeInUp 1s ease forwards 1.2s; + font-family: var(--font-body); + font-size: 1.25rem; + font-style: italic; + color: var(--color-charcoal-light); + margin-bottom: 3rem; + opacity: 0; + animation: fadeInUp 1s ease forwards 1.2s; } .hero-cta { - display: inline-flex; - align-items: center; - gap: 0.75rem; - font-family: var(--font-body); - font-size: 1rem; - font-weight: 500; - letter-spacing: 0.1em; - text-transform: uppercase; - color: var(--color-burgundy); - text-decoration: none; - padding: 1rem 2rem; - border: 1px solid var(--color-burgundy); - transition: var(--transition-smooth); - opacity: 0; - animation: fadeInUp 1s ease forwards 1.3s; + display: inline-flex; + align-items: center; + gap: 0.75rem; + font-family: var(--font-body); + font-size: 1rem; + font-weight: 500; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--color-burgundy); + text-decoration: none; + padding: 1rem 2rem; + border: 1px solid var(--color-burgundy); + transition: var(--transition-smooth); + opacity: 0; + animation: fadeInUp 1s ease forwards 1.3s; } .hero-cta:hover { - background: var(--color-burgundy); - color: var(--color-cream); + background: var(--color-burgundy); + color: var(--color-cream); } .hero-cta:hover .cta-arrow { - transform: translateY(4px); + transform: translateY(4px); } .cta-arrow { - width: 20px; - height: 20px; - transition: var(--transition-quick); + width: 20px; + height: 20px; + transition: var(--transition-quick); } .hero-scroll-indicator { - position: absolute; - bottom: 3rem; - left: 50%; - transform: translateX(-50%); - opacity: 0; - animation: fadeIn 1s ease forwards 1.5s; + position: absolute; + bottom: 3rem; + left: 50%; + transform: translateX(-50%); + opacity: 0; + animation: fadeIn 1s ease forwards 1.5s; } .scroll-line { - width: 1px; - height: 60px; - background: linear-gradient(to bottom, var(--color-gold), transparent); - animation: scrollPulse 2s ease-in-out infinite; + width: 1px; + height: 60px; + background: linear-gradient(to bottom, var(--color-gold), transparent); + animation: scrollPulse 2s ease-in-out infinite; } @keyframes scrollPulse { - 0%, 100% { opacity: 0.3; transform: scaleY(0.8); } - 50% { opacity: 1; transform: scaleY(1); } + 0%, + 100% { + opacity: 0.3; + transform: scaleY(0.8); + } + 50% { + opacity: 1; + transform: scaleY(1); + } } @keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(30px); - } - to { - opacity: 1; - transform: translateY(0); - } + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } } @keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } + from { + opacity: 0; + } + to { + opacity: 1; + } } /* ============================================ SECTION HEADERS ============================================ */ .section-header { - display: flex; - align-items: baseline; - gap: 1.5rem; - margin-bottom: 4rem; - padding-bottom: 1.5rem; - border-bottom: 1px solid rgba(201, 169, 98, 0.3); + display: flex; + align-items: baseline; + gap: 1.5rem; + margin-bottom: 4rem; + padding-bottom: 1.5rem; + border-bottom: 1px solid rgba(201, 169, 98, 0.3); } .section-number { - font-family: var(--font-display); - font-size: 0.875rem; - font-weight: 400; - color: var(--color-gold); - letter-spacing: 0.1em; + font-family: var(--font-display); + font-size: 0.875rem; + font-weight: 400; + color: var(--color-gold); + letter-spacing: 0.1em; } .section-title { - font-family: var(--font-display); - font-size: clamp(2rem, 5vw, 3rem); - font-weight: 400; - color: var(--color-charcoal); - font-style: italic; + font-family: var(--font-display); + font-size: clamp(2rem, 5vw, 3rem); + font-weight: 400; + color: var(--color-charcoal); + font-style: italic; } /* ============================================ ABOUT SECTION ============================================ */ .about { - padding: var(--section-padding) 0; - background: var(--color-cream); + padding: var(--section-padding) 0; + background: var(--color-cream); } .about-content { - display: grid; - grid-template-columns: 2fr 1fr; - gap: 4rem; - align-items: start; + display: grid; + grid-template-columns: 2fr 1fr; + gap: 4rem; + align-items: start; } .about-text { - max-width: 600px; + max-width: 600px; } .about-lead { - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 400; - line-height: 1.5; - color: var(--color-burgundy); - margin-bottom: 1.5rem; + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 400; + line-height: 1.5; + color: var(--color-burgundy); + margin-bottom: 1.5rem; } .about-text p { - margin-bottom: 1.25rem; - color: var(--color-charcoal-light); + margin-bottom: 1.25rem; + color: var(--color-charcoal-light); } .about-text em { - font-style: italic; - color: var(--color-charcoal); + font-style: italic; + color: var(--color-charcoal); } .about-stats { - display: flex; - flex-direction: column; - gap: 2rem; - padding: 2rem; - background: var(--color-ivory); - border-left: 3px solid var(--color-gold); + display: flex; + flex-direction: column; + gap: 2rem; + padding: 2rem; + background: var(--color-ivory); + border-left: 3px solid var(--color-gold); } .stat-item { - text-align: center; + text-align: center; } .stat-number { - display: block; - font-family: var(--font-display); - font-size: 2.5rem; - font-weight: 600; - color: var(--color-burgundy); - line-height: 1; + display: block; + font-family: var(--font-display); + font-size: 2.5rem; + font-weight: 600; + color: var(--color-burgundy); + line-height: 1; } .stat-label { - font-family: var(--font-body); - font-size: 0.875rem; - color: var(--color-sage); - letter-spacing: 0.1em; - text-transform: uppercase; + font-family: var(--font-body); + font-size: 0.875rem; + color: var(--color-sage); + letter-spacing: 0.1em; + text-transform: uppercase; } /* ============================================ CHARACTERS SECTION ============================================ */ .characters { - padding: var(--section-padding) 0; - background: linear-gradient(to bottom, var(--color-ivory), var(--color-cream)); + padding: var(--section-padding) 0; + background: linear-gradient( + to bottom, + var(--color-ivory), + var(--color-cream) + ); } .characters-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 2rem; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; } .character-card { - background: var(--color-cream); - border: 1px solid rgba(201, 169, 98, 0.2); - overflow: hidden; - transition: var(--transition-smooth); + background: var(--color-cream); + border: 1px solid rgba(201, 169, 98, 0.2); + overflow: hidden; + transition: var(--transition-smooth); } .character-card:hover { - transform: translateY(-8px); - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08); - border-color: var(--color-gold); + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08); + border-color: var(--color-gold); } .character-card.featured { - grid-column: span 1; + grid-column: span 1; } .character-portrait { - height: 200px; - background: linear-gradient(135deg, var(--color-parchment) 0%, var(--color-ivory) 100%); - position: relative; - overflow: hidden; + height: 200px; + background: linear-gradient( + 135deg, + var(--color-parchment) 0%, + var(--color-ivory) 100% + ); + position: relative; + overflow: hidden; } .character-portrait::before { - content: ''; - position: absolute; - inset: 0; - background: radial-gradient(circle at 30% 30%, rgba(201, 169, 98, 0.15) 0%, transparent 60%); + content: ""; + position: absolute; + inset: 0; + background: radial-gradient( + circle at 30% 30%, + rgba(201, 169, 98, 0.15) 0%, + transparent 60% + ); } .character-portrait.elizabeth::after { - content: '👒'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 4rem; - opacity: 0.6; + content: "👒"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 4rem; + opacity: 0.6; } .character-portrait.darcy::after { - content: '🎩'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 4rem; - opacity: 0.6; + content: "🎩"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 4rem; + opacity: 0.6; } .character-portrait.jane::after { - content: '🌸'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 3rem; - opacity: 0.5; + content: "🌸"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 3rem; + opacity: 0.5; } .character-portrait.bingley::after { - content: '🎭'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 3rem; - opacity: 0.5; + content: "🎭"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 3rem; + opacity: 0.5; } .character-portrait.lydia::after { - content: '💃'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 3rem; - opacity: 0.5; + content: "💃"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 3rem; + opacity: 0.5; } .character-portrait.wickham::after { - content: '🎪'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 3rem; - opacity: 0.5; + content: "🎪"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 3rem; + opacity: 0.5; } .character-info { - padding: 1.5rem; + padding: 1.5rem; } .character-info h3 { - font-family: var(--font-display); - font-size: 1.25rem; - font-weight: 500; - color: var(--color-charcoal); - margin-bottom: 0.25rem; + font-family: var(--font-display); + font-size: 1.25rem; + font-weight: 500; + color: var(--color-charcoal); + margin-bottom: 0.25rem; } .character-role { - font-family: var(--font-body); - font-size: 0.8rem; - font-weight: 500; - color: var(--color-gold); - letter-spacing: 0.1em; - text-transform: uppercase; - margin-bottom: 0.75rem; + font-family: var(--font-body); + font-size: 0.8rem; + font-weight: 500; + color: var(--color-gold); + letter-spacing: 0.1em; + text-transform: uppercase; + margin-bottom: 0.75rem; } .character-desc { - font-size: 0.95rem; - color: var(--color-charcoal-light); - line-height: 1.6; + font-size: 0.95rem; + color: var(--color-charcoal-light); + line-height: 1.6; } /* ============================================ THEMES SECTION ============================================ */ .themes { - padding: var(--section-padding) 0; - background: var(--color-charcoal); - color: var(--color-cream); + padding: var(--section-padding) 0; + background: var(--color-charcoal); + color: var(--color-cream); } .themes .section-title { - color: var(--color-cream); + color: var(--color-cream); } .themes .section-header { - border-bottom-color: rgba(201, 169, 98, 0.2); + border-bottom-color: rgba(201, 169, 98, 0.2); } .themes-content { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 3rem; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 3rem; } .theme-item { - padding: 2.5rem; - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(201, 169, 98, 0.15); - transition: var(--transition-smooth); + padding: 2.5rem; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(201, 169, 98, 0.15); + transition: var(--transition-smooth); } .theme-item:hover { - background: rgba(255, 255, 255, 0.06); - border-color: var(--color-gold); - transform: translateY(-4px); + background: rgba(255, 255, 255, 0.06); + border-color: var(--color-gold); + transform: translateY(-4px); } .theme-icon { - width: 48px; - height: 48px; - margin-bottom: 1.5rem; - color: var(--color-gold); + width: 48px; + height: 48px; + margin-bottom: 1.5rem; + color: var(--color-gold); } .theme-icon svg { - width: 100%; - height: 100%; + width: 100%; + height: 100%; } .theme-item h3 { - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 400; - color: var(--color-cream); - margin-bottom: 1rem; + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 400; + color: var(--color-cream); + margin-bottom: 1rem; } .theme-item p { - font-size: 1rem; - color: rgba(250, 247, 242, 0.7); - line-height: 1.7; + font-size: 1rem; + color: rgba(250, 247, 242, 0.7); + line-height: 1.7; } /* ============================================ QUOTES SECTION ============================================ */ .quotes { - padding: var(--section-padding) 0; - background: linear-gradient(135deg, var(--color-parchment) 0%, var(--color-ivory) 100%); - position: relative; - overflow: hidden; + padding: var(--section-padding) 0; + background: linear-gradient( + 135deg, + var(--color-parchment) 0%, + var(--color-ivory) 100% + ); + position: relative; + overflow: hidden; } .quotes::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23c9a962' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); - pointer-events: none; + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23c9a962' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + pointer-events: none; } .quotes-slider { - position: relative; - min-height: 300px; + position: relative; + min-height: 300px; } .quote-card { - position: absolute; - top: 0; - left: 0; - right: 0; - text-align: center; - padding: 2rem; - opacity: 0; - transform: translateX(50px); - transition: var(--transition-smooth); - pointer-events: none; + position: absolute; + top: 0; + left: 0; + right: 0; + text-align: center; + padding: 2rem; + opacity: 0; + transform: translateX(50px); + transition: var(--transition-smooth); + pointer-events: none; } .quote-card.active { - opacity: 1; - transform: translateX(0); - pointer-events: auto; + opacity: 1; + transform: translateX(0); + pointer-events: auto; } .quote-mark { - font-family: var(--font-display); - font-size: 6rem; - color: var(--color-gold); - opacity: 0.3; - line-height: 1; - display: block; - margin-bottom: -2rem; + font-family: var(--font-display); + font-size: 6rem; + color: var(--color-gold); + opacity: 0.3; + line-height: 1; + display: block; + margin-bottom: -2rem; } .quote-card blockquote { - font-family: var(--font-display); - font-size: clamp(1.5rem, 4vw, 2.25rem); - font-weight: 400; - font-style: italic; - color: var(--color-charcoal); - line-height: 1.5; - max-width: 800px; - margin: 0 auto 1.5rem; + font-family: var(--font-display); + font-size: clamp(1.5rem, 4vw, 2.25rem); + font-weight: 400; + font-style: italic; + color: var(--color-charcoal); + line-height: 1.5; + max-width: 800px; + margin: 0 auto 1.5rem; } .quote-card cite { - font-family: var(--font-body); - font-size: 1rem; - font-style: normal; - color: var(--color-sage); - letter-spacing: 0.1em; + font-family: var(--font-body); + font-size: 1rem; + font-style: normal; + color: var(--color-sage); + letter-spacing: 0.1em; } .quotes-nav { - display: flex; - justify-content: center; - gap: 0.75rem; - margin-top: 3rem; + display: flex; + justify-content: center; + gap: 0.75rem; + margin-top: 3rem; } .quote-dot { - width: 10px; - height: 10px; - border-radius: 50%; - border: 1px solid var(--color-gold); - background: transparent; - cursor: pointer; - transition: var(--transition-quick); + width: 10px; + height: 10px; + border-radius: 50%; + border: 1px solid var(--color-gold); + background: transparent; + cursor: pointer; + transition: var(--transition-quick); } .quote-dot.active { - background: var(--color-gold); - transform: scale(1.2); + background: var(--color-gold); + transform: scale(1.2); } .quote-dot:hover { - background: var(--color-gold-light); + background: var(--color-gold-light); } /* ============================================ FOOTER ============================================ */ .footer { - padding: 4rem 0; - background: var(--color-charcoal); - color: var(--color-cream); - position: relative; + padding: 4rem 0; + background: var(--color-charcoal); + color: var(--color-cream); + position: relative; } .footer-content { - text-align: center; + text-align: center; } .footer-logo { - font-family: var(--font-display); - font-size: 2rem; - font-weight: 600; - color: var(--color-gold); - letter-spacing: 0.15em; - display: block; - margin-bottom: 0.5rem; + font-family: var(--font-display); + font-size: 2rem; + font-weight: 600; + color: var(--color-gold); + letter-spacing: 0.15em; + display: block; + margin-bottom: 0.5rem; } .footer-brand p { - font-size: 1rem; - color: rgba(250, 247, 242, 0.6); - margin-bottom: 1.5rem; + font-size: 1rem; + color: rgba(250, 247, 242, 0.6); + margin-bottom: 1.5rem; } .footer-divider { - margin: 1.5rem 0; + margin: 1.5rem 0; } .footer-divider .divider-ornament { - color: var(--color-gold); - font-size: 1.5rem; + color: var(--color-gold); + font-size: 1.5rem; } .footer-credit { - font-size: 0.875rem; - color: rgba(250, 247, 242, 0.5); - font-style: italic; + font-size: 0.875rem; + color: rgba(250, 247, 242, 0.5); + font-style: italic; } /* Deerflow Signature */ .deerflow-signature { - position: fixed; - bottom: 1.5rem; - right: 1.5rem; - display: flex; - align-items: center; - gap: 0.5rem; - font-family: var(--font-body); - font-size: 0.75rem; - color: var(--color-sage); - text-decoration: none; - padding: 0.5rem 1rem; - background: rgba(250, 247, 242, 0.9); - border: 1px solid rgba(201, 169, 98, 0.3); - border-radius: 20px; - backdrop-filter: blur(10px); - transition: var(--transition-quick); - z-index: 999; + position: fixed; + bottom: 1.5rem; + right: 1.5rem; + display: flex; + align-items: center; + gap: 0.5rem; + font-family: var(--font-body); + font-size: 0.75rem; + color: var(--color-sage); + text-decoration: none; + padding: 0.5rem 1rem; + background: rgba(250, 247, 242, 0.9); + border: 1px solid rgba(201, 169, 98, 0.3); + border-radius: 20px; + backdrop-filter: blur(10px); + transition: var(--transition-quick); + z-index: 999; } .deerflow-signature:hover { - color: var(--color-burgundy); - border-color: var(--color-gold); - box-shadow: 0 4px 15px rgba(201, 169, 98, 0.2); + color: var(--color-burgundy); + border-color: var(--color-gold); + box-shadow: 0 4px 15px rgba(201, 169, 98, 0.2); } .signature-icon { - color: var(--color-gold); - font-size: 0.875rem; + color: var(--color-gold); + font-size: 0.875rem; } /* ============================================ RESPONSIVE DESIGN ============================================ */ @media (max-width: 1024px) { - .characters-grid { - grid-template-columns: repeat(2, 1fr); - } - - .about-content { - grid-template-columns: 1fr; - gap: 3rem; - } - - .about-stats { - flex-direction: row; - justify-content: space-around; - border-left: none; - border-top: 3px solid var(--color-gold); - } + .characters-grid { + grid-template-columns: repeat(2, 1fr); + } + + .about-content { + grid-template-columns: 1fr; + gap: 3rem; + } + + .about-stats { + flex-direction: row; + justify-content: space-around; + border-left: none; + border-top: 3px solid var(--color-gold); + } } @media (max-width: 768px) { - :root { - --section-padding: 5rem; - } - - .nav { - padding: 1rem 1.5rem; - } - - .nav-links { - gap: 1.25rem; - } - - .nav-links a { - font-size: 0.85rem; - } - - .characters-grid { - grid-template-columns: 1fr; - } - - .themes-content { - grid-template-columns: 1fr; - } - - .section-header { - flex-direction: column; - gap: 0.5rem; - align-items: flex-start; - } - - .deerflow-signature { - bottom: 1rem; - right: 1rem; - padding: 0.4rem 0.75rem; - } + :root { + --section-padding: 5rem; + } + + .nav { + padding: 1rem 1.5rem; + } + + .nav-links { + gap: 1.25rem; + } + + .nav-links a { + font-size: 0.85rem; + } + + .characters-grid { + grid-template-columns: 1fr; + } + + .themes-content { + grid-template-columns: 1fr; + } + + .section-header { + flex-direction: column; + gap: 0.5rem; + align-items: flex-start; + } + + .deerflow-signature { + bottom: 1rem; + right: 1rem; + padding: 0.4rem 0.75rem; + } } @media (max-width: 480px) { - .container { - padding: 0 1.25rem; - } - - .hero-content { - padding: 1rem; - } - - .about-stats { - flex-direction: column; - gap: 1.5rem; - } - - .theme-item { - padding: 1.5rem; - } + .container { + padding: 0 1.25rem; + } + + .hero-content { + padding: 1rem; + } + + .about-stats { + flex-direction: column; + gap: 1.5rem; + } + + .theme-item { + padding: 1.5rem; + } } /* ============================================ SCROLL REVEAL ANIMATIONS ============================================ */ .reveal { - opacity: 0; - transform: translateY(30px); - transition: var(--transition-smooth); + opacity: 0; + transform: translateY(30px); + transition: var(--transition-smooth); } .reveal.active { - opacity: 1; - transform: translateY(0); + opacity: 1; + transform: translateY(0); } diff --git a/frontend/public/demo/threads/d3e5adaf-084c-4dd5-9d29-94f1d6bccd98/thread.json b/frontend/public/demo/threads/d3e5adaf-084c-4dd5-9d29-94f1d6bccd98/thread.json index eaace6f66..47a09a054 100644 --- a/frontend/public/demo/threads/d3e5adaf-084c-4dd5-9d29-94f1d6bccd98/thread.json +++ b/frontend/public/demo/threads/d3e5adaf-084c-4dd5-9d29-94f1d6bccd98/thread.json @@ -1187,9 +1187,7 @@ { "name": "present_files", "args": { - "filepaths": [ - "/mnt/user-data/outputs/diana_hu_research.md" - ] + "filepaths": ["/mnt/user-data/outputs/diana_hu_research.md"] }, "id": "call_fiwu3557asbu155mzi3ore72", "type": "tool_call" @@ -1235,9 +1233,7 @@ }, "uploaded_files": [], "title": "Startup Tech Lead's Video Analysis Request", - "artifacts": [ - "/mnt/user-data/outputs/diana_hu_research.md" - ], + "artifacts": ["/mnt/user-data/outputs/diana_hu_research.md"], "todos": [ { "content": "Analyze video transcript and extract key insights, advice, and frameworks", @@ -1307,4 +1303,4 @@ "interrupts": [], "checkpoint_id": "1f0f99e8-0d7c-62d2-8051-7cb8958145fe", "parent_checkpoint_id": "1f0f99e8-0437-6ac8-8050-ce8edd831bf1" -} \ No newline at end of file +} diff --git a/frontend/public/demo/threads/d3e5adaf-084c-4dd5-9d29-94f1d6bccd98/user-data/outputs/diana_hu_research.md b/frontend/public/demo/threads/d3e5adaf-084c-4dd5-9d29-94f1d6bccd98/user-data/outputs/diana_hu_research.md index 63572ad43..67690ee17 100644 --- a/frontend/public/demo/threads/d3e5adaf-084c-4dd5-9d29-94f1d6bccd98/user-data/outputs/diana_hu_research.md +++ b/frontend/public/demo/threads/d3e5adaf-084c-4dd5-9d29-94f1d6bccd98/user-data/outputs/diana_hu_research.md @@ -1,6 +1,7 @@ # Diana Hu: Technical Startup Founder Advice - Comprehensive Research ## Video Overview + **Title:** Tips For Technical Startup Founders | Startup School **Speaker:** Diana Hu, Y Combinator Group Partner **Date:** April 21, 2023 @@ -10,11 +11,13 @@ ## Speaker Background ### Education + - **BS and MS in Electrical and Computer Engineering** from Carnegie Mellon University - Focus on **computer vision and machine learning** - Originally from Chile ### Career Path + 1. **Co-founder & CTO of Escher Reality** (YC S17) - Startup building augmented reality SDK for game developers - Company acquired by Niantic (makers of Pokémon Go) in February 2018 @@ -29,11 +32,13 @@ - Specializes in technical founder guidance ### Key Achievements + - Successfully built and sold AR startup to Niantic - Scaled systems from prototype to millions of users - Extensive experience mentoring technical founders ## Escher Reality Acquisition + - **Founded:** 2016 - **Y Combinator Batch:** Summer 2017 (S17) - **Product:** Augmented Reality backend/SDK for cross-platform mobile AR @@ -47,14 +52,17 @@ ### Three Stages of Technical Founder Journey #### Stage 1: Ideating (0:00-8:30) + **Goal:** Build a prototype as soon as possible (matter of days) **Key Principles:** + - Build something to show/demo to users - Doesn't have to work fully - CEO co-founder should be finding users to show prototype **Examples:** + 1. **Optimizely** (YC W10) - Built prototype in couple of days - JavaScript file on S3 for A/B testing @@ -71,11 +79,13 @@ - Enough to get users excited despite hard tech **Common Mistakes:** + - Overbuilding at this stage - Not talking/listening to users soon enough - Getting too attached to initial ideas #### Stage 2: Building MVP (8:30-19:43) + **Goal:** Build to launch quickly (weeks, not months) **Key Principles:** @@ -96,6 +106,7 @@ - Don't build from scratch **Examples:** + 1. **DoorDash** (originally Palo Alto Delivery) - Static HTML with PDF menus - Google Forms for orders @@ -114,12 +125,14 @@ - Hired "misfits" overlooked by Google **Tech Stack Philosophy:** + - "If you build a company and it works, tech choices don't matter as much" - Facebook: PHP → HipHop transpiler - JavaScript: V8 engine optimization - Choose what you're dangerous enough with #### Stage 3: Launch Stage (19:43-26:51) + **Goal:** Iterate towards product-market fit **Key Principles:** @@ -140,6 +153,7 @@ - Fix only what prevents product-market fit **Examples:** + 1. **WePay** (YC company) - Started as B2C payments (Venmo-like) - Analytics showed features unused @@ -159,6 +173,7 @@ - Added Node, PHP, WordPress support based on feedback ### Role Evolution Post Product-Market Fit + - **2-5 engineers:** 70% coding time - **5-10 engineers:** <50% coding time - **Beyond 10 engineers:** Little to no coding time @@ -167,17 +182,20 @@ ## Key Concepts Deep Dive ### 90/10 Solution (Paul Buchheit) + - Find ways to get 90% of the value with 10% of the effort - Available 90% solution now is better than 100% solution later - Restrict product dimensions: geography, user type, data type, functionality ### Technical Debt in Startups + - **Early stage:** Embrace technical debt - **Post product-market fit:** Address scaling issues - **Philosophy:** "Tech debt is totally fine - feel the heat of your tech burning" - Only fix what prevents reaching product-market fit ### MVP Principles + 1. **Speed over perfection:** Launch in weeks, not months 2. **Manual processes:** Founders do unscalable work 3. **Limited scope:** Constrain to prove core value @@ -186,30 +204,35 @@ ## Companies Mentioned (with Context) ### Optimizely (YC W10) + - A/B testing platform - Prototype: JavaScript file on S3, manual execution - Founders: Pete Koomen and Dan Siroker - Dan previously headed analytics for Obama campaign ### Remora (YC W21) + - Carbon capture device for semi-trucks - Prototype: 3D renderings to demonstrate concept - Captures 80%+ of truck emissions - Can make trucks carbon-negative with biofuels ### Justin TV/Twitch + - Live streaming platform → gaming focus - Founders: Justin Kan, Emmett Shear, Michael Seibel, Kyle Vogt - MVP built by 4 founders (3 technical) - Hired overlooked engineers from Google ### Stripe + - Payment processing API - Early days: Founders manually processed payments - Filled bank forms manually for each transaction - Classic "do things that don't scale" example ### DoorDash + - Originally "Palo Alto Delivery" - Static HTML with PDF menus - Google Forms for orders @@ -217,18 +240,21 @@ - Focused on suburbs vs metro areas (competitive advantage) ### WayUp (YC 2015) + - Job board for college students - CTO JJ chose Django/Python over Ruby/Rails - Prioritized iteration speed over popular choice - Simple, effective tech stack ### WePay (YC company) + - Started as B2C payments (Venmo competitor) - Pivoted to API after user discovery - GoFundMe became key customer - Example of data + user interviews driving pivot ### Segment + - Analytics infrastructure - Multiple launches in short timeframe - Started with limited integrations @@ -236,36 +262,42 @@ - Acquired by Twilio for $3.2B ### Algolia + - Search API mentioned as YC success - Part of Diana's network of advised companies ## Actionable Advice for Technical Founders ### Immediate Actions (Week 1) + 1. **Build clickable prototype** (Figma, InVision) in 1-3 days 2. **Find 10 potential users** to show prototype 3. **Use existing tools** rather than building from scratch 4. **Embrace ugly code** - it's temporary ### Tech Stack Selection + 1. **Choose familiarity over trendiness** 2. **Use third-party services** for non-core functions 3. **Keep infrastructure simple** (Heroku, Firebase, AWS) 4. **Only build what's unique** to your value proposition ### Hiring Strategy + 1. **Don't hire too early** (slows you down) 2. **Founders must build** to gain product insights 3. **Look for "misfits"** - overlooked talent 4. **Post product-market fit:** Scale team strategically ### Launch Strategy + 1. **Launch multiple times** (weekly iterations) 2. **Combine analytics with user interviews** 3. **Balance feature development with bug fixes** 4. **Accept technical debt** until product-market fit ### Mindset Shifts + 1. **From perfectionist to pragmatist** 2. **From specialist to generalist** (do whatever it takes) 3. **From employee to owner** (no task beneath you) @@ -274,12 +306,14 @@ ## Diana's Personal Insights ### From Her Experience + - "Technical founder is committed to the success of your company" - "Do whatever it takes to get it to work" - "Your product will evolve - if someone else builds it, you miss key learnings" - "The only tech choices that matter are tied to customer promises" ### Common Traps to Avoid + 1. **"What would Google do?"** - Building like a big company too early 2. **Hiring to move faster** - Actually slows you down initially 3. **Over-fixing vs building** - Focus on product-market fit first @@ -288,12 +322,14 @@ ## Resources & References ### YC Resources + - Y Combinator Library: "Tips for technical startup founders" - Paul Graham Essay: "Do Things That Don't Scale" - Paul Buchheit Concept: "90/10 Solution" - Startup School: Technical founder track ### Tools Mentioned + - **Prototyping:** Figma, InVision - **Analytics:** Google Analytics, Amplitude, Mixpanel - **Infrastructure:** Heroku, Firebase, AWS, GCP @@ -302,6 +338,7 @@ - **Landing Pages:** Webflow ### Further Reading + 1. Paul Graham essays (paulgraham.com) 2. Y Combinator Startup School materials 3. Case studies: Stripe, DoorDash, Segment early days @@ -310,18 +347,21 @@ ## Key Takeaways ### For Technical Founders + 1. **Speed is your superpower** - Move faster than established companies 2. **Embrace imperfection** - Good enough beats perfect when speed matters 3. **Stay close to users** - Insights come from conversations, not just data 4. **Tech debt is a feature, not a bug** - Early stage startups should accumulate it ### For Startup Strategy + 1. **Constrained focus** leads to better unit economics (DoorDash example) 2. **Manual processes** create customer intimacy and learning 3. **Continuous launching** builds momentum and feedback loops 4. **Break things at scale** is a good problem to have ### For Team Building + 1. **Founders build first** - Critical for product insights 2. **Hire for adaptability** over pedigree 3. **Evolve role with growth** - Coding time decreases with team size @@ -329,4 +369,4 @@ --- -*Research compiled from YouTube transcript, web searches, and Y Combinator resources. Last updated: January 25, 2026* \ No newline at end of file +_Research compiled from YouTube transcript, web searches, and Y Combinator resources. Last updated: January 25, 2026_ diff --git a/frontend/public/demo/threads/f4125791-0128-402a-8ca9-50e0947557e4/thread.json b/frontend/public/demo/threads/f4125791-0128-402a-8ca9-50e0947557e4/thread.json index 60d0d6446..e12a09c60 100644 --- a/frontend/public/demo/threads/f4125791-0128-402a-8ca9-50e0947557e4/thread.json +++ b/frontend/public/demo/threads/f4125791-0128-402a-8ca9-50e0947557e4/thread.json @@ -113,9 +113,7 @@ { "name": "present_files", "args": { - "filepaths": [ - "/mnt/user-data/outputs/index.html" - ] + "filepaths": ["/mnt/user-data/outputs/index.html"] }, "id": "present_files:2", "type": "tool_call" @@ -159,9 +157,7 @@ }, "uploaded_files": [], "title": "Interactive 3D Weather Forecast Interface", - "artifacts": [ - "/mnt/user-data/outputs/index.html" - ] + "artifacts": ["/mnt/user-data/outputs/index.html"] }, "next": [], "tasks": [], @@ -201,4 +197,4 @@ "interrupts": [], "checkpoint_id": "1f0fbffa-a7ba-674e-800f-42a5653bc5dc", "parent_checkpoint_id": "1f0fbffa-9f3c-6e8c-800e-411520ded234" -} \ No newline at end of file +} diff --git a/frontend/public/demo/threads/f4125791-0128-402a-8ca9-50e0947557e4/user-data/outputs/index.html b/frontend/public/demo/threads/f4125791-0128-402a-8ca9-50e0947557e4/user-data/outputs/index.html index 0a1e021d8..fcebc44f3 100644 --- a/frontend/public/demo/threads/f4125791-0128-402a-8ca9-50e0947557e4/user-data/outputs/index.html +++ b/frontend/public/demo/threads/f4125791-0128-402a-8ca9-50e0947557e4/user-data/outputs/index.html @@ -1,905 +1,1027 @@ - + - - - + + + Liquid Glass Weather - + - - + +
-
+
- -
-
- - - - San Francisco, CA -
- -
- -
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- -
-
72°
-
Sunny
-
-
-
Humidity
-
45%
-
-
-
Wind
-
8 mph
-
-
-
UV Index
-
High
-
-
-
+ +
+
+ + + + San Francisco, CA
- -
- - - +
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ +
+
72°
+
Sunny
+
+
+
Humidity
+
45%
+
+
+
Wind
+
8 mph
+
+
+
UV Index
+
High
+
+
+
+
+ + +
+ + + +
- ✦ Deerflow + ✦ Deerflow - + diff --git a/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/thread.json b/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/thread.json index 897d70ff1..13be822f2 100644 --- a/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/thread.json +++ b/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/thread.json @@ -1741,9 +1741,7 @@ { "name": "present_files", "args": { - "filepaths": [ - "/mnt/user-data/outputs/index.html" - ] + "filepaths": ["/mnt/user-data/outputs/index.html"] }, "id": "present_files:34", "type": "tool_call" @@ -1873,4 +1871,4 @@ "interrupts": [], "checkpoint_id": "1f0ff191-247b-6e28-8077-c6ae37cb0bc6", "parent_checkpoint_id": "1f0ff191-2479-6baa-8076-feaaf42a66ed" -} \ No newline at end of file +} diff --git a/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/user-data/outputs/index.html b/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/user-data/outputs/index.html index 2d65387b5..002946095 100644 --- a/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/user-data/outputs/index.html +++ b/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/user-data/outputs/index.html @@ -1,1114 +1,1275 @@ - + - - - + + + DeerFlow Research Report 2026 - - - + + + - - + +
-
-
Research Report 2026
-

DeerFlow:
Multi-Agent Deep Research

-

A comprehensive analysis of ByteDance's open-source framework that combines language models with specialized tools for automated research workflows.

-
-
-
0
-
GitHub Stars
-
-
-
0
-
Forks
-
-
-
0
-
Contributors
-
-
-
MIT
-
License
-
-
-
+
+
Research Report 2026
+

DeerFlow:
Multi-Agent Deep Research

+

+ A comprehensive analysis of ByteDance's open-source framework that + combines language models with specialized tools for automated research + workflows. +

+
+
+
0
+
GitHub Stars
+
+
+
0
+
Forks
+
+
+
0
+
Contributors
+
+
+
MIT
+
License
+
+
+
-
-
- -

Executive Summary

-

The framework that redefines automated research through intelligent multi-agent orchestration.

-
-
-

- DeerFlow (Deep Exploration and Efficient Research Flow) is an open-source multi-agent research automation framework developed by ByteDance and released under the MIT license in May 2025. The framework implements a graph-based orchestration of specialized agents that automate research pipelines end-to-end, combining language models with tools like web search engines, crawlers, and Python execution. -

- With 19,531 stars and 2,452 forks on GitHub, DeerFlow has established itself as a significant player in the deep research automation space, offering both console and web UI options with support for local LLM deployment and extensive tool integrations. -

-
-
+
+
+ +

Executive Summary

+

+ The framework that redefines automated research through intelligent + multi-agent orchestration. +

+
+
+

+ DeerFlow (Deep Exploration and Efficient Research + Flow) is an open-source multi-agent research automation framework + developed by ByteDance and released under the MIT license in May + 2025. The framework implements a + graph-based orchestration of specialized agents + that automate research pipelines end-to-end, combining language + models with tools like web search engines, crawlers, and Python + execution.

+ With 19,531 stars and + 2,452 forks on GitHub, DeerFlow has established + itself as a significant player in the deep research automation + space, offering both console and web UI options with support for + local LLM deployment and extensive tool integrations. +

+
+
-
-
- -

Development Timeline

-

From initial release to the upcoming DeerFlow 2.0 transition.

-
-
-
-
-
Phase 01
-
May — July 2025
-

Project Inception

-

DeerFlow was created by ByteDance and open-sourced on May 7, 2025. The initial release established the core multi-agent architecture built on LangGraph and LangChain frameworks, featuring specialized agents: Coordinator, Planner, Researcher, Coder, and Reporter.

-
-
-
-
Phase 02
-
August — December 2025
-

Feature Expansion

-

Major feature additions including MCP integration, text-to-speech capabilities, podcast generation, and support for multiple search engines (Tavily, InfoQuest, Brave Search, DuckDuckGo, Arxiv). The framework gained recognition for its human-in-the-loop collaboration features and was integrated into Volcengine's FaaS Application Center.

-
-
-
-
Phase 03
-
January 2026 — Present
-

DeerFlow 2.0 Transition

-

The project is transitioning to DeerFlow 2.0 with ongoing improvements to JSON repair handling, MCP tool integration, and fallback report generation. Now supports private knowledgebases including RAGFlow, Qdrant, Milvus, and VikingDB, along with comprehensive Docker deployment options.

-
-
-
+
+
+ +

Development Timeline

+

+ From initial release to the upcoming DeerFlow 2.0 transition. +

+
+
+
+
+
Phase 01
+
May — July 2025
+

Project Inception

+

+ DeerFlow was created by ByteDance and open-sourced on May 7, 2025. + The initial release established the core multi-agent architecture + built on LangGraph and LangChain frameworks, featuring specialized + agents: Coordinator, Planner, Researcher, Coder, and Reporter. +

+
+
+
+
Phase 02
+
August — December 2025
+

Feature Expansion

+

+ Major feature additions including MCP integration, text-to-speech + capabilities, podcast generation, and support for multiple search + engines (Tavily, InfoQuest, Brave Search, DuckDuckGo, Arxiv). The + framework gained recognition for its human-in-the-loop + collaboration features and was integrated into Volcengine's FaaS + Application Center. +

+
+
+
+
Phase 03
+
January 2026 — Present
+

DeerFlow 2.0 Transition

+

+ The project is transitioning to DeerFlow 2.0 with ongoing + improvements to JSON repair handling, MCP tool integration, and + fallback report generation. Now supports private knowledgebases + including RAGFlow, Qdrant, Milvus, and VikingDB, along with + comprehensive Docker deployment options. +

+
+
+
-
-
- -

Multi-Agent Architecture

-

A modular system built on LangGraph enabling flexible state-based workflows.

+
+
+ +

Multi-Agent Architecture

+

+ A modular system built on LangGraph enabling flexible state-based + workflows. +

+
+
+
+
+
Coordinator
+
Entry point & workflow lifecycle
-
-
-
-
Coordinator
-
Entry point & workflow lifecycle
-
-
-
-
Planner
-
Task decomposition & planning
-
-
-
-
-
🔍 Researcher
-
Web search & crawling
-
-
-
💻 Coder
-
Python execution & analysis
-
-
-
-
-
Reporter
-
Report generation & synthesis
-
-
+
+
+
Planner
+
Task decomposition & planning
-
+
+
+
+
🔍 Researcher
+
Web search & crawling
+
+
+
💻 Coder
+
Python execution & analysis
+
+
+
+
+
Reporter
+
Report generation & synthesis
+
+
+
+ -
-
- -

Key Features

-

Comprehensive tooling for end-to-end research automation.

-
-
-
-
🔍
-

Multi-Engine Search

-

Supports Tavily, InfoQuest (BytePlus), Brave Search, DuckDuckGo, and Arxiv for scientific papers with configurable parameters.

-
-
-
🔗
-

MCP Integration

-

Seamless integration with Model Context Protocol services for private domain access, knowledge graphs, and web browsing.

-
-
-
📚
-

Private Knowledgebase

-

Integrates with RAGFlow, Qdrant, Milvus, VikingDB, MOI, and Dify for research on users' private documents.

-
-
-
🤝
-

Human-in-the-Loop

-

Intelligent clarification mechanisms, plan review and editing, and auto-acceptance options for streamlined workflows.

-
-
-
🎙️
-

Content Creation

-

Podcast generation with TTS synthesis, PowerPoint creation, and Notion-style block editing for report refinement.

-
-
-
🐳
-

Production Ready

-

Docker and Docker Compose support, cloud deployment via Volcengine, and comprehensive API documentation.

-
-
-
+
+
+ +

Key Features

+

+ Comprehensive tooling for end-to-end research automation. +

+
+
+
+
🔍
+

Multi-Engine Search

+

+ Supports Tavily, InfoQuest (BytePlus), Brave Search, DuckDuckGo, + and Arxiv for scientific papers with configurable parameters. +

+
+
+
🔗
+

MCP Integration

+

+ Seamless integration with Model Context Protocol services for + private domain access, knowledge graphs, and web browsing. +

+
+
+
📚
+

Private Knowledgebase

+

+ Integrates with RAGFlow, Qdrant, Milvus, VikingDB, MOI, and Dify + for research on users' private documents. +

+
+
+
🤝
+

Human-in-the-Loop

+

+ Intelligent clarification mechanisms, plan review and editing, and + auto-acceptance options for streamlined workflows. +

+
+
+
🎙️
+

Content Creation

+

+ Podcast generation with TTS synthesis, PowerPoint creation, and + Notion-style block editing for report refinement. +

+
+
+
🐳
+

Production Ready

+

+ Docker and Docker Compose support, cloud deployment via + Volcengine, and comprehensive API documentation. +

+
+
+
-
-
- -

Competitive Comparison

-

How DeerFlow compares to other deep research solutions.

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FeatureDeerFlowOpenAI Deep ResearchLangChain OpenDeepResearch
Multi-Agent Architecture
Local LLM Support
MCP Integration
Code Execution✓ Python REPLLimited
Podcast Generation
Presentation Creation
Private Knowledgebase✓ (6+ options)LimitedLimited
Open Source✓ MIT✗ Proprietary✓ Apache 2.0
-
-
+
+
+ +

Competitive Comparison

+

+ How DeerFlow compares to other deep research solutions. +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureDeerFlowOpenAI Deep ResearchLangChain OpenDeepResearch
Multi-Agent Architecture
Local LLM Support
MCP Integration
Code Execution✓ Python REPLLimited
Podcast Generation
Presentation Creation
Private Knowledgebase✓ (6+ options)LimitedLimited
Open Source✓ MIT✗ Proprietary✓ Apache 2.0
+
+
-
-
- -

Strengths & Considerations

-

Balanced evaluation of the framework's capabilities.

-
-
-
-

💪 Strengths

-
    -
  • Comprehensive multi-agent architecture with specialized roles
  • -
  • Extensive tool integration across search, crawling, and databases
  • -
  • Local LLM deployment support for privacy and cost control
  • -
  • Human collaboration features bridging automation and oversight
  • -
  • Active community with 88+ contributors
  • -
  • Production-ready with Docker and cloud deployment options
  • -
-
-
-

⚠️ Considerations

-
    -
  • Extensive feature set may present learning curve for new users
  • -
  • Local deployment with multiple agents demands significant resources
  • -
  • Advanced features require technical expertise beyond basic usage
  • -
  • Version 2.0 transition may create temporary compatibility concerns
  • -
  • Integration complexity for custom MCP tools and workflows
  • -
-
-
-
+
+
+ +

Strengths & Considerations

+

+ Balanced evaluation of the framework's capabilities. +

+
+
+
+

💪 Strengths

+
    +
  • + Comprehensive multi-agent architecture with specialized roles +
  • +
  • + Extensive tool integration across search, crawling, and + databases +
  • +
  • Local LLM deployment support for privacy and cost control
  • +
  • + Human collaboration features bridging automation and oversight +
  • +
  • Active community with 88+ contributors
  • +
  • Production-ready with Docker and cloud deployment options
  • +
+
+
+

⚠️ Considerations

+
    +
  • + Extensive feature set may present learning curve for new users +
  • +
  • + Local deployment with multiple agents demands significant + resources +
  • +
  • + Advanced features require technical expertise beyond basic usage +
  • +
  • + Version 2.0 transition may create temporary compatibility + concerns +
  • +
  • Integration complexity for custom MCP tools and workflows
  • +
+
+
+
-
-
- -

Final Assessment

-

High confidence evaluation based on comprehensive analysis.

-
-
-

- DeerFlow represents a significant advancement in research automation, combining the power of multi-agent coordination, LLM-driven reasoning, and human-in-the-loop collaboration. Its modular architecture, deep tool integrations, and developer-friendly design make it a compelling choice for researchers and organizations seeking to accelerate complex workflows. -

- With ByteDance backing, an active open-source community, and continuous feature development, DeerFlow is well-positioned to remain a leading framework in the deep research automation space. The upcoming DeerFlow 2.0 transition promises further enhancements to an already robust platform. -

-
-
+
+
+ +

Final Assessment

+

+ High confidence evaluation based on comprehensive analysis. +

+
+
+

+ DeerFlow represents a significant advancement in + research automation, combining the power of multi-agent + coordination, LLM-driven reasoning, and human-in-the-loop + collaboration. Its modular architecture, deep tool integrations, and + developer-friendly design make it a + compelling choice for researchers and organizations + seeking to accelerate complex workflows.

+ With ByteDance backing, an active open-source + community, and continuous feature development, DeerFlow is + well-positioned to remain a leading framework in the deep research + automation space. The upcoming DeerFlow 2.0 transition promises + further enhancements to an already robust platform. +

+
+
- Created By Deerflow + Created By Deerflow - - \ No newline at end of file + + diff --git a/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/user-data/outputs/research_deerflow_20260201.md b/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/user-data/outputs/research_deerflow_20260201.md index 614cd370c..308a7ab4c 100644 --- a/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/user-data/outputs/research_deerflow_20260201.md +++ b/frontend/public/demo/threads/fe3f7974-1bcb-4a01-a950-79673baafefd/user-data/outputs/research_deerflow_20260201.md @@ -111,16 +111,16 @@ Open Issues: 196 (ongoing maintenance and feature development) ### Key Metrics -| Metric | Value | Assessment | -|--------|-------|------------| -| GitHub Stars | 19,531 | Exceptional popularity for research framework | -| Forks | 2,452 | Strong community adoption and potential derivatives | -| Contributors | 88 | Healthy open-source development ecosystem | -| Open Issues | 196 | Active maintenance and feature development | -| Primary Language | Python (1.29MB) | Main development language with extensive libraries | -| Secondary Language | TypeScript (503KB) | Modern web UI implementation | -| Repository Age | ~9 months | Rapid development and feature expansion | -| License | MIT | Permissive open-source licensing | +| Metric | Value | Assessment | +| ------------------ | ------------------ | --------------------------------------------------- | +| GitHub Stars | 19,531 | Exceptional popularity for research framework | +| Forks | 2,452 | Strong community adoption and potential derivatives | +| Contributors | 88 | Healthy open-source development ecosystem | +| Open Issues | 196 | Active maintenance and feature development | +| Primary Language | Python (1.29MB) | Main development language with extensive libraries | +| Secondary Language | TypeScript (503KB) | Modern web UI implementation | +| Repository Age | ~9 months | Rapid development and feature expansion | +| License | MIT | Permissive open-source licensing | --- @@ -128,18 +128,18 @@ Open Issues: 196 (ongoing maintenance and feature development) ### Feature Comparison -| Feature | DeerFlow | OpenAI Deep Research | LangChain OpenDeepResearch | -|---------|-----------|----------------------|----------------------------| -| Multi-Agent Architecture | ✅ | ❌ | ✅ | -| Local LLM Support | ✅ | ❌ | ✅ | -| MCP Integration | ✅ | ❌ | ❌ | -| Web Search Engines | Multiple (5+) | Limited | Limited | -| Code Execution | ✅ Python REPL | Limited | ✅ | -| Podcast Generation | ✅ | ❌ | ❌ | -| Presentation Creation | ✅ | ❌ | ❌ | -| Private Knowledgebase | ✅ (6+ options) | Limited | Limited | -| Human-in-the-Loop | ✅ | Limited | ✅ | -| Open Source | ✅ MIT | ❌ | ✅ Apache 2.0 | +| Feature | DeerFlow | OpenAI Deep Research | LangChain OpenDeepResearch | +| ------------------------ | --------------- | -------------------- | -------------------------- | +| Multi-Agent Architecture | ✅ | ❌ | ✅ | +| Local LLM Support | ✅ | ❌ | ✅ | +| MCP Integration | ✅ | ❌ | ❌ | +| Web Search Engines | Multiple (5+) | Limited | Limited | +| Code Execution | ✅ Python REPL | Limited | ✅ | +| Podcast Generation | ✅ | ❌ | ❌ | +| Presentation Creation | ✅ | ❌ | ❌ | +| Private Knowledgebase | ✅ (6+ options) | Limited | Limited | +| Human-in-the-Loop | ✅ | Limited | ✅ | +| Open Source | ✅ MIT | ❌ | ✅ Apache 2.0 | ### Market Positioning @@ -217,6 +217,7 @@ DeerFlow occupies a unique position in the deep research framework landscape by ## Confidence Assessment **High Confidence (90%+) Claims:** + - DeerFlow was created by ByteDance and open-sourced under MIT license in May 2025 - The framework implements multi-agent architecture using LangGraph and LangChain - Current GitHub metrics: 19,531 stars, 2,452 forks, 88 contributors, 196 open issues @@ -224,11 +225,13 @@ DeerFlow occupies a unique position in the deep research framework landscape by - Includes features for podcast generation, presentation creation, and human collaboration **Medium Confidence (70-89%) Claims:** + - Specific performance benchmarks compared to proprietary alternatives - Detailed breakdown of enterprise adoption rates and use cases - Exact resource requirements for various deployment scenarios **Lower Confidence (50-69%) Claims:** + - Future development roadmap beyond DeerFlow 2.0 transition - Specific enterprise customer implementations and case studies - Detailed comparison with emerging competitors not yet widely documented @@ -255,4 +258,4 @@ This report was compiled using: **Report Prepared By:** Github Deep Research by DeerFlow **Date:** 2026-02-01 **Report Version:** 1.0 -**Status:** Complete \ No newline at end of file +**Status:** Complete diff --git a/frontend/src/app/mock/api/threads/search/route.ts b/frontend/src/app/mock/api/threads/search/route.ts index 74ba79a3d..c0ddd8406 100644 --- a/frontend/src/app/mock/api/threads/search/route.ts +++ b/frontend/src/app/mock/api/threads/search/route.ts @@ -14,7 +14,8 @@ type MockThreadSearchResult = Record & { }; export async function POST(request: Request) { - const body = ((await request.json().catch(() => ({}))) ?? {}) as ThreadSearchRequest; + const body = ((await request.json().catch(() => ({}))) ?? + {}) as ThreadSearchRequest; const rawLimit = body.limit; let limit = 50; diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index 6241100ac..8805522ad 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -137,7 +137,10 @@ export default function ChatPage() { extraHeader={ isNewThread && } - disabled={env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" || isUploading} + disabled={ + env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" || + isUploading + } onContextChange={(context) => setSettings("context", context)} onSubmit={handleSubmit} onStop={handleStop} diff --git a/frontend/src/components/ai-elements/checkpoint.tsx b/frontend/src/components/ai-elements/checkpoint.tsx index d9a5d326c..6d912fec1 100644 --- a/frontend/src/components/ai-elements/checkpoint.tsx +++ b/frontend/src/components/ai-elements/checkpoint.tsx @@ -19,7 +19,10 @@ export const Checkpoint = ({ ...props }: CheckpointProps) => (
{children} diff --git a/frontend/src/components/ai-elements/context.tsx b/frontend/src/components/ai-elements/context.tsx index f49d7caea..7385d0683 100644 --- a/frontend/src/components/ai-elements/context.tsx +++ b/frontend/src/components/ai-elements/context.tsx @@ -115,7 +115,7 @@ export const ContextTrigger = ({ children, ...props }: ContextTriggerProps) => { {children ?? ( @@ -231,7 +231,7 @@ export const WebPreviewConsole = ({
@@ -244,7 +244,7 @@ export const WebPreviewConsole = ({ "text-xs", log.level === "error" && "text-destructive", log.level === "warn" && "text-yellow-600", - log.level === "log" && "text-foreground" + log.level === "log" && "text-foreground", )} key={`${log.timestamp.getTime()}-${index}`} > diff --git a/frontend/src/components/ui/alert.tsx b/frontend/src/components/ui/alert.tsx index 14213546e..aa7de24dd 100644 --- a/frontend/src/components/ui/alert.tsx +++ b/frontend/src/components/ui/alert.tsx @@ -1,7 +1,7 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const alertVariants = cva( "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", @@ -16,8 +16,8 @@ const alertVariants = cva( defaultVariants: { variant: "default", }, - } -) + }, +); function Alert({ className, @@ -31,7 +31,7 @@ function Alert({ className={cn(alertVariants({ variant }), className)} {...props} /> - ) + ); } function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { @@ -40,11 +40,11 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { data-slot="alert-title" className={cn( "col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", - className + className, )} {...props} /> - ) + ); } function AlertDescription({ @@ -56,11 +56,11 @@ function AlertDescription({ data-slot="alert-description" className={cn( "text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed", - className + className, )} {...props} /> - ) + ); } -export { Alert, AlertTitle, AlertDescription } +export { Alert, AlertTitle, AlertDescription }; diff --git a/frontend/src/components/ui/aurora-text.tsx b/frontend/src/components/ui/aurora-text.tsx index 85857c1ab..f8e475a59 100644 --- a/frontend/src/components/ui/aurora-text.tsx +++ b/frontend/src/components/ui/aurora-text.tsx @@ -1,12 +1,12 @@ -"use client" +"use client"; -import React, { memo } from "react" +import React, { memo } from "react"; interface AuroraTextProps { - children: React.ReactNode - className?: string - colors?: string[] - speed?: number + children: React.ReactNode; + className?: string; + colors?: string[]; + speed?: number; } export const AuroraText = memo( @@ -23,7 +23,7 @@ export const AuroraText = memo( WebkitBackgroundClip: "text", WebkitTextFillColor: "transparent", animationDuration: `${10 / speed}s`, - } + }; return ( @@ -36,8 +36,8 @@ export const AuroraText = memo( {children} - ) - } -) + ); + }, +); -AuroraText.displayName = "AuroraText" +AuroraText.displayName = "AuroraText"; diff --git a/frontend/src/components/ui/avatar.tsx b/frontend/src/components/ui/avatar.tsx index 71e428b4c..c4475c2e3 100644 --- a/frontend/src/components/ui/avatar.tsx +++ b/frontend/src/components/ui/avatar.tsx @@ -1,9 +1,9 @@ -"use client" +"use client"; -import * as React from "react" -import * as AvatarPrimitive from "@radix-ui/react-avatar" +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Avatar({ className, @@ -14,11 +14,11 @@ function Avatar({ data-slot="avatar" className={cn( "relative flex size-8 shrink-0 overflow-hidden rounded-full", - className + className, )} {...props} /> - ) + ); } function AvatarImage({ @@ -31,7 +31,7 @@ function AvatarImage({ className={cn("aspect-square size-full", className)} {...props} /> - ) + ); } function AvatarFallback({ @@ -43,11 +43,11 @@ function AvatarFallback({ data-slot="avatar-fallback" className={cn( "bg-muted flex size-full items-center justify-center rounded-full", - className + className, )} {...props} /> - ) + ); } -export { Avatar, AvatarImage, AvatarFallback } +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/frontend/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx index fd3a406ba..55f8352b7 100644 --- a/frontend/src/components/ui/badge.tsx +++ b/frontend/src/components/ui/badge.tsx @@ -1,8 +1,8 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const badgeVariants = cva( "inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", @@ -22,8 +22,8 @@ const badgeVariants = cva( defaultVariants: { variant: "default", }, - } -) + }, +); function Badge({ className, @@ -32,7 +32,7 @@ function Badge({ ...props }: React.ComponentProps<"span"> & VariantProps & { asChild?: boolean }) { - const Comp = asChild ? Slot : "span" + const Comp = asChild ? Slot : "span"; return ( - ) + ); } -export { Badge, badgeVariants } +export { Badge, badgeVariants }; diff --git a/frontend/src/components/ui/breadcrumb.tsx b/frontend/src/components/ui/breadcrumb.tsx index eb88f3212..f63ae19af 100644 --- a/frontend/src/components/ui/breadcrumb.tsx +++ b/frontend/src/components/ui/breadcrumb.tsx @@ -1,11 +1,11 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { ChevronRight, MoreHorizontal } from "lucide-react" +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { ChevronRight, MoreHorizontal } from "lucide-react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { - return