Communitygithub.com

2016judea/ascii-charts

用純 Unicode 區塊字符建立圖表(長條圖、範圍圖、子彈圖、缺口支架圖、星號直方圖、熱力圖、折線圖、文氏圖、圓餅圖、環圈圖和散佈圖),使用無依賴的 Python 腳本,適用於僅有等寬文字的環境(電子報程式碼區塊、Markdown、終端機、純文字電子郵件、聊天)。當圖表必須以純文字形式嵌入而非圖片或第三方圖表服務時使用,或當您想要無品牌註腳的手工終端機風格時使用。

ascii-charts 是什麼?

這是一個用於以純文字方式生成等寬圖表的 AI 代理技能,非常適合需要將圖表嵌入程式碼區塊、Markdown、電子郵件或聊天訊息中的情境,而無需依賴任何外部服務、圖像或品牌註腳。它提供多種圖表類型,包括長條圖、範圍圖、子彈圖、缺口支架圖、星號直方圖、熱力圖、折線圖、火花線圖、2 或 3 集合的文氏圖、圓餅圖、環圈圖和散佈圖,所有圖表均由一個無依賴的 Python 腳本 (`scripts/ascii_chart.py`) 生成。該技能強調優雅的降級,明確建議在圖表無法增加價值時使用純文字,優先考慮清晰度而非視覺複雜度。它應用「2 秒測試」——確保典型讀者能在無需解碼圖例的情況下直觀理解圖表。它支援幫助所有等寬渲染平台,如 Markdown、`<pre>` HTML 標籤、終端機、電子郵件和聊天應用程式。AI 代理可以使用此技能以程式化方式生成視覺化文字圖表,用於報告、比較和數據敘述,自動處理幾何計算和對齊,因此代理可以專注於數據敘事。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/2016judea/brick-mortar-skills/tree/HEAD/skills/ascii-charts

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

ASCII 圖表

使用純 Unicode 區塊字符構建的終端機風格圖表,可嵌入為純文字:Markdown 中的圍欄式程式碼區塊、HTML 中的 <pre> 標籤。無需圖表服務、無「由…製作」註腳、無外部帳戶,且在等寬渲染的任何地方呈現一致。

scripts/ascii_chart.py 對每種圖表類型有一個函數。執行 python3 scripts/ascii_chart.py 可列印每種類型的範例作為完整性檢查。

這裡有兩件事是經過測量而非假設的,而且最初都是錯誤的:在 390px 的手機視口中,超過 ~44 個字符的行會從右邊緣被截斷;而等寬字體中的「圓形」需要大約 2.49 的水平長寬比校正(而非 2.0)。請參閱「尺寸」和「圓餅圖/環圈圖」小節。

電子報平台在貼上程式碼區塊的方式上有所不同。在依賴某個平台之前,請先將測試圖表貼入實際草稿,並檢查網頁、應用程式和電子郵件的呈現效果。

字符集

  • █ ▓ ▒ ░ (U+2588, U+2593, U+2592, U+2591 — 全色/深色/中色/淺色陰影) — 填充密度,從「最多」到「最少」。長條圖僅使用 █/░(填充 vs. 軌道);圓餅圖/環圈圖使用全部四種(如果切片超過 4 個,還會使用 ▚/▞/▤/▥ 作為不同的逐切片填充);熱力圖使用全部四種加上 ·(中間點,非空白空間)作為最低層級,因此「安靜」的儲存格仍然可被視為網格的有意義部分,而非間隙。
  • 數字 1–7 — 文氏圖區域代碼(該區域所屬集合的位元遮罩 — 請參閱下方的文氏圖)
  • + — 範圍條上的開放式旗標,表示可能超過顯示的上限(不要拉伸條本身來偽造您沒有的數字)
  • ┃ — 子彈圖的目標刻度,繪製在任何填充/陰影之上,以便始終可見
  • └─┘ — 缺口支架,跨度在兩個條的末端之間
  • ★/☆ — 實心/空心星號,同時作為星號直方圖的行標籤
  • 框線繪製 (─│╭╮╰╯) — 折線圖和趨勢線的連接器
  • ● — 散佈圖點標記

