Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ right here, with the URL, the header and the data never passing through our serv
<img alt="Tailwind CSS" src="https://img.shields.io/badge/Tailwind_CSS-4-06B6D4?logo=tailwindcss&logoColor=white">
<img alt="Zustand" src="https://img.shields.io/badge/Zustand-5-443E38">
<a href="https://excel-to-go.vercel.app"><img alt="Open the app" src="https://img.shields.io/badge/▶_try_it-excel--to--go.vercel.app-2F9E44"></a>
<img alt="Vitest" src="https://img.shields.io/badge/tests-2013%20passing-2F9E44?logo=vitest&logoColor=white">
<img alt="Vitest" src="https://img.shields.io/badge/tests-2014%20passing-2F9E44?logo=vitest&logoColor=white">
<img alt="CI" src="https://github.com/SuruchBoss/ExcelToGo/actions/workflows/ci.yml/badge.svg">
</p>

Expand All @@ -65,7 +65,7 @@ self-hosted, straight from PostgreSQL/MySQL — one saved read-only query, and n
and full-fidelity Excel/PDF export, where a chart exported to `.xlsx` is a real, editable chart
bound to its cells, because the OOXML chart parts are written by hand (ExcelJS writes none). Plus optional
bring-your-own-backend cloud save and live co-editing over it — presence, last-writer-wins with the loser told, and
an undo that does not erase the other person's work. Bilingual UI (Thai/English), 2013 automated tests.
an undo that does not erase the other person's work. Bilingual UI (Thai/English), 2014 automated tests.

---

