feat(phase1): rebrand to TTRPG Player and drop Git updates feed
Rename product to TTRPG Player (TTRPGPlayer / com.ttrpgplayer.app), use .ttrpg.zip for new saves while keeping .dnd.zip import, accept TTRPG- and DND- license keys on client, and remove sync-update-feed plus CI push to DndGamePlayerUpdates. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
/** Отображаемое имя продукта (с пробелом), не путать с `productName` сборки (`TTRPGPlayer`). */
|
||||
export const APP_DISPLAY_NAME_EN = 'TTRPG Player';
|
||||
export const APP_DISPLAY_NAME_RU = 'НРИ Плеер';
|
||||
|
||||
/** Системный идентификатор приложения (exe, AppImage, appId). */
|
||||
export const APP_PRODUCT_NAME = 'TTRPGPlayer';
|
||||
export const APP_BUNDLE_ID = 'com.ttrpgplayer.app';
|
||||
|
||||
export function appDisplayNameForLocale(localeTag: string): string {
|
||||
const tag = localeTag.trim().toLowerCase();
|
||||
if (tag.startsWith('ru')) return APP_DISPLAY_NAME_RU;
|
||||
return APP_DISPLAY_NAME_EN;
|
||||
}
|
||||
@@ -1,16 +1,20 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import { isDndProductKey } from './productKey';
|
||||
import { isProductKey } from './productKey';
|
||||
|
||||
void test('isDndProductKey: пример пользователя', () => {
|
||||
assert.equal(isDndProductKey('DND-CEBEC1BF-AD0B-4312-BFDD-675AFF5955FD'), true);
|
||||
void test('isProductKey: legacy DND prefix', () => {
|
||||
assert.equal(isProductKey('DND-CEBEC1BF-AD0B-4312-BFDD-675AFF5955FD'), true);
|
||||
});
|
||||
|
||||
void test('isDndProductKey: токен с точкой — нет', () => {
|
||||
assert.equal(isDndProductKey('eyJ.xxx'), false);
|
||||
void test('isProductKey: TTRPG prefix', () => {
|
||||
assert.equal(isProductKey('TTRPG-CEBEC1BF-AD0B-4312-BFDD-675AFF5955FD'), true);
|
||||
});
|
||||
|
||||
void test('isDndProductKey: пробелы по краям', () => {
|
||||
assert.equal(isDndProductKey(' DND-CEBEC1BF-AD0B-4312-BFDD-675AFF5955FD '), true);
|
||||
void test('isProductKey: token with dot — no', () => {
|
||||
assert.equal(isProductKey('eyJ.xxx'), false);
|
||||
});
|
||||
|
||||
void test('isProductKey: trimmed', () => {
|
||||
assert.equal(isProductKey(' TTRPG-CEBEC1BF-AD0B-4312-BFDD-675AFF5955FD '), true);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
/** Продуктовый ключ активации (не путать с лицензионным токеном `base64.base64`). */
|
||||
const TTRPG_PRODUCT_KEY_RE = /^TTRPG-[0-9A-F]{8}-(?:[0-9A-F]{4}-){3}[0-9A-F]{12}$/iu;
|
||||
const DND_PRODUCT_KEY_RE = /^DND-[0-9A-F]{8}-(?:[0-9A-F]{4}-){3}[0-9A-F]{12}$/iu;
|
||||
|
||||
export function isDndProductKey(s: string): boolean {
|
||||
return DND_PRODUCT_KEY_RE.test(s.trim());
|
||||
export function isProductKey(s: string): boolean {
|
||||
const t = s.trim();
|
||||
return TTRPG_PRODUCT_KEY_RE.test(t) || DND_PRODUCT_KEY_RE.test(t);
|
||||
}
|
||||
|
||||
/** @deprecated Используйте {@link isProductKey}. */
|
||||
export function isDndProductKey(s: string): boolean {
|
||||
return isProductKey(s);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ void test('package.json: конфиг electron-builder (mac/win/linux)', () => {
|
||||
};
|
||||
};
|
||||
assert.ok(pkg.build);
|
||||
assert.equal(pkg.build.appId, 'com.dndplayer.app');
|
||||
assert.equal(pkg.build.appId, 'com.ttrpgplayer.app');
|
||||
assert.equal(pkg.build.asar, true, 'релизный артефакт: app.asar без «голого» дерева dist в .app/.exe');
|
||||
assert.ok(Array.isArray(pkg.build.asarUnpack));
|
||||
assert.ok(pkg.build.asarUnpack.some((p) => p.includes('preload')));
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/** Текущий формат архива проекта (новые сохранения). */
|
||||
export const PROJECT_ZIP_EXTENSION = '.ttrpg.zip';
|
||||
|
||||
/** Устаревший формат — только открытие / импорт (вариант B). */
|
||||
export const PROJECT_ZIP_EXTENSION_LEGACY = '.dnd.zip';
|
||||
|
||||
export function isProjectZipFileName(fileName: string): boolean {
|
||||
const lower = fileName.toLowerCase();
|
||||
return lower.endsWith(PROJECT_ZIP_EXTENSION) || lower.endsWith(PROJECT_ZIP_EXTENSION_LEGACY);
|
||||
}
|
||||
|
||||
export function isLegacyProjectZipFileName(fileName: string): boolean {
|
||||
return fileName.toLowerCase().endsWith(PROJECT_ZIP_EXTENSION_LEGACY);
|
||||
}
|
||||
|
||||
export function projectZipFileNameFromBase(stem: string): string {
|
||||
return `${stem}${PROJECT_ZIP_EXTENSION}`;
|
||||
}
|
||||
|
||||
/** Имя файла для сохранения/экспорта — всегда `.ttrpg.zip`. */
|
||||
export function normalizeSaveProjectZipPath(filePath: string): string {
|
||||
const lower = filePath.toLowerCase();
|
||||
if (lower.endsWith(PROJECT_ZIP_EXTENSION)) return filePath;
|
||||
if (lower.endsWith(PROJECT_ZIP_EXTENSION_LEGACY)) {
|
||||
return filePath.slice(0, -PROJECT_ZIP_EXTENSION_LEGACY.length) + PROJECT_ZIP_EXTENSION;
|
||||
}
|
||||
if (lower.endsWith('.zip')) {
|
||||
return filePath.replace(/\.zip$/iu, PROJECT_ZIP_EXTENSION);
|
||||
}
|
||||
return `${filePath}${PROJECT_ZIP_EXTENSION}`;
|
||||
}
|
||||
|
||||
export function stripProjectZipExtension(fileName: string): string {
|
||||
const lower = fileName.toLowerCase();
|
||||
if (lower.endsWith(PROJECT_ZIP_EXTENSION)) {
|
||||
return fileName.slice(0, -PROJECT_ZIP_EXTENSION.length);
|
||||
}
|
||||
if (lower.endsWith(PROJECT_ZIP_EXTENSION_LEGACY)) {
|
||||
return fileName.slice(0, -PROJECT_ZIP_EXTENSION_LEGACY.length);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
export const PROJECT_ZIP_OPEN_DIALOG_FILTER: Electron.FileFilter = {
|
||||
name: 'Проект TTRPG (*.ttrpg.zip, *.dnd.zip)',
|
||||
extensions: ['ttrpg.zip', 'dnd.zip'],
|
||||
};
|
||||
|
||||
export const PROJECT_ZIP_SAVE_DIALOG_FILTER: Electron.FileFilter = {
|
||||
name: 'Проект TTRPG (*.ttrpg.zip)',
|
||||
extensions: ['ttrpg.zip'],
|
||||
};
|
||||
@@ -98,7 +98,7 @@ export type Scene = {
|
||||
|
||||
export type ProjectMeta = {
|
||||
name: string;
|
||||
/** Имя файла проекта без суффикса `.dnd.zip` (то, что пользователь редактирует). */
|
||||
/** Имя файла проекта без суффикса `.ttrpg.zip` (то, что пользователь редактирует). */
|
||||
fileBaseName: string;
|
||||
createdAt: IsoDateTimeString;
|
||||
updatedAt: IsoDateTimeString;
|
||||
|
||||
Reference in New Issue
Block a user