pabal-resource-mcp 1.8.10 → 1.8.11

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.
@@ -5126,9 +5126,9 @@ Expected phone screenshots in: ${screenshotsDir2}/{locale}/phone/`
5126
5126
  // src/tools/app-icon/generate-app-icons.ts
5127
5127
  import { z as z10 } from "zod";
5128
5128
  import { zodToJsonSchema as zodToJsonSchema10 } from "zod-to-json-schema";
5129
- import fs16 from "fs";
5130
- import path16 from "path";
5131
- import sharp6 from "sharp";
5129
+ import fs15 from "fs";
5130
+ import path15 from "path";
5131
+ import sharp5 from "sharp";
5132
5132
 
5133
5133
  // src/tools/app-icon/utils/icon-specs.util.ts
5134
5134
  import path14 from "path";
@@ -5263,14 +5263,12 @@ async function trimIconPadding(inputPath, threshold = 10) {
5263
5263
  const trimmed = await sharp4(inputPath).trim({ threshold }).toBuffer();
5264
5264
  return trimmed;
5265
5265
  }
5266
- async function convertToWhiteMask(inputPath, threshold = 128) {
5267
- const grayscale = await sharp4(inputPath).greyscale().toBuffer();
5268
- const { data, info } = await sharp4(grayscale).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
5266
+ async function convertToWhiteMask(inputPath) {
5267
+ const { data, info } = await sharp4(inputPath).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
5269
5268
  const pixels = new Uint8Array(data.length);
5270
5269
  for (let i = 0; i < data.length; i += 4) {
5271
- const gray = data[i];
5272
5270
  const alpha = data[i + 3];
5273
- if (alpha > 10 && gray > threshold) {
5271
+ if (alpha > 10) {
5274
5272
  pixels[i] = 255;
5275
5273
  pixels[i + 1] = 255;
5276
5274
  pixels[i + 2] = 255;
@@ -5282,14 +5280,13 @@ async function convertToWhiteMask(inputPath, threshold = 128) {
5282
5280
  pixels[i + 3] = 0;
5283
5281
  }
5284
5282
  }
5285
- const whiteMask = await sharp4(pixels, {
5283
+ return sharp4(pixels, {
5286
5284
  raw: {
5287
5285
  width: info.width,
5288
5286
  height: info.height,
5289
5287
  channels: 4
5290
5288
  }
5291
5289
  }).png().toBuffer();
5292
- return whiteMask;
5293
5290
  }
5294
5291
  function calculateAlignmentArea(canvasSize, safeZoneLeft, safeZoneRight, safeZoneTop, safeZoneBottom, alignment) {
5295
5292
  const safeZoneDiameter = safeZoneRight - safeZoneLeft;
@@ -5400,56 +5397,6 @@ function calculateFinalPosition(areaLeft, areaTop, areaWidth, areaHeight, logoWi
5400
5397
  return { left, top };
5401
5398
  }
5402
5399
 
5403
- // src/tools/app-icon/utils/icon-masking.util.ts
5404
- import fs15 from "fs";
5405
- import path15 from "path";
5406
- import sharp5 from "sharp";
5407
- async function applyWhiteMasking(inputPath, outputPath, targetSize) {
5408
- try {
5409
- const outputDir = path15.dirname(outputPath);
5410
- if (!fs15.existsSync(outputDir)) {
5411
- fs15.mkdirSync(outputDir, { recursive: true });
5412
- }
5413
- const processedBuffer = await createWhiteMaskFromOriginal(inputPath);
5414
- await sharp5(processedBuffer).resize(targetSize, targetSize, {
5415
- fit: "contain",
5416
- background: { r: 0, g: 0, b: 0, alpha: 0 }
5417
- }).png().toFile(outputPath);
5418
- return { success: true };
5419
- } catch (error) {
5420
- const message = error instanceof Error ? error.message : String(error);
5421
- return {
5422
- success: false,
5423
- error: message
5424
- };
5425
- }
5426
- }
5427
- async function createWhiteMaskFromOriginal(inputPath) {
5428
- const { data, info } = await sharp5(inputPath).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
5429
- const pixels = new Uint8Array(data.length);
5430
- for (let i = 0; i < data.length; i += 4) {
5431
- const a = data[i + 3];
5432
- if (a > 10) {
5433
- pixels[i] = 255;
5434
- pixels[i + 1] = 255;
5435
- pixels[i + 2] = 255;
5436
- pixels[i + 3] = 255;
5437
- } else {
5438
- pixels[i] = 0;
5439
- pixels[i + 1] = 0;
5440
- pixels[i + 2] = 0;
5441
- pixels[i + 3] = 0;
5442
- }
5443
- }
5444
- return sharp5(pixels, {
5445
- raw: {
5446
- width: info.width,
5447
- height: info.height,
5448
- channels: 4
5449
- }
5450
- }).png().toBuffer();
5451
- }
5452
-
5453
5400
  // src/tools/app-icon/generate-app-icons.ts
5454
5401
  var TOOL_NAME6 = "generate-app-icons";
5455
5402
  var generateAppIconsInputSchema = z10.object({
@@ -5485,9 +5432,6 @@ var generateAppIconsInputSchema = z10.object({
5485
5432
  ]).optional().describe(
5486
5433
  "Logo alignment within the canvas (default: center or config default). Affects how the logo is positioned relative to the safe zone."
5487
5434
  ),
5488
- useAlphaMasking: z10.boolean().optional().default(false).describe(
5489
- "Use alpha channel for white masking (default: false). When false, uses Sharp-based threshold conversion (converts bright pixels to white). When true, uses original image's alpha channel (preserves exact logo shape)."
5490
- ),
5491
5435
  skipExisting: z10.boolean().optional().default(false).describe("Skip generation if output file already exists (default: false)"),
5492
5436
  dryRun: z10.boolean().optional().default(false).describe(
5493
5437
  "Preview mode - shows what would be generated without actually generating"
@@ -5520,16 +5464,6 @@ var generateAppIconsTool = {
5520
5464
  - **Style Variants**: Generate themed icons (christmas, halloween, etc.) with style-specific defaults
5521
5465
  - **Config Integration**: Uses config.json appIcon settings for default colors and alignment
5522
5466
 
5523
- **White Masking Options:**
5524
- - **Default (useAlphaMasking=false)**: Threshold conversion - converts bright pixels to white
5525
- - **Alpha-based (useAlphaMasking=true)**: Uses original alpha channel - preserves exact logo shape
5526
-
5527
- **\u26A0\uFE0F Important: After Generation**
5528
- After generating icons, always check **android-notification-icon.png**:
5529
- - If white masking is not clean (unwanted artifacts or shape issues)
5530
- - Regenerate with \`useAlphaMasking: true\` for alpha-based masking
5531
- - Example: \`{ "appName": "my-app", "useAlphaMasking": true, "iconTypes": ["android-notification-icon"] }\`
5532
-
5533
5467
  **Example:**
5534
5468
  \`\`\`
5535
5469
  INPUT: my-app/icons/icon.png (source logo with padding)
@@ -5555,11 +5489,11 @@ function validateApp4(appName) {
5555
5489
  );
5556
5490
  }
5557
5491
  const productsDir = getProductsDir();
5558
- const configPath = path16.join(productsDir, app.slug, "config.json");
5492
+ const configPath = path15.join(productsDir, app.slug, "config.json");
5559
5493
  let config;
5560
- if (fs16.existsSync(configPath)) {
5494
+ if (fs15.existsSync(configPath)) {
5561
5495
  try {
5562
- const configData = fs16.readFileSync(configPath, "utf-8");
5496
+ const configData = fs15.readFileSync(configPath, "utf-8");
5563
5497
  config = JSON.parse(configData);
5564
5498
  } catch (error) {
5565
5499
  console.warn(
@@ -5578,7 +5512,7 @@ function buildGenerationTasks(slug, iconTypes, skipExisting, styleFolder) {
5578
5512
  const tasks = [];
5579
5513
  const baseIconPath = getBaseIconPath(slug, styleFolder);
5580
5514
  const iconsDir = getIconsDir(slug, styleFolder);
5581
- if (!fs16.existsSync(baseIconPath)) {
5515
+ if (!fs15.existsSync(baseIconPath)) {
5582
5516
  throw new Error(
5583
5517
  `Base icon not found: ${baseIconPath}