Expand Down Expand Up @@ -109,7 +109,7 @@ Want the harder parts: [embedding a Thai font in the PDF, with stacked tone mark

---

### 🧪 What 2013 passing tests could not catch
### 🧪 What 2014 passing tests could not catch

Every test of the assistant **mocks the model** — it returns what I imagined it would. Put a real
API key behind it, ask fourteen ordinary questions, and **six answers used functions this engine
Expand All @@ -120,7 +120,7 @@ Then **the first fix made it worse.** The rule started as "give the closest form
allows", so _"join all the names into one line"_ came back as `=SUM(A2:A20)` — `0` in the cell, no
error, nothing to notice. **A visible `#NAME?` traded for an invisible wrong number.**

**And it happened again, in a different place.** With every gate green — 2013 tests, `axe` clean on
**And it happened again, in a different place.** With every gate green — 2014 tests, `axe` clean on
both pages at two widths — an hour of clicking through the public build the way a first-time visitor
would found three things no gate can see:

Expand Down Expand Up @@ -395,7 +395,7 @@ Other available commands:
| `npm run build` | Build a production bundle |
| `npm run start` | Run the production build (run `npm run build` first) |
| `npm run lint` | Check code quality with ESLint |
| `npm test` | Run the 2013-case Vitest suite |
| `npm test` | Run the 2014-case Vitest suite |
| `npm run check:readme` | Check the READMEs still match the code (links/images/test count/new modules/both languages) |
| `npm run check:screens` | Figures printed on a screenshot still match the source |
| `npm run check:rls` | Two real accounts against your own Supabase: does the database refuse what the policies say it should (needs env) |
Expand Down Expand Up @@ -1482,7 +1482,11 @@ ExcelJS writes no chart XML at all (`addImage` is its entire drawing API), so th
are written by hand and spliced into the finished package; see `xlsxChartXml.ts` and
`xlsxCharts.ts`. If that splice ever fails the export falls back to embedding pictures, because a
package with a dangling relationship is one Excel calls corrupt and refuses to open — a much worse
outcome than a chart that has stopped updating.
outcome than a chart that has stopped updating. Each series refers to the column it was actually
read from (#59). A text column between the labels and the numbers, such as "Notes" between the
branch and Q1, is not a series but still takes up a column; the references used to be counted by
series, so Excel drew Notes where Q1 should be and Q1 where Q2 should be, while the app's own chart
was right.

**Not supported:** charts in the PDF are still pictures (jsPDF has no chart primitive). A pie still
draws one series at a time. The chart parts carry no colour or theme styling, so whichever app opens
Expand Down Expand Up @@ -3041,7 +3045,7 @@ architecture behind it.
| `@anthropic-ai/sdk` | Connects to the Claude API for the AI assistant |
| `lucide-react` | UI icons |
| `clsx` | Conditional className composition |
| `vitest` | Unit tests for the formula engine, sort logic, JSON-to-table conversion, pagination, rate limiting, templates, file fidelity, conditional formatting and live blocks (2013 cases) |
| `vitest` | Unit tests for the formula engine, sort logic, JSON-to-table conversion, pagination, rate limiting, templates, file fidelity, conditional formatting and live blocks (2014 cases) |

> **Note:** No off-the-shelf formula library (e.g. HyperFormula) is used — the **formula engine is hand-written**
> (tokenizer, parser, evaluator, and functions) to keep full control over its behavior. See
Expand Down Expand Up @@ -4096,13 +4100,13 @@ the framework bundle itself, which isn't a trade worth making here. Written down
## 🧪 Testing

```bash
npm test # 2013 cases across 145 files, via Vitest
npm test # 2014 cases across 145 files, via Vitest
```

Testing is focused on the **formula engine, sort logic, JSON-to-table conversion, pagination, rate-limit backoff, Excel templates and live-block placement** — pure functions with no React/DOM dependency, so
they run fast and give high confidence.

**But not one of those 2013 cases opens the app**, and nearly every bug this project found by hand lived in
**But not one of those 2014 cases opens the app**, and nearly every bug this project found by hand lived in
the wiring *between* pieces that all passed their tests — the toolbar's "+ row" called `addRow`, which
announced nothing, while `insertRowAtSelection` next to it announced correctly (both tested) · the AI
assistant sent a range including its text header, because the context builder read raw `sheet.cells`
Expand Down Expand Up @@ -4221,10 +4225,10 @@ once; disable `ArrowRight` in the grid and two assertions in the third fail. (Th
second one stayed green: the `case` I inserted landed *after* the existing `case "ArrowRight"` and was dead
code. Proving a gate means checking that the thing you meant to break actually broke.)

> **2013 tests passed, and 43% of the assistant's answers were unusable** — because those tests mock
> **2014 tests passed, and 43% of the assistant's answers were unusable** — because those tests mock
> the model, so it returns what the test author imagined. A test count says what you thought to ask,
> not whether you asked enough. Only a real API key found this: see
> [What 2013 passing tests could not catch](#-what-2013-passing-tests-could-not-catch), repeatable
> [What 2014 passing tests could not catch](#-what-2014-passing-tests-could-not-catch), repeatable
> with `npm run check:ai`.

| File | Cases | Tests |
Expand Down
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<img alt="Tailwind CSS" src="https://img.shields.io/badge/Tailwind_CSS-4-06B6D4?logo=tailwindcss&logoColor=white">
<img alt="Zustand" src="https://img.shields.io/badge/Zustand-5-443E38">
<a href="https://excel-to-go.vercel.app"><img alt="เปิดแอป" src="https://img.shields.io/badge/▶_ลองใช้เลย-excel--to--go.vercel.app-2F9E44"></a>
<img alt="Vitest" src="https://img.shields.io/badge/tests-2013%20passing-2F9E44?logo=vitest&logoColor=white">
<img alt="Vitest" src="https://img.shields.io/badge/tests-2014%20passing-2F9E44?logo=vitest&logoColor=white">
<img alt="CI" src="https://github.com/SuruchBoss/ExcelToGo/actions/workflows/ci.yml/badge.svg">
</p>

Expand All @@ -61,7 +61,7 @@ and saying so when data came back incomplete. A range can be given a name (in Th
what it means, and a cell can carry a rule about what may go in it, refused before it is written rather than
flagged afterwards. Conditional formatting re-colours cells from their current values — comparisons,
top/bottom ranks, colour scales and data bars — and round-trips through Excel's own rule format. Bilingual UI
(Thai/English), 2013 automated tests. บันทึกบนคลาวด์และการแก้พร้อมกันหลายคนมีให้เลือกใช้ได้ โดยต่อ backend ของคุณเอง
(Thai/English), 2014 automated tests. บันทึกบนคลาวด์และการแก้พร้อมกันหลายคนมีให้เลือกใช้ได้ โดยต่อ backend ของคุณเอง

---

Expand Down Expand Up @@ -106,7 +106,7 @@ top/bottom ranks, colour scales and data bars — and round-trips through Excel'

---

### 🧪 สิ่งที่เทสต์ 2013 เคสจับไม่ได้
### 🧪 สิ่งที่เทสต์ 2014 เคสจับไม่ได้

เทสต์ของผู้ช่วย AI ใช้ **mock ทั้งหมด** — มันตอบตามที่ผมคิดว่ามันจะตอบ พอเอา API key จริงมายิงคำถามธรรมดา
14 ข้อ **6 คำตอบใช้ฟังก์ชันที่เอนจินนี้ไม่มี** (`TEXTJOIN` `FIND` `RANK.EQ` `SUMPRODUCT` `CEILING` `CHAR`)
Expand All @@ -116,7 +116,7 @@ top/bottom ranks, colour scales and data bars — and round-trips through Excel'
มันจึงตอบคำถาม *"ต่อชื่อทั้งหมดเป็นบรรทัดเดียว"* ด้วย `=SUM(A2:A20)` ซึ่งได้ `0` โดยไม่มีอะไรบอกว่าผิด
**เปลี่ยน error ที่มองเห็น เป็นตัวเลขผิดที่มองไม่เห็น**

**แล้วมันก็เกิดอีก คนละที่** ตอนที่ทุกด่านเขียวหมด — 2013 เทสต์ผ่าน, `axe` ไม่มี violation ทั้งสองหน้า
**แล้วมันก็เกิดอีก คนละที่** ตอนที่ทุกด่านเขียวหมด — 2014 เทสต์ผ่าน, `axe` ไม่มี violation ทั้งสองหน้า
ที่สองความกว้าง — ผมลองไล่คลิก build สาธารณะแบบคนที่เพิ่งเข้ามาครั้งแรกอยู่ชั่วโมงหนึ่ง แล้วเจอสามอย่าง
ที่ไม่มีด่านไหนมองเห็น:

Expand Down Expand Up @@ -381,7 +381,7 @@ npm run dev
| `npm run build` | build เป็นเวอร์ชัน production |
| `npm run start` | รันเวอร์ชันที่ build แล้ว (ต้อง `npm run build` ก่อน) |
| `npm run lint` | ตรวจสอบคุณภาพโค้ดด้วย ESLint |
| `npm test` | รัน unit test 2013 เคสด้วย Vitest |
| `npm test` | รัน unit test 2014 เคสด้วย Vitest |
| `npm run check:readme` | ตรวจว่า README ยังตรงกับโค้ด (ลิงก์/ภาพ/จำนวนเทสต์/โมดูลใหม่/สองภาษาตรงกัน) |
| `npm run check:screens` | ตัวเลขที่พิมพ์อยู่บนภาพหน้าจอยังตรงกับซอร์สไหม |
| `npm run check:rls` | ยิงจริงสองบัญชีบน Supabase ของคุณ ว่าฐานข้อมูลปฏิเสธตามที่ policy เขียนไว้ไหม (ต้องตั้ง env) |
Expand Down Expand Up @@ -1306,6 +1306,9 @@ conditional formatting ไม่นับมันเป็นตัวเลข
เราจึงเขียน OOXML chart part เอง แล้วแทรกลงในไฟล์ zip ที่ ExcelJS สร้างเสร็จ — ดู `xlsxChartXml.ts`
กับ `xlsxCharts.ts` ถ้าการแทรกล้มเหลวจะถอยไปฝังเป็นรูปภาพแบบเดิม เพราะไฟล์ที่มี relationship ห้อย
Excel จะบอกว่า "เสียหาย" แล้วเปิดไม่ขึ้นเลย ซึ่งแย่กว่ากราฟที่หยุดอัปเดตมาก
แต่ละชุดข้อมูลอ้างคอลัมน์ที่มันถูกอ่านมาจริง (#59) คอลัมน์ข้อความที่คั่นอยู่ เช่น "หมายเหตุ" ระหว่างชื่อสาขากับ Q1
ไม่ใช่ชุดข้อมูลแต่ยังกินหนึ่งคอลัมน์ เดิมอ้างอิงนับตามลำดับชุด กราฟใน Excel จึงวาดหมายเหตุแทน Q1 และ Q1 แทน Q2
ทั้งที่กราฟในแอปถูก

**ยังไม่รองรับ:** กราฟใน PDF ยังเป็นรูปภาพ (jsPDF ไม่มีกราฟของตัวเอง) · กราฟวงกลมยังวาดได้ทีละชุดข้อมูล ·
ไม่ได้ใส่สี/ธีมลงใน chart part ปล่อยให้โปรแกรมที่เปิดใช้ค่าเริ่มต้นของตัวเอง
Expand Down Expand Up @@ -2644,7 +2647,7 @@ stack ของ `font-mono` จึงต่อท้ายด้วย Plex Sans
| `@anthropic-ai/sdk` | เชื่อมต่อ Claude API สำหรับผู้ช่วย AI |
| `lucide-react` | ไอคอน UI |
| `clsx` | รวม className แบบมีเงื่อนไข |
| `vitest` | unit test เอนจินคำนวณสูตร, ตรรกะเรียงข้อมูล, แปลง JSON เป็นตาราง, การแบ่งหน้า/rate limit, แม่แบบ/รูปแบบจากไฟล์ และบล็อกข้อมูลสด (2013 เคส) |
| `vitest` | unit test เอนจินคำนวณสูตร, ตรรกะเรียงข้อมูล, แปลง JSON เป็นตาราง, การแบ่งหน้า/rate limit, แม่แบบ/รูปแบบจากไฟล์ และบล็อกข้อมูลสด (2014 เคส) |

> **หมายเหตุ:** ไม่ได้ใช้ไลบรารีคำนวณสูตรสำเร็จรูป (เช่น HyperFormula) แต่เขียน **เอนจินคำนวณสูตรขึ้นเอง**
> ทั้ง tokenizer, parser, evaluator และฟังก์ชันต่างๆ เพื่อควบคุมพฤติกรรมได้เต็มที่ ดูรายละเอียดที่หัวข้อ
Expand Down Expand Up @@ -3553,13 +3556,13 @@ B อ่านและแก้ได้ แต่ยังยึดเป็
## 🧪 การทดสอบ

```bash
npm test # 2013 เคส ใน 145 ไฟล์ ด้วย Vitest
npm test # 2014 เคส ใน 145 ไฟล์ ด้วย Vitest
```

โฟกัสเทสต์ไปที่ **เอนจินคำนวณสูตร, ตรรกะเรียงข้อมูล, การแปลง JSON เป็นตาราง, การไล่ดึงหน้าถัดไป, การถอยเมื่อโดน rate limit, แม่แบบจากไฟล์ Excel, กฎจัดรูปแบบตามเงื่อนไข และการวางบล็อกข้อมูลสด** — ส่วนที่เป็น pure function ล้วน ไม่ต้องพึ่ง React/DOM
จึงเทสต์ได้เร็วและมั่นใจได้สูง

**แต่ 2013 เคสนั้นไม่มีสักเคสที่เปิดแอป** และบั๊กที่โปรเจกต์นี้เจอด้วยการนั่งกดเอง อยู่ที่รอยต่อระหว่างชิ้นส่วนที่ผ่านเทสต์
**แต่ 2014 เคสนั้นไม่มีสักเคสที่เปิดแอป** และบั๊กที่โปรเจกต์นี้เจอด้วยการนั่งกดเอง อยู่ที่รอยต่อระหว่างชิ้นส่วนที่ผ่านเทสต์
ทุกตัวแทบทั้งหมด — ปุ่ม "+ แถว" บนแถบเครื่องมือเรียก `addRow` ที่ไม่ประกาศอะไรเลย ขณะที่ `insertRowAtSelection`
ข้าง ๆ ประกาศถูกต้อง (ทั้งคู่เทสต์ผ่าน) · ผู้ช่วย AI ส่งช่วงที่รวมหัวคอลัมน์ไปด้วย เพราะตัวประกอบ context อ่าน
`sheet.cells` ที่เป็นสูตรดิบแทนค่าที่คำนวณแล้ว (ทั้งคู่เทสต์ผ่าน) · ปุ่มใหม่หนึ่งปุ่มดันปุ่มสลับภาษาตกขอบจอไป 42px
Expand Down Expand Up @@ -3659,9 +3662,9 @@ seed ถูกปักไว้ ด่านที่แดงจึงแด
ในตารางแล้วสองข้อในflow ที่สามตกทันที (ครั้งแรกที่ลองปิดยังเขียวอยู่ เพราะ `case` ที่แทรกเข้าไปไปอยู่หลัง
`case "ArrowRight"` เดิม กลายเป็นโค้ดตาย — การพิสูจน์ต้องเช็กด้วยว่าของที่ตั้งใจทำให้พังนั้นพังจริง)

> **2013 เคสผ่านหมด แต่ 43% ของคำตอบจากผู้ช่วย AI ใช้ไม่ได้** — เพราะเทสต์ส่วนนั้น mock ตัวโมเดลไว้
> **2014 เคสผ่านหมด แต่ 43% ของคำตอบจากผู้ช่วย AI ใช้ไม่ได้** — เพราะเทสต์ส่วนนั้น mock ตัวโมเดลไว้
> มันจึงตอบตามที่คนเขียนเทสต์คิดว่ามันจะตอบ จำนวนเทสต์บอกได้แค่ว่าเราถามอะไรไปบ้าง ไม่ได้บอกว่าเราถาม
> ครบหรือยัง เรื่องนี้เจอได้ก็ต่อเมื่อเอา API key จริงมายิง — ดู [สิ่งที่เทสต์ 2013 เคสจับไม่ได้](#-สิ่งที่เทสต์-2013-เคสจับไม่ได้)
> ครบหรือยัง เรื่องนี้เจอได้ก็ต่อเมื่อเอา API key จริงมายิง — ดู [สิ่งที่เทสต์ 2014 เคสจับไม่ได้](#-สิ่งที่เทสต์-2014-เคสจับไม่ได้)
> และรันซ้ำเองได้ด้วย `npm run check:ai`

| ไฟล์ | เคส | ทดสอบอะไร |
Expand Down
Binary file modified public/screenshots/27-landing-stats.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/screenshots/en/27-landing-stats.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions public/screenshots/screenshots.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"27-landing-stats.png": {
"paletteFormulas": 37,
"engineFunctions": 66,
"tests": 2013,
"tests": 2014,
"securityTests": 325
},
"38-landing-problems.png": {
Expand All @@ -21,7 +21,7 @@
"en/27-landing-stats.png": {
"paletteFormulas": 37,
"engineFunctions": 66,
"tests": 2013,
"tests": 2014,
"securityTests": 325
},
"en/38-landing-problems.png": {
Expand Down
Binary file modified public/social-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading