@itcase/config 1.7.0 → 1.7.2

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 CHANGED
@@ -137,6 +137,16 @@ make smart-commit-dry # если есть Makefile
137
137
  make smart-commit
138
138
  ```
139
139
 
140
+ Пропустить husky (`lint-staged`, `commit-msg`): `--no-verify` уходит в `git commit` и хуки не запускаются. Сообщение smart-commit всё равно проверяет сам.
141
+
142
+ ```bash
143
+ npm run smart-commit -- --no-verify
144
+ make -- smart-commit --no-verify
145
+ make smart-commit FLAGS=--no-verify
146
+ ```
147
+
148
+ `make smart-commit --no-verify` без `--` не сработает: GNU make забирает `--*` как свою опцию.
149
+
140
150
  В самом `@itcase/config` (разработка пакета):
141
151
 
142
152
  ```bash
@@ -161,7 +171,7 @@ CLI ставится в `~/.local/bin`. Логин в Cursor IDE этот CLI н
161
171
 
162
172
  Обновление CLI: `agent update`.
163
173
 
164
- Пока Agent думает, в терминале крутится pixel-анимация (случайно nyan cat, dancing banana, упячка или Pac-Man) + статус `(Ns)`, а под ними живой лог агента (`log-update`). Пока процесс агента жив, CLI его не убивает по таймауту. Принудительно: `--cat` / `--banana` / `--upyachka` / `--pacman`. Для полного лога: `npm run smart-commit -- --verbose --dry-run`.
174
+ Пока Agent думает, в терминале крутится pixel-анимация (случайно nyan cat, dancing banana или упячка) + статус `(Ns)`, а под ними живой лог агента (`log-update`). Пока процесс агента жив, CLI его не убивает по таймауту. Принудительно: `--cat` / `--banana` / `--upyachka`. Для полного лога: `npm run smart-commit -- --verbose --dry-run`.
165
175
 
166
176
  > **Важно:** в npm есть одноимённый пакет `smart-commit`. Используйте `npm run smart-commit` из проекта с `@itcase/config` — bin берётся из `node_modules`. `npx smart-commit` без локальной установки может подтянуть чужой пакет; CLI перед запуском проверяет, что выполняется именно `@itcase/config`.
167
177
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itcase/config",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "author": "ITCase",
5
5
  "description": "ITCase Config",
6
6
  "engines": {
@@ -39,9 +39,9 @@
39
39
  "registry": "https://registry.npmjs.org/"
40
40
  },
41
41
  "dependencies": {
42
- "@commitlint/cli": "^21.2.2",
43
- "@commitlint/config-conventional": "^21.2.2",
44
- "@commitlint/cz-commitlint": "^21.2.2",
42
+ "@commitlint/cli": "^21.2.3",
43
+ "@commitlint/config-conventional": "^21.2.3",
44
+ "@commitlint/cz-commitlint": "^21.2.3",
45
45
  "@lehoczky/postcss-fluid": "^1.0.3",
46
46
  "@lerna-lite/cli": "^5.6.1",
47
47
  "@lerna-lite/exec": "^5.6.1",
@@ -62,12 +62,12 @@
62
62
  "@svgr/plugin-jsx": "^8.1.0",
63
63
  "@svgr/plugin-svgo": "^8.1.0",
64
64
  "@svgr/webpack": "^8.1.0",
65
- "autoprefixer": "^10.6.0",
65
+ "autoprefixer": "^10.6.1",
66
66
  "chokidar-cli": "^3.0.0",
67
67
  "commitizen": "^4.3.2",
68
68
  "conventional-changelog-conventionalcommits": "9.3.1",
69
- "cssnano": "^9.0.4",
70
- "cssnano-preset-default": "^9.0.4",
69
+ "cssnano": "^9.0.5",
70
+ "cssnano-preset-default": "^9.0.5",
71
71
  "inquirer": "^14.2.2",
72
72
  "log-update": "^8.0.0",
73
73
  "postcss": "^8.5.28",
@@ -78,7 +78,7 @@
78
78
  "postcss-combine-duplicated-selectors": "^10.0.3",
79
79
  "postcss-convert-unit": "^0.1.3",
80
80
  "postcss-dark-theme-class": "^2.0.0",
81
- "postcss-discard-duplicates": "^9.0.2",
81
+ "postcss-discard-duplicates": "^9.0.3",
82
82
  "postcss-each": "^1.1.0",
83
83
  "postcss-easings": "^4.0.0",
84
84
  "postcss-extend-rule": "^4.0.0",
@@ -108,15 +108,15 @@
108
108
  "rollup-plugin-peer-deps-external": "^2.2.4",
109
109
  "rollup-preserve-directives": "^1.1.3",
110
110
  "semantic-release": "^25.0.9",
111
- "semantic-release-lerna": "^3.6.4"
111
+ "semantic-release-lerna": "^3.6.5"
112
112
  },
113
113
  "devDependencies": {
114
- "@itcase/lint": "^1.1.125",
114
+ "@itcase/lint": "^1.1.126",
115
115
  "eslint": "9.39.2",
116
116
  "husky": "^9.1.7",
117
117
  "inquirer": "^14.2.2",
118
118
  "lint-staged": "^17.5.1",
119
- "prettier": "^3.9.6",
119
+ "prettier": "^3.9.8",
120
120
  "storybook": "^10.6.0",
121
121
  "stylelint": "^17.15.0",
122
122
  "typescript": "^6.0.3"
@@ -37,6 +37,14 @@ const releaseNotesGeneratorConfig = {
37
37
  },
38
38
  }
39
39
 