5584
5518
 
@@ -5587,7 +5521,7 @@ Please place your base icon at this location first.`
5587
5521
  }
5588
5522
  for (const iconType of iconTypes) {
5589
5523
  const outputPath = getIconOutputPath(slug, iconType, styleFolder);
5590
- if (skipExisting && fs16.existsSync(outputPath)) {
5524
+ if (skipExisting && fs15.existsSync(outputPath)) {
5591
5525
  continue;
5592
5526
  }
5593
5527
  tasks.push({
@@ -5599,7 +5533,7 @@ Please place your base icon at this location first.`
5599
5533
  }
5600
5534
  return { tasks, baseIconPath };
5601
5535
  }
5602
- async function generateIcons(tasks, backgroundColor, logoAlignment, useAlphaMasking, onProgress) {
5536
+ async function generateIcons(tasks, backgroundColor, logoAlignment, onProgress) {
5603
5537
  let generatedCount = 0;
5604
5538
  const errors = [];
5605
5539
  const total = tasks.length;
@@ -5613,27 +5547,16 @@ async function generateIcons(tasks, backgroundColor, logoAlignment, useAlphaMask
5613
5547
  };
5614
5548
  onProgress?.(progress);
5615
5549
  try {
5616
- const outputDir = path16.dirname(task.outputPath);
5617
- if (!fs16.existsSync(outputDir)) {
5618
- fs16.mkdirSync(outputDir, { recursive: true });
5550
+ const outputDir = path15.dirname(task.outputPath);
5551
+ if (!fs15.existsSync(outputDir)) {
5552
+ fs15.mkdirSync(outputDir, { recursive: true });
5619
5553
  }
5620
5554
  if (task.iconType === "android-notification-icon") {
5621
- if (useAlphaMasking) {
5622
- const result = await applyWhiteMasking(
5623
- task.inputPath,
5624
- task.outputPath,
5625
- task.spec.size
5626
- );
5627
- if (!result.success) {
5628
- throw new Error(result.error || "AI white masking failed");
5629
- }
5630
- } else {
5631
- const whiteMask = await convertToWhiteMask(task.inputPath);
5632
- await sharp6(whiteMask).resize(task.spec.size, task.spec.size, {
5633
- fit: "contain",
5634
- background: { r: 0, g: 0, b: 0, alpha: 0 }
5635
- }).png().toFile(task.outputPath);
5636
- }
5555
+ const whiteMask = await convertToWhiteMask(task.inputPath);
5556
+ await sharp5(whiteMask).resize(task.spec.size, task.spec.size, {
5557
+ fit: "contain",
5558
+ background: { r: 0, g: 0, b: 0, alpha: 0 }
5559
+ }).png().toFile(task.outputPath);
5637
5560
  } else {
5638
5561
  const iconBgColor = task.iconType === "ios-light" ? backgroundColor : "transparent";
5639
5562
  await resizeIconWithSafeZone(
@@ -5667,7 +5590,6 @@ async function handleGenerateAppIcons(input) {
5667
5590
  styleFolder,
5668
5591
  backgroundColor: bgColorInput,
5669
5592
  logoAlignment: logoAlignmentInput,
5670
- useAlphaMasking = false,
5671
5593
  skipExisting = false,
5672
5594
  dryRun = false
5673
5595
  } = input;
@@ -5715,11 +5637,6 @@ async function handleGenerateAppIcons(input) {
5715
5637
  const iconTypes = requestedIconTypes || ALL_ICON_TYPES;
5716
5638
  results.push(`\u{1F3AF} Icon types: ${iconTypes.join(", ")}`);
5717
5639
  results.push(`\u{1F4D0} Logo alignment: ${logoAlignment}`);
5718
- if (useAlphaMasking) {
5719
- results.push(`\u{1F3AD} White masking: alpha-based (preserves exact shape)`);
5720
- } else {
5721
- results.push(`\u{1F3AD} White masking: threshold-based (converts bright pixels)`);
5722
- }
5723
5640
  let tasks;
5724
5641
  let baseIconPath;
5725
5642
  try {
@@ -5787,7 +5704,6 @@ async function handleGenerateAppIcons(input) {
5787
5704
  tasks,
5788
5705
  bgColor,
5789
5706
  logoAlignment,
5790
- useAlphaMasking,
5791
5707
  (progress) => {
5792
5708
  const progressPrefix = `[${progress.current}/${progress.total}]`;
5793
5709
  if (progress.status === "generating") {
@@ -5819,21 +5735,6 @@ async function handleGenerateAppIcons(input) {
5819
5735
  \u{1F4C1} Output location: ${iconsDir}/`);
5820
5736
  results.push(`
5821
5737
  \u2705 Icon generation complete!`);
5822
- const hasNotificationIcon = tasks.some((t) => t.iconType === "android-notification-icon") && !generationResult.errors.some(
5823
- (e) => e.iconType === "android-notification-icon"
5824
- );
5825
- if (hasNotificationIcon && !useAlphaMasking) {
5826
- results.push(
5827
- `
5828
- \u26A0\uFE0F IMPORTANT: Check android-notification-icon.png`
5829
- );
5830
- results.push(
5831
- ` If the shape is not correct, regenerate with alpha masking:`
5832
- );
5833
- results.push(
5834
- ` { "appName": "${appInfo.slug}", "useAlphaMasking": true, "iconTypes": ["android-notification-icon"]${styleFolder ? `, "styleFolder": "${styleFolder}"` : ""} }`
5835
- );
5836
- }
5837
5738
  return {
5838
5739
  content: [
5839
5740
  {
@@ -5847,22 +5748,22 @@ async function handleGenerateAppIcons(input) {
5847
5748
  // src/tools/app-icon/stylize-app-icon.ts
5848
5749
  import { z as z11 } from "zod";
5849
5750
  import { zodToJsonSchema as zodToJsonSchema11 } from "zod-to-json-schema";
5850
- import fs18 from "fs";
5851
- import path18 from "path";
5852
- import sharp7 from "sharp";
5853
-
5854
- // src/tools/app-icon/utils/gemini.util.ts
5855
5751
  import fs17 from "fs";
5856
5752
  import path17 from "path";
5753
+ import sharp6 from "sharp";
5754
+
5755
+ // src/tools/app-icon/utils/gemini.util.ts
5756
+ import fs16 from "fs";
5757
+ import path16 from "path";
5857
5758
  import { GoogleGenAI as GoogleGenAI3 } from "@google/genai";
5858
5759
  function getGeminiClient3() {
5859
5760
  const apiKey = getGeminiApiKey();
5860
5761
  return new GoogleGenAI3({ apiKey });
5861
5762
  }
5862
5763
  function readImageAsBase643(imagePath) {
5863
- const buffer = fs17.readFileSync(imagePath);
5764
+ const buffer = fs16.readFileSync(imagePath);
5864
5765
  const base64 = buffer.toString("base64");
5865
- const ext = path17.extname(imagePath).toLowerCase();
5766
+ const ext = path16.extname(imagePath).toLowerCase();
5866
5767
  let mimeType = "image/png";
5867
5768
  if (ext === ".jpg" || ext === ".jpeg") {
5868
5769
  mimeType = "image/jpeg";
@@ -6006,12 +5907,12 @@ Generate the stylized icon with transparent background now.`;
6006
5907
  for (const part of parts) {
6007
5908
  if (part.inlineData?.data) {
6008
5909
  const imageBuffer = Buffer.from(part.inlineData.data, "base64");
6009
- const outputDir = path18.dirname(outputPath);
6010
- if (!fs18.existsSync(outputDir)) {
6011
- fs18.mkdirSync(outputDir, { recursive: true });
5910
+ const outputDir = path17.dirname(outputPath);
5911
+ if (!fs17.existsSync(outputDir)) {
5912
+ fs17.mkdirSync(outputDir, { recursive: true });
6012
5913
  }
6013
- const processedImage = await sharp7(imageBuffer).ensureAlpha().png().toBuffer();
6014
- await sharp7(processedImage).toFile(outputPath);
5914
+ const processedImage = await sharp6(imageBuffer).ensureAlpha().png().toBuffer();
5915
+ await sharp6(processedImage).toFile(outputPath);
6015
5916
  return { success: true };
6016
5917
  }
6017
5918
  }
@@ -6051,7 +5952,7 @@ async function handleStylizeAppIcon(input) {
6051
5952
  };
6052
5953
  }
6053
5954
  const baseIconPath = getBaseIconPath(appInfo.slug);
6054
- if (!fs18.existsSync(baseIconPath)) {
5955
+ if (!fs17.existsSync(baseIconPath)) {
6055
5956
  return {
6056
5957
  content: [
6057
5958
  {
@@ -6145,13 +6046,13 @@ Next step: Run generate-app-icons with styleFolder='${styleFolder}' to create pl
6145
6046
  }
6146
6047
 
6147
6048
  // src/tools/apps/init.ts
6148
- import fs19 from "fs";
6149
- import path19 from "path";
6049
+ import fs18 from "fs";
6050
+ import path18 from "path";
6150
6051
  import { z as z12 } from "zod";
6151
6052
  import { zodToJsonSchema as zodToJsonSchema12 } from "zod-to-json-schema";
6152
6053
  var listSlugDirs = (dir) => {
6153
- if (!fs19.existsSync(dir)) return [];
6154
- return fs19.readdirSync(dir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
6054
+ if (!fs18.existsSync(dir)) return [];
6055
+ return fs18.readdirSync(dir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
6155
6056
  };
6156
6057
  var initProjectInputSchema = z12.object({
6157
6058
  slug: z12.string().trim().optional().describe(
@@ -6176,7 +6077,7 @@ Steps:
6176
6077
  inputSchema: inputSchema12
6177
6078
  };
6178
6079
  async function handleInitProject(input) {
6179
- const pullDataDir = path19.join(getPullDataDir(), "products");
6080
+ const pullDataDir = path18.join(getPullDataDir(), "products");
6180
6081
  const publicDir = getProductsDir();
6181
6082
  const pullDataSlugs = listSlugDirs(pullDataDir);
6182
6083
  const publicSlugs = listSlugDirs(publicDir);
@@ -6259,14 +6160,14 @@ async function handleInitProject(input) {
6259
6160
  }
6260
6161
 
6261
6162
  // src/tools/content/create-blog-html.ts
6262
- import fs21 from "fs";
6263
- import path21 from "path";
6163
+ import fs20 from "fs";
6164
+ import path20 from "path";
6264
6165
  import { z as z13 } from "zod";
6265
6166
  import { zodToJsonSchema as zodToJsonSchema13 } from "zod-to-json-schema";
6266
6167
 
6267
6168
  // src/utils/blog.util.ts
6268
- import fs20 from "fs";
6269
- import path20 from "path";
6169
+ import fs19 from "fs";
6170
+ import path19 from "path";
6270
6171
  var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
6271
6172
  var BLOG_ROOT = "blogs";
6272
6173
  var removeDiacritics = (value) => value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
@@ -6354,13 +6255,13 @@ function resolveTargetLocales(input) {
6354
6255
  return fallback ? [fallback] : [];
6355
6256
  }
6356
6257
  function getBlogOutputPaths(options) {
6357
- const baseDir = path20.join(
6258
+ const baseDir = path19.join(
6358
6259
  options.publicDir,
6359
6260
  BLOG_ROOT,
6360
6261
  options.appSlug,
6361
6262
  options.slug
6362
6263
  );
6363
- const filePath = path20.join(baseDir, `${options.locale}.html`);
6264
+ const filePath = path19.join(baseDir, `${options.locale}.html`);
6364
6265
  const publicBasePath = toPublicBlogBase(options.appSlug, options.slug);
6365
6266
  return { baseDir, filePath, publicBasePath };
6366
6267
  }
@@ -6385,18 +6286,18 @@ function findExistingBlogPosts({
6385
6286
  publicDir,
6386
6287
  limit = 2
6387
6288
  }) {
6388
- const blogAppDir = path20.join(publicDir, BLOG_ROOT, appSlug);
6389
- if (!fs20.existsSync(blogAppDir)) {
6289
+ const blogAppDir = path19.join(publicDir, BLOG_ROOT, appSlug);
6290
+ if (!fs19.existsSync(blogAppDir)) {
6390
6291
  return [];
6391
6292
  }
6392
6293
  const posts = [];
6393
- const subdirs = fs20.readdirSync(blogAppDir, { withFileTypes: true });
6294
+ const subdirs = fs19.readdirSync(blogAppDir, { withFileTypes: true });
6394
6295
  for (const subdir of subdirs) {
6395
6296
  if (!subdir.isDirectory()) continue;
6396
- const localeFile = path20.join(blogAppDir, subdir.name, `${locale}.html`);
6397
- if (!fs20.existsSync(localeFile)) continue;
6297
+ const localeFile = path19.join(blogAppDir, subdir.name, `${locale}.html`);
6298
+ if (!fs19.existsSync(localeFile)) continue;
6398
6299
  try {
6399
- const htmlContent = fs20.readFileSync(localeFile, "utf-8");
6300
+ const htmlContent = fs19.readFileSync(localeFile, "utf-8");
6400
6301
  const { meta, body } = parseBlogHtml(htmlContent);
6401
6302
  if (meta && meta.locale === locale) {
6402
6303
  posts.push({
@@ -6539,7 +6440,7 @@ async function handleCreateBlogHtml(input) {
6539
6440
  }
6540
6441
  const output = {
6541
6442
  slug,
6542
- baseDir: path21.join(publicDir, "blogs", appSlug, slug),
6443
+ baseDir: path20.join(publicDir, "blogs", appSlug, slug),
6543
6444
  files: [],
6544
6445
  coverImage: coverImage && coverImage.trim().length > 0 ? coverImage.trim() : `/products/${appSlug}/og-image.png`,
6545
6446
  metaByLocale: {}
@@ -6553,7 +6454,7 @@ async function handleCreateBlogHtml(input) {
6553
6454
  })
6554
6455
  );
6555
6456
  const existing = plannedFiles.filter(
6556
- ({ filePath }) => fs21.existsSync(filePath)
6457
+ ({ filePath }) => fs20.existsSync(filePath)
6557
6458
  );
6558
6459
  if (existing.length > 0 && !overwrite) {
6559
6460
  const existingList = existing.map((f) => f.filePath).join("\n- ");
@@ -6562,7 +6463,7 @@ async function handleCreateBlogHtml(input) {
6562
6463
  - ${existingList}`
6563
6464
  );
6564
6465
  }
6565
- fs21.mkdirSync(output.baseDir, { recursive: true });
6466
+ fs20.mkdirSync(output.baseDir, { recursive: true });
6566
6467
  for (const locale of targetLocales) {
6567
6468
  const { filePath } = getBlogOutputPaths({
6568
6469
  appSlug,
@@ -6588,7 +6489,7 @@ async function handleCreateBlogHtml(input) {
6588
6489
  meta,
6589
6490
  content
6590
6491
  });
6591
- fs21.writeFileSync(filePath, html, "utf-8");
6492
+ fs20.writeFileSync(filePath, html, "utf-8");
6592
6493
  output.files.push({ locale, path: filePath });
6593
6494
  }
6594
6495
  const summaryLines = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pabal-resource-mcp",
3
- "version": "1.8.10",
3
+ "version": "1.8.11",
4
4
  "type": "module",
5
5
  "description": "MCP server for ASO data management with shared types and utilities",
6
6
  "author": "skyu",