tachlon 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +154 -3
  2. package/dist-lib/LiberationMono-Regular.woff2 +0 -0
  3. package/dist-lib/LiberationSans-Bold.woff2 +0 -0
  4. package/dist-lib/LiberationSans-Regular.woff2 +0 -0
  5. package/dist-lib/adapters/validate.js +159 -0
  6. package/dist-lib/app_bg.wasm +0 -0
  7. package/dist-lib/cli/args.js +36 -0
  8. package/dist-lib/cli/csp.js +68 -0
  9. package/dist-lib/cli/detect.js +86 -0
  10. package/dist-lib/cli/doctor.js +205 -0
  11. package/dist-lib/cli/gpu.js +66 -0
  12. package/dist-lib/cli/init.js +103 -0
  13. package/dist-lib/cli/main.js +7 -0
  14. package/dist-lib/cli/probe.js +35 -0
  15. package/dist-lib/cli/run.js +92 -0
  16. package/dist-lib/cli/template.js +52 -0
  17. package/dist-lib/engine-size.json +1 -0
  18. package/dist-lib/errors.js +44 -0
  19. package/dist-lib/gantt.css +2 -0
  20. package/dist-lib/product.js +4 -0
  21. package/dist-lib/react.js +75 -0
  22. package/dist-lib/schema.json +38 -0
  23. package/dist-lib/src-CTpvK70m.js +7212 -0
  24. package/dist-lib/tachlon.js +1 -0
  25. package/dist-lib/types/adapters/bryntum.d.ts +68 -0
  26. package/dist-lib/types/adapters/dhtmlx.d.ts +96 -0
  27. package/dist-lib/types/adapters/index.d.ts +7 -0
  28. package/dist-lib/types/adapters/plain.d.ts +35 -0
  29. package/dist-lib/types/adapters/validate.d.ts +26 -0
  30. package/dist-lib/types/api.d.ts +207 -0
  31. package/dist-lib/types/commands.d.ts +76 -0
  32. package/dist-lib/types/document.d.ts +59 -0
  33. package/dist-lib/types/edit-result.d.ts +51 -0
  34. package/dist-lib/types/errors.d.ts +33 -0
  35. package/dist-lib/types/events.d.ts +80 -0
  36. package/dist-lib/types/filter-names.d.ts +5 -0
  37. package/dist-lib/types/index.d.ts +92 -0
  38. package/dist-lib/types/mount-load.d.ts +30 -0
  39. package/dist-lib/types/open-schedule.d.ts +81 -0
  40. package/dist-lib/types/product.d.ts +2 -0
  41. package/dist-lib/types/react.d.ts +32 -0
  42. package/dist-lib/types/set-filter.d.ts +20 -0
  43. package/dist-lib/types/view-state.d.ts +95 -0
  44. package/package.json +61 -5
