<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ooo</title><description>Personal notes and posts.</description><link>https://ro.yikzero.com</link><item><title>Building an AI Translation Demo with Claude</title><link>https://ro.yikzero.com/en/blog/ai-translate-demo</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/ai-translate-demo</guid><description>Quickly build an AI translation demo to validate optimization strategies such as concurrent requests and streaming JSON output, reducing translation latency.</description><pubDate>Fri, 23 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Rapid validation through a demo during the requirements phase helps clarify how a feature should work and guide subsequent optimization.&lt;/p&gt;
&lt;p&gt;Recently, I have been working on requirements for AI translation and wanted to know: What limitations does AI translation have? Which model delivers the best quality and the fastest speed? In the past, we might only have been able to try competing products, but now we can use Claude to quickly build a demo and validate ideas.&lt;/p&gt;
&lt;p&gt;At the same time, I also used this demo to optimize the speed and quality of AI translation, providing preliminary experience and practice for development, which is more conducive to later planning.&lt;/p&gt;
&lt;video controls playsinline controlslist=&quot;nodownload&quot; preload=&quot;metadata&quot; poster=&quot;https://cdn.yikzero.com/markdown/images/Demo-poster.jpg&quot;&gt;
  &lt;source src=&quot;https://cdn.yikzero.com/markdown/images/Demo.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;p&gt;In this demo, the translation architecture went through a full iteration process: from serial execution, to concurrent requests, then to stable multi-segment JSON output, and finally to JSON streaming output.&lt;/p&gt;
&lt;p&gt;This change significantly reduced the overall time required for long-text translation from the initial 26s to around 2.7s. Although there is still a gap compared with traditional engines in terms of absolute speed, with multi-key polling and caching mechanisms, the experience already has plenty of room for further improvement.&lt;/p&gt;
&lt;p&gt;In addition, after repeated testing, I also worked out a set of parameter combinations that balance quality and stability.&lt;/p&gt;
&lt;p&gt;If you want to understand the specific problems and solutions, please keep reading:&lt;/p&gt;
&lt;h2 id=&quot;core-engineering-optimizations&quot;&gt;Core Engineering Optimizations&lt;/h2&gt;
&lt;p&gt;To address the inherent problems of AI translation being “slow” and prone to “hallucination,” I validated strategies in three directions in the demo code:&lt;/p&gt;
&lt;h3 id=&quot;1-response-experience-concurrency-limit-optimization&quot;&gt;1. Response Experience: Concurrency Limit Optimization&lt;/h3&gt;
&lt;p&gt;At present, choosing a fast model can improve speed to some extent, but compared with traditional translation, there is definitely still a gap. So I tried optimizing the request logic to deliver a relatively good translation experience while balancing model speed and concurrency limits:&lt;/p&gt;
&lt;p&gt;Concurrent requests are certainly the first step everyone would think of. That’s right—this does provide a considerable boost to translation speed.&lt;/p&gt;
&lt;p&gt;After optimization, the time required for the same text dropped from 24s to 6s, and the improvement was indeed obvious. But compared with the 1s of traditional translation, the gap still cannot be ignored.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/RoShot%202026-01-23%20at%2017.09.49@2x.png&quot; alt=&quot;AI translation serial and concurrent comparison&quot;&gt;&lt;/p&gt;
&lt;p&gt;Can it be even faster? The answer is yes.&lt;/p&gt;
&lt;p&gt;The previous strategy was simple concurrency, meaning “one text segment = one request.” Since the model supports long context, we can completely upgrade this to packaged sending: within the Token limit, merge multiple text segments into the same request. This makes full use of the context window and allows more content to be processed at once, greatly improving efficiency.&lt;/p&gt;
&lt;p&gt;A new challenge follows: when multiple segments are packaged and sent together, how can the returned translations be accurately mapped back to each original segment?&lt;/p&gt;
&lt;p&gt;To solve this alignment problem, I adopted a strategy of &lt;strong&gt;paragraph numbering + JSON structured output&lt;/strong&gt;. By adding an index to each text segment and forcing the AI to return structured JSON data, multiple segments can be translated simultaneously while keeping the order stable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/img/2026/06/d83c62cadc3c3a120fd78bb28b2190ae-ai-translation-batch-json.png&quot; alt=&quot;Process introduction&quot;&gt;&lt;/p&gt;
&lt;p&gt;After switching to JSON, streaming output was gone, which was also unacceptable. This led me to discover the concept of NDJSON, which can enable streaming output of JSON data.&lt;/p&gt;
&lt;p&gt;After this round of optimization, the AI translation time was reduced from 6.08s to 2.88s.&lt;/p&gt;
&lt;video controls playsinline controlslist=&quot;nodownload&quot; preload=&quot;metadata&quot; poster=&quot;https://cdn.yikzero.com/markdown/images/Speed_Comparison_h264-poster.jpg&quot;&gt;
  &lt;source src=&quot;https://cdn.yikzero.com/markdown/images/Speed_Comparison_h264.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;p&gt;That’s all for optimizing AI translation speed. Combined with multi-key polling and translation caching that can be implemented later on the server side, the experience can be further improved.&lt;/p&gt;