任何地方都不使用顏色。這就是媒介,而非需要繞過的限制——貼上的 Substack 程式碼區塊就是純文字字符,其他任何東西都無法保留(無 inline CSS、無樣式化的 span)。圖表需要進行的任何區分都存在於字符/數字/圖例中,而非不可用的顏色通道。

圖表類型選單

任務函數用途
數值比較,多個類別,全部從 0 開始bar(value, max_value, width) + render()加油(5 分鐘)與 DC 快充(45 分鐘)
最小-最大範圍,單一類別range_bar(start, end, scale_max, width, open_ended=) + render()「$100k–$300k+ 已安裝」
兩個值,之前/之後純文字:f"{start} → {end}"「批發 $220 → 品飲室 $650」——長條圖不會增加任何東西;箭頭就是圖表
單一度量 vs. 基準帶 + 目標bullet(value, scale_max, target=, benchmark=) + render()「您的評分:4.2,正常區間 3.8–4.5,目標 4.7」
明確指出兩個條之間的差距gap_bracket(a, b, scale_max, width, label=) 透過 render(bracket=)「您 vs. 市場中位數」——報告的招牌動作
1–5 星(或類似)評分分佈star_histogram(counts)Google/Yelp 星級細分,每個星數一行
兩個分類軸,按強度著色heatmap(matrix, row_labels, col_ticks=)按日 × 小時的人流量——「您何時忙碌/冷清」
隨時間變化的趨勢,單一系列line_chart(values, height, x_labels=)月營收、一年內的評論數量
趨勢,內嵌在句子中sparkline(values)「每月評論數:▂▃▅▆█▇▅▃,自三月起成長 3 倍」
2 或 3 個命名集合,其中重疊程度本身就是發現venn(labels)「您 38% 的一星評論也提到了等待時間」——重疊大小是重點
整體的組成部分,單一快照pie(slices, radius)營收組合:咖啡 / 熱食 / 飲料 / 樂透
整體的組成部分,附帶總數標題donut(slices, radius, center_text=)同上,但中心空心處顯示「247 則評論」
兩個數值變數,是否存在關係scatter(points, width, height)競爭對手的價格 vs. 評分
同上,加上「這是趨勢」scatter_with_trend(points, width, height)同上,加上擬合線證明相關性

如果此表中沒有任何內容符合,請停下來仔細思考,而不是強行套用不合適的形狀——技術上可渲染但令人困惑的圖表,比回退到 Chrome 無頭 PNG 管道更糟糕。

圖表的 2 秒測試

圖表與好句子的標準相同:讓疲憊的讀者在手機上,第一眼就能理解,無需重讀、無需解碼。在選擇圖表類型之前應用此測試,而不是在構建之後。

2 秒測試: 目標讀者——一位小型企業老闆,而非數據分析師——能否在 2 秒內說出此圖表的含義,而無需先閱讀圖例?如果理解某個區域或切片需要先解碼圖例,那麼這在視覺上等同於行話:技術上精確,但會讓讀者付出努力,而不是引導他們前進。

在真實問題中發現, 由編輯在閱讀已發佈的貼文時發現:

  • 3 集合文氏圖的數字位元遮罩圖需要在理解任何區域之前先閱讀一個 7 行的圖例。編輯:他們不理解,所以一般企業主也不會理解。底層事實——「您需要所有這三項,而不僅僅是一項」——就是一句話,而且圖表後面的編號列表已經用簡單的英文說明了。圖表增加了解碼成本,卻沒有增加散文未更清晰表達的資訊。修正:刪除它,讓句子 + 列表完成工作。
  • DC 快充樁成本範圍條(單一條,無軸,無比較點)沒有可比較的對象。長條圖的價值來自於顯示相對於某事物的大小——一個刻度、另一個條、一個基準。一個沒有參考點的單一條是裝飾性的,而非資訊性的。它所說明的數字($100k–$300k+)已經在之前一句話中清楚陳述了。修正:也刪除它。

