@typescript-calendar-lib/cli 0.2.5 → 0.2.7
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.
- package/README.md +278 -0
- package/dist/bin.d.ts +28 -0
- package/dist/bin.js +164 -0
- package/dist/bin.js.map +1 -0
- package/dist/calendar-DNDADt5T.js +241 -0
- package/dist/calendar-DNDADt5T.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +2 -0
- package/dist/theme-BMYtCxK2.d.ts +53 -0
- package/package.json +15 -10
- package/src/align.ts +0 -35
- package/src/ansi.ts +0 -25
- package/src/bin.ts +0 -209
- package/src/border.ts +0 -55
- package/src/calendar.ts +0 -31
- package/src/index.ts +0 -24
- package/src/month.ts +0 -144
- package/src/render.ts +0 -5
- package/src/theme.ts +0 -146
- package/src/types.ts +0 -27
- package/src/year.ts +0 -48
package/src/bin.ts
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
import type {
|
|
3
|
-
HighlightStyle,
|
|
4
|
-
Locale,
|
|
5
|
-
WeekStart,
|
|
6
|
-
} from "@typescript-calendar-lib/core";
|
|
7
|
-
import { createDate } from "@typescript-calendar-lib/core";
|
|
8
|
-
import { calendar } from "./calendar.ts";
|
|
9
|
-
import type { ColorSchemeName, ThemeName } from "./theme.ts";
|
|
10
|
-
|
|
11
|
-
export interface CliArgs {
|
|
12
|
-
year?: number;
|
|
13
|
-
month?: number;
|
|
14
|
-
theme?: ThemeName;
|
|
15
|
-
colorScheme?: ColorSchemeName;
|
|
16
|
-
color?: boolean;
|
|
17
|
-
locale?: Locale;
|
|
18
|
-
weekStart?: WeekStart;
|
|
19
|
-
highlight?: Date;
|
|
20
|
-
highlightStyle?: HighlightStyle;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface ParseResult {
|
|
24
|
-
args: CliArgs;
|
|
25
|
-
error?: string;
|
|
26
|
-
help?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const THEMES: readonly ThemeName[] = ["default", "modern"];
|
|
30
|
-
const COLOR_SCHEMES: readonly ColorSchemeName[] = [
|
|
31
|
-
"default",
|
|
32
|
-
"ocean",
|
|
33
|
-
"forest",
|
|
34
|
-
"sunset",
|
|
35
|
-
"mono",
|
|
36
|
-
];
|
|
37
|
-
const LOCALES: readonly Locale[] = ["en", "ja", "es", "de", "fr", "ko", "zh"];
|
|
38
|
-
const WEEK_STARTS: readonly WeekStart[] = ["sunday", "monday"];
|
|
39
|
-
const HIGHLIGHT_STYLES: readonly HighlightStyle[] = ["bracket", "reverse"];
|
|
40
|
-
|
|
41
|
-
/** 値を一つ取るオプションと、代入先のフィールド名 */
|
|
42
|
-
const VALUE_OPTIONS: Record<string, keyof CliArgs> = {
|
|
43
|
-
"--theme": "theme",
|
|
44
|
-
"--color-scheme": "colorScheme",
|
|
45
|
-
"--locale": "locale",
|
|
46
|
-
"--week-start": "weekStart",
|
|
47
|
-
"--highlight-style": "highlightStyle",
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/** YYYY-MM-DD 形式の日付文字列をパースする */
|
|
51
|
-
export function parseDate(value: string): Date | null {
|
|
52
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
53
|
-
if (!match) return null;
|
|
54
|
-
const year = Number(match[1]);
|
|
55
|
-
const month = Number(match[2]);
|
|
56
|
-
const day = Number(match[3]);
|
|
57
|
-
if (year < 1 || month < 1 || month > 12 || day < 1 || day > 31) {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const date = createDate(year, month - 1, day);
|
|
61
|
-
if (
|
|
62
|
-
date.getFullYear() !== year ||
|
|
63
|
-
date.getMonth() !== month - 1 ||
|
|
64
|
-
date.getDate() !== day
|
|
65
|
-
) {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
return date;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* CLI 引数をパースする。テスト可能なように副作用を分離している。
|
|
73
|
-
*/
|
|
74
|
-
export function parseArgs(args: readonly string[]): ParseResult {
|
|
75
|
-
const result: CliArgs = {};
|
|
76
|
-
const error = (message: string): ParseResult => ({
|
|
77
|
-
args: result,
|
|
78
|
-
error: message,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
for (let i = 0; i < args.length; i++) {
|
|
82
|
-
const arg = args[i]!;
|
|
83
|
-
|
|
84
|
-
if (arg === "-h" || arg === "--help") {
|
|
85
|
-
return { args: result, help: true };
|
|
86
|
-
}
|
|
87
|
-
if (arg === "--color") {
|
|
88
|
-
result.color = true;
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
if (arg === "--highlight") {
|
|
92
|
-
const value = args[++i];
|
|
93
|
-
if (value === undefined) {
|
|
94
|
-
return error("Missing value for option: --highlight");
|
|
95
|
-
}
|
|
96
|
-
const parsed = parseDate(value);
|
|
97
|
-
if (parsed === null) {
|
|
98
|
-
return error(
|
|
99
|
-
`Invalid --highlight date: "${value}" (expected YYYY-MM-DD, e.g. 2026-09-08)`,
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
result.highlight = parsed;
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const key = VALUE_OPTIONS[arg];
|
|
107
|
-
if (key !== undefined) {
|
|
108
|
-
const value = args[++i];
|
|
109
|
-
if (value === undefined) {
|
|
110
|
-
return error(`Missing value for option: ${arg}`);
|
|
111
|
-
}
|
|
112
|
-
(result as Record<string, unknown>)[key] = value;
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const numeric = /^-?\d+$/.test(arg);
|
|
117
|
-
if (arg.startsWith("-") && !numeric) {
|
|
118
|
-
return error(`Unknown option: ${arg}`);
|
|
119
|
-
}
|
|
120
|
-
if (!numeric) {
|
|
121
|
-
return error(
|
|
122
|
-
`Invalid argument: "${arg}" (expected a year or month number)`,
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
if (result.year === undefined) {
|
|
126
|
-
result.year = Number(arg);
|
|
127
|
-
} else if (result.month === undefined) {
|
|
128
|
-
result.month = Number(arg);
|
|
129
|
-
} else {
|
|
130
|
-
return error(`Too many arguments: "${arg}"`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// 値のバリデーション
|
|
135
|
-
if (result.year !== undefined && (result.year < 1 || result.year > 9999)) {
|
|
136
|
-
return error(`Invalid year: ${result.year} (expected 1–9999)`);
|
|
137
|
-
}
|
|
138
|
-
if (result.month !== undefined && (result.month < 1 || result.month > 12)) {
|
|
139
|
-
return error(`Invalid month: ${result.month} (expected 1–12)`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const choiceTable: Array<[string | undefined, readonly string[], string]> = [
|
|
143
|
-
[result.theme, THEMES, "theme"],
|
|
144
|
-
[result.colorScheme, COLOR_SCHEMES, "color-scheme"],
|
|
145
|
-
[result.locale, LOCALES, "locale"],
|
|
146
|
-
[result.weekStart, WEEK_STARTS, "week-start"],
|
|
147
|
-
[result.highlightStyle, HIGHLIGHT_STYLES, "highlight-style"],
|
|
148
|
-
];
|
|
149
|
-
for (const [value, choices, name] of choiceTable) {
|
|
150
|
-
if (value !== undefined && !choices.includes(value)) {
|
|
151
|
-
return error(
|
|
152
|
-
`Invalid ${name}: "${value}" (expected: ${choices.join(" | ")})`,
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return { args: result };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function printUsage(): string {
|
|
161
|
-
return `Usage: typescript-calendar-lib [YYYY] [MM] [options]
|
|
162
|
-
|
|
163
|
-
typescript-calendar-lib Render the current month
|
|
164
|
-
typescript-calendar-lib 2026 Render the current month of 2026
|
|
165
|
-
typescript-calendar-lib 2026 9 Render September 2026
|
|
166
|
-
|
|
167
|
-
Options:
|
|
168
|
-
--theme <name> Look: default | modern (default: default)
|
|
169
|
-
--color-scheme <name> Colors: default | ocean | forest | sunset | mono
|
|
170
|
-
--color Enable ANSI colors
|
|
171
|
-
--locale <lang> Language: en | ja | es | de | fr | ko | zh (default: en)
|
|
172
|
-
--week-start <day> First weekday: sunday | monday (default: sunday)
|
|
173
|
-
--highlight <YYYY-MM-DD> Highlight a date (e.g. 2026-09-08)
|
|
174
|
-
--highlight-style <style> Highlight style: bracket | reverse (default: bracket)
|
|
175
|
-
-h, --help Show this help
|
|
176
|
-
`;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (import.meta.main) {
|
|
180
|
-
const { args, error, help } = parseArgs(process.argv.slice(2));
|
|
181
|
-
|
|
182
|
-
if (help) {
|
|
183
|
-
console.log(printUsage());
|
|
184
|
-
process.exit(0);
|
|
185
|
-
}
|
|
186
|
-
if (error) {
|
|
187
|
-
console.error(`Error: ${error}`);
|
|
188
|
-
console.error(printUsage());
|
|
189
|
-
process.exit(1);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const now = new Date();
|
|
193
|
-
const year = args.year ?? now.getFullYear();
|
|
194
|
-
const month = args.month ?? now.getMonth() + 1;
|
|
195
|
-
|
|
196
|
-
console.log(
|
|
197
|
-
calendar({
|
|
198
|
-
year,
|
|
199
|
-
month,
|
|
200
|
-
theme: args.theme,
|
|
201
|
-
colorScheme: args.colorScheme,
|
|
202
|
-
color: args.color,
|
|
203
|
-
locale: args.locale,
|
|
204
|
-
weekStart: args.weekStart,
|
|
205
|
-
highlight: args.highlight,
|
|
206
|
-
highlightStyle: args.highlightStyle,
|
|
207
|
-
}),
|
|
208
|
-
);
|
|
209
|
-
}
|
package/src/border.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { FrameChars } from "./theme.ts";
|
|
2
|
-
|
|
3
|
-
// ─── 枠線 ─────────────────────────────────────────────────
|
|
4
|
-
|
|
5
|
-
/** 枠内のコンテンツ幅(例: 7列×3幅+区切り6 = 27) */
|
|
6
|
-
export function innerWidth(cellWidth: number, cols: number): number {
|
|
7
|
-
return cols * cellWidth + (cols - 1);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/** セル幅の水平線を cols 個つなげた区切り線を生成する */
|
|
11
|
-
function divider(
|
|
12
|
-
frame: FrameChars,
|
|
13
|
-
cellWidth: number,
|
|
14
|
-
cols: number,
|
|
15
|
-
left: string,
|
|
16
|
-
right: string,
|
|
17
|
-
join: string,
|
|
18
|
-
): string {
|
|
19
|
-
const segments = Array<string>(cols).fill(frame.h.repeat(cellWidth));
|
|
20
|
-
return `${left}${segments.join(join)}${right}`;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** 上枠: ┌────┬────...────┐ */
|
|
24
|
-
export function topBorder(
|
|
25
|
-
frame: FrameChars,
|
|
26
|
-
cellWidth: number,
|
|
27
|
-
cols: number,
|
|
28
|
-
): string {
|
|
29
|
-
return `${frame.topLeft}${frame.h.repeat(innerWidth(cellWidth, cols))}${frame.topRight}`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** 下枠: └────┴────...────┘ */
|
|
33
|
-
export function bottomBorder(
|
|
34
|
-
frame: FrameChars,
|
|
35
|
-
cellWidth: number,
|
|
36
|
-
cols: number,
|
|
37
|
-
): string {
|
|
38
|
-
return divider(
|
|
39
|
-
frame,
|
|
40
|
-
cellWidth,
|
|
41
|
-
cols,
|
|
42
|
-
frame.bottomLeft,
|
|
43
|
-
frame.bottomRight,
|
|
44
|
-
frame.footJ,
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** 区切り行: ├────┬────...┬────┤ */
|
|
49
|
-
export function separatorRow(
|
|
50
|
-
frame: FrameChars,
|
|
51
|
-
cellWidth: number,
|
|
52
|
-
cols: number,
|
|
53
|
-
): string {
|
|
54
|
-
return divider(frame, cellWidth, cols, "├", "┤", frame.j);
|
|
55
|
-
}
|
package/src/calendar.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { getMonthRange } from "@typescript-calendar-lib/core";
|
|
2
|
-
import { renderMonth, renderYear } from "./render.ts";
|
|
3
|
-
import type {
|
|
4
|
-
CalendarOptions,
|
|
5
|
-
CalendarRangeOptions,
|
|
6
|
-
CalendarYearOptions,
|
|
7
|
-
} from "./types.ts";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 月カレンダーをテキストで返す
|
|
11
|
-
*/
|
|
12
|
-
export function calendar(options: CalendarOptions): string {
|
|
13
|
-
return renderMonth(options.year, options.month, options);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* 年間カレンダーを4列×3行でテキストで返す
|
|
18
|
-
*/
|
|
19
|
-
export function calendarYear(options: CalendarYearOptions): string {
|
|
20
|
-
return renderYear(options.year, options);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 任意の日付範囲のカレンダーをテキストで返す
|
|
25
|
-
*/
|
|
26
|
-
export function calendarRange(options: CalendarRangeOptions): string {
|
|
27
|
-
const months = getMonthRange(options.from, options.to);
|
|
28
|
-
return months
|
|
29
|
-
.map(({ year, month }) => renderMonth(year, month, options))
|
|
30
|
-
.join("\n\n");
|
|
31
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
calendar,
|
|
3
|
-
calendarRange,
|
|
4
|
-
calendarYear,
|
|
5
|
-
} from "./calendar.ts";
|
|
6
|
-
export type {
|
|
7
|
-
CliPalette,
|
|
8
|
-
CliTheme,
|
|
9
|
-
ColorSchemeName,
|
|
10
|
-
FrameChars,
|
|
11
|
-
ThemeName,
|
|
12
|
-
} from "./theme.ts";
|
|
13
|
-
export {
|
|
14
|
-
COLOR_SCHEMES,
|
|
15
|
-
resolveColorScheme,
|
|
16
|
-
resolveTheme,
|
|
17
|
-
THEMES,
|
|
18
|
-
} from "./theme.ts";
|
|
19
|
-
export type {
|
|
20
|
-
CalendarOptions,
|
|
21
|
-
CalendarRangeOptions,
|
|
22
|
-
CalendarYearOptions,
|
|
23
|
-
RenderMonthOptions,
|
|
24
|
-
} from "./types.ts";
|
package/src/month.ts
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildMonthGrid,
|
|
3
|
-
createDate,
|
|
4
|
-
getMonthName,
|
|
5
|
-
getWeekdayHeaders,
|
|
6
|
-
isDateInRange,
|
|
7
|
-
isSameDay,
|
|
8
|
-
} from "@typescript-calendar-lib/core";
|
|
9
|
-
import {
|
|
10
|
-
centerText,
|
|
11
|
-
centerTextFull,
|
|
12
|
-
displayWidth,
|
|
13
|
-
padStartWidth,
|
|
14
|
-
} from "./align.ts";
|
|
15
|
-
import { colorize } from "./ansi.ts";
|
|
16
|
-
import { bottomBorder, innerWidth, separatorRow, topBorder } from "./border.ts";
|
|
17
|
-
import { resolveColorScheme, resolveTheme } from "./theme.ts";
|
|
18
|
-
import type { RenderMonthOptions } from "./types.ts";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* 1ヶ月分のカレンダーテキストを描画する
|
|
22
|
-
*/
|
|
23
|
-
export function renderMonth(
|
|
24
|
-
year: number,
|
|
25
|
-
month: number,
|
|
26
|
-
options: RenderMonthOptions = {},
|
|
27
|
-
): string {
|
|
28
|
-
const {
|
|
29
|
-
locale = "en",
|
|
30
|
-
weekStart = "sunday",
|
|
31
|
-
highlight,
|
|
32
|
-
highlightStyle = "bracket",
|
|
33
|
-
range,
|
|
34
|
-
color = false,
|
|
35
|
-
theme: themeOption = "default",
|
|
36
|
-
colorScheme: schemeOption = "default",
|
|
37
|
-
today = new Date(),
|
|
38
|
-
} = options;
|
|
39
|
-
|
|
40
|
-
const theme = resolveTheme(themeOption);
|
|
41
|
-
const palette = resolveColorScheme(schemeOption);
|
|
42
|
-
const frame = theme.frame;
|
|
43
|
-
|
|
44
|
-
const title = `${getMonthName(locale, month)} ${year}`;
|
|
45
|
-
const weekdays = getWeekdayHeaders(locale, weekStart);
|
|
46
|
-
const grid = buildMonthGrid(year, month, weekStart);
|
|
47
|
-
const cols = weekdays.length;
|
|
48
|
-
|
|
49
|
-
// セル幅はテーマ指定を基本としつつ、以下を満たすように広げる:
|
|
50
|
-
// - 曜日ヘッダーの表示幅(fr の "dim." 等がセル幅を超えると列が崩れる)
|
|
51
|
-
// - bracket ハイライトは2桁の日付で `[10]` の4文字になるため
|
|
52
|
-
const cellWidth = Math.max(
|
|
53
|
-
theme.cellWidth,
|
|
54
|
-
...weekdays.map(displayWidth),
|
|
55
|
-
highlight !== undefined && highlightStyle === "bracket" ? 4 : 2,
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
// 枠なしテーマは separator、枠ありテーマは縦線でセルを繋ぐ
|
|
59
|
-
const sep = frame === null ? theme.separator : frame.v;
|
|
60
|
-
|
|
61
|
-
/** 1セルを描画する(day は日数または null=空欄) */
|
|
62
|
-
const renderCell = (day: number | null): string => {
|
|
63
|
-
if (day === null) return " ".repeat(cellWidth);
|
|
64
|
-
|
|
65
|
-
const date = createDate(year, month - 1, day);
|
|
66
|
-
const isHighlight = highlight !== undefined && isSameDay(date, highlight);
|
|
67
|
-
const isInRange = isDateInRange(date, range);
|
|
68
|
-
const isToday = isSameDay(date, today);
|
|
69
|
-
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
|
|
70
|
-
|
|
71
|
-
const text = (
|
|
72
|
-
isHighlight && highlightStyle === "bracket" ? `[${day}]` : String(day)
|
|
73
|
-
).padStart(cellWidth);
|
|
74
|
-
|
|
75
|
-
let code: number | undefined;
|
|
76
|
-
if (isHighlight && highlightStyle === "reverse") {
|
|
77
|
-
code = palette.highlight ?? 7;
|
|
78
|
-
} else if (isInRange && !isHighlight) {
|
|
79
|
-
code = palette.range ?? 33;
|
|
80
|
-
} else if (isToday && palette.today !== undefined) {
|
|
81
|
-
code = palette.today;
|
|
82
|
-
} else if (isWeekend && palette.weekend !== undefined) {
|
|
83
|
-
code = palette.weekend;
|
|
84
|
-
} else if (palette.day !== undefined) {
|
|
85
|
-
code = palette.day;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return colorize(text, code, color);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const lines: string[] = [];
|
|
92
|
-
|
|
93
|
-
if (frame === null) {
|
|
94
|
-
// ── 枠なし(default) ──
|
|
95
|
-
const totalWidth = cols * cellWidth + (cols - 1) * sep.length;
|
|
96
|
-
lines.push(centerText(title, totalWidth));
|
|
97
|
-
lines.push(
|
|
98
|
-
weekdays
|
|
99
|
-
.map((d) =>
|
|
100
|
-
colorize(padStartWidth(d, cellWidth), palette.weekday, color),
|
|
101
|
-
)
|
|
102
|
-
.join(sep),
|
|
103
|
-
);
|
|
104
|
-
} else {
|
|
105
|
-
// ── 枠あり(modern) ──
|
|
106
|
-
lines.push(
|
|
107
|
-
colorize(topBorder(frame, cellWidth, cols), palette.frame, color),
|
|
108
|
-
);
|
|
109
|
-
lines.push(
|
|
110
|
-
colorize(
|
|
111
|
-
`${frame.v}${centerTextFull(title, innerWidth(cellWidth, cols))}${frame.v}`,
|
|
112
|
-
palette.title,
|
|
113
|
-
color,
|
|
114
|
-
),
|
|
115
|
-
);
|
|
116
|
-
lines.push(
|
|
117
|
-
colorize(separatorRow(frame, cellWidth, cols), palette.frame, color),
|
|
118
|
-
);
|
|
119
|
-
lines.push(
|
|
120
|
-
colorize(
|
|
121
|
-
`${frame.v}${weekdays.map((d) => padStartWidth(d, cellWidth)).join(frame.v)}${frame.v}`,
|
|
122
|
-
palette.weekday,
|
|
123
|
-
color,
|
|
124
|
-
),
|
|
125
|
-
);
|
|
126
|
-
lines.push(
|
|
127
|
-
colorize(separatorRow(frame, cellWidth, cols), palette.frame, color),
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
for (const row of grid) {
|
|
132
|
-
if (row.every((d) => d === null)) continue;
|
|
133
|
-
const cells = row.map(renderCell).join(sep);
|
|
134
|
-
lines.push(frame === null ? cells : `${frame.v}${cells}${frame.v}`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (frame !== null) {
|
|
138
|
-
lines.push(
|
|
139
|
-
colorize(bottomBorder(frame, cellWidth, cols), palette.frame, color),
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return lines.join("\n");
|
|
144
|
-
}
|
package/src/render.ts
DELETED
package/src/theme.ts
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
// ─── テーマ ───────────────────────────────────────────────
|
|
2
|
-
|
|
3
|
-
/** 組み込みテーマ名。カスタムテーマは CliTheme オブジェクトを直接渡せる */
|
|
4
|
-
export type ThemeName = "default" | "modern";
|
|
5
|
-
|
|
6
|
-
/** 枠線の文字セット(modern テーマで使用) */
|
|
7
|
-
export interface FrameChars {
|
|
8
|
-
topLeft: string;
|
|
9
|
-
topRight: string;
|
|
10
|
-
bottomLeft: string;
|
|
11
|
-
bottomRight: string;
|
|
12
|
-
/** 水平線 */
|
|
13
|
-
h: string;
|
|
14
|
-
/** 垂直線 */
|
|
15
|
-
v: string;
|
|
16
|
-
/** ヘッダー/本文の区切り行で使う交差(┬ や ┼) */
|
|
17
|
-
j: string;
|
|
18
|
-
/** 下端区切りで使う交差(┴) */
|
|
19
|
-
footJ: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** 文字ベースの見た目定義 */
|
|
23
|
-
export interface CliTheme {
|
|
24
|
-
/** セル幅(日付表記の文字幅) */
|
|
25
|
-
cellWidth: number;
|
|
26
|
-
/** セル間の区切り文字(default: " " / modern: "│") */
|
|
27
|
-
separator: string;
|
|
28
|
-
/** 枠線文字。null なら枠なし */
|
|
29
|
-
frame: FrameChars | null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const THEMES: Record<ThemeName, CliTheme> = {
|
|
33
|
-
default: {
|
|
34
|
-
cellWidth: 3,
|
|
35
|
-
separator: " ",
|
|
36
|
-
frame: null,
|
|
37
|
-
},
|
|
38
|
-
modern: {
|
|
39
|
-
cellWidth: 3,
|
|
40
|
-
separator: "│",
|
|
41
|
-
frame: {
|
|
42
|
-
topLeft: "┌",
|
|
43
|
-
topRight: "┐",
|
|
44
|
-
bottomLeft: "└",
|
|
45
|
-
bottomRight: "┘",
|
|
46
|
-
h: "─",
|
|
47
|
-
v: "│",
|
|
48
|
-
j: "┬",
|
|
49
|
-
footJ: "┴",
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export function resolveTheme(theme?: ThemeName | CliTheme): CliTheme {
|
|
55
|
-
return typeof theme === "string"
|
|
56
|
-
? (THEMES[theme] ?? THEMES.default)
|
|
57
|
-
: (theme ?? THEMES.default);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ─── カラースキーム ───────────────────────────────────────
|
|
61
|
-
|
|
62
|
-
/** 組み込みカラースキーム名。カスタムは CliPalette を直接渡せる */
|
|
63
|
-
export type ColorSchemeName =
|
|
64
|
-
| "default"
|
|
65
|
-
| "ocean"
|
|
66
|
-
| "forest"
|
|
67
|
-
| "sunset"
|
|
68
|
-
| "mono";
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* ANSIカラーパレット。
|
|
72
|
-
* 各フィールドは前景色(またはハイライト時の背景)のANSIコード。
|
|
73
|
-
* undefined はその要素を着色しない。
|
|
74
|
-
*/
|
|
75
|
-
export interface CliPalette {
|
|
76
|
-
title?: number;
|
|
77
|
-
weekday?: number;
|
|
78
|
-
day?: number;
|
|
79
|
-
weekend?: number;
|
|
80
|
-
today?: number;
|
|
81
|
-
/** highlightStyle: "reverse" のときに使うコード(default は 7 = 反転) */
|
|
82
|
-
highlight?: number;
|
|
83
|
-
range?: number;
|
|
84
|
-
frame?: number;
|
|
85
|
-
dim?: number;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export const COLOR_SCHEMES: Record<ColorSchemeName, CliPalette> = {
|
|
89
|
-
/** 従来どおり。着色は range(黄) と highlight(反転) のみ */
|
|
90
|
-
default: {
|
|
91
|
-
range: 33,
|
|
92
|
-
highlight: 7,
|
|
93
|
-
},
|
|
94
|
-
ocean: {
|
|
95
|
-
title: 36,
|
|
96
|
-
weekday: 36,
|
|
97
|
-
day: 37,
|
|
98
|
-
weekend: 34,
|
|
99
|
-
today: 36,
|
|
100
|
-
highlight: 7,
|
|
101
|
-
range: 34,
|
|
102
|
-
frame: 36,
|
|
103
|
-
dim: 90,
|
|
104
|
-
},
|
|
105
|
-
forest: {
|
|
106
|
-
title: 32,
|
|
107
|
-
weekday: 32,
|
|
108
|
-
day: 37,
|
|
109
|
-
weekend: 90,
|
|
110
|
-
today: 32,
|
|
111
|
-
highlight: 7,
|
|
112
|
-
range: 32,
|
|
113
|
-
frame: 32,
|
|
114
|
-
dim: 90,
|
|
115
|
-
},
|
|
116
|
-
sunset: {
|
|
117
|
-
title: 35,
|
|
118
|
-
weekday: 35,
|
|
119
|
-
day: 37,
|
|
120
|
-
weekend: 33,
|
|
121
|
-
today: 35,
|
|
122
|
-
highlight: 7,
|
|
123
|
-
range: 35,
|
|
124
|
-
frame: 35,
|
|
125
|
-
dim: 90,
|
|
126
|
-
},
|
|
127
|
-
mono: {
|
|
128
|
-
title: 37,
|
|
129
|
-
weekday: 37,
|
|
130
|
-
day: 37,
|
|
131
|
-
weekend: 90,
|
|
132
|
-
today: 37,
|
|
133
|
-
highlight: 7,
|
|
134
|
-
range: 90,
|
|
135
|
-
frame: 90,
|
|
136
|
-
dim: 90,
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
export function resolveColorScheme(
|
|
141
|
-
scheme?: ColorSchemeName | CliPalette,
|
|
142
|
-
): CliPalette {
|
|
143
|
-
return typeof scheme === "string"
|
|
144
|
-
? (COLOR_SCHEMES[scheme] ?? COLOR_SCHEMES.default)
|
|
145
|
-
: (scheme ?? COLOR_SCHEMES.default);
|
|
146
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
CalendarOptions as CoreCalendarOptions,
|
|
3
|
-
CalendarRangeOptions as CoreCalendarRangeOptions,
|
|
4
|
-
CalendarYearOptions as CoreCalendarYearOptions,
|
|
5
|
-
RenderMonthOptions as CoreRenderMonthOptions,
|
|
6
|
-
} from "@typescript-calendar-lib/core";
|
|
7
|
-
import type {
|
|
8
|
-
CliPalette,
|
|
9
|
-
CliTheme,
|
|
10
|
-
ColorSchemeName,
|
|
11
|
-
ThemeName,
|
|
12
|
-
} from "./theme.ts";
|
|
13
|
-
|
|
14
|
-
/** CLI 固有のオプション(core のオプションに追加で受け付ける) */
|
|
15
|
-
export interface CliExtraOptions {
|
|
16
|
-
/** 見た目テーマ。既定は "default" */
|
|
17
|
-
theme?: ThemeName | CliTheme;
|
|
18
|
-
/** カラースキーム。color: true のとき有効。既定は "default" */
|
|
19
|
-
colorScheme?: ColorSchemeName | CliPalette;
|
|
20
|
-
/** 今日の基準日。カラースキームの today 着色に使用 */
|
|
21
|
-
today?: Date;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type CalendarOptions = CoreCalendarOptions & CliExtraOptions;
|
|
25
|
-
export type CalendarYearOptions = CoreCalendarYearOptions & CliExtraOptions;
|
|
26
|
-
export type CalendarRangeOptions = CoreCalendarRangeOptions & CliExtraOptions;
|
|
27
|
-
export type RenderMonthOptions = CoreRenderMonthOptions & CliExtraOptions;
|
package/src/year.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { displayWidth } from "./align.ts";
|
|
2
|
-
import { stripAnsi } from "./ansi.ts";
|
|
3
|
-
import { renderMonth } from "./month.ts";
|
|
4
|
-
import type { RenderMonthOptions } from "./types.ts";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 年間カレンダーを4列×3行でテキストで返す
|
|
8
|
-
*
|
|
9
|
-
* 列幅・パディングは「ANSI 除去後のターミナル表示幅」で計算する。
|
|
10
|
-
* 全角文字(日本語・韓国語・中国語の月名や曜日)も正しく揃う。
|
|
11
|
-
*/
|
|
12
|
-
export function renderYear(
|
|
13
|
-
year: number,
|
|
14
|
-
options: RenderMonthOptions = {},
|
|
15
|
-
): string {
|
|
16
|
-
const widthOf = (line: string): number => displayWidth(stripAnsi(line));
|
|
17
|
-
|
|
18
|
-
const months = Array.from({ length: 12 }, (_, i) =>
|
|
19
|
-
renderMonth(year, i + 1, options).split("\n"),
|
|
20
|
-
);
|
|
21
|
-
const maxLines = Math.max(...months.map((lines) => lines.length));
|
|
22
|
-
const colWidths = months.map((lines) => Math.max(...lines.map(widthOf)));
|
|
23
|
-
|
|
24
|
-
/** 月の各行をその列幅まで右パディングする(行が足りなければ空行として埋める) */
|
|
25
|
-
const padMonth = (monthIdx: number): string[] =>
|
|
26
|
-
Array.from({ length: maxLines }, (_, li) => {
|
|
27
|
-
const line = months[monthIdx]![li] ?? "";
|
|
28
|
-
return (
|
|
29
|
-
line + " ".repeat(Math.max(0, colWidths[monthIdx]! - widthOf(line)))
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const padded = months.map((_, mi) => padMonth(mi));
|
|
34
|
-
|
|
35
|
-
const rows: string[] = [];
|
|
36
|
-
for (let row = 0; row < 3; row++) {
|
|
37
|
-
rows.push(
|
|
38
|
-
Array.from({ length: maxLines }, (_, li) =>
|
|
39
|
-
Array.from({ length: 4 }, (_, col) => {
|
|
40
|
-
const monthIdx = row * 4 + col;
|
|
41
|
-
return monthIdx < 12 ? padded[monthIdx]![li]! : "";
|
|
42
|
-
}).join(" "),
|
|
43
|
-
).join("\n"),
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return rows.join("\n\n");
|
|
48
|
-
}
|