<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: xin zhang</title>
    <description>The latest articles on DEV Community by xin zhang (@selahsco).</description>
    <link>https://dev.to/selahsco</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4114870%2Fe7414399-7d95-4432-90c8-4c1b9fb5ca2c.png</url>
      <title>DEV Community: xin zhang</title>
      <link>https://dev.to/selahsco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/selahsco"/>
    <language>en</language>
    <item>
      <title>I Built a Browser-Local PDF Redaction Tool Because a Black Rectangle Isn't Redaction</title>
      <dc:creator>xin zhang</dc:creator>
      <pubDate>Tue, 06 Oct 2026 14:23:15 +0000</pubDate>
      <link>https://dev.to/selahsco/i-built-a-browser-local-pdf-redaction-tool-because-a-black-rectangle-isnt-redaction-m9h</link>
      <guid>https://dev.to/selahsco/i-built-a-browser-local-pdf-redaction-tool-because-a-black-rectangle-isnt-redaction-m9h</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvrzkag9gip7ruhlbnqiv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvrzkag9gip7ruhlbnqiv.png" alt="RedactGo — browser-local PDF redaction" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A black rectangle can hide text on screen without removing the text stored inside a PDF. Someone may still be able to select, copy, search, or recover what appears to be covered.&lt;/p&gt;

&lt;p&gt;That gap is why I built &lt;a href="https://redactgo.com/" rel="noopener noreferrer"&gt;RedactGo&lt;/a&gt;, a free PDF redaction tool that runs locally in the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem I wanted to solve
&lt;/h2&gt;

&lt;p&gt;People often need to share contracts, invoices, forms, or reports while removing names, contact details, reference numbers, signatures, or image regions.&lt;/p&gt;

&lt;p&gt;The easy-looking solution is to draw a black box over the sensitive area. But a visual overlay and actual redaction are different operations. A safer workflow should create a new output file and let the user inspect that result before sharing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What RedactGo does
&lt;/h2&gt;

&lt;p&gt;With RedactGo, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open one PDF up to 10 MB&lt;/li&gt;
&lt;li&gt;draw multiple redaction areas across multiple pages&lt;/li&gt;
&lt;li&gt;undo marks or redact an entire page&lt;/li&gt;
&lt;li&gt;generate a new PDF&lt;/li&gt;
&lt;li&gt;review the actual generated output before downloading&lt;/li&gt;
&lt;li&gt;keep unmarked text searchable where the document allows it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no account, no daily limit, and no watermark.&lt;/p&gt;

&lt;p&gt;The output step also removes basic metadata and embedded-file entries for supported documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it runs in the browser
&lt;/h2&gt;

&lt;p&gt;PDF documents can contain private or regulated information. I wanted the default workflow to avoid sending the document to an application server.&lt;/p&gt;

&lt;p&gt;The preview and redaction work in the browser. PDF.js handles viewing, while MuPDF.js runs the redaction work in a background worker. The PDF bytes are not uploaded to a RedactGo document server.&lt;/p&gt;

&lt;p&gt;Analytics is deliberately separate from document processing: it records page views and a small set of tool events, without PDF filenames, text, or redaction coordinates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure should look like failure
&lt;/h2&gt;

&lt;p&gt;One design choice mattered more than adding another feature: if the tool cannot safely process a document, it should not produce a download that looks successful.&lt;/p&gt;

&lt;p&gt;RedactGo therefore refuses some PDFs with unsupported features, including encrypted files and certain interactive, tagged, layered, or scripted documents. This is less convenient than accepting everything, but it is more honest than showing a green success state for an output that was not safely rewritten.&lt;/p&gt;

&lt;p&gt;It is also why the result preview displays the actual output PDF rather than a mock preview of the original page with boxes on top.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current limits
&lt;/h2&gt;

&lt;p&gt;RedactGo is useful for a focused task, but it is not a universal PDF security guarantee.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one file at a time&lt;/li&gt;
&lt;li&gt;maximum file size of 10 MB&lt;/li&gt;
&lt;li&gt;complex PDFs may be rejected&lt;/li&gt;
&lt;li&gt;users still need to review every page of the downloaded result&lt;/li&gt;
&lt;li&gt;metadata cleanup cannot automatically discover every kind of sensitive information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those limits are shown because privacy tools should make uncertainty visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open source
&lt;/h2&gt;