遵循的規則:

  • 需要自己的圖例才能閱讀的圖表是一個警告信號——不僅僅對於文氏圖。具有未標記軸的熱力圖,或切片數量超出讀者記憶能力的圓餅圖,也會以同樣的方式失敗。
  • 如果一個簡單句子能像圖表一樣清晰地說明事實,那就跳過圖表。與在散文中用實際計數替換「一些/許多/各種」的本能相同:因為比較或趨勢是重點,而不僅僅是數據具有形狀。
  • 每個長條圖都需要一個軸(render(..., axis=))或同一刻度上的第二個長條圖來進行比較。一個既沒有軸也沒有比較對象的單一條什麼也無法傳達——與沒有真正主語的被動語態句子相同的失敗。但一旦滿足了這個條件,如果軸只會重複已經存在於每行數值標籤中的數字,就省略它——一個汽油與充電的長條圖正好出於這個原因刪除了它的 0 ... 45 min 軸線:兩行都已經帶有它們自己的數值(「5 分鐘」、「45 分鐘」),所以軸線只是第二次重複了「45 分鐘」。與刪除句子中冗餘從句的原則相同。
  • 不要圖表化已經是一個清晰句子的數字。「$100k–$300k+ 已安裝」作為文字已經非常清晰;對其進行圖表化不會揭示比較、趨勢或重疊,只會無謂地增加解碼步驟。
  • 像實際讀者那樣冷讀完成的圖表——在手機上的加油站老闆,而不是剛剛構建它並且已經知道它應該說什麼的人。

工作流程

使用 scripts/ascii_chart.py,而不是手動對齊字符或自行光柵化幾何圖形——列數學和圓形/角度數學正是那種容易透過目測微妙出錯,並且只有在事後才會表現為一個字符的錯位或不圓的圓圈的問題。隨時執行 python3 scripts/ascii_chart.py 以列印每種圖表類型的一個範例作為完整性檢查。

長條圖 / 範圍條

from ascii_chart import bar, range_bar, render

rows = [
    ("加油", bar(5, 45, 24), "5 分鐘"),
    ("DC 快充", bar(45, 45, 24), "45 分鐘"),
]
print(render(rows, axis=("0", "45 分鐘")))

row = ("DC 快充樁", range_bar(100, 300, 300, 24, open_ended=True), "$100k–$300k+")
print(render([row]))

render() 接受 (label, bar_string, value_str) 元組並處理所有列對齊——傳遞您自己預先格式化的 value_str(貨幣、單位等,取決於文案需要);該函數僅處理幾何圖形。

子彈圖

from ascii_chart import bullet, render

rows = [("平均評分", bullet(4.2, 5.0, target=4.7, benchmark=(3.8, 4.5), width=24), "4.2 (目標 4.7)")]
print(render(rows, axis=("0", "5.0")))

一個用於測量 value 的實心條,繪製在可選的陰影 benchmark 帶(「正常」範圍,▒)之上,帶有可選的 target 刻度(┃,始終最後繪製,以便在底部的任何填充/陰影上保持可見)。一眼就能回答「我是否在正常範圍內,距離目標多遠」——仍然只有一個真正的條(度量);帶只是背景資訊。

缺口支架

from ascii_chart import bar, gap_bracket, render

rows = [("您", bar(42000, 60000, 24), "$42,000"), ("市場中位數", bar(58000, 60000, 24), "$58,000")]
print(render(rows, bracket=gap_bracket(42000, 58000, 60000, 24)))

一個 └─┘ 支架跨越兩個條的末端之間,間隙的大小居中顯示在支架內(從兩個值自動格式化,或傳遞 label= 以使用您自己的文字)。對條和支架使用相同的 scale_max/width,否則跨度將無法對齊。這是「您 vs. 市場」,明確顯示,而不是留給讀者目測。

星號直方圖

from ascii_chart import star_histogram

print(star_histogram([(5, 61), (4, 22), (3, 9), (2, 4), (1, 4)]))

counts 是 [(stars, count), ...],按您想要列印的順序排列(通常是 5 到 1)。實心/空心星號字形(★/☆)同時充當行標籤——無需再在文字中拼出「5 星」。完全基於 bar() + render() 構建。

熱力圖

from ascii_chart import heatmap

