mirror of
https://github.com/hanzoai/extension.git
synced 2026-10-31 21:57:06 +00:00
The surface a non-developer actually uses — a Microsoft Office task-pane
add-in that puts Hanzo AI inside the document. Select text or a range,
ask, insert. Distinct from the browser extension and the IDE extensions;
same backend as everything else: model calls through api.hanzo.ai/v1
(OpenAI-compatible), sign-in via Hanzo IAM (hanzo.id, auth-code + PKCE,
HIP-0111). No new API surface.
Decomplected so the logic is pure and unit-tested (26 tests), Office.js
glue kept thin:
- chat.ts — request/response shaping (buildMessages fences the selection
as data; extractContent tolerates the gateway's response shapes)
- pkce.ts — PKCE + authorize URL, verified against the RFC 7636 S256 test
vector
- host.ts — per-host read/insert; Excel range↔text helpers tested
- config.ts — canonical endpoints pinned (api.hanzo.ai, /v1/iam/oauth/*)
- taskpane/auth/commands — the Office.js + Dialog-API glue
manifest.xml passes `office-addin-manifest validate` ("The manifest is
valid" — Word/Excel/PowerPoint on web, Windows, Mac). build.js stamps the
hosting base URL (office.hanzo.ai, override for localhost dev) into a
sideloadable/AppSource-submittable dist/manifest.xml.
CI runs the tests and builds+zips a sideloadable artifact. Publishing to
AppSource needs the dist hosted at office.hanzo.ai + a hanzo-office IAM
client + a Partner Center submission (documented in the README).
27 lines
918 B
TypeScript
27 lines
918 B
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { excelValuesToText, splitLines } from '../src/host';
|
|
|
|
describe('excelValuesToText', () => {
|
|
it('flattens a 2-D range to TSV rows', () => {
|
|
expect(excelValuesToText([['a', 'b'], ['c', 'd']])).toBe('a\tb\nc\td');
|
|
});
|
|
it('renders null/undefined cells as empty', () => {
|
|
expect(excelValuesToText([[1, null], [undefined, 4]])).toBe('1\t\n\t4');
|
|
});
|
|
it('empty range → empty string', () => {
|
|
expect(excelValuesToText([])).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('splitLines', () => {
|
|
it('one line per cell down a column', () => {
|
|
expect(splitLines('a\nb\nc')).toEqual([['a'], ['b'], ['c']]);
|
|
});
|
|
it('drops trailing blank lines so a stray newline does not clear a cell', () => {
|
|
expect(splitLines('a\nb\n\n')).toEqual([['a'], ['b']]);
|
|
});
|
|
it('single line stays a 1x1', () => {
|
|
expect(splitLines('solo')).toEqual([['solo']]);
|
|
});
|
|
});
|