&lt;h3 id=&quot;2-translation-quality-prompt-tuning-and-terminology-protection&quot;&gt;2. Translation Quality: Prompt Tuning and Terminology Protection&lt;/h3&gt;
&lt;p&gt;I referred to Immersive Translate’s Web3-domain translation prompt and clarified the output specifications. It requires retaining professional terms (such as ETH, Gas fee, etc.), and also ensures consistent output order during batch translation through an indexed input format (&lt;code&gt;[1] text... [2] text...&lt;/code&gt;), avoiding the problem of multiple segments being returned out of order.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;You are a professional ${targetLang} native translator specialized &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Web3 and blockchain content.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;## Translation Rules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Output only the translated content, without explanations or additional text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Maintain all blockchain terminology, cryptocurrency names, and token symbols &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   in&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; their original &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;form&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (e.g., &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ETH&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;USDT&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, Solana, Ethereum)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;3.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Preserve technical Web3 concepts &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; commonly accepted &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;translations&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   DeFi, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;NFT&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;DAO&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;DEX&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;CEX&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;AMM&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;TVL&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;APY&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;APR&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, gas fee, smart contract, &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   wallet, staking, &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;yield&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; farming, liquidity pool&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;4.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Keep all addresses, transaction hashes, and code snippets exactly &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; original&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;5.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Translate from ${sourceLang} to ${targetLang} &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;while&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; preserving technical meaning&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;6.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Ensure fluent, natural expression &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ${targetLang} &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;while&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; maintaining technical accuracy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-parameter-control&quot;&gt;3. Parameter Control&lt;/h3&gt;
&lt;p&gt;Although formal development had not yet started during the requirements phase, I rehearsed the future control logic at the demo level, mainly covering two dimensions:&lt;/p&gt;
&lt;h4 id=&quot;token-limits-and-intelligent-chunking&quot;&gt;Token Limits and Intelligent Chunking&lt;/h4&gt;
&lt;p&gt;Based on the debugging results, I applied certain controls to the number of segments and character length in the same request:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Temperature = 0&lt;/strong&gt; - Deterministic output, more stable results&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Control the number of paragraphs (4 segments)&lt;/strong&gt; - Avoid timeout caused by overly large single requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maximum text characters per request (3,000)&lt;/strong&gt; - Balance output speed and content length&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;thinking-mode-limitation&quot;&gt;Thinking Mode Limitation&lt;/h4&gt;
&lt;p&gt;For models with thinking capabilities, such as Gemini 3.0 Flash, adjusting &lt;code&gt;thinkingLevel&lt;/code&gt; can also improve translation speed in a clear-cut task like translation.&lt;/p&gt;
</content:encoded></item><item><title>Automated Translation with Lokalise MCP</title><link>https://ro.yikzero.com/en/blog/lokalise-mcp</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/lokalise-mcp</guid><description>There are many scenarios at work that require internationalization, so an automated translation solution is needed to improve efficiency.</description><pubDate>Mon, 17 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;preface&quot;&gt;Preface&lt;/h2&gt;
&lt;p&gt;As the saying goes, “laziness” is the primary productive force.&lt;/p&gt;
&lt;p&gt;The current i18n localization workflow is relatively cumbersome: English copy is designed first; after that, every word and sentence is translated into up to 20 languages on the Lokalise platform; then the Translation Keys are annotated back into Figma for developers to use.&lt;/p&gt;
&lt;p&gt;This &lt;strong&gt;“translate -&amp;gt; annotate -&amp;gt; code”&lt;/strong&gt; workflow is extremely time-consuming in App requirement design. Faced with this highly repetitive and inefficient work, I redesigned an automated solution based on my team’s previous i18n practice experience.&lt;/p&gt;
&lt;p&gt;The multilingual platform we currently use is &lt;a href=&quot;https://lokalise.com/&quot;&gt;Lokalise&lt;/a&gt;, so the solution I describe next will mainly revolve around Lokalise. Other platforms can also take it as a reference.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/mcp-success-20251119.png&quot; alt=&quot;Lokalise MCP automatic translation solution&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;basic-idea&quot;&gt;Basic Idea&lt;/h2&gt;
&lt;p&gt;To achieve full automation, it actually comes down to two steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Find a translator who knows the business&lt;/strong&gt;: AI translation is powerful, but it does not understand business terminology (for example, translating “Gas” as “气体”), and it may translate some terms that should not be translated. So we need to inject business context into the AI and set rules for it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Give the AI “hands”&lt;/strong&gt;: After translation, copying and pasting entries one by one into the Lokalise backend is the most exhausting part. Through the MCP protocol, the AI can directly call the Lokalise API.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;final-solution&quot;&gt;Final Solution&lt;/h2&gt;
&lt;p&gt;The tech stack I chose is &lt;strong&gt;Node.js&lt;/strong&gt; + &lt;strong&gt;MCP SDK&lt;/strong&gt; + &lt;strong&gt;client tools&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The choice of client is very flexible, as long as it supports MCP. At first I used &lt;strong&gt;&lt;a href=&quot;https://www.claude.com/download&quot;&gt;Claude Desktop&lt;/a&gt;&lt;/strong&gt;, but for friends who prefer the command line, it can be completely replaced with &lt;strong&gt;&lt;a href=&quot;https://www.claude.com/product/claude-code&quot;&gt;Claude Code&lt;/a&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;a href=&quot;https://github.com/google-gemini/gemini-cli&quot;&gt;Gemini CLI&lt;/a&gt;&lt;/strong&gt;, with an equally smooth experience.&lt;/p&gt;
&lt;p&gt;Gemini may be more skilled at liberal-arts-type tasks like translation, so I recommend it.&lt;/p&gt;
&lt;h3 id=&quot;1-core-code-mcp-server&quot;&gt;1. Core Code (MCP Server)&lt;/h3&gt;
&lt;p&gt;Lokalise itself provides a complete &lt;a href=&quot;https://developers.lokalise.com/reference/lokalise-rest-api&quot;&gt;REST API&lt;/a&gt;, which makes it convenient for us to operate it through code.&lt;/p&gt;
&lt;p&gt;To write less boilerplate code, I directly used the official Node.js SDK (&lt;code&gt;@lokalise/node-lokalise-api&lt;/code&gt;). Referring to the MCP &lt;a href=&quot;https://modelcontextprotocol.io/docs/develop/build-server#node&quot;&gt;official documentation&lt;/a&gt;, I wrapped these APIs into an MCP Server.&lt;/p&gt;
&lt;p&gt;It mainly exposes two tools to the AI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;search-keys&lt;/code&gt;: Used for duplicate checking, to see whether someone has already translated it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create-keys&lt;/code&gt;: Used to do the work, writing the multilingual copy generated by the AI into the backend.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/mcp-20251119.png&quot; alt=&quot;Calling the Lokalise MCP Server&quot;&gt;&lt;/p&gt;
&lt;p&gt;The logic is actually not complicated. In essence, it is just acting as an API relay:&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Click to view the core index.ts code&lt;/summary&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#!/usr/bin/env node&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { McpServer } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;@modelcontextprotocol/sdk/server/mcp.js&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { StdioServerTransport } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;@modelcontextprotocol/sdk/server/stdio.js&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { LokaliseApi, SupportedPlatforms } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;@lokalise/node-api&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { z } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;zod&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; LOKALISE_API_KEY&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; process.env.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;LOKALISE_API_KEY&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; DEFAULT_PROJECT_ID&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; process.env.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;DEFAULT_PROJECT_ID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; DEFAULT_PLATFORMS&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;web&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;ios&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;android&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;other&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; PLATFORMS&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  process.env.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;PLATFORMS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ?&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; process.env.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;PLATFORMS&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; p.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;trim&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    :&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; DEFAULT_PLATFORMS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; SupportedPlatforms&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; lokaliseApi&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; LokaliseApi&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ apiKey: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;LOKALISE_API_KEY&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; server&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; McpServer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    name: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;lokalise-mcp&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    version: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    author: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;yikZero&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    capabilities: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      tools: {},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ... get-project-info 和 search-keys 工具代码略 ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;server.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;registerTool&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &quot;create-keys&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    title: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Create Keys&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    description: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Creates one or more keys in the project&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    inputSchema: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      projectId: z&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;describe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          &quot;A unique project identifier, if not provide, first use get-project-info tool to get the project id&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      keys: z.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        z.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          keyName: z.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;describe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Key identifier&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          platforms: z&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(z.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;enum&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;web&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;ios&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;android&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;other&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;describe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;              &quot;Platforms for the key. If not provided, use the default platforms: &quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; +&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;                PLATFORMS&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          translations: z&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;              z.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                languageIso: z&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;describe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;                    &quot;Unique code of the language of the translation, get the list from the get-project-info tool&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                  ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                translation: z&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;describe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;The actual translation. Pass as an object&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;              })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;describe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Translations for all languages&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;projectId&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;keys&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; lokaliseApi.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;keys&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        keys: keys.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          key_name: key.keyName,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          platforms: key.platforms,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          translations: key.translations.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            language_iso: t.languageIso,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            translation: t.translation,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          })),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        })),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      { project_id: projectId }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      content: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          type: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          text: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(response),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      structuredContent: response &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ... update-keys 工具代码略 ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; transport&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; StdioServerTransport&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; server.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(transport);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Lokalise MCP Server running on stdio&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Fatal error in main():&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, error);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  process.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;exit&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h3 id=&quot;2-training-the-ai-prompt&quot;&gt;2. Training the AI (Prompt)&lt;/h3&gt;
&lt;p&gt;This is actually similar to the Project feature in Claude’s web version, where it lets you preset some system prompts and provide some materials. These all serve as background knowledge for the AI, making it easier for it to work better.&lt;/p&gt;
&lt;p&gt;So to make the AI act like an experienced employee, I wrote all the OneKey terminology (Glossary) and Key naming conventions (Snake Case) into a &lt;code&gt;GEMINI.md&lt;/code&gt; file. If you are using Claude Code, you can also write a &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is actually also a unified &lt;a href=&quot;https://agents.md/&quot;&gt;AGENTS.md&lt;/a&gt; now, but the grand unification has not yet been completed.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;View details of the GEMINI.md prompt&lt;/summary&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;You are a professional web3 project i18n translation expert specializing in translating word/sentence into multiple languages for software localization.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;onekey_background&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;OneKey is a comprehensive cryptocurrency wallet solution...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/onekey_background&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;i18n_supported_languages&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;English (United States), Bengali, Chinese Simplified...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/i18n_supported_languages&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;technical_terms&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;OneKey, WalletConnect, iCloud, Google Drive, OneKey Lite, OneKey KeyTag, Face ID, Touch ID...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/technical_terms&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;translation_namespaces&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;global, onboarding, wallet, swap, earn, market, browser, settings, prime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/translation_namespaces&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Translation Process:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;1.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Direct Translation:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Provide a literal translation of the word/sentence into all supported languages&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; If the text contains a number, replace it with {number}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Preserve all technical terms and variables, like {variableName}, HTML tags, etc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;2.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Refined Translation:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Create an optimized version that enhances clarity and natural flow in each language&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Ensure cultural appropriateness while preserving the original meaning&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; When translating, take cultural factors into account and try to use expressions that fit the cultural habits of the target language, especially when dealing with metaphors, slang, or idioms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Preserve specific professional terminologies within certain industries or fields unchanged, unless there is a widely accepted equivalent expression in the target language&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; The title needs to be used Sentence case&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; A space (half-width space) between a full-width character (Kanji) and a half-width character (letter, number, or symbol)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;3.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Key Generation:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Create a self-explanatory key in snake_case format&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Structure as namespace::descriptive_key_name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; When recognized as a common term, please use global namespace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Key must accurately represent the content&apos;s purpose&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;   -&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Avoid hyphens (-) and curly braces ({})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Output Format:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;1.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Key: [&lt;/span&gt;&lt;span style=&quot;color:#DBEDFF;text-decoration:underline&quot;&gt;namespace::descriptive_key_name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;2.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Direct Translation Table (brief reference)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;3.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Refined Translation Table (primary focus)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Items need checked:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;1.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; If the user provides the Key, use the user-provided Key.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;2.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; If there is an obvious spelling error, I need your help to correct it and inform you.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;3.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; After the translation is completed, need to call the tool to create/update the corresponding key.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;4.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; First, provide the Table, then create or update the Key based on the translations in the Refined Table.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h3 id=&quot;3-configuration-file&quot;&gt;3. Configuration File&lt;/h3&gt;
&lt;p&gt;Finally, in the MCP configuration file (such as &lt;code&gt;claude_desktop_config.json&lt;/code&gt; or &lt;code&gt;.mcp.json&lt;/code&gt;), mount the Server we wrote.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;mcpServers&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;lokalise&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;command&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;args&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/Users/yikzero/Code/lokalise-mcp/build/index.js&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;env&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;LOKALISE_API_KEY&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;xxxx969xxxxxxxx7c18760c&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;DEFAULT_PROJECT_ID&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;44xxxxxe4ef8.8099xx5&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;PLATFORMS&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;ios,android,web,other&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;practical-case&quot;&gt;Practical Case&lt;/h2&gt;
&lt;h3 id=&quot;fully-automated-workflow-from-design-draft-to-code&quot;&gt;Fully Automated Workflow from Design Draft to Code&lt;/h3&gt;
&lt;p&gt;Take the “network detection feature” we recently developed as an example. This is a relatively complex business scenario involving prompts for various network error states, and each error message needs to be i18n-ized. In total, there are over 50 text strings that need translation. If handled manually, translation, key filling, and copy alignment alone would probably take an entire day.&lt;/p&gt;
&lt;p&gt;Here is how I solved it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Claude Code organizes requirements&lt;/strong&gt;: Let Claude analyze the code logic, organize all scenarios that need translation, and output a Markdown list.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gemini translates&lt;/strong&gt;: Feed the Markdown to Gemini 2.5 Pro (CLI), and let it translate based on our built-in Prompt. It will properly handle professional terms such as “VPN” and “Proxy” according to the rules in &lt;code&gt;&amp;lt;technical_terms&amp;gt;&lt;/code&gt;, and automatically generate standardized Keys (such as &lt;code&gt;settings::network_error_timeout&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Claude Code replaces code&lt;/strong&gt;: After receiving the translation draft containing the Keys, throw it back to Claude Code and let it replace the hardcoded strings in the code with one click.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Claude is good at code analysis, Gemini is good at multilingual translation, and MCP is responsible for breaking through data silos. Each does its own job, and the efficiency improvement is more than just a little.&lt;/p&gt;
&lt;h3 id=&quot;daily-fragmented-translation&quot;&gt;Daily Fragmented Translation&lt;/h3&gt;
&lt;p&gt;For small day-to-day requirements, it also works great with design drafts.&lt;/p&gt;
&lt;p&gt;For example, after I just finished drawing a new Onboarding modal in Figma, I can casually take a screenshot and throw it to the AI, saying:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“This is a new import wallet modal. Process the copy inside, and put the Keys under onboarding.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The AI will automatically execute: &lt;strong&gt;visual recognition&lt;/strong&gt; (extract copy) -&amp;gt; &lt;strong&gt;dual translation&lt;/strong&gt; (literal translation + refined translation) -&amp;gt; &lt;strong&gt;automatic entry&lt;/strong&gt; (write through MCP).&lt;/p&gt;
&lt;p&gt;All you need to do is have a cup of coffee and wait for the review.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Use Claude to write an MCP Server, then provide it to Gemini CLI for calling, so they can work better. At this point, I don’t even know who the capitalist is anymore (just kidding).&lt;/p&gt;
&lt;p&gt;The related code mentioned in this article has all been open-sourced. Interested friends can get it here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MCP Server: &lt;a href=&quot;https://github.com/yikZero/lokalise-mcp&quot;&gt;lokalise-mcp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Integrated configuration example: &lt;a href=&quot;https://github.com/yikZero/onekey-translate&quot;&gt;onekey-translate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Building a Personalized Watchlist with AI</title><link>https://ro.yikzero.com/en/blog/watch-this</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/watch-this</guid><description>This article explains how to leverage AI’s powerful data analysis capabilities to integrate multiple film and TV rankings and generate a weekly list of popular titles to watch.</description><pubDate>Sat, 23 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;preface&quot;&gt;Preface&lt;/h2&gt;
&lt;p&gt;Both Xiao Chen and I really enjoy watching movies and TV series, but we often don’t know what to watch—maybe we have a bit of choice paralysis. Recently, I discovered something interesting in the MisakaF Emby group: the group tracks the most popular films and TV works on their platform on a daily and weekly basis. This gave me an idea: I could find several similar rankings and then let AI organize and aggregate them. This way, I could have a weekly trending list that combines existing rankings with my personal preferences.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202411261805598.png!/fw/1920/format/webp&quot; alt=&quot;Personal trending list result&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;basic-idea&quot;&gt;Basic Idea&lt;/h2&gt;
&lt;p&gt;The core workflow of the project can be summarized in three steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Collect trending ranking data&lt;/li&gt;
&lt;li&gt;Analyze and process the data with AI&lt;/li&gt;
&lt;li&gt;Push the processed results to a specified channel&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;solution-iteration&quot;&gt;Solution Iteration&lt;/h2&gt;
&lt;p&gt;Initially, I designed an integrated solution based on multiple professional services:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First, obtain the information feed of Telegram channels through &lt;a href=&quot;https://rsshub.app/&quot;&gt;RSSHub&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then use automation tools such as &lt;a href=&quot;https://ifttt.com/&quot;&gt;IFTTT&lt;/a&gt;, &lt;a href=&quot;https://zapier.com/&quot;&gt;Zapier&lt;/a&gt;, or &lt;a href=&quot;https://n8n.io/&quot;&gt;n8n&lt;/a&gt; to synchronize and organize RSS data into document databases such as Notion or Feishu for storage.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, &lt;a href=&quot;https://dify.ai/&quot;&gt;Dify&lt;/a&gt; would regularly filter data from the past week from the database based on predefined requirements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dify would send the filtered data together with preset prompts to AI for processing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, the processed results returned by AI would be sent to a specified group via &lt;a href=&quot;https://core.telegram.org/bots/api&quot;&gt;Telegram Bot&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Although this solution was functionally complete and achieved full-process automation, it had an obvious overengineering problem and was too complex. In the end, I gave up halfway through implementation.&lt;/p&gt;
&lt;h2 id=&quot;final-solution&quot;&gt;Final Solution&lt;/h2&gt;
&lt;p&gt;Considering the project scale and actual needs, I ultimately adopted a more lightweight and direct tech stack:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Core implementation: Node.js&lt;/li&gt;
&lt;li&gt;Automated scheduling: GitHub Actions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;data-inputorganization&quot;&gt;Data Input/Organization&lt;/h3&gt;
&lt;p&gt;During the process of selecting data sources, I mainly considered data authenticity and sample representativeness.&lt;/p&gt;
&lt;p&gt;Based on the above criteria, I finally selected three data sources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two highly active Telegram channels, both providing weekly viewing data:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://t.me/s/misakaf_emby&quot;&gt;MisakaF-Emby&lt;/a&gt;: over 22K followers, with an average of 2,000+ views per post&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://t.me/s/odysseyplus?q=%23TopOnOdyssey&quot;&gt;Odyssey+&lt;/a&gt;: over 23K followers, with an average of 3,000+ views per post&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;One third-party film and TV platform:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.aiyifan.tv/rank/all&quot;&gt;iYF&lt;/a&gt;: its ranking data is updated promptly and has relatively high market representativeness&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The data from iYF is relatively easy to obtain; simply inspect the network requests and call its API.&lt;/p&gt;
&lt;p&gt;For Telegram channel-related data, it needs to be relayed through &lt;a href=&quot;https://rsshub.app/&quot;&gt;RSSHub&lt;/a&gt;. It also supports the &lt;code&gt;searchQuery&lt;/code&gt; parameter to filter channel content. For specific parameters, refer to the &lt;a href=&quot;https://docs.rsshub.app/routes/social-media#channel&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;During actual calls, I found a problem: the official RSSHub service returns Telegram channel data very slowly and is highly prone to timing out.&lt;/p&gt;
&lt;p&gt;Here’s a little trick: try searching Google for &lt;a href=&quot;https://www.google.com/search?q=Welcome+to+RSSHub&quot;&gt;Welcome to RSSHub&lt;/a&gt;. Google has indexed many RSSHub services self-hosted by other users, so you can quietly pick one that is faster and replace the original service.&lt;/p&gt;
&lt;p&gt;Once we have the raw data, we can filter out this week’s information based on publication time and process it.&lt;/p&gt;
&lt;p&gt;The final organized data will look something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;MisakaF 热度数据:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#每周榜单&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;在过去的一周内，共有2122部电影和2424部电视剧被各位宠幸，其中表现较为突出的有：&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Movies：&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 毒液：最后一舞-870&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 浴火之路-585&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 某种物质-333&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 逆鳞-270&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 死侍与金刚狼-245&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;TV Shows：&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 小巷人家-13210&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 永夜星河-5893&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 英雄联盟：双城之战-3667&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 宿敌-3535&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find some data sources you trust and organize them together yourself.&lt;/p&gt;
&lt;h3 id=&quot;ai-application&quot;&gt;AI Application&lt;/h3&gt;
&lt;p&gt;I use the &lt;a href=&quot;https://sdk.vercel.ai/&quot;&gt;AI SDK&lt;/a&gt; provided by Vercel. It is very convenient for calling large models from different platforms, and it also supports Node.js nicely.&lt;/p&gt;
&lt;p&gt;As for the large model, I’m using Anthropic’s latest &lt;a href=&quot;https://www.anthropic.com/claude/sonnet&quot;&gt;Claude Sonnet 3.5&lt;/a&gt;. The $5 credit I received when I first registered still hasn’t been used up.&lt;/p&gt;
&lt;p&gt;For prompt writing, here are a few lessons learned that may help you write a better prompt:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prompts should be written in English; the final result can be required to output in the language you need&lt;/li&gt;
&lt;li&gt;Specific rules should be listed directly: what can be done and what cannot be done&lt;/li&gt;
&lt;li&gt;Give the large model enough room to think, and encourage it to output the process first before the final result. There is an academic term for this called Chain of Thought/CoT prompting&lt;/li&gt;
&lt;li&gt;Provide concrete examples of the desired output&lt;/li&gt;
&lt;li&gt;Use XML tags to structure your prompt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more specific details, you can also refer to Claude’s &lt;a href=&quot;https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview&quot;&gt;Prompt engineering&lt;/a&gt; documentation.&lt;/p&gt;
&lt;p&gt;Below is the prompt I used to have it output a viewing list. The &lt;code&gt;rssRankings&lt;/code&gt; and &lt;code&gt;hotSearchRanking&lt;/code&gt; variables are the data organized earlier, which need to be concatenated together and provided to Claude:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;You are a professional film and TV analyst specialized in entertainment rankings. Your task is to analyze weekly entertainment data and generate a ranking list of popular TV series and movies.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;First, review the input data:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;rss_rankings&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;${rssRankings}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/rss_rankings&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;hot_search_ranking&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;${hotSearchRanking}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/hot_search_ranking&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Instructions:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;1.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Data Analysis:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Exclude children&apos;s content from consideration.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Works must appear in at least 2 data sources to be ranked.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Analyze ranking data using the Scoring Criteria.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;2.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Scoring Criteria:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Primary source (MisakaF热度数据) ranking positions: 1-2 = 40 points, 3-4 = 30 points, 5 = 20 points&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Public service (Odyssey+公益服点播数据) ranking positions: 1-2 = 40 points, 3-4 = 35 points, 5-6 = 30 points, 7-8 = 25 points&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Search ranking (搜索热度) positions: Top 5 = 20 points, 6-10 = 10 points&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Sum up weighted scores to determine the final ranking&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;3.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Ranking Generation:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Generate separate rankings for TV series and movies&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; List exactly 5 works per category&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;4.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Output Formatting:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Use the following template, writing in Simplified Chinese:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8;font-weight:bold&quot;&gt;**热门剧集**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;1.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [TV Series 1]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;2.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [TV Series 2]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;3.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [TV Series 3]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;4.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [TV Series 4]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;5.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [TV Series 5]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8;font-weight:bold&quot;&gt;**热门电影**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;1.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [Movie 1]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;2.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [Movie 2]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;3.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [Movie 3]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;4.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [Movie 4]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;5.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [Movie 5]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Use numerical prefixes (1.-5.) for each item&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Only bold category headers using markdown &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8;font-weight:bold&quot;&gt;**header**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Remove all markdown formatting from list items&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Before providing the final output, wrap your scoring and ranking process inside &amp;#x3C;scoring_process&gt; tags. In this section:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; List each TV series and movie with their respective scores from each source.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Show your calculations for the weighted scores.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Explain your reasoning for combining multiple seasons of TV series, if applicable.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Demonstrate how you arrived at the final rankings.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  This will help ensure accuracy and alignment with the given criteria. It&apos;s OK for this section to be quite long.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;After your scoring process, present the final ranking list in the specified format, the final ranking must inside &amp;#x3C;final_ranking&gt; tags.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Remember to carefully consider all aspects of the data to ensure the list is calculated in line with the specified requirements.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the prompt, you can decide the weight of each ranking based on your personal preferences.&lt;/p&gt;
&lt;p&gt;For film and TV works you don’t like, you can also try asking AI to filter them out.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Claude 3.5 Sonnet output result&lt;/summary&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plain&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;scoring_process&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;TV Series Scoring:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. 小巷人家&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- MisakaF: #1 (40 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Odyssey+: #2 (40 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Hot Search: #4 (20 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Total: 100 points&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. 永夜星河&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- MisakaF: #2 (40 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Odyssey+: #4 (35 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Hot Search: #7 (10 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Total: 85 points&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. 英雄联盟：双城之战&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- MisakaF: #3 (30 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Odyssey+: #3 (35 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Total: 65 points&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. 白夜追凶/白夜破晓 (combined as same series)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Odyssey+: #1 (40 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Hot Search: #2 (20 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Total: 60 points&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;5. 宿敌&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- MisakaF: #4 (30 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Odyssey+: #6 (30 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Total: 60 points&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Movies Scoring:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. 夺命微笑&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Odyssey+: #1 (40 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Insufficient appearances - excluded&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. 夫妻吵架狗都不理，而查理笑了&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Odyssey+: #2 (40 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Insufficient appearances - excluded&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. 毒液：最后一舞&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- MisakaF: #1 (40 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Insufficient appearances - excluded&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. 浴火之路&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- MisakaF: #2 (40 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Insufficient appearances - excluded&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;5. 某种物质&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- MisakaF: #3 (30 points)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Insufficient appearances - excluded&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Note: For movies, none appeared in multiple data sources, so we&apos;ll need to rely on single-source appearances despite the criteria. This is a limitation of the available data.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/scoring_process&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;final_ranking&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;**热门剧集**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. 小巷人家&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. 永夜星河&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. 英雄联盟：双城之战&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. 白夜追凶&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;5. 宿敌&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;**热门电影**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. 毒液：最后一舞&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. 浴火之路&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. 某种物质&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. 逆鳞&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;5. 死侍与金刚狼&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/final_ranking&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;We can see that from the scoring process to the final content output, everything is very stable. Claude’s ability to follow instructions is quite strong!&lt;/p&gt;
&lt;p&gt;We only need to extract the content inside &lt;code&gt;&amp;lt;final_ranking&amp;gt;&lt;/code&gt; to get the main body of the viewing list. The output result is very standardized and stable.&lt;/p&gt;
&lt;p&gt;If you find adjusting prompts troublesome, you can also choose Anthropic’s &lt;a href=&quot;https://console.anthropic.com/dashboard&quot;&gt;Prompt Generator&lt;/a&gt;, which can generate fairly professional prompts.&lt;/p&gt;
&lt;h3 id=&quot;result-push&quot;&gt;Result Push&lt;/h3&gt;
&lt;p&gt;There are many possible push channels: Feishu bots, Telegram bots, Bark push notifications, and so on. Choose whichever you like. The implementation is not complicated either.&lt;/p&gt;
&lt;p&gt;Once we have the output data, we can add a header image, bold text, tags, and so on to the viewing list according to the specific syntax format of the platform. I also added a quick-jump button to open the film and TV app with one tap :)&lt;/p&gt;
&lt;h3 id=&quot;scheduled-execution&quot;&gt;Scheduled Execution&lt;/h3&gt;
&lt;p&gt;Because everything is developed with Node.js, the lightest and most convenient option is GitHub Actions. Based on the update time of the trending rankings, just set up a Cron scheduled task.&lt;/p&gt;
&lt;p&gt;Since MisakaF’s weekly trending list updates at 7 p.m. on Fridays, I set the scheduled build to run at 7:30 p.m. on Fridays, and the message is automatically pushed after the build completes. A wonderful weekend begins.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202411272319711.png&quot; alt=&quot;GitHub Action run records&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;final-words&quot;&gt;Final Words&lt;/h2&gt;
&lt;p&gt;The simplest version of the implementation is like this. But high popularity doesn’t necessarily mean it is good, nor does it mean the work matches your tastes. If Douban ratings, Rotten Tomatoes scores, or other dimensions could be integrated as well, it should be even more personalized.&lt;/p&gt;
&lt;p&gt;In any case, &lt;strong&gt;happy watching, everyone. Cheers🍻&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202411272325006.jpg&quot; alt=&quot;Dark rows of numbered cinema seats&quot;&gt;&lt;/p&gt;
</content:encoded></item><item><title>Building a Blog from a Product Designer’s Perspective</title><link>https://ro.yikzero.com/en/blog/roominess-develop</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/roominess-develop</guid><description>The website has been redesigned for a while now, and the content modules are relatively complete. So this time, I’d like to share some of my own experience, from design to development.</description><pubDate>Sat, 21 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;preface&quot;&gt;Preface&lt;/h2&gt;
&lt;p&gt;The design and development of &lt;strong&gt;Roominess 5.0&lt;/strong&gt; has finally come to an end. To be honest, I feel like this blog project has really put all the design and development knowledge I’ve learned to use, making it a pretty good practice project. So, from the perspective of a product designer, I’d like to chat with you about how to build your own blog.&lt;/p&gt;
&lt;p&gt;This article will cover the following two parts: the complete process of taking a blog from concept to launch, and recommendations for design/development tools.&lt;/p&gt;
&lt;h2 id=&quot;blog-development-process&quot;&gt;Blog Development Process&lt;/h2&gt;
&lt;p&gt;Blog development mainly includes the following major stages: planning and design, frontend and backend development, deployment and launch, and application optimization. In this section, we’ll introduce the first three parts. Website optimization will be covered later as supplementary content.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/2409212246.png&quot; alt=&quot;Overall blog development process&quot;&gt;&lt;/p&gt;
&lt;p&gt;Blog development is a simplified project development process. It gives us an excellent opportunity to improve our understanding and control of an entire project.&lt;/p&gt;
&lt;p&gt;Oh, right. One more thing before we begin: if you simply want to have your own website and don’t want to tinker with everything yourself, then “no-code” tools may be more suitable for you. You don’t need to write code or maintain servers yourself; you only need to plan, design, and pay. For example, &lt;a href=&quot;https://www.framer.com/&quot;&gt;Framer&lt;/a&gt;, &lt;a href=&quot;https://webflow.com/&quot;&gt;Webflow&lt;/a&gt;, or you can try having AI help you build a website with &lt;a href=&quot;https://wegic.ai/&quot;&gt;Wegic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As for me, I still prefer the traditional development and deployment approach. It gives us more room to work with, allows for a higher degree of customization, and is generally more flexible.&lt;/p&gt;
&lt;h3 id=&quot;planning-and-design&quot;&gt;Planning and Design&lt;/h3&gt;
&lt;p&gt;Blog planning mainly involves deciding what content the blog needs and whether that content leans more toward being static or dynamic. The distinction between static and dynamic content mainly affects the subsequent technology choices and development difficulty.&lt;/p&gt;
&lt;p&gt;Most content, such as personal project showcases, article lists, portfolios or photo collections, personal bios, reading lists, website update logs, and so on, is relatively fixed and can be regarded as static content that does not need frequent updates.&lt;/p&gt;
&lt;p&gt;There is also content that needs to be updated in real time, such as article views, likes, comment counts, or the music the blogger is currently listening to, today’s water intake, and so on. This dynamic content requires real-time data retrieval and can be regarded as dynamic content.&lt;/p&gt;
&lt;p&gt;I’ve also put together a table. You can roughly look at how to distinguish static and dynamic content, as well as their impact on subsequent development.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/2409221108.png&quot; alt=&quot;Early-stage blog planning&quot;&gt;&lt;/p&gt;
&lt;p&gt;Reading this article may help you understand static and dynamic websites: &lt;a href=&quot;https://www.shopify.com/in/blog/static-vs-dynamic-website&quot;&gt;Choosing Between Static and Dynamic Websites&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;During the early planning stage of a project, the more thoroughly you think things through, the easier development will be later. However, the technical difficulty of developing a blog itself is very low, so even if your initial thinking isn’t comprehensive enough, the impact will be small. Don’t put too much pressure on yourself.&lt;/p&gt;
&lt;p&gt;After the content architecture is set, you can move on to building the design specifications. For a blog, basic elements such as a color system, layout, and typography are enough. There isn’t much need for a huge design system.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202409222131610.png&quot; alt=&quot;Tailwind CSS Color Generator&quot;&gt;&lt;/p&gt;
&lt;p&gt;I recommend first learning about frontend thinking and development methods before starting the design. This will make things much easier. The key is how to make things more systematic and modular.&lt;/p&gt;
&lt;p&gt;Taking the definition of a blog color system as an example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First choose a theme color you like, &lt;code&gt;#0284C7&lt;/code&gt;, and use &lt;a href=&quot;https://uicolors.app/create&quot;&gt;Tailwind CSS Color Generator&lt;/a&gt; to generate a color palette&lt;/li&gt;
&lt;li&gt;Then use &lt;a href=&quot;https://huetone.ardov.me/&quot;&gt;Huetone&lt;/a&gt; to check and adjust whether the generated palette complies with WCAG 3.0 standards&lt;/li&gt;
&lt;li&gt;Finally, just like Tailwind CSS theme colors, you can fine-tune it yourself based on your color sense.&lt;/li&gt;
&lt;li&gt;The article &lt;a href=&quot;https://stripe.com/blog/accessible-color-systems&quot;&gt;Designing accessible color systems&lt;/a&gt; may give you more systematic methods for building a color system.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202409241252664.png&quot; alt=&quot;Huetone: a tool for creating accessible color systems&quot;&gt;&lt;/p&gt;
&lt;p&gt;On the design side, you need to define text colors and theme colors; functional colors are not used that much. If you also need to support dark mode, you’ll need to further consider how colors appear in dark mode. The layout framework should also be defined in advance. If the website isn’t particularly personalized, the positions of several functional modules usually won’t change. After defining the two parts—colors and layout—development can also begin in parallel.&lt;/p&gt;
&lt;h3 id=&quot;frontend-and-backend-development&quot;&gt;Frontend and Backend Development&lt;/h3&gt;
&lt;p&gt;For the development part, I’ll mainly introduce frontend development, since I don’t know much about backend development.&lt;/p&gt;
&lt;h4 id=&quot;frontend-environment-setup&quot;&gt;Frontend Environment Setup&lt;/h4&gt;
&lt;p&gt;Taking Mac as an example, frontend development requires the following tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code editor: Visual Studio Code (VS Code): software used to write and manage code&lt;/li&gt;
&lt;li&gt;Runtime environment: Node.js: a platform that allows JavaScript to run on your computer&lt;/li&gt;
&lt;li&gt;Version control system: Git: a tool used to track and manage code changes&lt;/li&gt;
&lt;li&gt;Package manager: Homebrew (optional): a tool that simplifies the software installation process on Mac&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The basic process is to first install a software package marketplace, then install development-related software such as Node.js, Git, and VS Code through that marketplace. Once installation is complete, you can write code in VS Code and use Node.js to build your blog. Then you can use Git to record the change history of your code, making it easier to manage.&lt;/p&gt;
&lt;p&gt;Run the following commands in the macOS terminal to configure the frontend environment you need:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;zsh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 安装 macOS 软件包管理：brew&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;/bin/bash&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;$(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -fsSL&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 通过 brew 安装 VS Code、git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --cask&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; visual-studio-code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 安装 node 管理工具：nvm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -o-&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 通过 nvm 安装长期支持的 node 版本&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nvm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --lts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# （可选）安装 pnpm 软件包管理器&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -g&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pnpm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The required frontend environment is now configured. If you need to install anything extra, you can also do so through the &lt;code&gt;brew&lt;/code&gt; software marketplace. Network issues need to be resolved on your own.&lt;/p&gt;
&lt;h4 id=&quot;frontend-technology-selection&quot;&gt;Frontend Technology Selection&lt;/h4&gt;
&lt;p&gt;The frontend environment is the foundation for developing all frontend projects. Now we need to consider the technology choices for this project.&lt;/p&gt;
&lt;p&gt;As we discussed earlier, the “content framework” has an important impact on technology selection. To help you make a better choice, I’ve put together a “technology selection” recommendation table based on content characteristics (static/dynamic).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202409301502160.png&quot; alt=&quot;Blog technology selection recommendations&quot;&gt;&lt;/p&gt;
&lt;p&gt;I have used &lt;a href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt;, &lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt;, and &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; from the table, while &lt;a href=&quot;https://nuxt.com/&quot;&gt;Nuxt&lt;/a&gt; is a strong Vue-based competitor to Next.js. My blog has also gone through changes from Hugo -&amp;gt; Next.js -&amp;gt; Astro, so I suppose that gives me at least a little credibility. To learn about the history and reasons behind these changes, you can check the blog’s &lt;a href=&quot;https://ro.yikzero.com/changelog&quot;&gt;changelog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Among these frameworks, &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; is the one I currently recommend most. The specific reasons are as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Websites built with Astro have excellent performance and load fast enough&lt;/li&gt;
&lt;li&gt;Astro natively supports Markdown, making it very suitable as a blog framework&lt;/li&gt;
&lt;li&gt;Astro has a relatively gentle learning curve and only requires familiarity with HTML, CSS, and JavaScript&lt;/li&gt;
&lt;li&gt;Static builds make it convenient for me to quickly deploy to different platforms later&lt;/li&gt;
&lt;li&gt;The server I purchased has relatively weak performance, and pure static sites have low server requirements&lt;/li&gt;
&lt;li&gt;Static content is also very suitable for CDN acceleration&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can also check the &lt;a href=&quot;https://astro.build/blog/2023-web-framework-performance-report/&quot;&gt;2023 Web Framework Performance Report&lt;/a&gt;, which compares the performance of web frameworks such as Astro and Next.js. It may help you make a choice.&lt;/p&gt;
&lt;p&gt;After the environment is configured, taking an Astro project as an example, we can follow the &lt;a href=&quot;https://docs.astro.build/en/install-and-setup/&quot;&gt;Astro documentation&lt;/a&gt; to create a new Astro-based project:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;zsh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 如果没有安装 pnpm，npm 请使用 npm create astro@latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; astro@latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you can find some tutorials and learn step by step how to write code based on Astro. It’s not very difficult to get started.&lt;/p&gt;
&lt;p&gt;After the project initialization is complete, you may wonder how to connect it with the specifications on the design side. What I’m using is &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss&quot;&gt;Tailwindcss - A utility-first CSS framework for rapid UI development&lt;/a&gt;. It’s like a plugin for the project that can help you with frontend development. In Tailwindcss, you can predefine fonts, theme colors, functional colors, and even spacing, border radius, and so on. At this point, doesn’t it work perfectly with the color and other specifications you defined during design? Configure it once, and it becomes very convenient to use and adjust uniformly.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;zsh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 如果没安装 pnpm，npm 请使用 npx astro add tailwind&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; astro&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; tailwind&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tailwindcss configuration example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/** &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; {import(&apos;tailwindcss&apos;).Config}&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  darkMode: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;selector&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 暗黑模式支持&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  theme: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    colors: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      white: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#ffffff&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 定义颜色名称 调用方式：text-white bg-white&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      black: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#000000&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      brand: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        500&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#0ea5e9&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 定义品牌色 调用方式一致：text-brand-500&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        600&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#0284c7&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;backend-development&quot;&gt;Backend Development&lt;/h4&gt;
&lt;p&gt;Earlier, we chose Astro as the technology stack, which is more suitable for developing static content. If your blog needs a lot of dynamic data and requires a simple backend, then I recommend using &lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt; or &lt;a href=&quot;https://nuxt.com/&quot;&gt;Nuxt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Basic process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Have a database (I recommend Supabase’s free plan or a database from a domestic cloud provider) to store your data&lt;/li&gt;
&lt;li&gt;Connect it to your Next.js project through the plugin &lt;a href=&quot;https://www.prisma.io/&quot;&gt;Prisma&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Write logic code in the project to operate on the data in the database&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is an example of an API for querying my “recent updates”:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; GET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;req&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; NextRequest&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Get 方法&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Number&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(req.nextUrl.searchParams.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;number&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 让程序知道你想要几条数据&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; records&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; prisma.recent_states.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;findMany&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    take: number, &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 程序知道之后，去数据库对应数量的数据&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    orderBy: { created_at: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;desc&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }, &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 排序方法 按照时间降序&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Response.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(records); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 返回 json 格式的数据 方便返回到页面中&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By calling this API using the &lt;code&gt;Get&lt;/code&gt; method, I can query my recent updates and then display them on the blog, allowing you to see and understand what has been happening with me recently.&lt;/p&gt;
&lt;p&gt;Compared with static content, there are more operations involved. This adds the process of querying and processing data from the database. If database access is slow, or your cloud server performance is poor, this will directly affect your website’s access speed. Also, dynamic content is not suitable for deployment through traditional CDN distribution. If your website’s access speed is poor, you should give up dynamic content.&lt;/p&gt;
&lt;h3 id=&quot;deployment-and-launch&quot;&gt;Deployment and Launch&lt;/h3&gt;
&lt;p&gt;After the blog has been developed and tested locally, I recommend uploading it to &lt;a href=&quot;https://github.com/&quot;&gt;Github&lt;/a&gt;. Github is used for online project hosting with Git. This platform is like a version management tool for files, and it can manage the code of your project.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202410010911982.png&quot; alt=&quot;Github repository code commit history&quot;&gt;&lt;/p&gt;
&lt;p&gt;Every time you modify a piece of code, you can commit it to Github. If the project code build has problems at some point, you can roll it back very conveniently.&lt;/p&gt;
&lt;p&gt;After the blog is developed, what we mainly want is for others to see it. So we need a place to host the website, and that place also needs to be accessible by everyone. For example: home devices with a public IP, cloud servers, lightweight application servers, PaaS platforms, and so on.&lt;/p&gt;
&lt;p&gt;A laptop at home can actually be used as a device to host a website, provided that you have a public IP and others can access you on the Internet. Currently, public IPs need to be requested from customer service. If you use your laptop as a server, your device also cannot be shut down, and so on. There are still some limitations.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202410010918138.png&quot; alt=&quot;Tencent Cloud Lightweight Application Server product page&quot;&gt;&lt;/p&gt;
&lt;p&gt;So at present, most people mainly deploy websites on cloud servers or lightweight application servers. These all have public IPs and run 24 hours a day, making them very suitable as website servers. If you don’t need very strong performance, devices costing around 99 yuan per year are enough. It’s still quite affordable. If you’re still a student, you can get a free server from Alibaba Cloud.&lt;/p&gt;
&lt;p&gt;Taking a cloud server as an example, here is a brief introduction to the process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A Linux server, like a macOS system, needs to have the frontend environment installed and configured before building&lt;/li&gt;
&lt;li&gt;If the files are not very large, you can also build locally and then upload the &lt;code&gt;dist&lt;/code&gt; package to the server&lt;/li&gt;
&lt;li&gt;Configure nginx forwarding, and you can access your website through the cloud server’s IP address&lt;/li&gt;
&lt;li&gt;If you don’t want to access it directly through an IP address, you can also purchase your own dedicated domain name. Several major domestic cloud provider platforms allow you to purchase domain names&lt;/li&gt;
&lt;li&gt;After purchasing a domain name, you need to configure DNS resolution. You can handle this step on the platform where you purchased the domain name or on a dedicated DNS resolution platform, such as &lt;a href=&quot;https://www.dnspod.cn/&quot;&gt;Dnspod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Bind the IP address of the A record to the domain name, and then you’ll be able to access your website through the domain name&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The above introduces a simple process. If you need a detailed deployment and launch process, you can also refer to Teacher Zuo Zizhen’s article: &lt;a href=&quot;https://zuozizhen.com/blog/website-for-designer-1/&quot;&gt;A Personal Website Building Guide for Designers (Part 1): From Domain Name to Website&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you don’t want to use your own server and only want to design and write code, there are actually many platforms that can handle the server and domain name steps for you. After authorization, these platforms pull your project code from your Github repository, then build and publish it to the web on their cloud platforms.&lt;/p&gt;
&lt;p&gt;Similar platforms include &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt;, &lt;a href=&quot;https://zeabur.com/&quot;&gt;Zeabur&lt;/a&gt;, &lt;a href=&quot;https://pages.cloudflare.com/&quot;&gt;Cloudflare Page&lt;/a&gt;, and so on. If you don’t want to maintain your own cloud server, this is also a good approach.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202410010013326.png&quot; alt=&quot;Vercel project deployment - Rosure&quot;&gt;&lt;/p&gt;
&lt;p&gt;Taking Vercel as an example, here is a brief introduction to the process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a Github repository and upload your local project code to a private Github repository via Git.&lt;/li&gt;
&lt;li&gt;Register and log in to the Vercel platform, grant the platform permission to read your Github repository, and select the corresponding project repository.&lt;/li&gt;
&lt;li&gt;The project framework is automatically recognized. Click the button to build. The platform will display build logs, so if something goes wrong, you can troubleshoot it in time.&lt;/li&gt;
&lt;li&gt;Once the build is complete, the platform will provide a free subdomain for you to use. You can directly visit the domain name and see that your website has already been published online.&lt;/li&gt;
&lt;li&gt;You can also choose to purchase a domain name, then follow the platform’s prompts to bind the project to the domain name you own.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The above provides two ways to deploy a website to the public Internet. You can choose according to your preferences and the level of difficulty. These two methods can also be combined.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Due to length limitations, every part of the process has been covered, but not in great detail. You’ll still need to look up more materials on your own. This article can only provide general directional guidance. I hope this introduction to the development process can help you.&lt;/p&gt;
&lt;h2 id=&quot;design--development-tool-recommendations&quot;&gt;Design &amp;amp; Development Tool Recommendations&lt;/h2&gt;
&lt;p&gt;As everyone may or may not know, I quite like hoarding things, so when it comes to website development, I’ve also collected quite a few useful tools and resources. I’ll take this opportunity to share them with you; perhaps they can help with your development.&lt;/p&gt;
&lt;h3 id=&quot;design&quot;&gt;Design&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://figma.com/&quot;&gt;&lt;strong&gt;Figma&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Collaborative interface design tool&lt;/td&gt;
&lt;td&gt;General&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://rosure.yikzero.com/&quot;&gt;&lt;strong&gt;Rosure&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;My design collection, including icons, fonts, website inspiration, and more&lt;/td&gt;
&lt;td&gt;Inspiration&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://toolfolio.io/&quot;&gt;&lt;strong&gt;Toolfolio&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Design tools and inspiration aggregator&lt;/td&gt;
&lt;td&gt;Inspiration&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://uicolors.app/create&quot;&gt;&lt;strong&gt;Tailwind CSS Gradient Color Generator&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Generates color scales based on the color you provide&lt;/td&gt;
&lt;td&gt;Color&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://palettte.app/&quot;&gt;&lt;strong&gt;Palettte&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A clear palette editing and remapping tool&lt;/td&gt;
&lt;td&gt;Color&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.radix-ui.com/colors/custom&quot;&gt;&lt;strong&gt;Radix Colors Custom Palette&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Generates color palettes and allows effect previews, with dark mode support&lt;/td&gt;
&lt;td&gt;Color&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://toolfolio.io/&quot;&gt;&lt;strong&gt;Huetone&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A tool for creating accessible color systems&lt;/td&gt;
&lt;td&gt;Color&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;development&quot;&gt;Development&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://brew.sh/&quot;&gt;&lt;strong&gt;Homebrew&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;macOS (or Linux) package manager&lt;/td&gt;
&lt;td&gt;General&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://pnpm.io/&quot;&gt;&lt;strong&gt;pnpm&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Project package manager&lt;/td&gt;
&lt;td&gt;General&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/nvm-sh/nvm&quot;&gt;&lt;strong&gt;nvm&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Node version manager&lt;/td&gt;
&lt;td&gt;General&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://tailwindcss.com/docs/installation&quot;&gt;&lt;strong&gt;Tailwindcss&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Utility-first CSS framework&lt;/td&gt;
&lt;td&gt;CSS&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded></item><item><title>New Paradigms for Visual Interaction in the Age of AI</title><link>https://ro.yikzero.com/en/blog/ai-ui-future</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/ai-ui-future</guid><description>Explores new paradigms in visual interaction in the era of artificial intelligence, including innovations in both hardware and software.</description><pubDate>Wed, 24 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;preface&quot;&gt;Preface&lt;/h2&gt;
&lt;p&gt;I’ve been thinking about what form these “tools” that we take for granted will take in the age of artificial intelligence.&lt;/p&gt;
&lt;p&gt;Currently, the most common application of artificial intelligence is usually limited to chat interfaces, where interaction typically follows a text-based question-and-answer model. As technology continues to advance, Vercel has also introduced &lt;a href=&quot;https://sdk.vercel.ai/docs/concepts/ai-rsc&quot;&gt;Generative UI&lt;/a&gt;, which can dynamically generate customized interactive user interfaces based on conversation content and needs. What form will future UI take? Will it evolve into what is shown in movies, with cool holographic displays and intuitive interactive HUDs, or might we enter a new era that no longer relies on traditional GUIs?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Natural language becoming the fundamental interface for every software application might mean the end of GUIs&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;random-thoughts&quot;&gt;Random Thoughts&lt;/h2&gt;
&lt;p&gt;In the future, could I simply say, “Siri, book me a plane ticket,” or, just like Musk’s brain-computer interface experiments, transmit information with only a thought? The information that needs to be presented would appear directly in front of the eyes (in that case, would blind people also be able to see?), without the need for any external devices. The content that needs to be controlled could also be interacted with through fingers and eye movements, like with VR glasses. Perhaps in the future, it really will be like &lt;a href=&quot;https://www.themoviedb.org/tv/42009-black-mirror&quot;&gt;Black Mirror&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;research&quot;&gt;Research&lt;/h2&gt;
&lt;h3 id=&quot;ai-pin&quot;&gt;AI Pin&lt;/h3&gt;
&lt;p&gt;Humane’s &lt;a href=&quot;https://humane.com/aipin&quot;&gt;Ai Pin&lt;/a&gt; can be considered a pioneer. This product attaches magnetically to clothing. It has no screen and is controlled through voice and a small touch surface. It has a laser that can project images onto your palm, as well as a camera that can recognize gestures for switching and other operations.&lt;/p&gt;
&lt;p&gt;Features it currently and may potentially support in the future: laser projection display, gesture and voice interaction, AI assistant, real-time translation, health monitoring, music playback, message notifications, and more.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/2024042301.png&quot; alt=&quot;AI Pin product screenshot&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;rabbit&quot;&gt;Rabbit&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rabbit.tech/rabbit-r1&quot;&gt;Rabbit R1&lt;/a&gt; is an AI device launched by Rabbit, positioned as your pocket companion. It mainly consists of a screen, camera, scroll wheel, and button.&lt;/p&gt;
&lt;p&gt;Features it currently and may potentially support in the future: understanding and executing actions (hailing rides, ordering food, playing music), real-time translation, teaching mode, etc. You can watch the &lt;a href=&quot;https://www.youtube.com/watch?v=ddTV12hErTc&quot;&gt;review video&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/2024042303.png&quot; alt=&quot;Rabbit R1 front render&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;dot&quot;&gt;Dot&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://new.computer/&quot;&gt;Dot&lt;/a&gt; is a chat app on iOS. Users can use it to send text, voice memos, images, PDF files, and it can also search the web for you. Currently, Dot communicates through text and aims to become an always-available companion. Unlike most AI conversations, Dot can remember what you’ve said before and almost never forgets anything.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/2024042304.jpg&quot; alt=&quot;Dot product interface&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;amazon-lex&quot;&gt;Amazon Lex&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/lexv2/latest/dg/what-is.html&quot;&gt;Amazon Lex V2&lt;/a&gt; is a service provided by AWS that allows developers to build conversational interfaces for applications, supporting voice and text input. Users can interact using natural language, such as placing orders. In addition, Lex V2 can also be integrated with web elements (buttons, select boxes, etc.) to solve users’ problems or help them complete specific tasks through Semi-Guided Conversations.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;These examples may not necessarily be representative, but they do provide a simple glimpse of the overall trend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The hardware carrier has changed. In addition to phones and computers, people are also trying to present content on more portable devices, such as necklaces, badges, and pagers.&lt;/li&gt;
&lt;li&gt;The ways in which visual images are presented are becoming more diverse, such as projection and laser displays. Due to size constraints, the main forms are primarily text, simple charts, and button information.&lt;/li&gt;
&lt;li&gt;Natural language interaction will be used more and more widely.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;my-thoughts&quot;&gt;My Thoughts&lt;/h2&gt;
&lt;h4 id=&quot;generative-ui-is-a-future-trend&quot;&gt;&lt;strong&gt;Generative UI Is a Future Trend&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;At present, interface design must satisfy as many people as possible. Any experienced professional designer knows the main drawback of this approach — you can never make anyone completely satisfied. Personalization and customization also play a relatively small role.&lt;/p&gt;
&lt;p&gt;This application scenario is somewhat similar to Alipay’s dynamic personalized cards on the home screen based on location (showing airport-related information when at the airport).&lt;/p&gt;
&lt;video controls playsinline controlslist=&quot;nodownload&quot; preload=&quot;metadata&quot; poster=&quot;https://cdn.yikzero.com/markdown/images/2024042305-poster.jpg&quot;&gt;
  &lt;source src=&quot;https://cdn.yikzero.com/markdown/images/2024042305.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/2024042306.jpg&quot; alt=&quot;iOS Smarter Siri by @upintheozone&quot;&gt;&lt;/p&gt;
&lt;p&gt;Moreover, generative UI is not limited to dynamic cards; it can also dynamically present an entire system for you based on personal data. The article &lt;a href=&quot;https://www.nngroup.com/articles/generative-ui/&quot;&gt;Generative UI and Outcome-Oriented Design&lt;/a&gt; gives examples of what true personalization for every individual looks like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dyslexia — the application displays special fonts and color contrast&lt;/li&gt;
&lt;li&gt;Users who care about cost and time — explicitly display this information and automatically increase the weight of corresponding flights&lt;/li&gt;
&lt;li&gt;Although it is the same application, the interface, features, and more are all personally customized&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/2024042307.png&quot; alt=&quot;Comparison between traditional UI and generative UI&quot;&gt;&lt;/p&gt;
&lt;p&gt;Designers will also shift from universal design to personalized design, defining boundaries for various personalized scenarios to strengthen constraints on artificial intelligence.&lt;/p&gt;
&lt;h4 id=&quot;traditional-interaction-will-not-be-replaced-but-it-will-be-greatly-simplified&quot;&gt;&lt;strong&gt;Traditional Interaction Will Not Be Replaced, but It Will Be Greatly Simplified&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Although natural language interaction has advantages, in scenarios that require fast and precise input, visual elements such as buttons, icons, and gestures are still necessary. Pure voice input may reduce efficiency. Also, not all tasks can be completed well through voice conversations alone.&lt;/p&gt;
&lt;p&gt;Different interaction methods suit different situations. The coexistence of visual and voice input/output can maximize adherence to accessibility design principles, allowing people with disabilities to choose the method that suits them. Once LLMs can complete specific tasks, interfaces will be greatly simplified.&lt;/p&gt;
&lt;p&gt;In addition, natural language interaction is not applicable in all scenarios. For example, in companies, libraries, and similar places, it may also cause concerns such as privacy leakage.&lt;/p&gt;
&lt;h2 id=&quot;final-notes&quot;&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;Although I wanted to expand on this as much as possible, as I kept writing, I still ended up returning to the GUI itself — I was merely imagining the future forms of existing products…&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://research.google/blog/screenai-a-visual-language-model-for-ui-and-visually-situated-language-understanding/&quot;&gt;ScreenAI: A visual language model for UI and visually-situated language understanding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bloomberg.com/news/newsletters/2024-04-14/humane-ai-pin-review-the-device-isn-t-going-to-kill-apple-s-iphone-luziqlew&quot;&gt;The AI Device Revolution Isn’t Going to Kill the Smartphone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.fastcompany.com/90975882/meet-dot-an-ai-companion-designed-by-an-apple-alum-here-to-help-you-live-your-best-life&quot;&gt;Meet Dot, an AI companion designed by an Apple alum, here to help you live your best life&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geoffreylitt.com/2023/03/25/llm-end-user-programming.html&quot;&gt;Malleable software in the age of LLMs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nngroup.com/articles/generative-ui/&quot;&gt;Generative UI and Outcome-Oriented Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2402.07939&quot;&gt;UFO: A UI-Focused Agent for Windows OS Interaction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2404.05719&quot;&gt;Ferret-UI: Grounded Mobile UI Understanding with Multimodal LLMs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>A Study of Modular UI Design: Icons</title><link>https://ro.yikzero.com/en/blog/icon-learn</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/icon-learn</guid><description>A comprehensive introduction to the importance and application of icons in UI design</description><pubDate>Sat, 11 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;
&lt;p&gt;There are already many high-quality icon libraries, such as &lt;a href=&quot;https://www.untitledui.com/icons&quot;&gt;Untitled&lt;/a&gt;, &lt;a href=&quot;https://lucide.dev/icons/&quot;&gt;Lucide&lt;/a&gt;, and &lt;a href=&quot;https://remixicon.com/&quot;&gt;Remix&lt;/a&gt;. For more comprehensive options, you can also refer to &lt;a href=&quot;https://react-icons.github.io/react-icons&quot;&gt;React Icons&lt;/a&gt; and &lt;a href=&quot;https://www.iconfont.cn/&quot;&gt;Iconfont&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Although there are so many existing icon libraries, if all you can do is choose icons based on their meanings… even someone who is not a designer can do that.&lt;/p&gt;
&lt;p&gt;We cannot “know what something is but not know why it is so.” So let’s start with icons and understand the theory behind design.&lt;/p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;In the field of UI design, icons play a central role. They transform textual information into visual symbols, simplify users’ understanding of content, and enhance the aesthetics of the interface. Especially in B-end design, although icons are not used as frequently or as complexly as in C-end design, in form-oriented page layouts, appropriate and suitable icon design can highlight the style and brand tone of a product or even the entire enterprise.&lt;/p&gt;
&lt;p&gt;The earliest form of visual communication was basically carried out through graphics. This is a prehistoric cave mural by North American Indians. Although murals are often said to be the predecessor of writing, I personally feel they are also very similar to icons. Icons are more like a universal language, crossing various languages such as Simplified Chinese, English, and Korean.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311112055138.png&quot; alt=&quot;Prehistoric cave mural by North American Indians&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-ins-and-outs-of-icons&quot;&gt;The Ins and Outs of Icons&lt;/h2&gt;
&lt;p&gt;First of all, an icon should clearly represent an object, operation, or idea. If users cannot see/understand its meaning, the icon becomes a flashy but impractical decoration, and may even hinder task completion.&lt;/p&gt;
&lt;h3 id=&quot;icon-size&quot;&gt;Icon Size&lt;/h3&gt;
&lt;p&gt;In fact, when using various component libraries, I discovered that the icons in these libraries are all based on a &lt;code&gt;24px&lt;/code&gt; size.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311121908492.png&quot; alt=&quot;Untitled UI Icons&quot;&gt;&lt;/p&gt;
&lt;p&gt;After searching through online materials, I found several possible reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The 24px size ensures that icons remain clear and readable across various screen resolutions and devices.&lt;/li&gt;
&lt;li&gt;The 24px size adapts to multiple grid systems (such as the &lt;a href=&quot;https://medium.com/built-to-adapt/intro-to-the-8-point-grid-system-d2573cde8632#.5h95d066j&quot;&gt;8-Point Grid System&lt;/a&gt;), making it easier for designers and developers to collaborate.&lt;/li&gt;
&lt;li&gt;24dp*24dp @x1 is the “perfect size” for icons recommended by the &lt;a href=&quot;https://m3.material.io/styles/icons/applying-icons#661945dc-8621-41c6-b88d-bdb7c945b4e7&quot;&gt;Material Design 3&lt;/a&gt; specification. At the same time, Material Design recommends a minimum touch target area of 48dp.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have not yet found supporting data behind this, so I will apply it based on convention:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311112329467.png&quot; alt=&quot;Icon sizes&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The recommended standard icon size is 24px*24px, usually with 2px of padding around it, but this is not absolute; the specific margin should be judged based on the Keyline. The corresponding recommended touch target size is 48px*48px.&lt;/li&gt;
&lt;li&gt;In desktop environments, the icon size can be adjusted to 20px*20px, with a recommended corresponding touch target area of 40px*40px.&lt;/li&gt;
&lt;li&gt;16px is usually used as the minimum size. At this size, icons should be designed to be more concise, and their decorative role will be greater than their functional role.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;“Rules are rigid, but people are flexible.” We can apply this guideline while also taking actual circumstances into account.&lt;/p&gt;
&lt;p&gt;px, dp, and pt are not equal; they each have their own conversion relationships. You can search online for specific information.&lt;/p&gt;
&lt;h3 id=&quot;icon-styles&quot;&gt;Icon Styles&lt;/h3&gt;
&lt;p&gt;Existing icon styles are varied and complex, and have long since gone beyond just linear and filled styles. Icons on the C-end are even more diverse. Here, we will temporarily analyze them based on basic styles.&lt;/p&gt;
&lt;p&gt;Below are the icon styles of Untitled Icons that I found: Line, Duocolor, Duotone, and Solid.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311122131581.png&quot; alt=&quot;Examples of various icon styles&quot;&gt;&lt;/p&gt;
&lt;p&gt;The four icon styles above can basically meet all the needs of B-end design.&lt;/p&gt;
&lt;p&gt;If divided in a more detailed way, Material Design further divides “linear icons” into: OutLine, Rounded, and Sharp.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311122203904.png&quot; alt=&quot;Material Design icon styles&quot;&gt;&lt;/p&gt;
&lt;p&gt;Combined with the stroke thickness and radius size of linear icons, this is basically enough to meet the customization needs for a company’s and product’s character.&lt;/p&gt;
&lt;p&gt;In addition, the style of an icon not only affects the overall interface style, but also affects the functionality and user experience of the icon:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complex icons are not suitable for overly small sizes (16px * 16px icons need a more concise linear style)&lt;/li&gt;
&lt;li&gt;Filled icons are generally easier to recognize than linear icons, but the specifics need to be judged based on the icon shape &lt;a href=&quot;https://cdr.lib.unc.edu/concern/masters_papers/6w924g35w&quot;&gt;source of conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;icon-design&quot;&gt;Icon Design&lt;/h2&gt;
&lt;p&gt;First of all, I want to say that I have never completely independently designed an icon. In the past, if there was no icon that met the requirements, I would basically take an icon with a similar meaning and then simply adjust its shape.&lt;/p&gt;
&lt;p&gt;So, taking this opportunity, I can also get some exposure to icon design. Ant Design’s &lt;a href=&quot;https://ant.design/docs/spec/icon-cn&quot;&gt;icon design guidelines&lt;/a&gt; have actually already covered a lot of related content, and I recommend learning about them first.&lt;/p&gt;
&lt;h3 id=&quot;using-keyline&quot;&gt;Using Keyline&lt;/h3&gt;
&lt;p&gt;Keyline is the foundation of the grid. By using these core shapes as guides, we can maintain consistent visual proportions among system icons.&lt;/p&gt;
&lt;p&gt;The image below is a Keyline grid drawn according to the specifications of the &lt;a href=&quot;https://m3.material.io/styles/icons/designing-icons#8754553f-29d9-4671-b163-123b8e7b8f1e&quot;&gt;Material Design 3 Icon design template&lt;/a&gt;. Based on this grid, you can basically draw all kinds of icons.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311131752115.png&quot; alt=&quot;Keyline demonstration&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311131752144.png&quot; alt=&quot;Complete collection of Keyline basic shapes&quot;&gt;&lt;/p&gt;
&lt;p&gt;In today’s world where design resources are everywhere, you can absolutely find icons on &lt;a href=&quot;https://www.iconfont.cn/&quot;&gt;iconfont&lt;/a&gt; whose style meets your needs. Then, based on the product’s style, simply redraw them. There are very few cases where a designer truly needs to create something completely from scratch.&lt;/p&gt;
&lt;p&gt;I also tried applying Keyline to draw several basic icons: home, card, lock, and file. These are also the four basic shapes on the Keyline.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311131902436.png&quot; alt=&quot;Icon design attempt&quot;&gt;&lt;/p&gt;
&lt;p&gt;During my own hands-on practice, I discovered a few small tips for icon design:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Try to use Boolean operations with “basic shapes” to construct icons.&lt;/li&gt;
&lt;li&gt;Keyline provides numerical balance, but numerical equality does not equal visual balance (for example: a play icon).&lt;/li&gt;
&lt;li&gt;When choosing a stroke alignment method, icon design mostly uses center alignment (Center) or inside alignment (Inside). Center-aligned icons will look larger under the same conditions.&lt;/li&gt;
&lt;li&gt;In Figma, using &lt;code&gt;Shift + Backspace&lt;/code&gt; to delete points and making use of the pen tool’s center point feature can greatly improve the efficiency of icon design.&lt;/li&gt;
&lt;li&gt;After icon design is complete, you can apply Flatten to integrate the icon lines together, making unified processing easier. There is no need to turn strokes into fills, because this is not conducive to later adjustment of line thickness.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;icon-application&quot;&gt;Icon Application&lt;/h2&gt;
&lt;p&gt;After icon design is complete, the next step is to consider how to effectively present these icons to the audience. As the saying goes, “even an ugly daughter-in-law eventually has to meet her parents-in-law,” which means that no matter what the design result is, there needs to be a process of presentation and feedback. This is not only to showcase the results, but also to obtain feedback, improve, and optimize the design.&lt;/p&gt;
&lt;h3 id=&quot;image-method&quot;&gt;Image Method&lt;/h3&gt;
&lt;p&gt;The most traditional display method is naturally images. Designers export icons in @1x and @2x according to states such as default, hover, pressed, active, and disabled, package them into a compressed file, and send it to the front-end developer. Or the designer exports the images and uploads them to Lanhu, and the front-end developer downloads the icons they need from “Lanhu” by themselves.&lt;/p&gt;
&lt;p&gt;If the front end needs icons in image format, I usually compress the icons through &lt;a href=&quot;https://tinypng.com/&quot;&gt;TinyPNG&lt;/a&gt; to make the icons occupy as little space as possible while ensuring their clarity. My most commonly used tool is &lt;a href=&quot;https://github.com/kyleduo/TinyPNG4Mac&quot;&gt;TinyPNG4Mac&lt;/a&gt;, which is quite convenient. Some front-end scaffolding tools also come with image compression features, and images occupying less than a certain value will also be converted into Base64 encoding for storage.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;data:image/png;base64,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&quot; alt=&quot;Image after Base64&quot;&gt;&lt;/p&gt;
&lt;p&gt;Tracing back to earlier technologies, we encounter “sprites,” which is a practice of integrating multiple icons into a single image with a transparent background. Designers place all icons on one image, and then the front end displays them on the web page based on each icon’s position and size. The advantage of this method is that it reduces the number of web requests. In fact, some websites today, when creating dynamic images, also often use a method similar to “sprites,” creating animation effects through continuous static frames.&lt;/p&gt;
&lt;p&gt;Images in JPG / PNG format are the safest display method. All browsers support them. Although they are bitmap formats and are not clear enough after being enlarged, image formats can basically solve most website needs, and the solutions are mature enough.&lt;/p&gt;
&lt;p&gt;Existing CDN service providers and/or some frameworks, such as Next.js, can intelligently identify whether a browser supports WEBP or AVIF formats. These two image formats can provide higher compression rates while maintaining higher clarity.&lt;/p&gt;
&lt;h3 id=&quot;font-method&quot;&gt;Font Method&lt;/h3&gt;
&lt;p&gt;As mentioned earlier, icons need to be exported by designers separately according to various states and then provided to developers. If the overall color scheme of the official website is adjusted, and all icons need to be re-exported, this might be a disaster.&lt;/p&gt;
&lt;p&gt;Perhaps because of this or other reasons, the icon font solution appeared. The most commonly used icon font platform in China is iconfont. This requires designers to outline the icons and then upload them to iconfont. The front end can then reference icons by icon name and conveniently adjust their color and size.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311132044218.png&quot; alt=&quot;iconfont icons&quot;&gt;&lt;/p&gt;
&lt;p&gt;Until the time I wrote this article, font icons were still quite widely used. They are convenient to call and take up less space. However, the following main shortcomings affect the use of font icons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Icons need to be processed one by one by designers. For some icons, outlining alone is not enough; you also need to use the Figma &lt;a href=&quot;https://www.figma.com/community/plugin/771155994770327940/fill-rule-editor&quot;&gt;Fill Rule Editor&lt;/a&gt; plugin to adjust the icon fill mode to “Non-zero rule” so that it can display normally in iconfont. For the specific solution, you can &lt;a href=&quot;https://zhuanlan.zhihu.com/p/137298478&quot;&gt;refer to&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;If an icon disappears when adjusting the fill style through Fill Rule Editor, you can try copying the icon as-is into illustrator / Sketch, and then copying it back to solve the problem. (kidding)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Font icons do not support multicolor icons well enough, and the degree of customization is not high enough.&lt;/li&gt;
&lt;li&gt;Font icons may also fail to render properly and require special handling (I once naively thought it was a problem with my browser).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I can only evaluate font-style icons from a designer’s perspective, because I have never used this method during development. Recommended article: &lt;a href=&quot;https://getdevdone.com/blog/icon-fonts-vs-svg-icons.html&quot;&gt;Icon Fonts vs SVG Icons: What Works Best for You?&lt;/a&gt;, which compares the pros and cons of fonts and SVG in relatively great detail.&lt;/p&gt;
&lt;h3 id=&quot;svg-method&quot;&gt;SVG Method&lt;/h3&gt;
&lt;p&gt;SVG icons not only inherit the main advantages of font icons, but also solve some limitations of font icons. Their main advantages include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support for each icon having multiple colors, including gradients.&lt;/li&gt;
&lt;li&gt;Support for more complex graphics and shapes, without being limited to the basic shapes of fonts.&lt;/li&gt;
&lt;li&gt;Support for direct embedding in HTML code, making it convenient to edit and control icons.&lt;/li&gt;
&lt;li&gt;Support for CSS and JavaScript animations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;iconfont also does not only support the font icon method. The official platform also supported referencing SVG icons through the symbol method several years ago.&lt;/p&gt;
&lt;p&gt;We can briefly understand SVG through an example. The following is a “home” icon in SVG format exported from Figma, and its code is as follows:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;svg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;24&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  height&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;24&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  viewBox&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  fill&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  xmlns&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    d&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;M15 21V13C15 12.4477 14.5523 12 14 12H10C9.44772 12 9 12.4477 9 13V21M15 21H20C20.5523 21 21 20.5523 21 20V9.48908C21 9.18049 20.8575 8.88919 20.6139 8.69973L12.6139 2.47751C12.2528 2.19665 11.7472 2.19665 11.3861 2.47751L3.38606 8.69973C3.14247 8.88919 3 9.18049 3 9.48908V20C3 20.5523 3.44772 21 4 21H9M15 21H9&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    stroke&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;black&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    stroke-width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This SVG contains several key attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;path:&lt;/strong&gt; The path element defines the outline of the graphic, describing points on the path through a series of commands and parameters to form the graphic. In this example, these commands and parameters together form the shape of a house.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fill:&lt;/strong&gt; The fill attribute is used to set the internal color of the graphic. It can be a hexadecimal color code (for example, #FF0000), or a common color name (for example, &lt;code&gt;black&lt;/code&gt;). none means there is no fill, while currentColor uses the value of the color attribute of the element or its parent element.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stroke:&lt;/strong&gt; The stroke attribute is used to define the color of the graphic outline, and it is used in a way similar to fill.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stroke-width:&lt;/strong&gt; The stroke-width attribute specifies the width of the stroke.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If an SVG contains multiple paths, there will be multiple path elements. Each path can have independent fill and stroke attributes to control its own fill and stroke styles. If a certain part of the graphic does not need a stroke, its stroke attribute can be set to none.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311142114226.png&quot; alt=&quot;Icon display on the Remix platform&quot;&gt;&lt;/p&gt;
&lt;p&gt;As shown in the image above, what we see is a preview of an icon and its corresponding code. If you look closely, you will notice that the code does not specify the icon’s width, height, fill color, or stroke color.&lt;/p&gt;
&lt;p&gt;This approach is intended to provide SVG icon code that is as concise as possible, containing only core information such as the icon paths. The purpose of this design is to allow frontend developers to control the color and size of icons more flexibly. For example, if the SVG icon code includes a &lt;code&gt;fill&lt;/code&gt; attribute by default, frontend developers may find that they cannot change its color when applying these icons. To solve this problem, one approach is to remove the &lt;code&gt;fill&lt;/code&gt; attribute or set &lt;code&gt;fill&lt;/code&gt; to &lt;code&gt;currentColor&lt;/code&gt;, and then define the color through CSS. Another approach is to specify the desired color directly in the &lt;code&gt;fill&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;It is not recommended to use hacky methods such as filters to adjust colors, because this approach can be both complex and imprecise.&lt;/p&gt;
&lt;p&gt;Similarly, SVG icons support compression, and tools such as &lt;a href=&quot;https://github.com/svg/svgo&quot;&gt;SVGO&lt;/a&gt; can be used to clean up redundant parts of the SVG code.&lt;/p&gt;
&lt;p&gt;For frontend practices when using SVGs, you can refer to the examples below, which use Tailwindcss and Lucide Icons:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Direct Usage Example&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This code example demonstrates how to use simple classes to control an icon’s size, color, and display effect in dark mode. The operation is very intuitive and convenient:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Container&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;svg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;h-5 w-5 stroke-blue-500 hover:stroke-blue-600 dark:stroke-blue-400 dark:hover:stroke-blue-500&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    viewBox&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    xmlns&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;M15 21V13C15 12.4477 14.5523 12 14 12H10C9.44772 12 9 12.4477 9 13V21M15 21H20C20.5523 21 21 20.5523 21 20V9.48908C21 9.18049 20.8575 8.88919 20.6139 8.69973L12.6139 2.47751C12.2528 2.19665 11.7472 2.19665 11.3861 2.47751L3.38606 8.69973C3.14247 8.88919 3 9.18049 3 9.48908V20C3 20.5523 3.44772 21 4 21H9M15 21H9&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Package Usage Example&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The following example shows how to apply icons through a package. This method is also simple: you only need to import the required icons and set the styles:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { Home } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;lucide-react&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; App&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Home&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;h-5 w-5 stroke-blue-500 hover:stroke-blue-600 dark:stroke-blue-400 dark:hover:stroke-blue-500&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; App;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To deliver design icons to the development team, you can choose iconfont or use the &lt;a href=&quot;https://github.com/leadream/figma-icon-automation&quot;&gt;figma-icon-automation&lt;/a&gt; plugin to package the icons as an npm library, making it convenient for the development team to use them via an installed package. This method only needs to be configured once and is highly efficient.&lt;/p&gt;
&lt;p&gt;Although the way SVG icons are used involves code, this section is still written for designers. The purpose is simply to tell everyone that SVGs do not have issues with being difficult to adjust in color, nor are they difficult to use. Actively encourage developers to follow up~&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Behind this tiny icon lies rich and complex content, and this article is merely an initial exploration of this vast topic. As a beginner, mistakes in my article are inevitable, and I sincerely welcome readers to offer valuable feedback and corrections.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://m3.material.io/&quot;&gt;Material Design 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://getdevdone.com/blog/icon-fonts-vs-svg-icons.html&quot;&gt;Icon Fonts vs SVG Icons: What Works Best for You?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.yarsalabs.com/guide-to-design-icons/&quot;&gt;Design Icons from Scratch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/built-to-adapt/intro-to-the-8-point-grid-system-d2573cde8632#.5h95d066j&quot;&gt;Intro to The 8-Point Grid System&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines&quot;&gt;Apple - Human Interface Guidelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ant.design/index-cn/&quot;&gt;Ant Design 5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lucide.dev/&quot;&gt;Lucide Icons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://remixicon.com/&quot;&gt;Remix Icons&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Notes on Calling the Notion API with Next.js</title><link>https://ro.yikzero.com/en/blog/brogbuild-1</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/brogbuild-1</guid><description>Development notes on building a blog with Next.js and the Notion API, covering lessons learned from data fetching, image optimization, style rendering, and more.</description><pubDate>Wed, 04 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;pitfall-collection&quot;&gt;Pitfall Collection&lt;/h2&gt;
&lt;p&gt;The pitfalls come partly from Next.js and partly from the Notion API. Of course, I myself am the biggest pitfall.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Next.js server-side rendering&lt;/li&gt;
&lt;li&gt;Notion API data filtering&lt;/li&gt;
&lt;li&gt;Notion Block data rendering&lt;/li&gt;
&lt;li&gt;Notion temporary image expiration&lt;/li&gt;
&lt;li&gt;Using the Next.js Image component&lt;/li&gt;
&lt;li&gt;Rendering styles for Notion article pages&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The above is basically all the pitfalls I ran into. Before introducing my experience dealing with them, let me briefly talk about the original intention of the project.&lt;/p&gt;
&lt;h2 id=&quot;original-intention&quot;&gt;Original Intention&lt;/h2&gt;
&lt;p&gt;At first, it was because I had learned frontend development and then wanted to understand backend development. Initially, I made a full-stack text-to-speech project based on edge-tts called &lt;a href=&quot;https://rotts.yikzero.com/&quot;&gt;Rotts&lt;/a&gt; (frontend React + backend Flask). But this project was very simple: the backend basically just requested the API, obtained the voice list, then passed information such as voice, pitch, and text to edge-tts, waited for the audio to return, and then played/downloaded it. I felt like the backend part was over before I had really experienced much of it.&lt;/p&gt;
&lt;p&gt;So I wanted to find another project to continue learning backend development. The first thing that came to mind was Node.js. During my research, I saw many people recommending Next.js, which is also based on Node.js. So it clicked immediately. I also wanted to simply reinvent the wheel and build a blog system that uses Notion as a CMS. Visually, everything would be kept simple to reduce burden, with a focus on content rendering.&lt;/p&gt;
&lt;p&gt;Originally, I thought that with ChatGPT, I could just ask it whenever I didn’t know something. As it turned out, the Next.js App Router was too new, and it wasn’t even in its knowledge base. Every time I asked it related questions, the answers were not accurate enough.&lt;/p&gt;
&lt;h2 id=&quot;project-information&quot;&gt;Project Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Name: &lt;strong&gt;Brog&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Introduction: A minimalist blog system built with Next.js, using Notion as the CMS&lt;/li&gt;
&lt;li&gt;Tech stack: React + Next.js + Typescript + TailwindCSS&lt;/li&gt;
&lt;li&gt;Github: &lt;a href=&quot;https://github.com/yikZero/Brog&quot;&gt;https://github.com/yikZero/Brog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;nextjs-pitfalls&quot;&gt;Next.js Pitfalls&lt;/h2&gt;
&lt;h3 id=&quot;duplicate-requests&quot;&gt;Duplicate Requests&lt;/h3&gt;
&lt;p&gt;Actually, when I first started fetching data, I had doubts. I fetch here, then fetch there. It’s all the same data—wouldn’t that cause multiple requests and be very wasteful?&lt;/p&gt;
&lt;p&gt;Because of this, I kept trying to separate function logic from requests and just pass several props around… But in fact, at least in React, you don’t need to worry about this.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Passing data between a parent layout and its children is not possible. However, you can fetch the same data in a route more than once, and React will automatically dedupe the requests without affecting performance.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the link and parameters you fetch remain unchanged, then even if you make multiple requests, React will help merge them into a single request. So there’s no need to keep forwarding props between components. Just request the same data multiple times; it’s not a problem. React will merge the requests on its own and won’t repeatedly make duplicate requests that affect the network.&lt;/p&gt;
&lt;h3 id=&quot;server-components&quot;&gt;Server Components&lt;/h3&gt;
&lt;p&gt;What counts as a server component? Anything placed under the &lt;code&gt;app/&lt;/code&gt; directory should belong to server components. When I wasn’t sure whether something was a server component, I threw all the &lt;code&gt;components&lt;/code&gt; and &lt;code&gt;lib&lt;/code&gt; directories I used into &lt;code&gt;app/&lt;/code&gt;… just for server components.&lt;/p&gt;
&lt;p&gt;If you want it to become a client component, add &lt;code&gt;use client&lt;/code&gt; at the top, and it will be recognized as a client component. The official Next.js recommendation is to use server components whenever possible.&lt;/p&gt;
&lt;h3 id=&quot;nextimage-usage&quot;&gt;next/image Usage&lt;/h3&gt;
&lt;p&gt;Because of &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#lcp-image-optimization&quot;&gt;LCP performance&lt;/a&gt;, Next.js officially optimizes the usage of the img element and recommends using the official Image component.&lt;/p&gt;
&lt;p&gt;For specific usage, refer to &lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/optimizing/images&quot;&gt;Image Optimization&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I thought it would be simple—just replace the name. But it requires me to provide explicit height and width. For images I fetch dynamically, how would I know the image height and width? So I used a bit of a workaround… also a solution from &lt;a href=&quot;https://stackoverflow.com/questions/65169431/how-to-set-the-next-image-component-to-100-height&quot;&gt;stackoverflow&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;figure&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;relative&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    src&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;{src}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;{0}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    height&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;{0}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    sizes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;100vw&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    className&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;w-full h-auto&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;figure&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;notion-client-pitfalls&quot;&gt;Notion-Client Pitfalls&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311050659712.png&quot; alt=&quot;Notion API&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;data-fetching&quot;&gt;Data Fetching&lt;/h3&gt;
&lt;p&gt;Notion officially provides &lt;a href=&quot;https://www.npmjs.com/package/@notionhq/client&quot;&gt;@notionhq/client&lt;/a&gt;, and react-notion-x also has a &lt;a href=&quot;https://www.npmjs.com/package/notion-client&quot;&gt;notion-client&lt;/a&gt;. I learned about notion-client first, so after finishing the styles, I tried integrating the third-party implementation.&lt;/p&gt;
&lt;p&gt;How should I put it? notion-client is also quite good. By providing a public &lt;code&gt;NOTION_PAGE_ID&lt;/code&gt; environment variable, you can fetch the data. But what you get is the full dataset, and there is no way to filter by field name like with the official API, so organizing the data becomes much more troublesome. For a beginner like me, this was way too much trouble… After all, the data obtained from Notion is nested layer by layer, and every block has its own ID, which made me completely dizzy.&lt;/p&gt;
&lt;p&gt;In the end, I still referred to the official API usage in &lt;a href=&quot;https://github.com/samuelkraft/notion-blog-nextjs&quot;&gt;notion-blog-nextjs&lt;/a&gt; to obtain the corresponding data.&lt;/p&gt;
&lt;h3 id=&quot;image-expiration&quot;&gt;Image Expiration&lt;/h3&gt;
&lt;p&gt;The image links in the data officially provided by Notion are temporary Amazon links, which expire after one hour. This causes the images to break.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vercel Cron:&lt;/strong&gt; Many people also choose to use the Vercel cron feature to rebuild and render every hour. But I think this wastes too many resources and is too environmentally unfriendly (:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Converting temporary addresses into permanent addresses:&lt;/strong&gt; For this solution, you can refer to &lt;a href=&quot;https://github.com/tangly1024/NotionNext/pull/524/files&quot;&gt;fix: solve linked img crash #524&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Uploading to Upyun via PicGo:&lt;/strong&gt; Because I have migrated data before, if all Markdown images use relative paths, it is simply a disaster. So now I choose to upload them to Upyun via PicGo and return my CDN address. If I need to migrate, at least the images won’t be lost.&lt;/p&gt;
</content:encoded></item><item><title>HomeLab Environment Setup</title><link>https://ro.yikzero.com/en/blog/homelab-config</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/homelab-config</guid><description>Documenting the configuration of the optical modem, R4S software router, and Linksys hardware router</description><pubDate>Tue, 12 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;background-and-purpose&quot;&gt;Background and Purpose&lt;/h2&gt;
&lt;p&gt;Home network configuration typically involves an optical modem as the entry point for the ISP connection, an R4S soft router (NanoPi R4S based on OpenWrt), and a Linksys hardware router for managing the internal network. The goal of this article is to optimize network performance through bridge mode, reduce NAT conflicts, and use the R4S and Linksys to achieve efficient network management and wireless coverage.&lt;/p&gt;
&lt;h2 id=&quot;topology-diagram&quot;&gt;Topology Diagram&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311052100776.png&quot; alt=&quot;Home Network Topology Diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;The optical modem in bridge mode connects to the R4S WAN port. The R4S serves as the main router and handles PPPoE. The Linksys connects its WAN port to the R4S LAN port and is set to bridge mode as a wireless access point.&lt;/p&gt;
&lt;h2 id=&quot;optical-modem-bridge-configuration&quot;&gt;Optical Modem Bridge Configuration&lt;/h2&gt;
&lt;h3 id=&quot;reasons-for-bridging&quot;&gt;Reasons for Bridging&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Prevent duplicate NAT forwarding and avoid double NAT issues that affect certain applications.&lt;/li&gt;
&lt;li&gt;Reduce the network load on the optical modem; the optical modem is only responsible for optical/electrical signal conversion and does not perform routing.&lt;/li&gt;
&lt;li&gt;The optical modem has limited functionality; after bridging, the R4S or Linksys can provide more powerful routing functions.&lt;/li&gt;
&lt;li&gt;The optical modem’s WiFi signal is weak; after bridging, Linksys can provide better wireless coverage.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;specific-configuration-steps&quot;&gt;Specific Configuration Steps&lt;/h3&gt;
&lt;h4 id=&quot;hangzhou-unicom-optical-modem&quot;&gt;Hangzhou Unicom Optical Modem&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Login information&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Address: &lt;a href=&quot;http://192.168.1.1/cu.html&quot;&gt;http://192.168.1.1/cu.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Account: &lt;code&gt;CUAdmin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Password: &lt;code&gt;CUAdmin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PPPoE settings&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Account: Usually starts with 0571 (Hangzhou area code); check the backend to obtain it.&lt;/li&gt;
&lt;li&gt;Password: The last six digits of the account.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bridge mode settings&lt;/strong&gt;: Find the WAN settings through the login interface, change the mode to bridge, save, and reboot.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;hangzhou-mobile-optical-modem&quot;&gt;Hangzhou Mobile Optical Modem&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Login information&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Address: &lt;a href=&quot;http://192.168.1.1/&quot;&gt;http://192.168.1.1/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Account: &lt;code&gt;CMCCAdmin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Password: &lt;code&gt;aDm8H%MdA&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PPPoE settings&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Account: Starts with &lt;code&gt;hz&lt;/code&gt;, followed by the initial letters of the district (for example, Binjiang District is &lt;code&gt;hzbj&lt;/code&gt;), then followed by a letter from a-z according to the broadband activation order (for example, the first line is &lt;code&gt;hzbja&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Password: Try &lt;code&gt;000000&lt;/code&gt;, &lt;code&gt;888888&lt;/code&gt;, &lt;code&gt;123456&lt;/code&gt;, the last six digits of the account, or the last six/eight digits of the ID card number.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bridge mode settings&lt;/strong&gt;: After logging in, change WAN to bridge mode, save, and reboot.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;using-pppoe-in-bridge-mode&quot;&gt;Using PPPOE in Bridge Mode&lt;/h3&gt;
&lt;p&gt;After bridging, the R4S or Linksys needs to dial up using the PPPoE account and password to ensure the Internet connection works properly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;linksys-bridge-mode&quot;&gt;Linksys Bridge Mode&lt;/h2&gt;
&lt;h3 id=&quot;initial-configuration&quot;&gt;Initial Configuration&lt;/h3&gt;
&lt;p&gt;Regardless of whether you later want to change it to “Bridge Mode” or “Automatic Configuration - DHCP”, you first need to perform the initial setup. Follow the “Configuration Wizard” to set the router password normally and configure the WiFi password. Without any additional configuration, and as long as the network cable is fine, you should be able to access the Internet normally.&lt;/p&gt;
&lt;h3 id=&quot;advanced-settings&quot;&gt;Advanced Settings&lt;/h3&gt;
&lt;h4 id=&quot;bridge-mode&quot;&gt;Bridge Mode&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Connection&lt;/strong&gt;: Connect the R4S LAN port to the Linksys WAN port (Internet port).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Login interface&lt;/strong&gt;: Access the Linksys web interface (default 192.168.1.1) and skip the initial setup prompts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set bridge mode&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Go to &lt;code&gt;Configuration &amp;gt; Connectivity &amp;gt; WAN Setup&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Bridge Mode&lt;/code&gt; to use the router as a wireless access point.&lt;/li&gt;
&lt;li&gt;Select “Obtain an IP address automatically” or manually specify an IP (such as 192.168.31.2, with the default gateway as 192.168.31.1).&lt;/li&gt;
&lt;li&gt;Save and reboot the router.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After setup is complete, click “Save” to reboot the router.&lt;/p&gt;
&lt;h4 id=&quot;additional-optimizations&quot;&gt;Additional Optimizations&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wireless Network - Advanced Settings - Roaming Assistant&lt;/strong&gt;: Select Off to improve stability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wireless Network - Advanced Settings - Region&lt;/strong&gt;: Select Australia, which may enhance signal strength, but pay attention to compliance; it may not be applicable in all regions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If using Homeassistant&lt;/strong&gt;: It is recommended to disable WiFi dual-band merging mode to avoid compatibility issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;This article provides detailed steps for optical modem bridge mode, R4S PPPOE configuration, and Linksys bridge mode to ensure efficient operation of the home network.&lt;/p&gt;
</content:encoded></item><item><title>Installing Plex Server on R4S: Notes</title><link>https://ro.yikzero.com/en/blog/plex-server-1</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/plex-server-1</guid><description>A Complete Record of Installing Plex Media Server on an R4S Software Router, Solving Issues Such as Architecture Compatibility, Remote Access, and Port Forwarding</description><pubDate>Sat, 18 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;environment-configuration&quot;&gt;Environment Configuration&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Model: FriendlyElec NanoPi R4S&lt;/li&gt;
&lt;li&gt;Firmware version: OpenWrt R22.3.13 (2022-06-22)&lt;/li&gt;
&lt;li&gt;Architecture: ARMv8 Processor x 6&lt;/li&gt;
&lt;li&gt;Environment: Google Direct&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;preparations&quot;&gt;Preparations&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Prepare a network that can access Google on your own&lt;/li&gt;
&lt;li&gt;Install an OpenWrt system with docker&lt;/li&gt;
&lt;li&gt;Expand the storage card capacity for docker&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;pitfall-notes&quot;&gt;Pitfall Notes&lt;/h2&gt;
&lt;h3 id=&quot;architecture-issue&quot;&gt;Architecture Issue&lt;/h3&gt;
&lt;p&gt;Most existing tutorials for setting up Plex with docker are based on the X86 architecture. If you set it up using the docker image from &lt;a href=&quot;https://hub.docker.com/r/plexinc/pms-docker/&quot;&gt;plexinc/pms-docker&lt;/a&gt;, an error will appear in the docker container logs: &lt;code&gt;stderr: exec /init: exec format error&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is because the docker container you installed does not match your system architecture.&lt;/p&gt;
&lt;p&gt;So I found the ARM-architecture PLEX from &lt;a href=&quot;https://hub.docker.com/r/linuxserver/plex&quot;&gt;linuxserver/plex&lt;/a&gt; and started the installation:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-d &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;--name &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;plex&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-p &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;32400:32400/tcp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-p &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;3005:3005/tcp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-p &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;8324:8324/tcp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-p &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;32469:32469/tcp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-p &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;1901:1900/udp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-p &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;32410:32410/udp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-p &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;32412:32412/udp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-p &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;32413:32413/udp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-p &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;32414:32414/udp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-e &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;TZ=Asia/Shanghai&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-e &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;PLEX_CLAIM=claim-xfczZtmEH5QPvZQc1HsF&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-e &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;ADVERTISE_IP=&quot;http://192.168.31.1:32400/&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-h &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Zero-R4s&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-v &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;/myplex/plex/database:/config&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-v &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;/myplex/transcode/temp:/transcode&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;-v &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;/mnt/sda/Media:/data&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;--restart &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;unless-stopped&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;lscr.io/linuxserver/plex:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The specific tutorial can also be found at &lt;a href=&quot;https://hub.docker.com/r/linuxserver/plex&quot;&gt;linuxserver/plex&lt;/a&gt;. Just adjust it according to your own situation.&lt;/p&gt;
&lt;h2 id=&quot;plex-has-no-initial-setup-page&quot;&gt;PLEX Has No Initial Setup Page&lt;/h2&gt;
&lt;p&gt;This means that when entering the Plex application interface for the first time, you may encounter a “server not found” issue because remote access has not been enabled. In this case, you can use the following two methods to solve it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. SSH Port Forwarding&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You need to open the PowerShell tool on your local computer, then run the command above to forward the Plex port of the remote server (default is 32400) to a port on your local computer (for example, 8888). After the port forwarding succeeds, you can access &lt;code&gt;localhost:8888/web&lt;/code&gt; in your local computer’s browser to open the Plex application interface.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Direct Connection&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If remote access has already been allowed on the remote server, you can try directly accessing the remote server’s IP address and Plex port in the browser (for example, &lt;a href=&quot;http://13.124.4.66:32400/web&quot;&gt;http://13.124.4.66:32400/web&lt;/a&gt;) to open the Plex application interface.&lt;/p&gt;
&lt;h3 id=&quot;solution&quot;&gt;Solution&lt;/h3&gt;
&lt;p&gt;On macOS, you can use the iTerm application to set up SSH port forwarding. The specific steps are as follows:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Connect to the Remote Server&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Enter the following command to connect to the remote server, where username is the username on the remote server, and remote_server_ip is the IP address of the remote server:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ssh -L 8888:localhost:32400 username@remote_server_ip&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will forward Plex’s 32400 port on the remote server to port 8888 on the local computer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Enter the Password on the Remote Server and Verify&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now, you can open the browser on your local computer and enter &lt;code&gt;http://localhost:8888/web&lt;/code&gt; to access the Plex interface on the remote server.&lt;/p&gt;
&lt;h3 id=&quot;another-error&quot;&gt;Another Error&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    WARNING:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; REMOTE&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; HOST&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; IDENTIFICATION&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; HAS&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CHANGED!&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;     @&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;IT&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; IS&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; POSSIBLE&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; THAT&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; SOMEONE&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; IS&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; DOING&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; SOMETHING&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; NASTY!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Someone&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; could&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; be&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; eavesdropping&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; on&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; you&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; right&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; now&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (man-in-the-middle &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;attack&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;It&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; also&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; possible&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; that&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; a&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; host&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; has&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; just&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; been&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; changed.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;The&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; fingerprint&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ED25519&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; sent&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; by&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; remote&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; host&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; is&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;SHA256:OC9qmnB9nQtFN/OiPH7jGEoKb/zbX4NghGv/glu5dwQ.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Please&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; contact&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; your&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; system&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; administrator.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; correct&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; host&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /Users/yikzero/.ssh/known_hosts&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; rid&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; message.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Offending&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ED25519&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /Users/yikzero/.ssh/known_hosts:1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Host&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 192.168.31.1&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; has&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; changed&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; and&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; you&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; have&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; requested&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; strict&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; checking.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Host&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; verification&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; failed.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This error message indicates that the key of the remote server previously connected to has changed. This may be because the server underwent a system update or the administrator changed the server’s key. To protect information security, the SSH client checks and verifies the remote server’s key. If the key has changed, this error message will appear.&lt;/p&gt;
&lt;p&gt;Update the remote server key data on the local computer to resolve the issue. The steps are as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Delete the old key:&lt;/strong&gt;
Run &lt;code&gt;ssh-keygen -R &amp;lt;remote_server_ip&amp;gt;&lt;/code&gt; to delete the old key data on the local computer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Connect to the remote server:&lt;/strong&gt;
Connect using &lt;code&gt;ssh username@remote_server_ip&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The system will prompt “Are you sure you want to continue connecting (yes/no)?”. Enter yes and press Enter.&lt;/li&gt;
&lt;li&gt;Enter the remote server password to verify.&lt;/li&gt;
&lt;li&gt;After connecting, run the command again to set up SSH port forwarding.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After completion, you can access the PLEX server.&lt;/p&gt;
</content:encoded></item><item><title>Hugo Setup Notes: From Zero to One</title><link>https://ro.yikzero.com/en/blog/hugo-build-1</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/hugo-build-1</guid><description>The Complete Process of Building a Hugo Blog from Scratch, Covering Installation and Configuration, Page Structure, and CSS Layout Tips</description><pubDate>Wed, 23 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;hugo-installation&quot;&gt;Hugo Installation&lt;/h2&gt;
&lt;h3 id=&quot;official-documentation-tutorial&quot;&gt;Official Documentation Tutorial&lt;/h3&gt;
&lt;p&gt;The official website provides detailed installation tutorials for different systems: &lt;a href=&quot;https://gohugo.io/installation/&quot;&gt;https://gohugo.io/installation/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;brew&quot;&gt;Brew&lt;/h3&gt;
&lt;p&gt;If you are a Mac user, you can use the &lt;code&gt;brew&lt;/code&gt; command to install it.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; hugo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This installation method may not install the latest version of hugo. If you want to use the latest version of hugo, please install it in another way.&lt;/p&gt;
&lt;h2 id=&quot;configuration-file-format&quot;&gt;Configuration File Format&lt;/h2&gt;
&lt;p&gt;As a beginner, which configuration file format to choose—yaml, toml, or json—became my first question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TOML&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Case-sensitive&lt;/li&gt;
&lt;li&gt;Files can only contain UTF-8 encoded Unicode characters&lt;/li&gt;
&lt;li&gt;Not sensitive to indentation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;YAML&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The file starts with ’ - ’, marking the beginning of the document&lt;/li&gt;
&lt;li&gt;Key-value pairs are separated by colons&lt;/li&gt;
&lt;li&gt;Lists start with hyphens&lt;/li&gt;
&lt;li&gt;Uses indentation with one or more spaces to describe nested collections&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;JSON&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Data is stored in name/value pairs&lt;/li&gt;
&lt;li&gt;Records are separated by commas. Trailing commas without following properties are not allowed&lt;/li&gt;
&lt;li&gt;Property names and strings are wrapped in double quotation marks&lt;/li&gt;
&lt;li&gt;Single quotation marks are not allowed&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;string-differences&quot;&gt;String Differences&lt;/h3&gt;
&lt;p&gt;Strings are supported in any format, but JSON does not support multiline strings and also does not support comments.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# TOML&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;key = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;String Value&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;multiline = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&quot;&quot;\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       The quick brown \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       fox jumps over \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       the lazy dog.\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# YAML&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; : &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;String Value&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;multilinePreservedLinebreaks&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  L1 - The quick brown&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  L2 - fox jumps over&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  L3 - the lazy dog.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;multilineReplaceLinebreaksWithWhitespace&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  This sentence ist just too long to keep it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  on the same line.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// JSON&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;key&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;String Value&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because of the relatively troublesome comment syntax of &lt;code&gt;JSON&lt;/code&gt; and the strict indentation requirements of &lt;code&gt;YAML&lt;/code&gt;, I ultimately chose &lt;code&gt;TOML&lt;/code&gt; as the configuration file format. I can also indent freely, making it easier for me to read.&lt;/p&gt;
&lt;h2 id=&quot;scattered-knowledge-points&quot;&gt;Scattered Knowledge Points&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;This symbol &lt;code&gt;:=&lt;/code&gt; is an assignment operator in the Python language (mainly called the walrus operator). The walrus operator compresses our code to make it shorter.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; a; &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;//定义&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;//赋值&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;code&gt;layout/baseof.html&lt;/code&gt; is the foundation of all pages&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;layout/partials&lt;/code&gt; folder can contain partial sections such as &lt;code&gt;header&lt;/code&gt;, &lt;code&gt;footer&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Steps to add a new page:&lt;/li&gt;
&lt;li&gt;You need to add a &lt;code&gt;.md&lt;/code&gt; file in &lt;code&gt;content&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add a page with the same name in &lt;code&gt;layouts/_default&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Code for activating the &lt;code&gt;active&lt;/code&gt; state of the menu:&lt;/li&gt;
&lt;li&gt;According to the method in the official website’s &lt;a href=&quot;https://gohugo.io/templates/menu-templates/#readout&quot;&gt;instructions&lt;/a&gt;, the &lt;code&gt;active&lt;/code&gt; state cannot be added successfully, so I looked for another way to determine &lt;code&gt;active&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{{ range .Site.Menus.main }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {{&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; $menu_item_url&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;cond&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (strings.HasSuffix .&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;URL&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;URL&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (printf &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;%s/&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;URL&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) ) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; absLangURL }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {{&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; $page_url&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $currentPage.Permalink &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; absLangURL }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {{- if eq $menu_item_url $page_url }} &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;active&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {{- end }} &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;href&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;{{ .URL }}&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                {{ .Pre }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{{ .Name }}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {{ end }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;A small knowledge point about the &lt;code&gt;position&lt;/code&gt; function in css!&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.outer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  position&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;relative&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.inner&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  position&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;absolute&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  top&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  left&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;&lt;code&gt;Shortcode&lt;/code&gt; can simplify code and write some methods. It is similar to a &lt;code&gt;function&lt;/code&gt;. For specific writing methods, refer to: &lt;a href=&quot;https://www.letswrite.tw/hugo-basic/&quot;&gt;Hugo-basic&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://martin-ueding.de/posts/json-vs-yaml-vs-toml/&quot;&gt;JSON vs. YAML vs. TOML&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/sunsky303/p/9208848.html&quot;&gt;In-depth Comparison of TOML, JSON, and YAML&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Automated Push Notifications with IFTTT</title><link>https://ro.yikzero.com/en/blog/ifttt-1</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/ifttt-1</guid><description>Using IFTTT to Push School Notifications to WeChat, Email, and More</description><pubDate>Thu, 14 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;preface&quot;&gt;Preface&lt;/h2&gt;
&lt;p&gt;I previously published an article about RSS subscriptions for the school’s official website. With an RSS subscription URL, we can push RSS updates to our “RSS client” to read newly updated articles on the website. When I wrote that article, I only knew how to set up update notifications for the school’s official website on my own “RssBot” on &lt;a href=&quot;https://telegram.org/&quot;&gt;Telegram&lt;/a&gt;. I couldn’t push them directly to WeChat, so now I’m writing an article that can achieve pushing to WeChat.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081017596.jpeg&quot; alt=&quot;IFTTT result preview&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;getting-the-rss-subscription-url&quot;&gt;Getting the RSS Subscription URL&lt;/h2&gt;
&lt;p&gt;💡 First, we need to be clear that not every website provides RSS subscriptions. Only some websites provide RSS. For websites without RSS subscriptions, you can only create the feed yourself or find an RSS feed created by others. Here I recommend a feed-sharing website: &lt;strong&gt;&lt;a href=&quot;https://docs.rsshub.app/&quot;&gt;RssHub&lt;/a&gt;&lt;/strong&gt; Author: &lt;strong&gt;&lt;a href=&quot;https://github.com/DIYgod&quot;&gt;DIYgod&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will use our school’s subscription as a demonstration:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;1. 在RssHub中中找到我们学校的订阅地址： #路径：路由-大学通知-浙江工业大学&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;https://docs.rsshub.app/university.html#zhe-jiang-gong-ye-da-xue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;2.根据需要修改参数，这里我们需要公告栏（板块ID：1）的通知更新，因此我们的订阅链接为：&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;https://rsshub.app/zjut/1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;#如果需要每周会议的RSS订阅地址，那么将1改成2，即：https://rsshub.app/zjut/2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first step is complete. The RSS feed URL we obtained is: &lt;a href=&quot;https://rsshub.app/zjut/1&quot;&gt;https://rsshub.app/zjut/1&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;push-school-notification-updates-to-email&quot;&gt;Push School Notification Updates to Email&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Open the IFTTT website and register an account. &lt;strong&gt;(You can log in with a Google account in one click)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Click Create in the upper right corner to create a project.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;IFTTT is the abbreviation of “if this then that.” Simply put, it means if this happens, then that happens. If this operation is completed, it will automatically complete that operation. For example: a teacup is placed under a water dispenser, and the water dispenser detects the teacup — this is this. The water flows down by itself and fills the teacup. This is that. If a teacup is detected, the water dispenser releases water.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081023660.png&quot; alt=&quot;IFTTT create your own applet screen&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What we want to achieve today is: &lt;strong&gt;If a notification update is detected on ZJUT’s official website, then send us the title and content of this new notification by email.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Click this and find RSS Feed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081027922.png&quot; alt=&quot;IFTTT RSS Feed service card&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Select New feed item, enter &lt;strong&gt;&lt;a href=&quot;https://rsshub.app/zjut/1&quot;&gt;https://rsshub.app/zjut/1&lt;/a&gt;&lt;/strong&gt; in Feed URL, and click Create trigger. This completes the this operation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081029853.png&quot; alt=&quot;IFTTT applet with an RSS trigger&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Similarly, search for email in that, then bind your own email according to the prompts. Future official website notifications will be sent to this email.&lt;/li&gt;
&lt;li&gt;No need to modify anything here. Just click Create action, then Finish.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081029804.png&quot; alt=&quot;IFTTT email action fields&quot;&gt;&lt;/p&gt;
&lt;p&gt;💡 This implements pushing official website notification updates to your email. Due to website-related reasons, there may be a delay of more than ten minutes, so please understand.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;push-to-wechat&quot;&gt;Push to WeChat&lt;/h2&gt;
&lt;p&gt;Pushing to WeChat is slightly more difficult. If you want to configure it yourself, you can continue reading. If you don’t want to configure it yourself, you can scan the QR code below to follow the official account and receive update notifications from the school’s official website. Notifications from college websites will be added later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/20231108112204.png&quot; alt=&quot;QR code for following the WxPusher channel&quot;&gt;&lt;/p&gt;
&lt;p&gt;Pushing RSS updates to WeChat uses &lt;a href=&quot;http://wxpusher.zjiecode.com/docs/#/&quot;&gt;WxPusher&lt;/a&gt; and &lt;a href=&quot;https://ifttt.com/&quot;&gt;IFTTT&lt;/a&gt;. The function is implemented together with Webhook.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WxPusher (WeChat push service) is a real-time information push platform that uses a WeChat official account as the channel. You can push information to WeChat by calling the API, without installing any additional software, enabling real-time information notifications. You can use WxPusher for server alarm notifications, course enrollment notifications, ticket-snatching notifications, information update reminders, and more.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Click the address, scan with WeChat to follow, and log in to the &lt;a href=&quot;http://wxpusher.zjiecode.com/admin&quot;&gt;WxPusher backend&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; First create an application, and remember to save the APP_TOKEN.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081031936.png&quot; alt=&quot;WxPusher create application dialog&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081031070.png&quot; alt=&quot;WxPusher application created screen&quot;&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;APP_TOKEN：AT_iFym3l0dClijYPJk16AMjcjfPcazj8Ne&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;#每个人获取到的APP_TOKEN都不一样，请勿复制我的&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Create a topic.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081124096.png&quot; alt=&quot;WxPusher create topic dialog&quot;&gt;&lt;/p&gt;
&lt;p&gt;Remember the topic ID you just created.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081125242.png&quot; alt=&quot;WxPusher topic list with the topic ID highlighted&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Click Follow. A QR code will appear. Scan the QR code with WeChat to follow the topic.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081126488.png&quot; alt=&quot;WxPusher topic follow dialog with a QR code and follow link&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt; Test whether the function works properly through the GET interface.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;GET&lt;/code&gt; interface is a simplified version of the &lt;code&gt;POST&lt;/code&gt; interface. It is mainly for convenient calls in certain situations and only supports sending text (&lt;code&gt;contentType=1&lt;/code&gt;). Example:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;http://wxpusher.zjiecode.com/api/send/message/?appToken=AT_iFym3l0dClijYPJk16AMjcjfPcazj8Ne&amp;#x26;content=Zero测试&amp;#x26;topicId=608&amp;#x26;url=https://example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;请求参数：appToken、uid、topicId、content、url ，其中 content 和 url 请进行&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;urlEncode 编码。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;appToken：AT_iFym3l0dClijYPJk16AMjcjfPcazj8Ne topicId：608 #即主题ID&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modify the address above according to your &lt;code&gt;appToken&lt;/code&gt; and &lt;code&gt;topicId&lt;/code&gt;. Fill in the push content after &lt;code&gt;Content=&lt;/code&gt;, and if &lt;code&gt;url&lt;/code&gt; is not needed, you can choose to delete &lt;code&gt;&amp;amp;url=https://example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After modification, paste the address into the browser address bar.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081127922.png&quot; alt=&quot;Browser response from the WxPusher GET message API&quot;&gt;&lt;/p&gt;
&lt;p&gt;If it fails, there will also be a prompt indicating where the error is. Just modify it according to the prompt. At this point, if nothing goes wrong, your WeChat should receive the message you just sent.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081128088.jpg&quot; alt=&quot;WeChat message received from WxPusher&quot;&gt;&lt;/p&gt;
&lt;p&gt;At this point, the &lt;code&gt;WxPusher&lt;/code&gt; section is configured.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;6.&lt;/strong&gt; Create a new IFTTT project. For the this part, still choose RSS. For details, please refer to the section on pushing from IFTTT to email.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081129746.png&quot; alt=&quot;IFTTT Webhooks service card&quot;&gt;&lt;/p&gt;
&lt;p&gt;For the that part, select Webhooks and configure it as shown in the image.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081129811.png&quot; alt=&quot;IFTTT Webhooks action fields for a WxPusher POST request&quot;&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;http://wxpusher.zjiecode.com/api/send/message #URL POST #Method application/json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;#Content Type&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modify the Body section according to your actual situation and parameters. For complete parameters, click to view: &lt;a href=&quot;http://wxpusher.zjiecode.com/docs/#/?id=http%e8%b0%83%e7%94%a8#/?id=http%e8%b0%83%e7%94%a8#/?id=http%e8%b0%83%e7%94%a8&quot;&gt;Complete POST Usage&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is how I configured it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;appToken&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;AT_iFym3l0dClijYPJk16AMjcjfPcazj8Ne&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;浙江工业大学： {{EntryTitle}}&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;contentType&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;topicIds&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;608&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;url&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;{{EntryUrl}}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two parameters are used here: &lt;code&gt;{{EntryTitle}}&lt;/code&gt; and &lt;code&gt;{{EntryUrl}}&lt;/code&gt;. Through these two parameters, IFTTT can obtain the title and link of the new notification from RSS, ultimately enabling push notifications of school official website updates to WeChat.&lt;/p&gt;
&lt;p&gt;Through IFTTT as the medium, we can not only push RSS updates to WeChat, but also push notifications to DingTalk and a series of other software that provides Webhook functionality.&lt;/p&gt;
</content:encoded></item><item><title>Emulating a Campus Card with PN532</title><link>https://ro.yikzero.com/en/blog/pn532-1</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/pn532-1</guid><description>A Complete Tutorial on Using a PN532 Device to Parse Campus Card Data and Write It to a Blank CUID Card, Enabling Access Control and Payment Functions</description><pubDate>Sun, 03 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;preface&quot;&gt;Preface&lt;/h2&gt;
&lt;p&gt;Some time ago, I published a tutorial on emulating a campus card, but that tutorial was the most basic and simplest one. At the same time, however, it also brought some issues. To ensure security, phone manufacturers do not support emulating encrypted cards with the phone’s NFC. Even if an encrypted card can be emulated, the encrypted functions are not usable (unless you ROOT your phone). In short, the payment function in a campus card cannot be directly achieved through the simulation method in the previous tutorial. How could that be acceptable! The whole point was convenience, and yet the payment function cannot be implemented? That won’t do. Therefore, I looked up a large amount of information, ran into quite a few pitfalls, and, with the help of many tutorials, successfully emulated an encrypted card.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;At noon today, I went to the supermarket to buy water, and it worked pretty well.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;screenshots&quot;&gt;Screenshots&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081211243.jpg&quot; alt=&quot;PN532 NFC reader connected by USB&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;preparations&quot;&gt;Preparations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/xcicode/MifareOneTool/releases/latest&quot;&gt;MifareOneTool Software&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PN532&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A phone that supports NFC&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blank CUID card&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Campus card&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cracking-the-original-card&quot;&gt;Cracking the Original Card&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Connect the PN532 to the computer&lt;/li&gt;
&lt;li&gt;Open MifareOne Tool&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Follow the steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detect connection&lt;/li&gt;
&lt;li&gt;Scan the card. Cards with SAK values such as 28 cannot be cracked. The campus card at our school has an SAK of 08, which supports cracking.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081211470.png&quot; alt=&quot;nfc-list output showing a detected NFC card&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Detect encryption&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One-click crack original card&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081212853.png&quot; alt=&quot;MifareOne Tool cracking card keys&quot;&gt;&lt;/p&gt;
&lt;p&gt;I failed many, many times at this step. It took me an entire morning to successfully extract the original card data. You can try a few more times; maybe I was just extremely unlucky. After the original card data is extracted, a pop-up window will appear automatically, allowing you to specify a file name and save the cracked original card data. The file extension is .dump&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;At this point, the card is temporarily no longer needed, so you can take it away first.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;writing-data-to-the-blank-cuid-card&quot;&gt;Writing Data to the Blank CUID Card&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081213335.png&quot; alt=&quot;MifareOne Tool advanced operation mode and Hex Editor button&quot;&gt;&lt;/p&gt;
&lt;p&gt;In MifareOne Fool, select the Hex Editor under Advanced Operation Mode.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081215534.png&quot; alt=&quot;S50HTool opening a dump file&quot;&gt;&lt;/p&gt;
&lt;p&gt;Open the .dump file you just saved.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081216191.png&quot; alt=&quot;S50HTool showing sector 0 card ID data&quot;&gt;&lt;/p&gt;
&lt;p&gt;In sector 0, block 0, copy the first eight characters. These characters are the card ID. You can also open the .dump file with Notepad and copy the first eight characters from the first line, without spaces.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081217856.png&quot; alt=&quot;S50HTool New menu item&quot;&gt;&lt;/p&gt;
&lt;p&gt;Select New,&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081218941.png&quot; alt=&quot;S50HTool Modify UID dialog&quot;&gt;&lt;/p&gt;
&lt;p&gt;After creating a new file, choose Modify UID from the tools, enter the eight-character card ID you just copied, then save it as a file with the .mfd extension.&lt;/p&gt;
&lt;p&gt;Place the blank CUID card on the PN532, then go through the steps of scanning the card and detecting encryption.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081219805.png&quot; alt=&quot;Write C/FUID Card button in MifareOne Tool&quot;&gt;&lt;/p&gt;
&lt;p&gt;After completing the steps above, select Write C/FUID Card, and choose the file with the .mfd extension! Pay attention to this.&lt;/p&gt;
&lt;p&gt;The prompt box below will indicate that writing 64/64 is complete. If it shows 63/64, write the CUID card again. Generally, there should be no problem.&lt;/p&gt;
&lt;p&gt;Then you can use your NFC phone’s access card emulation function to emulate this blank CUID card. At this point, you should understand that you have only emulated the card ID; the encrypted part has not been emulated yet. This is not the end.&lt;/p&gt;
&lt;h2 id=&quot;writing-encryption-to-the-phone&quot;&gt;Writing Encryption to the Phone&lt;/h2&gt;
&lt;p&gt;💡 The tools for this part are the phone, PN532, and MifareOne Tool. It has nothing to do with the blank card anymore. The blank card has already fulfilled its mission.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081220132.jpg&quot; alt=&quot;Phone access card emulator ready to write a campus card&quot;&gt;&lt;/p&gt;
&lt;p&gt;Turn on the NFC function on the phone, then place it on the PN532. At this point, the phone will be recognized as a CUID card.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081220482.png&quot; alt=&quot;Write C/FUID Card option highlighted for writing the dump file&quot;&gt;&lt;/p&gt;
&lt;p&gt;Select Write C/FUID Card again. This time, choose the file with the .dump extension, because only this file contains the encrypted card data. If the prompt box shows 63/64 written, it means success. The card ID will not be read again, so this 1/64 will not succeed.&lt;/p&gt;
&lt;p&gt;Congratulations, at this point, your phone can already implement all the functions of the campus card. Similarly, replacing the phone in the third part of the tutorial with a smart band follows the same procedure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311081221822.jpg&quot; alt=&quot;Smart band showing the campus card near-reader prompt&quot;&gt;&lt;/p&gt;
</content:encoded></item><item><title>Sharing Dorm WiFi: Bypassing Campus Network Restrictions</title><link>https://ro.yikzero.com/en/blog/dorm-router</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/dorm-router</guid><description>This article explains how to flash third-party firmware (such as Padavan firmware) and configure L2TP to bypass the school’s one-account-per-person network restriction (using China Mobile broadband at the Pingfeng campus as an example), enabling shared WiFi in the dormitory.</description><pubDate>Tue, 21 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;preface&quot;&gt;Preface&lt;/h2&gt;
&lt;p&gt;Dormitory broadband generally requires account login, which makes it impossible to directly use a router to share WiFi. This article will introduce how to configure a router (using &lt;strong&gt;Padavan firmware&lt;/strong&gt; as an example) to bypass the campus network’s “one account per person” restriction.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This tutorial is tested based on &lt;strong&gt;China Mobile broadband&lt;/strong&gt; (Pingfeng Campus). For other carriers, please refer to the general approach and configure it yourself.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;screenshots&quot;&gt;Screenshots&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/1565839237.png&quot; alt=&quot;Physical photo of the router&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;preparation&quot;&gt;Preparation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Router (recommended flashable models such as &lt;strong&gt;Newifi 3&lt;/strong&gt;, &lt;strong&gt;K2P&lt;/strong&gt;, etc.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Breed bootloader&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Padavan firmware&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;School broadband account and password&lt;/li&gt;
&lt;li&gt;Ethernet cable&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cost estimate&lt;/strong&gt;: About ￥150&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Purchase advice&lt;/strong&gt;:
You can purchase them on platforms such as Taobao.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you are good at hands-on work, you can buy a &lt;strong&gt;brand-new unopened&lt;/strong&gt; device and flash it yourself.&lt;/li&gt;
&lt;li&gt;If you are not familiar with flashing firmware, please buy a device that already has &lt;strong&gt;Breed bootloader&lt;/strong&gt; and &lt;strong&gt;Padavan firmware&lt;/strong&gt; flashed; it is usually more hassle-free.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: This article does not include a firmware flashing tutorial. Please search for relevant materials yourself.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;initial-configuration-and-router-usage&quot;&gt;Initial Configuration and Router Usage&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: If, after normal use for a period of time, you suddenly find that you cannot connect to the Internet, your device may have been blacklisted.
&lt;strong&gt;Solution&lt;/strong&gt;: Enter the router admin panel → &lt;strong&gt;External Network (WAN)&lt;/strong&gt; → scroll down to &lt;strong&gt;MAC Address&lt;/strong&gt; → click the &lt;code&gt;+&lt;/code&gt; icon to change your MAC address.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;1-unbind-the-device-important&quot;&gt;1. Unbind the Device (Important)&lt;/h3&gt;
&lt;p&gt;First, connect to the school’s official WiFi &lt;code&gt;Wlan-edu&lt;/code&gt;. After connecting, ignore the authentication window that pops up.&lt;/p&gt;
&lt;p&gt;Enter &lt;code&gt;192.168.210.100&lt;/code&gt; in the browser address bar to access the &lt;strong&gt;User Self-Service System&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/3877436746.jpg&quot; alt=&quot;Login system interface&quot;&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plain&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;账号：学号&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;密码：身份证后八位 (或你修改后的密码)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After logging in, find and click &lt;strong&gt;Unbind All MAC Addresses&lt;/strong&gt;. After completing this, disconnect from the &lt;code&gt;Wlan-edu&lt;/code&gt; WiFi.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/127070192.jpg&quot; alt=&quot;Unbind all MAC addresses&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-connect-the-router&quot;&gt;2. Connect the Router&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Power on the router.&lt;/li&gt;
&lt;li&gt;Use an Ethernet cable to connect the network port &lt;strong&gt;on the wall&lt;/strong&gt; (usually the protruding port dedicated to China Mobile broadband) to the router’s &lt;strong&gt;WAN port&lt;/strong&gt; (usually the blue port).&lt;/li&gt;
&lt;li&gt;After the router starts up, connect to its WiFi (the default SSID is usually &lt;code&gt;PDCN&lt;/code&gt; or &lt;code&gt;PDCN 5G&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plain&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;默认Wifi名称：PDCN 或 PDCN 5G&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;默认密码：1234567890&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;configuration-method&quot;&gt;Configuration Method&lt;/h2&gt;
&lt;h3 id=&quot;1-log-in-to-the-firmware-admin-panel&quot;&gt;1. Log in to the Firmware Admin Panel&lt;/h3&gt;
&lt;p&gt;After connecting to the router WiFi, open a browser and enter &lt;code&gt;192.168.123.1&lt;/code&gt; in the address bar (the default admin address of &lt;strong&gt;Padavan firmware&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/3199039527.jpg&quot; alt=&quot;Login popup&quot;&gt;&lt;/p&gt;
&lt;p&gt;Enter the default account and password:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plain&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;账号：admin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;密码：admin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;If you cannot log in&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Please try switching browsers (Chrome is recommended).&lt;/li&gt;
&lt;li&gt;Please check whether you have correctly connected to the router WiFi.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;2-vpn-configuration&quot;&gt;2. VPN Configuration&lt;/h3&gt;
&lt;p&gt;After logging in to the admin panel, find and click &lt;strong&gt;External Network (WAN)&lt;/strong&gt; in the left menu.&lt;/p&gt;
&lt;p&gt;On the &lt;strong&gt;External Network (WAN)&lt;/strong&gt; settings page, configure as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;WAN Connection Type&lt;/strong&gt;: Select &lt;code&gt;L2TP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VPN Server&lt;/strong&gt;: &lt;code&gt;192.168.113.1&lt;/code&gt; (Pingfeng Campus; please look it up yourself for other campuses)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Username&lt;/strong&gt;: &lt;code&gt;hzxhaXXXXXXX&lt;/code&gt; (Pingfeng Campus defaults to &lt;code&gt;hzxha&lt;/code&gt; + the last eight digits of your phone number)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Password&lt;/strong&gt;: &lt;code&gt;XXXXXX&lt;/code&gt; (Pingfeng Campus defaults to the last six digits of your phone number)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/4129165676.jpg&quot; alt=&quot;VPN configuration interface&quot;&gt;&lt;/p&gt;
&lt;p&gt;To prevent detection by the carrier (not necessarily effective), you can configure the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find the &lt;strong&gt;Do not decrement TTL for routed packets&lt;/strong&gt; option and change it to &lt;strong&gt;For all packets&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/3452305555.jpg&quot; alt=&quot;Change to for all packets&quot;&gt;&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Apply settings on this page&lt;/strong&gt; at the bottom of the page to save the configuration.&lt;/p&gt;
&lt;h3 id=&quot;3-log-in-to-the-campus-network&quot;&gt;3. Log in to the Campus Network&lt;/h3&gt;
&lt;p&gt;After saving the settings, wait a moment. Open any webpage, and theoretically it will automatically redirect to the &lt;strong&gt;campus network login page&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: If it does not redirect automatically, manually enter &lt;code&gt;192.168.210.111&lt;/code&gt; in your browser.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/3204033419.jpg&quot; alt=&quot;Redirect to this page&quot;&gt;&lt;/p&gt;
&lt;p&gt;Enter your account and password:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plain&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;账号：学号&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;密码：身份证后八位 (或你修改后的密码)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/956320776.jpg&quot; alt=&quot;Login successful page&quot;&gt;&lt;/p&gt;
&lt;p&gt;After logging in successfully, you can share the network through the router.&lt;/p&gt;
&lt;h2 id=&quot;information-summary&quot;&gt;Information Summary&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. User Self-Service System (for unbinding)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Address: &lt;code&gt;192.168.210.100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Account: Student ID&lt;/li&gt;
&lt;li&gt;Password: Last eight digits of your ID card number&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. Campus Network Login Page (for Internet access)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Address: &lt;code&gt;192.168.210.111&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Account: Student ID&lt;/li&gt;
&lt;li&gt;Password: Last eight digits of your ID card number&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. Router Admin Panel (for configuration)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Address: &lt;code&gt;192.168.123.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Account: admin&lt;/li&gt;
&lt;li&gt;Password: admin&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There may be errors in the article; corrections are welcome.&lt;/p&gt;
</content:encoded></item><item><title>Sending Email Notifications When Aria2 Downloads Complete</title><link>https://ro.yikzero.com/en/blog/aria2-1</link><guid isPermaLink="true">https://ro.yikzero.com/en/blog/aria2-1</guid><description>Configure automatic email notifications after Aria2 downloads complete, sending notifications that include the file path and system information via sendmail and a custom script.</description><pubDate>Sun, 09 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;preface&quot;&gt;Preface&lt;/h2&gt;
&lt;p&gt;Since I don’t really have any coding background and have never studied anything related to code, some of my descriptions may not be very accurate. I thought of this script while downloading movies. Nowadays, everything in society is becoming intelligent, so I also wanted to tinker around and make an email download notification.&lt;/p&gt;
&lt;h2 id=&quot;screenshot&quot;&gt;Screenshot&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.yikzero.com/markdown/images/202311050924340.png&quot; alt=&quot;Email Screenshot&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;setting-up-the-email-system&quot;&gt;Setting Up the Email System&lt;/h2&gt;
&lt;p&gt;I ran into quite a few problems during installation and debugging, but after searching around and following this blogger’s &lt;a href=&quot;https://www.cnblogs.com/kevingrace/p/6143977.html&quot;&gt;operation record&lt;/a&gt;, together with Baota, it can generally be configured successfully. Just test it a few more times yourself.&lt;/p&gt;
&lt;h2 id=&quot;finding-and-modifying-the-script&quot;&gt;Finding and Modifying the Script&lt;/h2&gt;
&lt;p&gt;I first found a notification script modified by the original poster on the &lt;a href=&quot;https://www.right.com.cn/forum/thread-163098-1-1.html&quot;&gt;Enshan Wireless Forum&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the script provided by that author, things like the email address just need to be filled in. However, at the time, I had some issues with the &lt;code&gt;sendmail&lt;/code&gt; command, so I followed the records in the first section. Some of the script-related things had already been written into my server, so I changed a lot of the original script.&lt;/p&gt;
&lt;p&gt;When I was debugging with the author’s script, the &lt;code&gt;aria2.log&lt;/code&gt; kept showing the problem &lt;code&gt;no such file or directory&lt;/code&gt;, but I couldn’t find the cause. Then I thought I would write the entire script using &lt;code&gt;pipes&lt;/code&gt;… It seems a bit clumsy… but I didn’t know any other method.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;文件路径：&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;$3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;完成时间 `&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;date&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; +%Y/%m/%d`  `&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;date&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; +%T`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;负载 &quot;`&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;uptime&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;版本 &quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;uname&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -a&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;From Zero.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Have a nice day.&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; mail&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Aria2通知&apos;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example@qq.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the content of my script. It’s very simple, right… But it actually took me a long time to make something usable. &lt;code&gt;Aria2通知&lt;/code&gt; is the email subject. The specific meanings of the parameters should all be found in the article link in the first section.&lt;/p&gt;
&lt;h2 id=&quot;final-debugging&quot;&gt;Final Debugging&lt;/h2&gt;
&lt;p&gt;Specific steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Upload the &lt;code&gt;email.sh&lt;/code&gt; script to the server&lt;/li&gt;
&lt;li&gt;Modify the &lt;code&gt;on-download-complete=&lt;/code&gt; parameter in &lt;code&gt;aria2.conf&lt;/code&gt;; after &lt;code&gt;=&lt;/code&gt;, fill in the script’s &lt;code&gt;absolute path&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order to achieve both:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Email notification&lt;/li&gt;
&lt;li&gt;Automatic upload&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I call &lt;code&gt;email.sh&lt;/code&gt; from the &lt;code&gt;autoupload.sh&lt;/code&gt; script&lt;/p&gt;
&lt;p&gt;Specific writing:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /root/.aria2/email.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My &lt;code&gt;Aria2&lt;/code&gt; uses the &lt;a href=&quot;https://github.com/P3TERX/aria2.sh&quot;&gt;Aria2 One-Click Installation Management Script&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I’m not very satisfied with this script myself. Although it can implement the basic functions, it has the following &lt;strong&gt;problems&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;One download will send two emails: one email when the torrent download completes, and one email when the file download completes.&lt;/li&gt;
&lt;li&gt;I don’t understand the syntax. I tried things like &lt;code&gt;basename&lt;/code&gt; and &lt;code&gt;##/&lt;/code&gt;, but after trying for quite a while, I still couldn’t successfully get the filename.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;This is my first time writing an article. I don’t know anything about formatting, and it’s also quite wordy. But it was pretty fun.&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>