print(heatmap(matrix, ["週一", "週二", "週三", "週四", "週五", "週六", "週日"],
              col_ticks=[(0, "上午 12 點"), (6, "上午 6 點"), (12, "中午 12 點"), (18, "下午 6 點")]))

matrix 是行 × 列的數值強度(例如 7 天 × 24 小時),在整個矩陣(非逐行)上進行歸一化,以便各行的陰影可進行比較。col_ticks 是 [(column_index, label), ...]——無自動間距,為您傳遞的標籤選擇不會衝突的位置。

折線圖 / 火花線

from ascii_chart import line_chart, sparkline

print(line_chart([120, 135, 128, 150, 175, 190, 205, 198, 220],
                  height=10, x_labels=("一月", "九月")))

print(sparkline([3, 5, 4, 8, 12, 10, 15, 14, 18]))

line_chart 使用框線繪製的角落(╭╮╰╯│─)繪製一條連接線——沒有字符表示任意中間斜率,因此寬度固定為 len(values) - 1 列(每段一列)。x_labels(如果給定)只是在軸行上列印 (first, last)——在周圍的散文中說明中間點,不要嘗試標記每個刻度。sparkline 是一個單行的 8 層級趨勢(▁▂▃▄▅▆▇█),用於句子中的一個子句,而不是一個獨立的圖表。

文氏圖(2 或 3 集合)

from ascii_chart import venn

diagram, legend = venn(["高速公路出口", "大商店", "電網容量"])
print(diagram)
print(legend)

將重疊的圓形光柵化,並用數字(它所屬集合的位元遮罩:1、2、4 用於純集合,3/5/6 用於成對重疊,7 用於所有三個)填充每個區域,而不是試圖將標籤文字塞入彎曲的邊界內,後者在等寬渲染中無法存活。圖例(單獨返回,列印兩者)將每個數字映射回真實的標籤組合,例如 7 = 高速公路出口 + 大商店 + 電網容量 <- 全部三者。這最初被認為在 ASCII 中是不可能的(早期的貼文退回到使用無頭 Chrome PNG 來呈現文氏圖)——數字區域技術乾淨地解決了這個問題;不再有文氏圖需要 PNG 回退的情況。

但在使用之前,請透過上述的 2 秒測試進行檢查——數字圖例本身正是使一個真實的文氏圖對普通讀者造成困惑的原因。 當重疊程度是發現時(例如「您 38% 的一星評論也提到了等待時間」),文氏圖才有其存在價值。當真正的訊息是「您需要全部三者一起,沒有部分積分」時,即使文氏圖正確渲染,它也是錯誤的工具——純文字可以在無需圖例的情況下完成這項工作,例如:

高速公路出口  +  大商店  +  電網容量
        錯過其中一個,跳過這個站點

圓餅圖 / 環圈圖

from ascii_chart import pie, donut

diagram, legend = pie([("咖啡", 40), ("熱食", 25), ("飲料", 20), ("樂透", 15)], radius=9)
print(diagram); print(legend)

diagram, legend = donut([("咖啡", 40), ("熱食", 25), ("飲料", 20), ("樂透", 15)],
                         radius=9, center_text="247")
print(diagram); print(legend)

切片從鐘面 12 點方向開始,順時針方向(標準圓餅圖慣例),每個切片獲得一個不同的填充字符(█▓▒░▚▞▤▥,對於典型的 2–6 個切片計數,從不在相鄰切片上重複),以便邊界在沒有顏色的情況下保持可讀。donut 是帶有空心中心的 pie——center_text 將一個標題數字/標籤放在那裡(例如總數),這是 ASCII 環圈圖可以做到而真實環圈圖通常浪費在空白空間上的一件事。radius 以行為單位;該函數在內部校正等寬字符的長寬比(共享的 ASPECT 常數,venn() 也使用它),因此結果實際上是圓形的,而不是扁平的橢圓——不要分別手動調整寬度/高度,只需傳遞 radius。

修正: ASPECT 之前被硬編碼為假設的 2.0,並且從未……

…(翻譯截斷)

Individual skills in this repo

This repo contains 7 individual skills — each has its own dedicated page.