@@ -0,0 +1,33 @@
1
+ import { PRODUCT_NAME } from "./product.ts";
2
+ /** Every cause `mount` names, with the sentence that says what to do. */
3
+ declare const FIXES: {
4
+ readonly DATA_INVALID: "Correct the fields `message` lists and call `mount` again; `validate(data)` checks plain task data against the same rules without mounting.";
5
+ readonly ENGINE_FETCH: "Serve the engine file (`app_bg.wasm`, the file the chart's script names with `new URL`) from the address the page asks for, with status 200; the `message` has the status.";
6
+ readonly ENGINE_NOT_WASM: "The address answers with something other than the engine. Serve the file `app_bg.wasm` itself, as `application/wasm`, and make a single-page fallback route skip `.wasm` files.";
7
+ readonly ENGINE_LOAD_FAILED: "The file is the engine, but it did not start. Rebuild so `app.js` and `app_bg.wasm` come from the same build, and read the browser console and the error in `cause` for the underlying error.";
8
+ readonly ENGINE_CSP: "Add `'wasm-unsafe-eval'` to the page's `script-src` (or `default-src`) in its Content-Security-Policy, so the browser may compile the engine.";
9
+ readonly GPU_NONE: "The browser has neither WebGPU nor WebGL2 here. Try a current Chrome, Edge, Firefox or Safari with hardware acceleration on; a headless or remote desktop session often has none.";
10
+ readonly FILE_UNREADABLE: "The browser could not read the `File` given as `schedule`: it was moved, deleted or is locked. Pick the file again and call `mount` again.";
11
+ };
12
+ export type Cause = keyof typeof FIXES;
13
+ /** A stable name for a cause: the product name in capitals, an underscore and the cause, like `<NAME>_ENGINE_FETCH`. */
14
+ export type ErrorCode = `${Uppercase<typeof PRODUCT_NAME>}_${Cause}`;
15
+ /** Where the codes are explained: a heading in `docs/WASM_API.md`, the page that lists every code with its `fix`. */
16
+ export declare const ERROR_DOCS = "docs/WASM_API.md#mount-error-codes";
17
+ /** The code for a cause. */
18
+ export declare function codeFor(cause: Cause): ErrorCode;
19
+ /** The sentence that says what to do about `code`. */
20
+ export declare function fixFor(code: ErrorCode): string;
21
+ /** What a probe of the engine's address found: the status (`null` when the request itself threw) and the first bytes of the answer. */
22
+ export interface EngineProbe {
23
+ status: number | null;
24
+ head: Uint8Array;
25
+ }
26
+ /** The code and the one line for a failed engine load, from the probe and from what `init` threw. A pure function of both. */
27
+ export declare function engineFailure(probe: EngineProbe, thrown: unknown): {
28
+ code: ErrorCode;
29
+ message: string;
30
+ };
31
+ /** `GPU_NONE` when the browser has neither a WebGPU adapter (`gpu`) nor WebGL2 (`webgl2`, asked only when `gpu` is false), else `undefined`. */
32
+ export declare function graphicsCode(gpu: boolean, webgl2: () => boolean): ErrorCode | undefined;
33
+ export {};
@@ -0,0 +1,80 @@
1
+ import type { Status } from "./api.js";
2
+ import type { PickerProject } from "./open-schedule.js";
3
+ import type { ViewState } from "./view-state.js";
4
+ /** What each event carries. */
5
+ export interface GanttEvents {
6
+ /** The selected task changed to `task` (a row or a bar was clicked, or a key moved the selection). Clearing the selection fires nothing. */
7
+ select: {
8
+ task: number;
9
+ };
10
+ /** The schedule changed by an edit, an undo or a redo; `revision` is `Status.revision` after it. */
11
+ edit: {
12
+ revision: number;
13
+ };
14
+ /** A bar drag was released and moved `task`, whose early start is now `start` (minutes from the origin Monday). */
15
+ move: {
16
+ task: number;
17
+ start: number;
18
+ };
19
+ /** A file was opened in place; `epoch` is `Status.epoch` after it. */
20
+ open: {
21
+ epoch: number;
22
+ };
23
+ /** The first frame was drawn. A handler added later is still called, once. */
24
+ load: Record<string, never>;
25
+ /** The view state a toolbar shows (`Gantt.state()`) changed: a filter, the lookahead window, the row or task count, the zoom level, the selection, the move report. At most once a frame. */
26
+ change: ViewState;
27
+ /**
28
+ * A file could not be opened, a frame failed, or WebGPU failed and the chart started again on WebGL2 in its root (`backend`, once, with
29
+ * the reason); `message` is the line a person sees. `fatal`: the chart has stopped (a panic, frames that keep failing, or a switch to
30
+ * WebGL2 that could not start), and only a reload of the page brings it back; the host decides whether to do that.
31
+ */
32
+ error: {
33
+ source: ErrorSource;
34
+ message: string;
35
+ fatal: boolean;
36
+ projects?: readonly PickerProject[];
37
+ };
38
+ /** The chart's find key (Ctrl+F, Cmd+F) was pressed while focus was in it: a host puts focus in its find box. With a handler the browser's own find does not open. */
39
+ find: Record<string, never>;
40
+ /** The chart's status key (`s`) was pressed while focus was in it: a host shows or hides its status line. */
41
+ "toggle-status": Record<string, never>;
42
+ /** The chart's open key (Ctrl+O, Cmd+O) was pressed while focus was in it: a host shows its file chooser. With no handler the key is left to the browser. */
43
+ "choose-file": Record<string, never>;
44
+ /** The chart's keys key (`?`) was pressed while focus was in it: a host shows or hides its list of keys. */
45
+ "toggle-keys": Record<string, never>;
46
+ }
47
+ /** What an `error` event is about. */
48
+ export type ErrorSource = "open" | "frame" | "backend";
49
+ /** The events a key of the chart asks for: the host acts on them. */
50
+ export type KeyRequest = "find" | "toggle-status" | "choose-file" | "toggle-keys";
51
+ export type GanttEventName = keyof GanttEvents;
52
+ export type GanttHandler<K extends GanttEventName> = (detail: GanttEvents[K]) => void;
53
+ /**
54
+ * `Gantt.on`: call `handler` when the chart `select`s a task, `edit`s the schedule, `move`s a bar by a drag, `open`s a file, `load`s (its
55
+ * first frame), its view state `change`s or it reports an `error`, and when its find key asks for the host's `find` box or its status
56
+ * key asks to `toggle-status`. Each fires once per change, read from the loop's `Status`, never per frame. Returns the function
57
+ * that removes the handler. The detail of each is `GanttEvents`.
58
+ */
59
+ export type On = <K extends GanttEventName>(name: K, handler: GanttHandler<K>) => () => void;
60
+ /** The member `Gantt` takes from here. */
61
+ export interface HasOn {
62
+ on: On;
63
+ }
64
+ export interface Events {
65
+ /** Call `handler` for each `name` event. Returns the function that removes it. */
66
+ on: On;
67
+ /** Fire the events `s` shows against the status before it. */
68
+ watch(s: Status): void;
69
+ /** Fire `error`; `fatal` when the chart has stopped. */
70
+ fail(source: ErrorSource, message: string, fatal?: boolean, projects?: readonly PickerProject[]): void;
71
+ /**
72
+ * A new app drives the chart (the switch to WebGL2): the next status is compared with nothing, so its counters fire no event, and its
73
+ * view state fires one `change`, since the new start's view is not the one the last `change` carried.
74
+ */
75
+ restart(): void;
76
+ /** Fire a key's event. True when a handler heard it, so the key is taken from the browser. */
77
+ request(name: KeyRequest): boolean;
78
+ }
79
+ /** `view` turns a frame's `Status` into the view state; it returns the same object while nothing the state carries moved. */
80
+ export declare function createEvents(view: (s: Status) => ViewState): Events;
@@ -0,0 +1,5 @@
1
+ import type { Status } from "./api.js";
2
+ /** The row filter that is on, one at a time. `"status"` and `"code"` carry their values in `statusFilter` and `codeFilter`. */
3
+ export type RowFilterName = "off" | "critical" | "longestPath" | "nearCritical" | "slipped" | "floatLost" | "joinedCritical" | "leftCritical" | "moved" | "status" | "code";
4
+ /** The public name of a status filter name. */
5
+ export declare const publicFilterName: (name: Status["filter"]) => RowFilterName;
@@ -0,0 +1,92 @@
1
+ import { type ChecksReport, type ExportResult, type LoadResult, type LoadSource } from "./document.js";
2
+ import { type OpenOptions } from "./open-schedule.js";
3
+ import { type Commands } from "./commands.js";
4
+ import { type HasOn } from "./events.js";
5
+ import { type FilterInputs, type ViewState } from "./view-state.js";
6
+ import { PRODUCT_NAME } from "./product.js";
7
+ import type { PlainLink, PlainTask } from "./adapters/plain.js";
8
+ export { fromBryntum, fromDhtmlx, fromTasks, PlainDataError, validate } from "./adapters/index.js";
9
+ export type { BryntumResult, DhtmlxOptions, DhtmlxResult, PlainIssue, PlainLink, PlainOptions, PlainResult, PlainTask, PlainValidation, ScheduleDocument } from "./adapters/index.js";
10
+ /** What `mount` takes: a `schedule`, or `tasks` and `links` as `fromTasks` reads them. */
11
+ export interface MountOptions {
12
+ /** A schedule file to read (XER or MS Project XML, every project of it, or a `.gantt` document), or the demo or the stress fixture at `n` tasks. */
13
+ schedule?: File | {
14
+ demo: number;
15
+ } | {
16
+ stress: number;
17
+ };
18
+ /** Plain tasks to draw when there is no `schedule`, and the links between them (`fromTasks`). */
19
+ tasks?: PlainTask[];
20
+ links?: PlainLink[];
21
+ /** The licence key; the root's `data-<product>-license` reads `"licensed"` or `"trial"`. A local page is always licensed; a chart that reads `"trial"` shows a small "Unlicensed" mark and works as before. */
22
+ licenseKey?: string;
23
+ /** Draw with WebGL2 and never try WebGPU. Without it a WebGPU failure switches the chart to WebGL2 in its root, and `on("error")` says so. */
24
+ forceWebgl?: boolean;
25
+ /** Where to fetch the engine file (`app_bg.wasm`) from, a URL or an address read against the page's own. Without it the chart loads the file beside its script (`dist-lib/app_bg.wasm`). When `preloadEngine` already started the load, its `wasmUrl` is the one used. */
26
+ wasmUrl?: string | URL;
27
+ }
28
+ /** What `mount` returns: the chart, as a host page may drive it. */
29
+ export interface Gantt extends HasOn {
30
+ /**
31
+ * Replace the schedule with `file`. Resolves when the attempt has ended; a file that cannot be opened is an `error` event (source `"open"`), not a throw.
32
+ * `expectedNotes` are the beginnings of import notes the host knows its own file will report (a generated sample): a line that
33
+ * starts with one is left out of `ViewState.importNotes`. `window.gantt.unsupported` still holds every line the reader reported.
34
+ * A file with several projects stops at their list, which the `error` event carries as `projects`; `project` (an id from that list, or `"all"`) opens it.
35
+ */
36
+ open(file: File, options?: OpenOptions): Promise<void>;
37
+ /**
38
+ * Download the open schedule as a schedule document (gzipped JSON), named after the opened file with `.gantt`, `untitled.gantt`
39
+ * for a sample. Never writes over the opened file. Resolves once the download has started; it does not wait for the file to be
40
+ * written. It clears the unsaved mark beside the file name, as of the schedule when the save began. The write runs on the main thread.
41
+ */
42
+ save(): Promise<void>;
43
+ /** Download the visible list as `<name>.csv` (`untitled.csv` for a sample), the rows and columns Ctrl+C copies, as the hosted file menu's "Export the list as CSV". */
44
+ exportCsv(): void;
45
+ /** Download a picture of the first printed page as `<name>.png` (`untitled.png` for a sample): 2112 by 1632 pixels, drawn from `App.print_pages` without the chart's canvas. Resolves once the download has started. */
46
+ exportImage(): Promise<void>;
47
+ /**
48
+ * Replace the schedule with a document (the JSON `toDocument` returns) or a file (XER, MS Project XML, or a gzipped document). Resolves
49
+ * `{ ok: true }`, or `{ ok: false, error }` with the schedule as it was: a bad source is a result, never a throw. Unsaved edits are
50
+ * replaced without asking.
51
+ */
52
+ load(source: LoadSource): Promise<LoadResult>;
53
+ /** The open schedule as a document, the JSON that `save` writes (gzipped) and `load({ document })` reads. Reads the session and changes nothing. */
54
+ toDocument(): unknown;
55
+ /** The open schedule as a P6 XER file: `bytes` and `leftOut`, one line for each thing the file cannot carry. Throws when a date cannot be written. */
56
+ exportXer(): ExportResult;
57
+ /** The open schedule as an MS Project XML file, as `exportXer` returns it. */
58
+ exportMspdi(): ExportResult;
59
+ /** The schedule-quality counts of the open schedule, as the Checks panel lists them: no pass or fail, just the counts. Reads the session and changes nothing; ask it when the panel opens, not every frame. */
60
+ checks(): ChecksReport;
61
+ /** The backend that is drawing: `"WebGL2"` once a WebGPU failure has switched the chart. */
62
+ backend(): "WebGPU" | "WebGL2";
63
+ /** The view state a toolbar shows: the filters, the lookahead window, the row and task counts, the zoom level, the selection, the move report and the backend. `on("change")` fires when it changes. */
64
+ state(): ViewState;
65
+ /** What the filter controls offer: the activity codes, whether tasks have activity IDs, whether the baseline holds float. Read it again after an `open`, an `edit`, or a `change` whose `baseline` moved. */
66
+ filterInputs(): FilterInputs;
67
+ /** Whether a cell is being edited or a bar or link is being pressed or dragged: the edit commands refuse then (`"busy"`). It has no event; ask it when a command needs to. */
68
+ busy(): boolean;
69
+ /**
70
+ * Take the chart down: stop the frame loop, remove what it put on `window`, `document` and the media queries, disconnect its observers,
71
+ * free the WASM app (which lets the GPU device go) and empty the root. Safe to call twice. Every other method throws once it has run
72
+ * (`open` and `save` reject), and a call already waiting on the app, such as an `open` that has not resolved, is not waited for.
73
+ */
74
+ destroy(): void;
75
+ commands: Commands;
76
+ }
77
+ export type { Commands, ScrollTarget } from "./commands.js";
78
+ export type { GanttEvents } from "./events.js";
79
+ export type { OpenOptions, PickerProject } from "./open-schedule.js";
80
+ export type { CompareResult, EditRefusal, EditResult, LinkResult, Refusal, RowResult } from "./edit-result.js";
81
+ export type { Filter, PlainFilter } from "./set-filter.js";
82
+ export type { CodeValue, FilterInputs, RowFilterName, StatusKind, ViewState, ZoomLevel } from "./view-state.js";
83
+ export { MountError, preloadEngine } from "./mount-load.js";
84
+ export { PRODUCT_NAME };
85
+ /**
86
+ * Start the chart in `root`: the page gives a `div` with the class `gantt` and a box with a height, and the chart
87
+ * puts its own elements inside it, with ids of their own, so a page may hold several charts; its stylesheets come
88
+ * with this module. Rejects with a `MountError` (a `code`, a `fix` and `docs` when the cause is known) when the engine cannot load, the schedule cannot be read or the first frame cannot be set up; any
89
+ * other failure is a bug and is thrown as it is. A WebGPU failure, at the start or in a later frame, starts the
90
+ * chart again on WebGL2 in the same root with the same schedule (`webgl-switch.ts`); the page is never reloaded.
91
+ */
92
+ export declare function mount(root: HTMLDivElement, options: MountOptions): Promise<Gantt>;
@@ -0,0 +1,30 @@
1
+ import { type ErrorCode } from "./errors.js";
2
+ import type { MountOptions } from "./index.js";
3
+ /** Why `mount` could not start the chart: `stage` is where it failed, and `message` is one line for a person. */
4
+ export declare class MountError extends Error {
5
+ readonly stage: "engine" | "schedule" | "render";
6
+ /** A stable name for the cause when there is one (`errors.ts` lists them); `fix` says what to do about it and `docs` where it is explained. */
7
+ readonly code?: ErrorCode;
8
+ readonly fix?: string;
9
+ readonly docs?: string;
10
+ constructor(stage: "engine" | "schedule" | "render", message: string, options?: ErrorOptions & {
11
+ code?: ErrorCode;
12
+ });
13
+ }
14
+ /** The render `MountError` for a failed start. In a browser with no WebGPU adapter and no WebGL2 it is `GPU_NONE`, whatever the engine's own message says; the probes run only on this failure path. */
15
+ export declare function renderError(message: string, cause: unknown): Promise<MountError>;
16
+ /** A host page's inline script may start `WebAssembly.compileStreaming` on the engine's response and keep the promise here, so the compile does not wait for this module to load (gs#1354). */
17
+ declare global {
18
+ interface Window {
19
+ __engineModule?: Promise<WebAssembly.Module>;
20
+ }
21
+ }
22
+ /** Start loading the engine, or join the load already started: a host page calls it first thing, so the file downloads and compiles while the page reads its data (gs#1336). Settles with no value when the engine has loaded. */
23
+ export declare function preloadEngine(options?: Pick<MountOptions, "wasmUrl">): Promise<void>;
24
+ /** What a chart starts on: the schedule `mount` was given (`undefined` for its `tasks` and `links`), or the document of the one it held when it switched to WebGL2. */
25
+ export type StartSource = MountOptions["schedule"] | {
26
+ document: unknown;
27
+ } | {
28
+ tasks: MountOptions["tasks"];
29
+ links: MountOptions["links"];
30
+ };
@@ -0,0 +1,81 @@
1
+ /** The largest file the page reads. A larger one gets a message and is not read at all. */
2
+ export declare const MAX_OPEN_BYTES: number;
3
+ /**
4
+ * Where the time of one open went, in milliseconds on `performance.now()`. The phases are back to back and do
5
+ * not overlap, so `read + paint + copy + parse + build + compute + swap + firstFrame` is `total` (up to the
6
+ * rounding of the clock). `parse`, `build` and `compute` are `LoadedSchedule.timings()`; the rest are the page's.
7
+ * A value that is not a finite number, or is negative, is an invalid measurement: it is kept as it is (NaN stays
8
+ * NaN, a negative stays negative), never turned into 0, and `measure.mjs` rejects the open that holds it.
9
+ */
10
+ export interface OpenTimings {
11
+ /** The change or drop event to `file.arrayBuffer()` resolved. */
12
+ read: number;
13
+ /** The wait that lets "Opening…" paint before the parse, which does not yield: one frame and a task. */
14
+ paint: number;
15
+ /**
16
+ * The call to `parse_bytes` minus its three Rust phases: the copy of the bytes into linear memory, building the
17
+ * result, and the clock calls around the phases. It is a remainder, so it would absorb a wrong Rust phase; the
18
+ * check that does not is `rustSpan` against the phases. Not clamped: a negative `copy` is an invalid measurement.
19
+ */
20
+ copy: number;
21
+ /** Detect the format, read the file, map it into the engine schedule (Rust). */
22
+ parse: number;
23
+ /** The display data, the outline and the engine schedule (Rust). */
24
+ build: number;
25
+ /** The solve, the row order and the in-progress date check (Rust). */
26
+ compute: number;
27
+ /** `parse_bytes` returned to `App.open` returned, with the reads of the result in between. */
28
+ swap: number;
29
+ /** `App.open` returned to the first frame submitted after it (the first increment of `Status.frames`); not the display's vblank. */
30
+ firstFrame: number;
31
+ /** The event to the first frame. */
32
+ total: number;
33
+ /**
34
+ * Rust's first to last clock read of `load_bytes`: the same reads that bound the three phases, so it equals
35
+ * `parse + build + compute` up to float rounding however long the process was paused between two reads (gs#248).
36
+ * It is not a phase and not part of `total`, and `measure.mjs` rejects an open where it differs by 3 ms or more.
37
+ */
38
+ rustSpan: number;
39
+ }
40
+ /** What the page records about a successful open. */
41
+ export interface OpenedFile {
42
+ name: string;
43
+ bytes: number;
44
+ /** "xer" or "mspdi", chosen by the file's content, never its name. */
45
+ format: string;
46
+ /** Tasks in the schedule, summary rows included. */
47
+ tasks: number;
48
+ /**
49
+ * Only for a file with several projects: the milliseconds the first pass took (the read and the parse that
50
+ * stopped at the project list). The open that counts starts when the person chooses.
51
+ */
52
+ probeMs?: number;
53
+ /**
54
+ * Set at the first frame after the swap, on this same object, so it is missing for the moment between the swap
55
+ * and that frame (and for good if a newer open replaces this one first).
56
+ */
57
+ timings?: OpenTimings;
58
+ }
59
+ /** One row of the project list a multi-project XER file carries on its `ChooseProject` error. */
60
+ export interface PickerProject {
61
+ /** The P6 `proj_id`, as the text `parse_bytes` takes for `selection`. */
62
+ id: string;
63
+ name: string;
64
+ activities: number;
65
+ }
66
+ /** The projects of a `ChooseProject` error, or null for any other thrown value. */
67
+ export declare function chooseProjects(err: unknown): PickerProject[] | null;
68
+ /** What `Gantt.open` takes: the notes the host expects, and the project of a file with several (`"all"` for every one). */
69
+ export interface OpenOptions {
70
+ expectedNotes?: readonly string[];
71
+ project?: string;
72
+ }
73
+ export interface OpenControls {
74
+ /**
75
+ * Open `file`: the size limit, the messages, the project list and the `open` event. Resolves when that open has ended (a file
76
+ * with several projects ends it at the project list, which the `error` event carries); a failure is an `error` event, never thrown.
77
+ */
78
+ openFile(file: File, options?: OpenOptions): Promise<void>;
79
+ /** Call once per frame, after `App.frame()`, with `Status.frames`; it completes the timings of the last open at the first new frame. */
80
+ frame(frames: number): void;
81
+ }
@@ -0,0 +1,2 @@
1
+ /** The product's name, as a person sees it in the tab, the toolbar and the hosted page. */
2
+ export declare const PRODUCT_NAME = "Tachlon";
@@ -0,0 +1,32 @@
1
+ import { type CSSProperties, type JSX } from "react";
2
+ import { type GanttEvents, type MountOptions } from "./index.js";
3
+ /** The prop that takes each event of `Gantt.on`. */
4
+ declare const EVENTS: {
5
+ readonly select: "onSelect";
6
+ readonly edit: "onEdit";
7
+ readonly move: "onMove";
8
+ readonly open: "onOpen";
9
+ readonly load: "onLoad";
10
+ readonly change: "onChange";
11
+ readonly error: "onError";
12
+ readonly find: "onFind";
13
+ readonly "toggle-status": "onToggleStatus";
14
+ readonly "choose-file": "onChooseFile";
15
+ readonly "toggle-keys": "onToggleKeys";
16
+ };
17
+ /**
18
+ * The mount options, one prop each, and a callback for each event: `onSelect({ task })`, `onMove({ task, start })` and the rest of `GanttEvents`.
19
+ * A handler is called with the latest function the component was given. A handler for `find` or `choose-file` takes that key from the browser,
20
+ * as `Gantt.on` does, so give one only when the app answers the key. The chart mounts again when `schedule`, `tasks`, `links`, `licenseKey` or
21
+ * `forceWebgl` changes (compared by reference, as a hook's dependencies are), so keep `tasks` and `links` stable between renders.
22
+ * `className` and `style` are the chart's root, where the theme tokens go; the root has no size of its own, so give it one. The page imports
23
+ * the chart's stylesheet. A start that fails is thrown while rendering, for an error boundary.
24
+ */
25
+ export type GanttProps = MountOptions & {
26
+ className?: string;
27
+ style?: CSSProperties;
28
+ } & {
29
+ [K in keyof GanttEvents as (typeof EVENTS)[K]]?: (detail: GanttEvents[K]) => void;
30
+ };
31
+ export declare function Gantt(props: GanttProps): JSX.Element;
32
+ export {};
@@ -0,0 +1,20 @@
1
+ import { type RowFilterName } from "./filter-names.js";
2
+ import type { CodeValue, StatusKind } from "./view-state.js";
3
+ /** The row filters that take no value. */
4
+ export type PlainFilter = Exclude<RowFilterName, "status" | "code">;
5
+ /**
6
+ * What `Commands.setFilter` takes. A row filter replaces the one that is on; `"off"` turns the row filter off. `{ status }` and
7
+ * `{ code }` are the row filters that take a value. `{ lookahead }` sets the window (a day `YYYY-MM-DD` and 1 to 520 weeks) or,
8
+ * with null, removes it; the window combines with the row filter and leaves it as it is. `setFilter(state().filter)` does not
9
+ * type-check for `"status"` and `"code"` by design: those two need their value (`{ status }`, `{ code }`).
10
+ */
11
+ export type Filter = PlainFilter | {
12
+ status: StatusKind;
13
+ } | {
14
+ code: CodeValue;
15
+ } | {
16
+ lookahead: {
17
+ start: string;
18
+ weeks: number;
19
+ } | null;
20
+ };
@@ -0,0 +1,95 @@
1
+ import type { Status } from "./api.js";
2
+ import { type RowFilterName } from "./filter-names.js";
3
+ export type { RowFilterName } from "./filter-names.js";
4
+ /** The time axis's unit at the current zoom. */
5
+ export type ZoomLevel = "hour" | "day" | "week" | "month" | "year";
6
+ /** The task states the Status filter keeps. */
7
+ export type StatusKind = "in-progress" | "not-started" | "complete";
8
+ /** An activity code type and one of its values, as `filterInputs().activityCodes` lists them. */
9
+ export interface CodeValue {
10
+ type: string;
11
+ value: string;
12
+ }
13
+ /** What `Gantt.state()` returns and the `change` event carries. Frozen: a host reads it, the chart never changes it. */
14
+ export interface ViewState {
15
+ /** The row filter that is on, `"off"` for none. */
16
+ filter: RowFilterName;
17
+ /** The Status filter's state while `filter` is `"status"`, else null. */
18
+ statusFilter: StatusKind | null;
19
+ /** The activity code filter while `filter` is `"code"`, else null. */
20
+ codeFilter: CodeValue | null;
21
+ /** The lookahead window, which combines with the row filter. While `on` is false, `start` and `weeks` are what turning it on would use. */
22
+ lookahead: {
23
+ on: boolean;
24
+ start: string;
25
+ weeks: number;
26
+ };
27
+ /** The rows the filters and the collapsed summaries leave. */
28
+ rowCount: number;
29
+ /** The activities (leaf tasks) the row filter and the lookahead window keep, or null while neither is on. */
30
+ activityCount: number | null;
31
+ /** The live tasks of the schedule, rows or not. */
32
+ taskCount: number;
33
+ /** The time axis's unit at this zoom. */
34
+ zoomLevel: ZoomLevel;
35
+ /** The selected task with the counts of its driving predecessors and driven successors, or null. */
36
+ selection: {
37
+ task: number;
38
+ backCount: number;
39
+ forwardCount: number;
40
+ } | null;
41
+ /**
42
+ * The last edit's report of what it moved, or null: `others` is the number of tasks besides the edited one whose early start or
43
+ * finish changed, `finishDays` the calendar days the project finish moved (negative when earlier). `id` tells one report from the
44
+ * next and means nothing else. A host words the report itself; the chart's own sentence is not part of the API.
45
+ */
46
+ moved: {
47
+ id: number;
48
+ others: number;
49
+ finishDays: number;
50
+ } | null;
51
+ /** The data date, the day written `YYYY-MM-DD` (a file's time of day is not in it), or null with none. `commands.setDataDate` takes the same form. */
52
+ dataDate: string | null;
53
+ /** The data date's instant as the toolbar shows it, `YYYY-MM-DD HH:MM` (`2024-01-10 08:00`), or null with none. */
54
+ dataDateTime: string | null;
55
+ /**
56
+ * The baseline, or null with none: `rows` is the number of tasks it holds, `dataDate` the data date's day when it was set (`YYYY-MM-DD`),
57
+ * null if there was none, `dayText` that same day as the chart writes it (`14 Oct`, the form of the Baseline button's title), null if
58
+ * there was none, `slipped` the tasks whose early finish is now later than the baseline's, and `added` the live tasks it has no entry for
59
+ * (inserted since it was set), which "Add new" gives their current dates.
60
+ */
61
+ baseline: {
62
+ rows: number;
63
+ dataDate: string | null;
64
+ dayText: string | null;
65
+ slipped: number;
66
+ added: number;
67
+ } | null;
68
+ /** The backend that is drawing. */
69
+ backend: "WebGPU" | "WebGL2";
70
+ /** The name of the file opened in place (`one-project.xer`), or null for a sample or a schedule loaded with `load({ document })`. A mount with a File and `load({ file })` give that file's name. */
71
+ fileName: string | null;
72
+ /** True after an edit, undo or redo since the last open or save: the mark beside the file name. */
73
+ unsaved: boolean;
74
+ /** The import notes the panel lists: always what it shows (after a mount, an open, a load and a restart on WebGL2), the reader's lines without the ones `expectedNotes` names. Frozen. */
75
+ importNotes: readonly string[];
76
+ }
77
+ /** What the chart knows of the open file; the reader compares these with the last state it built. */
78
+ export interface OpenFacts {
79
+ fileName: string | null;
80
+ unsaved: boolean;
81
+ importNotes: readonly string[];
82
+ }
83
+ /** What `Gantt.filterInputs()` returns: what the filter controls offer. It changes with an open (`open`), an edit (`edit`) and a baseline set, cleared or added to, which fires no `edit`: a `change` whose `baseline` is a new object. */
84
+ export interface FilterInputs {
85
+ /** The activity code types the tasks carry, each with its values, by name: `[type, values]`. Empty for a file with none. */
86
+ activityCodes: [string, string[]][];
87
+ /** Whether any task has an activity ID (the table then shows the ID column). */
88
+ hasCodes: boolean;
89
+ /** Whether the baseline holds float, which the `"floatLost"` filter compares with: false with no baseline or one read from a file. */
90
+ baselineHasFloat: boolean;
91
+ }
92
+ export interface ViewStateReader {
93
+ /** The view state `s` shows: the object `read` returned last when nothing it carries moved, else a new frozen one. */
94
+ read(s: Status): ViewState;
95
+ }
package/package.json CHANGED
@@ -1,9 +1,65 @@
1
1
  {
2
2
  "name": "tachlon",
3
- "version": "0.0.1",
4
- "description": "Tachlon: a fast, beautiful Gantt scheduling component by Inner Stack Labs. Name reserved; coming soon.",
5
- "homepage": "https://tachlon.com",
6
- "author": "Inner Stack Labs",
3
+ "version": "0.1.0",
7
4
  "license": "UNLICENSED",
8
- "files": ["README.md"]
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/innerstacklabs/gantt.git",
8
+ "directory": "packages/gantt"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "type": "module",
14
+ "bin": {
15
+ "tachlon": "./dist-lib/cli/main.js"
16
+ },
17
+ "files": [
18
+ "dist-lib"
19
+ ],
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist-lib/types/index.d.ts",
23
+ "default": "./dist-lib/tachlon.js"
24
+ },
25
+ "./react": {
26
+ "types": "./dist-lib/types/react.d.ts",
27
+ "default": "./dist-lib/react.js"
28
+ },
29
+ "./gantt.css": "./dist-lib/gantt.css",
30
+ "./schema.json": "./dist-lib/schema.json"
31
+ },
32
+ "peerDependencies": {
33
+ "react": ">=18"
34
+ },
35
+ "peerDependenciesMeta": {
36
+ "react": {
37
+ "optional": true
38
+ }
39
+ },
40
+ "packageManager": "pnpm@11.25.0",
41
+ "scripts": {
42
+ "dev": "vite",
43
+ "build": "vite build",
44
+ "build:lib": "vite build --config vite.lib.config.ts && node ../../scripts/emit-types.ts . && node ../../scripts/emit-engine-size.ts . && tsc -p tsconfig.cli.json",
45
+ "docs:api": "api-extractor run && api-documenter markdown -i dist-docs/model -o dist-docs/pages",
46
+ "docs:book": "node ../../scripts/emit-types.ts . && pnpm docs:api",
47
+ "docs:check": "node ../../scripts/test-api-docs-check.ts && pnpm docs:api && node ../../scripts/api-docs-check.ts dist-docs/model/package.api.json",
48
+ "preview": "vite preview",
49
+ "typecheck": "tsc --noEmit && oxlint .",
50
+ "test:e2e": "playwright test"
51
+ },
52
+ "devDependencies": {
53
+ "@microsoft/api-documenter": "7.30.20",
54
+ "@microsoft/api-extractor": "7.60.1",
55
+ "@playwright/test": "1.64.1",
56
+ "@types/node": "22.20.5",
57
+ "@types/react": "19.3.0",
58
+ "@types/react-dom": "19.3.0",
59
+ "oxlint": "1.91.0",
60
+ "react": "19.3.1",
61
+ "react-dom": "19.3.1",
62
+ "typescript": "7.0.2",
63
+ "vite": "8.3.4"
64
+ }
9
65
  }