40
+ const defaultGithubOptions = {
41
+ failComment: false,
42
+ failCommentCondition: false,
43
+ failTitle: false,
44
+ releasedLabels: false,
45
+ successComment: false,
46
+ }
47
+
40
48
  const commonPlugins = {
41
49
  commitAnalyzer: [
42
50
  '@semantic-release/commit-analyzer',
@@ -52,16 +60,7 @@ const commonPlugins = {
52
60
  releaseNotesGeneratorConfig,
53
61
  ],
54
62
  changelog: ['@semantic-release/changelog', { changelogFile: CHANGELOG_FILE }],
55
- github: [
56
- '@semantic-release/github',
57
- {
58
- failComment: false,
59
- failCommentCondition: false,
60
- failTitle: false,
61
- releasedLabels: false,
62
- successComment: false,
63
- },
64
- ],
63
+ github: ['@semantic-release/github', defaultGithubOptions],
65
64
  }
66
65
 
67
66
  /**
@@ -70,7 +69,9 @@ const commonPlugins = {
70
69
  * @param {string|string[]} options.branches - Ветки для релиза
71
70
  * @param {Object|null} options.npmConfig - Конфигурация для @semantic-release/npm. Если null, плагин не добавляется
72
71
  * @param {Object} options.gitConfig - Конфигурация для @semantic-release/git
73
- * @param {Array} options.additionalPlugins - Дополнительные плагины для добавления перед changelog и git
72
+ * @param {Object} [options.githubConfig] - Доп. опции для @semantic-release/github (например assets)
73
+ * @param {Array} options.additionalPlugins - Дополнительные плагины перед changelog
74
+ * @param {Array} [options.preparePlugins] - Плагины после npm и перед git (prepare-шаг)
74
75
  * @param {boolean} options.skipReleaseNotesGenerator - Пропустить @semantic-release/release-notes-generator
75
76
  * @returns {Object} Конфигурация semantic-release
76
77
  */
@@ -78,7 +79,9 @@ function createSemanticReleaseConfig({
78
79
  branches,
79
80
  npmConfig = {},
80
81
  gitConfig,
82
+ githubConfig = {},
81
83
  additionalPlugins = [],
84
+ preparePlugins = [],
82
85
  skipReleaseNotesGenerator = false,
83
86
  }) {
84
87
  const plugins = [commonPlugins.commitAnalyzer]
@@ -95,7 +98,12 @@ function createSemanticReleaseConfig({
95
98
  plugins.push(['@semantic-release/npm', npmConfig])
96
99
  }
97
100
 
98
- plugins.push(['@semantic-release/git', gitConfig], commonPlugins.github)
101
+ plugins.push(...preparePlugins)
102
+
103
+ plugins.push(
104
+ ['@semantic-release/git', gitConfig],
105
+ ['@semantic-release/github', { ...defaultGithubOptions, ...githubConfig }],
106
+ )
99
107
 
100
108
  return {
101
109
  branches,
@@ -1,9 +1,15 @@
1
+ import {
2
+ createSemanticReleaseApp,
3
+ semanticReleaseApp,
4
+ } from './semanticReleaseApp.js'
1
5
  import { semanticReleaseMonorepo } from './semanticReleaseMonorepo.js'
2
6
  import { semanticReleasePackage } from './semanticReleasePackage.js'
3
7
  import { semanticReleaseProject } from './semanticReleaseProject.js'
4
8
  import { semanticReleaseVSMarketplace } from './semanticReleaseVSMarketplace.js'
5
9
 
6
10
  export {
11
+ createSemanticReleaseApp,
12
+ semanticReleaseApp,
7
13
  semanticReleaseMonorepo,
8
14
  semanticReleasePackage,
9
15
  semanticReleaseProject,
@@ -0,0 +1,58 @@
1
+ import { createSemanticReleaseConfig } from './common.js'
2
+
3
+ const DEFAULT_VERSION_ASSETS = [
4
+ 'package.json',
5
+ 'package-lock.json',
6
+ 'CHANGELOG.md',
7
+ 'android/app/build.gradle',
8
+ ]
9
+
10
+ const DEFAULT_RELEASE_ASSETS = ['builds/*.apk']
11
+
12
+ /**
13
+ * Конфигурация semantic-release для Android app репозиториев.
14
+ *
15
+ * - Не публикует в npm
16
+ * - Коммитит package + Android version-файлы
17
+ * - Прикрепляет к GitHub release APK из `builds/` (если есть; отсутствующие пропускаются)
18
+ *
19
+ * Сборку APK делайте в prepare проекта или отдельным CI job до publish.
20
+ *
21
+ * @param {Object} [options]
22
+ * @param {string|string[]} [options.branches=['master']]
23
+ * @param {string[]} [options.versionAssets] - файлы для @semantic-release/git
24
+ * @param {string[]|Object[]} [options.releaseAssets] - assets для @semantic-release/github
25
+ * @param {Array} [options.preparePlugins] - плагины после npm (например bump Android version + build APK)
26
+ * @param {Object} [options.githubConfig] - доп. опции github-плагина
27
+ * @param {Object} [options.gitConfig] - доп. опции git-плагина (мержатся поверх message/assets)
28
+ */
29
+ function createSemanticReleaseApp({
30
+ branches = ['master'],
31
+ gitConfig = {},
32
+ githubConfig = {},
33
+ preparePlugins = [],
34
+ releaseAssets = DEFAULT_RELEASE_ASSETS,
35
+ versionAssets = DEFAULT_VERSION_ASSETS,
36
+ } = {}) {
37
+ return createSemanticReleaseConfig({
38
+ branches,
39
+ gitConfig: {
40
+ message:
41
+ 'release: publishing new version ${nextRelease.version} [skip ci]',
42
+ assets: versionAssets,
43
+ ...gitConfig,
44
+ },
45
+ githubConfig: {
46
+ assets: releaseAssets,
47
+ ...githubConfig,
48
+ },
49
+ npmConfig: {
50
+ npmPublish: false,
51
+ },
52
+ preparePlugins,
53
+ })
54
+ }
55
+
56
+ const semanticReleaseApp = createSemanticReleaseApp()
57
+
58
+ export { createSemanticReleaseApp, semanticReleaseApp }
@@ -17,7 +17,6 @@ import {
17
17
  } from '../commitlint/commitlintCommon.js'
18
18
  import { getBananaFrame } from './dancing-banana.js'
19
19
  import { getNyanFrame } from './nyan-cat.js'
20
- import { getPacmanFrame } from './pacman.js'
21
20
  import { getUpyachkaFrame } from './upyachka.js'
22
21
 
23
22
  const PACKAGE_NAME = '@itcase/config'
@@ -40,7 +39,6 @@ const AGENT_LOG_ROWS = 8
40
39
  const AGENT_LOG_MAX = 160
41
40
  const NYAN_FRAME_MS = 90
42
41
  const BANANA_FRAME_MS = 100
43
- const PACMAN_FRAME_MS = 120
44
42
  const UPYACHKA_FRAME_MS = 100
45
43
  const INDEX_LOCK_RETRIES = 8
46
44
  const INDEX_LOCK_WAIT_MS = 250
@@ -50,16 +48,20 @@ const AGENT_WAIT_STEPS = [
50
48
  'Still thinking',
51
49
  'Waiting for response',
52
50
  ]
53
- const HELP = `smart-commit [--dry-run] [--yes|-y] [--type t] [--model id] [--verbose]
54
- [--cat|--nyan] [--banana] [--upyachka] [--pacman]
51
+ const HELP = `smart-commit [--dry-run] [--yes|-y] [--no-verify] [--type t] [--model id] [--verbose]
52
+ [--cat|--nyan] [--banana] [--upyachka]
55
53
 
56
54
  Cursor Agent → commit messages by SCOPES → [y/N] → git commit
57
55
  Run via: npm exec smart-commit (from project with @itcase/config)
58
56
  Needs: agent login (or CURSOR_API_KEY) + commitlint.config.* with SCOPES
59
57
  Default model: ${DEFAULT_MODEL} (override with --model / SMART_COMMIT_MODEL).
60
58
 
61
- Progress sprite is random (nyan cat, banana, upyachka, or Pac-Man).
62
- Force with --cat, --banana, --upyachka, or --pacman. Agent log is below.
59
+ --no-verify is passed to git commit and skips hooks
60
+ (husky pre-commit / lint-staged and commit-msg). Commit message
61
+ is still checked by smart-commit before git runs.
62
+
63
+ Progress sprite is random (nyan cat, banana, or upyachka).
64
+ Force with --cat, --banana, or --upyachka. Agent log is below.
63
65
  `
64
66
  const AGENT_MISSING = `Cursor Agent CLI not found — smart-commit cannot run.
65
67
 
@@ -183,9 +185,17 @@ async function main(argv) {
183
185
  )
184
186
  }
185
187
 
186
- process.stdout.write(' git commit (husky)…\n')
188
+ process.stdout.write(
189
+ option.noVerify
190
+ ? ' git commit --no-verify (hooks skipped)…\n'
191
+ : ' git commit (husky)…\n',
192
+ )
187
193
  try {
188
- git(cwd, ['commit', '-m', plan.message], { inherit: true })
194
+ const commitArgs = ['commit', '-m', plan.message]
195
+ // Skips pre-commit (lint-staged) and commit-msg. lint-staged has no
196
+ // equivalent flag — the hook simply does not run.
197
+ if (option.noVerify) commitArgs.push('--no-verify')
198
+ git(cwd, commitArgs, { inherit: true })
189
199
  } catch (error) {
190
200
  throw new Error(`git commit failed: ${plan.message}\n${error.message}`, {
191
201
  cause: error,
@@ -210,6 +220,7 @@ function parseArgs(argv) {
210
220
  type: '',
211
221
  dryRun: false,
212
222
  model: '',
223
+ noVerify: false,
213
224
  sprite: '',
214
225
  staged: false,
215
226
  verbose: false,
@@ -218,13 +229,13 @@ function parseArgs(argv) {
218
229
  for (let i = 0; i < argv.length; i++) {
219
230
  const arg = argv[i]
220
231
  if (arg === '--dry-run') option.dryRun = true
232
+ else if (arg === '--no-verify') option.noVerify = true
221
233
  else if (arg === '--yes' || arg === '-y') option.yes = true
222
234
  else if (arg === '--verbose') option.verbose = true
223
235
  else if (arg === '--staged') option.staged = true
224
236
  else if (arg === '--cat' || arg === '--nyan') option.sprite = 'cat'
225
237
  else if (arg === '--banana') option.sprite = 'banana'
226
238
  else if (arg === '--upyachka') option.sprite = 'upyachka'
227
- else if (arg === '--pacman') option.sprite = 'pacman'
228
239
  else if (arg === '--type') option.type = argv[++i] || ''
229
240
  else if (arg === '--model') option.model = argv[++i] || ''
230
241
  else if (arg.startsWith('--type=')) option.type = arg.slice(7)
@@ -235,7 +246,7 @@ function parseArgs(argv) {
235
246
  }
236
247
 
237
248
  function pickSprite(forced) {
238
- const sprites = ['cat', 'banana', 'upyachka', 'pacman']
249
+ const sprites = ['cat', 'banana', 'upyachka']
239
250
  if (sprites.includes(forced)) return forced
240
251
  return sprites[Math.floor(Math.random() * sprites.length)]
241
252
  }
@@ -556,10 +567,6 @@ async function withAgentProgress(context, callback) {
556
567
  render: (index) => getNyanFrame(index, columns),
557
568
  frameMs: NYAN_FRAME_MS,
558
569
  },
559
- pacman: {
560
- render: (index) => getPacmanFrame(index, columns, spriteRows),
561
- frameMs: PACMAN_FRAME_MS,
562
- },
563
570
  upyachka: {
564
571
  render: (index) => getUpyachkaFrame(index, columns, spriteRows),
565
572
  frameMs: UPYACHKA_FRAME_MS,
@@ -1,380 +1,265 @@
1
- // Native pixels from the classic dancing banana GIF (Trym Stene, 1999).
2
- // 33×35px, 8 frames — 2-space cells, integer scale to the terminal.
1
+ // Extracted from the classic dancing banana GIF (Trym Stene, 1999).
2
+ // 8 frames, 24×25px — fixed canvas, solid 2-space blocks.
3
3
 
4
4
  const RESET = '\x1b[0m'
5
- const FRAME_WIDTH = 33
6
- const FRAME_HEIGHT = 35
5
+ const FRAME_WIDTH = 24
6
+ const FRAME_HEIGHT = 25
7
7
 
8
8
  const COLORS = {
9
9
  B: 236,
10
- d: 142,
10
+ d: 136,
11
+ E: 255,
11
12
  R: 196,
12
- W: 231,
13
+ W: 255,
13
14
  Y: 226,
14
- y: 184,
15
+ y: 214,
15
16
  }
16
17
 
17
18
  const FRAMES = [
18
19
  [
19
- '.................................',
20
- '.................................',
21
- '.................................',
22
- '.................................',
23
- '.................................',
24
- '.................................',
25
- '.................BB..............',
26
- '................BBBB.............',
27
- '................BddB.............',
28
- '................BdYBB............',
29
- '................BYYYBB...........',
30
- '................BYYYyBB..........',
31
- '................BYYYyyB..........',
32
- '..............BBBBBYYyBB.........',
33
- '.............BWBWWWBYyyB.........',
34
- '.............BBBBWWBYyyB.........',
35
- '.............BWBWWWBYyyB.........',
36
- '..............BBBBBYYByB.........',
37
- '...............BYYYBBByB.........',
38
- '...............BBBBRRByB.........',
39
- '................BRRRByyB.........',
40
- '................BRBBYyyB.........',
41
- '...............BBBYYYyyB.........',
42
- '.........BBB...BYYYYyyBB.BBB.....',
43
- '........BWWBB.BBYYYYyyBWBBWWB....',
44
- '........BWWWB.BYYYYyyBBWBWWWB....',
45
- '........BWWBB.BYYYyyyBWWBBWWB....',
46
- '.........BBBWBBYYyyyBBWWBBBB.....',
47
- '...........BWBYYyyyBBBWBB........',
48
- '...........BBBByyBBBWBBB.........',
49
- '..........BBBBBBBBBBBBB..........',
50
- '.........BBB........BBBB.........',
51
- '........BWWWBBB...BBBWWWB........',
52
- '........BWWWWWB...BWWWWWB........',
53
- '.........BBBBBB...BBBBBB.........',
20
+ '........................',
21
+ '........................',
22
+ '........................',
23
+ '........................',
24
+ '............BB..........',
25
+ '............BBB.........',
26
+ '............dYB.........',
27
+ '............YYYB........',
28
+ '............YYYBB.......',
29
+ '..........BBBBYyB.......',
30
+ '.........BEBEEByy.......',
31
+ '.........BEBEEByy.......',
32
+ '..........BBBBYBy.......',
33
+ '...........BYYBBy.......',
34
+ '............RRRyy.......',
35
+ '............RBByy.......',
36
+ '.......BB..BYYYyB.BB....',
37
+ '......BEB.BBYYYyBBBEB...',
38
+ '......BEE.BYYYyBBBEEB...',
39
+ '.......BBBBYyyyBWBBB....',
40
+ '........BBYYyyBBWB......',
41
+ '.......BBBBBBBBBB.......',
42
+ '.......BB......BB.......',
43
+ '......BWWBB..BBWWB......',
44
+ '.......BBBB..BBBB.......',
54
45
  ],
55
46
  [
56
- '.................................',
57
- '.................................',
58
- '.................................',
59
- '.................................',
60
- '.................BB..............',
61
- '................BBBB.............',
62
- '................BddB.............',
63
- '................BdYBB............',
64
- '................BYYYBB...........',
65
- '................BYYYyBB..........',
66
- '................BYYYyyB..........',
67
- '..............BBBBBYYyBB.........',
68
- '.............BWBWWWBYyyB.........',
69
- '.............BBBBWWBYyyB.........',
70
- '.............BWBWWWBYyyB.........',
71
- '..............BBBBBYYByB.........',
72
- '...............BYYYBBByB.........',
73
- '...............BBBBRRByB.........',
74
- '................BRRRByyB.........',
75
- '................BRBBYyyB.........',
76
- '...............BBBYYYyyB.........',
77
- '.......BBB.....BYYYYYyyB...BBB...',
78
- '......BWWBB....BYYYYyyBB..BBWWB..',
79
- '......BWWWB...BBYYYYyyB...BWWWB..',
80
- '......BWWBB...BYYYYyyBB...BBWWB..',
81
- '.......BBBBB.BBYYYyyyBBB.BBBBB...',
82
- '...........BBBBYYyyyBB.BBB.......',
83
- '.............BYYyyyBB............',
84
- '............BBByyBBB.............',
85
- '...........BBBBBBB.BB............',
86
- '...........B........B............',
87
- '.........BBBB.......BBBB.........',
88
- '........BWWWWBB...BBWWWWB........',
89
- '........BWWWWWB...BWWWWWB........',
90
- '.........BBBBB.....BBBBB.........',
47
+ '........................',
48
+ '........................',
49
+ '........................',
50
+ '............BB..........',
51
+ '............ddB.........',
52
+ '............dYB.........',
53
+ '............YYYBB.......',
54
+ '............YYYyB.......',
55
+ '..........BBBBYyB.......',
56
+ '.........BBBWWByy.......',
57
+ '.........BEBEEByy.......',
58
+ '...........BYYBBy.......',
59
+ '...........BBBRBy.......',
60
+ '............RRRyy.......',
61
+ '...........BBYYyy.......',
62
+ '.....BB....BYYYyy...BB..',
63
+ '....BEEB..BBYYYyB..BEEB.',
64
+ '....BEEB..BYYYyBB..BEEB.',
65
+ '.....BBBBBBYYyyBB.BBBB..',
66
+ '.........BYYyyB.........',
67
+ '.........BByBBB.........',
68
+ '........B...............',
69
+ '.......BB......BB.......',
70
+ '......BWWBB..BBWWB......',
71
+ '.......BBB....BBB.......',
91
72
  ],
92
73
  [
93
- '................BB...............',
94
- '...............BddB..............',
95
- '...............BddB..............',
96
- '...............BydBB.............',
97
- '..............BBYYyB.............',
98
- '..............BYYYyBB............',
99
- '..............BYYYYyB............',
100
- '..BBB........BBBYBBBB........BBB.',
101
- '.BWWBB......BWWWBWWWBB......BBWWB',
102
- '.BWWWB......BWBWBWBWBB......BWWWB',
103
- '.BWWBB......BWWWBWWWBB......BBWWB',
104
- '..BBBB.......BBBYBBByB......BBBB.',
105
- '.....BBB.....BYYYYYdBB....BBB....',
106
- '.......BBB...BBBBBBRBB..BBB......',
107
- '.........BBB.BRRRRRByB.BB........',
108
- '...........BBBBRRRBYyBBB.........',
109
- '.............BYBBBYYyB...........',
110
- '.............BYYYYYyyB...........',
111
- '.............BYYYYYyyB...........',
112
- '.............BYYYYYyBB...........',
113
- '.............BYYYYYyB............',
114
- '.............BYYYYyyB............',
115
- '.............BYYYYyBB............',
116
- '.............BYYYyyB.............',
117
- '.............BYYyyBB.............',
118
- '.............BBByBB..............',
119
- '..............BBBBB..............',
120
- '..............B...B..............',
121
- '.............BB...BB.............',
122
- '.............B.....B.............',
123
- '.............B.....B.............',
124
- '..........BBBBB...BBBBB..........',
125
- '........BBWWWWB...BWWWWBB........',
126
- '........BWWWWBB...BBWWWWB........',
127
- '........BBBBB.......BBBBB........',
74
+ '............B...........',
75
+ '...........BdB..........',
76
+ '...........BdBB.........',
77
+ '..........BBYyB.........',
78
+ '..........BYYYy.........',
79
+ '.BBB.....BBBBBB......BB.',
80
+ '.WWW.....WBWWBWB....BWWB',
81
+ '.WWB.....WWWWWWB....BBWB',
82
+ '.BBB.....BBBBBBB....BBB.',
83
+ '.....BB..BBBBBRB.BBB....',
84
+ '.......BBBRRRRBB.B......',
85
+ '.........BYBBYYB........',
86
+ '.........BYYYYyB........',
87
+ '.........BYYYYyB........',
88
+ '.........BYYYYy.........',
89
+ '.........BYYYyy.........',
90
+ '.........BYYyyB.........',
91
+ '.........BYYyBB.........',
92
+ '.........BBBBB..........',
93
+ '..........B..B..........',
94
+ '.........BB..BB.........',
95
+ '.........B....B.........',
96
+ '.......BBBB..BBBB.......',
97
+ '......BWWWB..BWWWB......',
98
+ '......BBB......BBB......',
128
99
  ],
129
100
  [
130
- '.................................',
131
- '.................................',
132
- '..............BB.................',
133
- '.............BddB................',
134
- '.............BddB................',
135
- '............BBdyB................',
136
- '............ByYYBB...............',
137
- '...........BByYYYB...............',
138
- '...........ByYYYYB...............',
139
- '...........BBBBYBBB..............',
140
- '..........BBWWWBWWWB.............',
141
- '..........BBWBWBWBWB.............',
142
- '..BB......BBWWWBWWWB....BBB......',
143
- '.BWWBB....ByBBBYBBB....BBWWB.....',
144
- '.BWWWB....BBdYYYYYB....BWWWB.....',
145
- '.BWWBB....BBRBBBBBB....BBWWB.....',
146
- '..BBBB....ByBRRRRRB....BBBB......',
147
- '.....BB...ByYBRRRBB...BB.........',
148
- '......BB..ByYYBBBYB..BB..........',
149
- '.......BBBByyYYYYYBBBB...........',
150
- '..........BByYYYYYB..............',
151
- '...........ByYYYYYB..............',
152
- '...........ByyYYYYB..............',
153
- '...........BByYYYYB..............',
154
- '............ByyYYYB..............',
155
- '............BByyYYB..............',
156
- '.............BByBBB..............',
157
- '.............BBBBBB..............',
158
- '.............B....B..............',
159
- '............BB....BB.............',
160
- '............B......B.............',
161
- '..........BBB......BBBB..........',
162
- '........BBWWWBB...BBWWWBB........',
163
- '........BWWWWWB...BWWWWWB........',
164
- '........BBBBBB.....BBBBBB........',
101
+ '........................',
102
+ '..........BB............',
103
+ '.........Bdd............',
104
+ '.........Bdd............',
105
+ '.........yYYB...........',
106
+ '........ByYYB...........',
107
+ '........BBBYBB..........',
108
+ '.......BBWWBWWB.........',
109
+ '.......BBBWBBWB.........',
110
+ '.WWB...ByBBYBB...BWW....',
111
+ '.EEE...BBYYYYB...EEE....',
112
+ '.BBB...ByRRRRB...BBB....',
113
+ '....B..ByBRRBB..B.......',
114
+ '....BB.ByYBBYB.BB.......',
115
+ '.......BBYYYYB..........',
116
+ '........BYYYYB..........',
117
+ '........ByYYYB..........',
118
+ '.........yyYYB..........',
119
+ '.........ByyYB..........',
120
+ '.........BBBBB..........',
121
+ '.........B...B..........',
122
+ '..............B.........',
123
+ '.......BB.....BBB.......',
124
+ '......BWWBB..BBWWB......',
125
+ '......BBBB....BBBB......',
165
126
  ],
166
127
  [
167
- '.................................',
168
- '.................................',
169
- '.................................',
170
- '.................................',
171
- '.................................',
172
- '.................................',
173
- '..............BB.................',
174
- '.............BBBB................',
175
- '.............BddB................',
176
- '............BBYdB................',
177
- '...........BBYYYB................',
178
- '..........BByYYYB................',
179
- '..........ByyYYYB................',
180
- '.........BByYYBBBBB..............',
181
- '.........ByyYBWWWBWB.............',
182
- '.........ByyYBWWBBBB.............',
183
- '.........ByyYBWWWBWB.............',
184
- '.........ByBYYBBBBB..............',
185
- '.........ByBBBYYYB...............',
186
- '.........ByBRRBBBB...............',
187
- '.........ByyBRRRB................',
188
- '.........ByyYBBRB................',
189
- '.........ByyYYYBBB...............',
190
- '.....BBB.BByyYYYYB...BBB.........',
191
- '....BWWBBWByyYYYYBB.BBWWB........',
192
- '....BWWWBWBByyYYYYB.BWWWB........',
193
- '....BWWBBWWByyyYYYB.BBWWB........',
194
- '.....BBBBWWBByyyYYBB.BBB.........',
195
- '........BBWBBByyyYYB.B...........',
196
- '.........BBBWBBByyBBBB...........',
197
- '..........BBBBBBBBBBBBB..........',
198
- '.........BBBB........BBB.........',
199
- '........BWWWBBB...BBBWWWB........',
200
- '........BWWWWWB...BWWWWWB........',
201
- '.........BBBBBB...BBBBBB.........',
128
+ '........................',
129
+ '........................',
130
+ '........................',
131
+ '........................',
132
+ '..........BB............',
133
+ '.........BBB............',
134
+ '.........BYd............',
135
+ '........BYYY............',
136
+ '.......BBYYY............',
137
+ '.......ByYBBBB..........',
138
+ '.......yyBEEBEB.........',
139
+ '.......yyBEEBEB.........',
140
+ '.......yBYBBBB..........',
141
+ '.......yBBYYB...........',
142
+ '.......yyRRR............',
143
+ '.......yyBBR............',
144
+ '....BB.ByYYYB..BB.......',
145
+ '...BEBBByYYYBB.BEB......',
146
+ '...BEEBBByYYYB.EEB......',
147
+ '....BBBWByyyYBBBB.......',
148
+ '......BWBByyYYBB........',
149
+ '.......BBBBBBBBBB.......',
150
+ '.......BB......BB.......',
151
+ '......BWWBB..BBWWB......',
152
+ '.......BBBB..BBBB.......',
202
153
  ],
203
154
  [
204
- '.................................',
205
- '.................................',
206
- '.................................',
207
- '.................................',
208
- '..............BB.................',
209
- '.............BBBB................',
210
- '.............BddB................',
211
- '............BBYdB................',
212
- '...........BBYYYB................',
213
- '..........BByYYYB................',
214
- '..........ByyYYYB................',
215
- '.........BByYYBBBBB..............',
216
- '.........ByyYBWWWBWB.............',
217
- '.........ByyYBWWBBBB.............',
218
- '.........ByyYBWWWBWB.............',
219
- '.........ByBYYBBBBB..............',
220
- '.........ByBBBYYYB...............',
221
- '.........ByBRRBBBB...............',
222
- '.........ByyBRRRB................',
223
- '.........ByyYBBRB................',
224
- '.........ByyYYYBBB...............',
225
- '...BBB...ByyYYYYYB.....BBB.......',
226
- '..BWWBB..BByyYYYYB....BBWWB......',
227
- '..BWWWB...ByyYYYYBB...BWWWB......',
228
- '..BWWBB...BByyYYYYB...BBWWB......',
229
- '...BBBBB.BBByyyYYYBB.BBBBB.......',
230
- '.......BBB.BByyyYYBBBB...........',
231
- '............BByyyYYB.............',
232
- '.............BBByyBBB............',
233
- '............BB.BBBBBBB...........',
234
- '............B........B...........',
235
- '.........BBBB.......BBBB.........',
236
- '........BWWWWBB...BBWWWWB........',
237
- '........BWWWWWB...BWWWWWB........',
238
- '.........BBBBB.....BBBBB.........',
155
+ '........................',
156
+ '........................',
157
+ '........................',
158
+ '..........BB............',
159
+ '.........Bdd............',
160
+ '.........BYd............',
161
+ '.......BBYYY............',
162
+ '.......ByYYY............',
163
+ '.......ByYBBBB..........',
164
+ '.......yyBWWBBB.........',
165
+ '.......yyBEEBEB.........',
166
+ '.......yBBYYB...........',
167
+ '.......yBRBBB...........',
168
+ '.......yyRRR............',
169
+ '.......yyYYBB...........',
170
+ '..BB...yyYYYB....BB.....',
171
+ '.BEEB..ByYYYBB..BEEB....',
172
+ '.BEEB..BByYYYB..BEEB....',
173
+ '..BBBB.BByyYYBBBBBB.....',
174
+ '.........ByyYYB.........',
175
+ '.........BBByBB.........',
176
+ '...............B........',
177
+ '.......BB......BB.......',
178
+ '......BWWBB..BBWWB......',
179
+ '.......BBB....BBB.......',
239
180
  ],
240
181
  [
241
- '...............BB................',
242
- '..............BddB...............',
243
- '..............BddB...............',
244
- '.............BBdyB...............',
245
- '.............ByYYBB..............',
246
- '............BByYYYB..............',
247
- '............ByYYYYB..............',
248
- '.BBB........BBBBYBBB........BBB..',
249
- 'BWWBB......BBWWWBWWWB......BBWWB.',
250
- 'BWWWB......BBWBWBWBWB......BWWWB.',
251
- 'BWWBB......BBWWWBWWWB......BBWWB.',
252
- '.BBBB......ByBBBYBBB.......BBBB..',
253
- '....BBB....BBdYYYYYB.....BBB.....',
254
- '......BBB..BBRBBBBBB...BBB.......',
255
- '........BB.ByBRRRRRB.BBB.........',
256
- '.........BBByYBRRRBBBB...........',
257
- '...........ByYYBBBYB.............',
258
- '...........ByyYYYYYB.............',
259
- '...........ByyYYYYYB.............',
260
- '...........BByYYYYYB.............',
261
- '............ByYYYYYB.............',
262
- '............ByyYYYYB.............',
263
- '............BByYYYYB.............',
264
- '.............ByyYYYB.............',
265
- '.............BByyYYB.............',
266
- '..............BByBBB.............',
267
- '..............BBBBB..............',
268
- '..............B...B..............',
269
- '.............BB...BB.............',
270
- '.............B.....B.............',
271
- '.............B.....B.............',
272
- '..........BBBBB...BBBBB..........',
273
- '........BBWWWWB...BWWWWBB........',
274
- '........BWWWWBB...BBWWWWB........',
275
- '........BBBBB.......BBBBB........',
182
+ '...........B............',
183
+ '..........BdB...........',
184
+ '.........BBdB...........',
185
+ '.........ByYBB..........',
186
+ '.........yYYYB..........',
187
+ '.BB......BBBBBB.....BBB.',
188
+ 'BWWB....BWBWWBW.....WWW.',
189
+ 'BWBB....BWWWWWW.....BWW.',
190
+ '.BBB....BBBBBBB.....BBB.',
191
+ '....BBB.BRBBBBB..BB.....',
192
+ '......B.BBRRRRBBB.......',
193
+ '........BYYBBYB.........',
194
+ '........ByYYYYB.........',
195
+ '........ByYYYYB.........',
196
+ '.........yYYYYB.........',
197
+ '.........yyYYYB.........',
198
+ '.........ByyYYB.........',
199
+ '.........BByYYB.........',
200
+ '..........BBBBB.........',
201
+ '..........B..B..........',
202
+ '.........BB..BB.........',
203
+ '.........B....B.........',
204
+ '.......BBBB..BBBB.......',
205
+ '......BWWWB..BWWWB......',
206
+ '......BBB......BBB......',
276
207
  ],
277
208
  [
278
- '.................................',
279
- '.................................',
280
- '.................BB..............',
281
- '................BddB.............',
282
- '................BddB.............',
283
- '................BydBB............',
284
- '...............BBYYyB............',
285
- '...............BYYYyBB...........',
286
- '...............BYYYYyB...........',
287
- '..............BBBYBBBB...........',
288
- '.............BWWWBWWWBB..........',
289
- '.............BWBWBWBWBB..........',
290
- '......BBB....BWWWBWWWBB.....BBB..',
291
- '.....BWWBB....BBBYBBByB....BBWWB.',
292
- '.....BWWWB....BYYYYYdBB....BWWWB.',
293
- '.....BWWBB....BBBBBBRBB....BBWWB.',
294
- '......BBBB....BRRRRRByB....BBBB..',
295
- '.........BB...BBRRRBYyB...BB.....',
296
- '..........BB..BYBBBYYyB..BB......',
297
- '...........BBBBYYYYYyyBBBB.......',
298
- '..............BYYYYYyBB..........',
299
- '..............BYYYYYyB...........',
300
- '..............BYYYYyyB...........',
301
- '..............BYYYYyBB...........',
302
- '..............BYYYyyB............',
303
- '..............BYYyyBB............',
304
- '..............BBByBB.............',
305
- '..............BBBBBB.............',
306
- '..............B....B.............',
307
- '.............BB....BB............',
308
- '.............B......B............',
309
- '..........BBBB......BBB..........',
310
- '........BBWWWBB...BBWWWBB........',
311
- '........BWWWWWB...BWWWWWB........',
312
- '........BBBBBB.....BBBBBB........',
209
+ '........................',
210
+ '............BB..........',
211
+ '............ddB.........',
212
+ '............ddB.........',
213
+ '...........BYYy.........',
214
+ '...........BYYyB........',
215
+ '..........BBYBBB........',
216
+ '.........BWWBWWBB.......',
217
+ '.........BWBBWBBB.......',
218
+ '....WWB...BBYBByB...BWW.',
219
+ '....EEE...BYYYYBB...EEE.',
220
+ '....BBB...BRRRRyB...BBB.',
221
+ '.......B..BBRRByB..B....',
222
+ '.......BB.BYBBYyB.BB....',
223
+ '..........BYYYYBB.......',
224
+ '..........BYYYYB........',
225
+ '..........BYYYyB........',
226
+ '..........BYYyy.........',
227
+ '..........BYyyB.........',
228
+ '..........BBBBB.........',
229
+ '..........B...B.........',
230
+ '.........B..............',
231
+ '.......BBB.....BB.......',
232
+ '......BWWBB..BBWWB......',
233
+ '......BBBB....BBBB......',
313
234
  ],
314
235
  ]
315
236
 
316
- function pickIndexes(srcSize, maxSize) {
317
- if (srcSize <= maxSize) {
318
- return Array.from({ length: srcSize }, (_, i) => i)
319
- }
320
- return Array.from({ length: maxSize }, (_, i) =>
321
- Math.round((i * (srcSize - 1)) / (maxSize - 1)),
322
- )
323
- }
324
-
325
- function cellScale(terminalColumns, terminalRows) {
326
- const maxCols = Math.max(8, Math.floor((terminalColumns - 1) / 2))
327
- const maxRows = Math.max(8, (terminalRows || 24) - 2)
328
- return Math.max(
329
- 1,
330
- Math.min(
331
- Math.floor(maxCols / FRAME_WIDTH),
332
- Math.floor(maxRows / FRAME_HEIGHT),
333
- ),
334
- )
335
- }
336
-
337
- function renderRow(row, colIndexes, scale) {
237
+ function renderRow(row) {
338
238
  const padded = row.padEnd(FRAME_WIDTH, '.').slice(0, FRAME_WIDTH)
339
239
  let line = ''
340
- let last = -1
341
- for (const x of colIndexes) {
342
- const code = padded[x] === '.' ? undefined : COLORS[padded[x]]
343
- for (let s = 0; s < scale; s += 1) {
344
- if (code === undefined) {
345
- if (last !== -1) {
346
- line += RESET
347
- last = -1
348
- }
349
- line += ' '
350
- continue
351
- }
352
- if (code !== last) {
353
- line += `\x1b[48;5;${code}m`
354
- last = code
355
- }
240
+ for (const cell of padded) {
241
+ if (cell === '.') {
242
+ line += ' '
243
+ continue
244
+ }
245
+ const code = COLORS[cell]
246
+ if (code === undefined) {
356
247
  line += ' '
248
+ continue
357
249
  }
250
+ const bold = cell === 'E' ? '\x1b[1m' : ''
251
+ line += `${bold}\x1b[48;5;${code}m ${RESET}`
358
252
  }
359
- if (last !== -1) line += RESET
360
253
  return line
361
254
  }
362
255
 
363
- export function getBananaFrame(index, terminalColumns = 80, terminalRows = 24) {
364
- const scale = cellScale(terminalColumns, terminalRows)
365
- const maxCols = Math.max(8, Math.floor((terminalColumns - 1) / (2 * scale)))
366
- const maxRows = Math.max(8, Math.floor(((terminalRows || 24) - 2) / scale))
367
- const colIndexes = pickIndexes(FRAME_WIDTH, maxCols)
368
- const rowIndexes = pickIndexes(FRAME_HEIGHT, maxRows)
256
+ export function getBananaFrame(index) {
369
257
  const frame = FRAMES[index % FRAMES.length]
370
- const lines = []
371
- for (const y of rowIndexes) {
372
- const rendered = renderRow(frame[y], colIndexes, scale)
373
- for (let s = 0; s < scale; s += 1) {
374
- lines.push(rendered)
375
- }
258
+ const lines = frame.map(renderRow)
259
+ while (lines.length < FRAME_HEIGHT) {
260
+ lines.push(' '.repeat(FRAME_WIDTH * 2))
376
261
  }
377
- return lines.join('\n')
262
+ return lines.slice(0, FRAME_HEIGHT).join('\n')
378
263
  }
379
264
 
380
265
  export function getBananaFrameCount() {
@@ -1,140 +0,0 @@
1
- // High-resolution terminal sprites reconstructed from the supplied reference.
2
- // Two vertical source pixels share one terminal cell via half-block glyphs.
3
-
4
- const RESET = '\x1b[0m'
5
- const FRAME_WIDTH = 80
6
- const FRAME_HEIGHT = 16
7
- const FRAME_COUNT = 82
8
- const SPRITE_WIDTH = 14
9
- const SPRITE_HEIGHT = 16
10
- const COLORS = { B: 33, C: 51, O: 208, P: 218, R: 196, W: 231, Y: 226 }
11
-
12
- const GHOST = [
13
- '................',
14
- '......XXXX......',
15
- '....XXXXXXXX....',
16
- '...XXXXXXXXXX...',
17
- '..XXXXXXXXXXXX..',
18
- '..XWWXXXXWWWXXX.',
19
- '..WWWWXXWWWWXX..',
20
- '..BBWWXXBBWWXX..',
21
- '.XBBWWXXBBWWXXX.',
22
- '.XXWWXXXXWWXXXX.',
23
- '.XXXXXXXXXXXXXX.',
24
- '.XXXXXXXXXXXXXX.',
25
- '.XXXXXXXXXXXXXX.',
26
- '.XXXXXXXXXXXXXX.',
27
- '.XXXXXXXXXXXXXX.',
28
- '.XX.XXX..XXX.XX.',
29
- '.X...XX..XX...X.',
30
- '................',
31
- ]
32
-
33
- const PACMAN_CLOSED = [
34
- '................',
35
- '.....YYYYYY.....',
36
- '...YYYYYYYYYY...',
37
- '..YYYYYYYYYYYY..',
38
- '.YYYYYYYYYYYYYY.',
39
- 'YYYYYYYYYYYYYYYY',
40
- 'YYYYYYYYYYYYYYYY',
41
- 'YYYYYYYYYYYYYYYY',
42
- 'YYYYYYYYYYYYYYYY',
43
- 'YYYYYYYYYYYYYYYY',
44
- 'YYYYYYYYYYYYYYYY',
45
- 'YYYYYYYYYYYYYYYY',
46
- 'YYYYYYYYYYYYYYYY',
47
- '.YYYYYYYYYYYYYY.',
48
- '..YYYYYYYYYYYY..',
49
- '...YYYYYYYYYY...',
50
- '.....YYYYYY.....',
51
- '................',
52
- ]
53
-
54
- const PACMAN_OPEN = [
55
- '................',
56
- '.....YYYYYY.....',
57
- '...YYYYYYYYYY...',
58
- '..YYYYYYYYYYYY..',
59
- '.YYYYYYYYYYYYYY.',
60
- 'YYYYYYYYYYYYYYYY',
61
- '..YYYYYYYYYYYYYY',
62
- '....YYYYYYYYYYYY',
63
- '......YYYYYYYYYY',
64
- '........YYYYYYYY',
65
- '......YYYYYYYYYY',
66
- '....YYYYYYYYYYYY',
67
- '..YYYYYYYYYYYYYY',
68
- '.YYYYYYYYYYYYYY.',
69
- '..YYYYYYYYYYYY..',
70
- '...YYYYYYYYYY...',
71
- '.....YYYYYY.....',
72
- '................',
73
- ]
74
-
75
- function drawSprite(canvas, sprite, left, color) {
76
- const columns = pickIndexes(sprite[0].length, SPRITE_WIDTH)
77
- const rows = pickIndexes(sprite.length, SPRITE_HEIGHT)
78
- for (let y = 0; y < rows.length; y++) {
79
- for (let x = 0; x < columns.length; x++) {
80
- const cell = sprite[rows[y]][columns[x]]
81
- const targetX = left + x
82
- if (cell === '.' || targetX < 0 || targetX >= FRAME_WIDTH) continue
83
- canvas[y][targetX] = cell === 'X' ? color : cell
84
- }
85
- }
86
- }
87
-
88
- function buildFrame(index) {
89
- const canvas = Array.from({ length: FRAME_HEIGHT }, () =>
90
- Array(FRAME_WIDTH).fill('.'),
91
- )
92
- const pacmanLeft = FRAME_WIDTH - 7 - index * 2
93
- const pacman = Math.floor(index / 2) % 2 ? PACMAN_CLOSED : PACMAN_OPEN
94
- drawSprite(canvas, pacman, pacmanLeft, 'Y')
95
-
96
- for (const [ghostIndex, color] of ['R', 'C', 'P', 'O'].entries()) {
97
- drawSprite(canvas, GHOST, pacmanLeft + 20 + ghostIndex * 18, color)
98
- }
99
- return canvas
100
- }
101
-
102
- function pickIndexes(srcSize, maxSize) {
103
- if (srcSize <= maxSize) {
104
- return Array.from({ length: srcSize }, (_, index) => index)
105
- }
106
- return Array.from({ length: maxSize }, (_, index) =>
107
- Math.round((index * (srcSize - 1)) / (maxSize - 1)),
108
- )
109
- }
110
-
111
- function paint(top, bottom) {
112
- const topColor = COLORS[top]
113
- const bottomColor = COLORS[bottom]
114
- if (topColor === undefined && bottomColor === undefined) return ' '
115
- if (topColor === bottomColor) return `\x1b[48;5;${topColor}m \x1b[0m`
116
- if (topColor === undefined) return `\x1b[38;5;${bottomColor}m▄\x1b[0m`
117
- if (bottomColor === undefined) return `\x1b[38;5;${topColor}m▀\x1b[0m`
118
- return `\x1b[38;5;${topColor};48;5;${bottomColor}m▀\x1b[0m`
119
- }
120
-
121
- export function getPacmanFrame(index, terminalColumns = 80, terminalRows = 24) {
122
- const frame = buildFrame(index % FRAME_COUNT)
123
- const maxColumns = Math.max(16, (terminalColumns || 80) - 1)
124
- const maxLogicalRows = Math.max(4, ((terminalRows || 24) - 2) * 2)
125
- const columns = pickIndexes(FRAME_WIDTH, maxColumns)
126
- const rowCount = Math.min(FRAME_HEIGHT, maxLogicalRows)
127
- const rows = pickIndexes(FRAME_HEIGHT, rowCount - (rowCount % 2))
128
- const lines = []
129
-
130
- for (let index = 0; index < rows.length; index += 2) {
131
- const top = frame[rows[index]]
132
- const bottom = frame[rows[index + 1]]
133
- lines.push(columns.map((x) => paint(top[x], bottom[x])).join('') + RESET)
134
- }
135
- return lines.join('\n')
136
- }
137
-
138
- export function getPacmanFrameCount() {
139
- return FRAME_COUNT
140
- }