2016judea/buffett-checklist

Apply Warren Buffett's investment framework as a discipline lens when weighing a buy, hold, or sell decision — circle of competence, economic moat, owner earnings & intrinsic value, margin of safety, management/capital-allocation quality, and market psychology (Mr. Market, fear vs greed, temperament). Use when asked to "run this through Buffett", "what would Buffett say", "does this pass the moat test", "is this cheap enough", or for a second, more skeptical read on a name being evaluated or already held. This is a discipline overlay, not a replacement for a strategy — it applies the Buffett lens to how someone actually invests, rather than issuing generic advice.

2016judea/image-riff

Turn one or more reference images into original art in that reference's own visual language, then cut it to whatever surface it's for. Use when you paste or point at art and say "use this as the base image", "generate art from this", "riff on this", "some creative ideation with these images", "make me a channel banner / avatar / cover / plate", "wilder version of this", or want a spread of options off one piece of reference. This is the TASK skill; the engine is the `make-art` tool skill.

2016judea/leonardo

Draw the way Leonardo da Vinci drew — a study sheet, a notebook folio, a red-chalk or silverpoint study, a machine cutaway, a storm of vortices — in his real materials and page-logic, generated with the make-art engine and anchored on fifteen of his actual (public-domain) sheets. Use when you say "Leonardo", "da Vinci", "like his notebooks", "a codex page of X", "draw this as one of his study sheets", "sanguine", "silverpoint", or wants a design, diagram, or illustration in that hand. Also a reading guide to his notebooks (Richter's 1883 public-domain translation) for "what did Leonardo say about shadow / trees / water". This is a TASK skill; the engine is `make-art`.

2016judea/mcp-stack

Which free MCP server or framework to reach for, and its traps — finance/gov data (SEC EDGAR, Yahoo, SAM.gov, USAspending), web research (Exa, Firecrawl, Crawl4AI, Jina reader), design/art/brand (Canva, Inkscape, Penpot, Figma, Orshot, Hugging Face image gen, vectorizing, charts), data/spatial (DuckDB spatial, live ArcGIS layers, OpenStreetMap/Overpass, Census, MapLibre), and ops (PostHog, iMessage, context7 docs). Use when making a social post, OG card, poster, logo/vector, chart, map, or brand asset; when querying parquet/geojson/duckdb or a city's open-data layer; when an MCP "needs authentication" or fails; or when asked "what tools do we have", "is there an MCP for X", "should we add X".

2016judea/records-request

Get a specific record out of a US government office by public-records request — "request the assessor's model", "file a data practices request", "FOIA this", "open records request", "can we just ask the county for it", "who do I write to at <county>", "chase that request", "did they ever answer". Covers checking the website first, finding the right office and statute, the carve-out paragraph that makes a request answerable instead of refused, web-form traps (hidden character caps, WAFs that reject apostrophes), chasing (there is usually no deadline), whether what comes back may be republished, and parsing multi-year tables without silent errors.

2016judea/sms-native-product

Build a product whose interface is text messages rather than an app or a website — "no app, no website", "they just text a number", "chat with our agent natively in Text Messages", "can we do this over text", "an SMS bot", "a Telegram bot", "make it a group chat", "minimum viable friction". Fires equally when it has already gone wrong — "my campaign got rejected", "our number is blocked", "the texts aren't arriving", "why do I need to register". Covers the gate that decides whether such a product can send at all (US A2P 10DLC registration, SHAFT content rules, permanent rejection codes), group MMS from code, what each channel can't do, an architecture that survives losing the channel, and guarding model-rewritten user content.

2016judea/vertical-market-agent

Build a chat agent that knows one industry in one metro better than any general model because it carries a precomputed digest of real public records (or a mined review corpus) and can pull live records mid-conversation — finding the moat, building a byte-stable substrate, enumerating what the records can't, the data traps that produce plausible wrong numbers, deterministic math in tools, measured prompt cost and cache TTL, shipping as an ad destination, an off-sector ending, and a precomputed spatial "what is this next to" layer. Use when building a vertical AI agent grounded in records rather than general knowledge.

相關技能