&lt;p&gt;RedactGo is licensed under AGPL-3.0-or-later. The application source, license information, third-party notices, and corresponding MuPDF source details are available on the &lt;a href="https://redactgo.com/source/" rel="noopener noreferrer"&gt;source and licenses page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can try the tool here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://redactgo.com/" rel="noopener noreferrer"&gt;Redact a PDF in your browser with RedactGo&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I would especially appreciate feedback on the editing workflow, output review step, and PDF compatibility.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>privacy</category>
      <category>opensource</category>
      <category>pdf</category>
    </item>
    <item>
      <title>How I Choose a Chart for a Small Spreadsheet</title>
      <dc:creator>xin zhang</dc:creator>
      <pubDate>Wed, 30 Sep 2026 00:50:48 +0000</pubDate>
      <link>https://dev.to/selahsco/how-i-choose-a-chart-for-a-small-spreadsheet-4h4a</link>
      <guid>https://dev.to/selahsco/how-i-choose-a-chart-for-a-small-spreadsheet-4h4a</guid>
      <description>&lt;p&gt;Making a chart is often easier than deciding which chart answers the question. A small spreadsheet rarely needs a full dashboard, but choosing the first chart in a menu can hide the story in the data.&lt;/p&gt;

&lt;p&gt;Here is the short decision guide I use, with three fictional CSV examples you can copy and test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the question, not the chart
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What do you want to see?&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Data shape&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Change over time&lt;/td&gt;
&lt;td&gt;Line chart&lt;/td&gt;
&lt;td&gt;Date or ordered label + numeric series&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Differences between categories&lt;/td&gt;
&lt;td&gt;Bar chart&lt;/td&gt;
&lt;td&gt;Category + one or more numeric series&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parts of one complete whole&lt;/td&gt;
&lt;td&gt;Pie or donut chart&lt;/td&gt;
&lt;td&gt;Category + non-negative value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A relationship between two measurements&lt;/td&gt;
&lt;td&gt;Scatter plot&lt;/td&gt;
&lt;td&gt;Numeric X + numeric Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The distribution of raw measurements&lt;/td&gt;
&lt;td&gt;Histogram&lt;/td&gt;
&lt;td&gt;One numeric column of observations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Patterns across rows and columns&lt;/td&gt;
&lt;td&gt;Heatmap&lt;/td&gt;
&lt;td&gt;Labeled matrix or X/Y/value table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pairwise linear relationships&lt;/td&gt;
&lt;td&gt;Correlation matrix&lt;/td&gt;
&lt;td&gt;Several numeric columns of observations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These choices are about the &lt;em&gt;question&lt;/em&gt;, not just the file format. For example, a bar chart compares named categories; a histogram groups raw numeric observations into intervals. They may look similar, but they answer different questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 1: Two sales series
&lt;/h2&gt;

&lt;p&gt;This fictional table has a date and two values measured in the same unit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Month,Online sales,Store sales
2026-01-01,120,96
2026-02-01,132,102
2026-03-01,128,110
2026-04-01,151,115
2026-05-01,164,126
2026-06-01,179,139
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a &lt;strong&gt;line chart&lt;/strong&gt; if you want to follow how each channel changes over time. Use a &lt;strong&gt;grouped bar chart&lt;/strong&gt; if the main task is comparing online and store sales month by month. A &lt;strong&gt;heatmap&lt;/strong&gt; can help you scan the whole month-by-channel table for high and low values.&lt;/p&gt;

&lt;p&gt;The input is the same. The question changes the chart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3sakiqpw58i18sy9cqzw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3sakiqpw58i18sy9cqzw.png" alt="Line chart of fictional monthly online and store sales" width="800" height="728"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 2: Shares of one budget
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Channel,Budget
Search,420
Social,240
Email,180
Events,160
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fictional budget totals 1,000. A &lt;strong&gt;pie or donut chart&lt;/strong&gt; can show the four shares: 42%, 24%, 18%, and 16%. If your reader needs to compare two nearly equal categories precisely, use bars instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faez960s51wg70k7b96i9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faez960s51wg70k7b96i9.png" alt="Bar chart comparing the four fictional channel budgets" width="800" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A pie chart only works when the categories are parts of the &lt;em&gt;same whole&lt;/em&gt;. Independent conversion rates, for example, should not be added together to make a pie.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 3: Two measurements per campaign
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Campaign,Ad spend,Sign-ups
A,100,12
B,140,18
C,180,17
D,220,29
E,260,34
F,300,31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose a &lt;strong&gt;scatter plot&lt;/strong&gt; to inspect ad spend against sign-ups, with one point per campaign. You can also calculate a &lt;strong&gt;correlation matrix&lt;/strong&gt; from the two numeric columns, but a correlation coefficient does not establish causation. With only six fictional observations, this example demonstrates the workflow—not a reliable marketing conclusion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmefkf0zxrv31m8fv13m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmefkf0zxrv31m8fv13m.png" alt="Scatter plot of fictional campaign ad spend and sign-ups" width="799" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  From table to export
&lt;/h2&gt;

&lt;p&gt;After importing a spreadsheet, check the detected fields rather than assuming the first guess is correct. Then adjust the title and presentation, and export a PNG or SVG. Keep the source spreadsheet: a downloaded chart is not a substitute for the underlying data.&lt;/p&gt;

&lt;p&gt;The fictional CSV files and screenshots are in this &lt;a href="https://github.com/AuroraSco/charttoolbox-csv-chart-examples" rel="noopener noreferrer"&gt;GitHub example repository&lt;/a&gt;. I made the screenshots with &lt;a href="https://www.charttoolbox.com/" rel="noopener noreferrer"&gt;Charttoolbox&lt;/a&gt;, a browser-based chart tool that accepts CSV, TSV, XLS/XLSX, and pasted table data. Spreadsheet contents are processed locally in the browser for chart generation. Its optional analytics loads only with consent and does not receive the table contents.&lt;/p&gt;

&lt;p&gt;This workflow is useful for a small, one-off chart. It does not replace a BI dashboard or statistical review. If you try the fictional examples, I would be interested to know where the chart choice or field selection still feels unclear.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I used AI assistance to prepare this article.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>datascience</category>
    </item>
    <item>
      <title>How to Convert Word (DOCX) to Clean Markdown Without Uploading Your File</title>
      <dc:creator>xin zhang</dc:creator>
      <pubDate>Tue, 08 Sep 2026 04:24:25 +0000</pubDate>
      <link>https://dev.to/selahsco/how-to-convert-word-docx-to-clean-markdown-without-uploading-your-file-1gmf</link>
      <guid>https://dev.to/selahsco/how-to-convert-word-docx-to-clean-markdown-without-uploading-your-file-1gmf</guid>
      <description>&lt;p&gt;Most “Word to Markdown” workflows jump straight to Pandoc flags or paste-and-pray into an editor.&lt;/p&gt;

&lt;p&gt;This guide starts one step earlier: &lt;strong&gt;when browser-local conversion beats a cloud upload&lt;/strong&gt;, what structure actually survives, and how to review output before it hits GitHub or Obsidian.&lt;/p&gt;

&lt;h2&gt;
  
  
  When local conversion is the better default
&lt;/h2&gt;

&lt;p&gt;Upload converters are fine for throwaway files. They’re a poor fit when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The draft is client work or unpublished writing&lt;/li&gt;
&lt;li&gt;You don’t want another account just to convert one file&lt;/li&gt;
&lt;li&gt;You need editable Markdown in under a minute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the file can be processed &lt;strong&gt;in your browser&lt;/strong&gt;, you keep the document on your device.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare the DOCX
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Use a modern &lt;strong&gt;&lt;code&gt;.docx&lt;/code&gt;&lt;/strong&gt; (old &lt;code&gt;.doc&lt;/code&gt; → open in Word → Save As DOCX).&lt;/li&gt;
&lt;li&gt;Prefer real heading styles (Heading 1–6), not just bold big text.&lt;/li&gt;
&lt;li&gt;Keep tables simple when possible.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What usually survives well
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Headings (H1–H6)&lt;/li&gt;
&lt;li&gt;Bulleted / numbered lists&lt;/li&gt;
&lt;li&gt;Tables&lt;/li&gt;
&lt;li&gt;Links&lt;/li&gt;
&lt;li&gt;Bold and italic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Honest limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Complex page layouts may need a quick cleanup&lt;/li&gt;
&lt;li&gt;Embedded images often need a manual pass&lt;/li&gt;
&lt;li&gt;Footnotes and exotic Word features vary&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A simple workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open a browser-local converter such as &lt;a href="https://wordtomarkdown.net/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=tutorial" rel="noopener noreferrer"&gt;WordToMarkdown&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Drop your &lt;code&gt;.docx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Review the Markdown beside the converter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy&lt;/strong&gt; into Obsidian / Notion / a docs PR — or &lt;strong&gt;Download &lt;code&gt;.md&lt;/code&gt;&lt;/strong&gt; for GitHub&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Checklist before you publish the Markdown
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Heading levels make sense in the new context&lt;/li&gt;
&lt;li&gt;[ ] Tables render&lt;/li&gt;
&lt;li&gt;[ ] Links still point where you expect&lt;/li&gt;
&lt;li&gt;[ ] Images added back if needed&lt;/li&gt;
&lt;li&gt;[ ] No leftover Word junk (extra spaces, broken list markers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it: portable text, without turning a Word file into another upload.&lt;/p&gt;

&lt;p&gt;Try one file here: &lt;a href="https://wordtomarkdown.net/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=tutorial" rel="noopener noreferrer"&gt;wordtomarkdown.net&lt;/a&gt;&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>productivity</category>
      <category>writing</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
