tsoft-cli 3.6.1 → 3.7.1

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/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to T-Soft CLI will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.7.1] - 2026-04-16
9
+
10
+ ### Fixed
11
+ - Theme list in `tsoft theme dev` now paginates — previously only the first page was fetched, hiding themes on large stores
12
+ - Node 25+ compatibility — added `main` entry in `package.json`
13
+
14
+ ## [3.7.0] - 2026-04-15
15
+
16
+ ### Added
17
+ - Live preview link in `tsoft theme dev` — press **P** to open the dev theme in your browser
18
+ - HMAC-signed preview URL with cookie-based session (default 24h) so navigation across pages keeps the dev theme rendered
19
+ - `?_preview_clear=1` to exit preview mode
20
+
8
21
  ## [3.5.1] - 2026-04-09
9
22
 
10
23
  ### Fixed
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "tsoft-cli",
3
- "version": "3.6.1",
3
+ "version": "3.7.1",
4
4
  "description": "tsoft360 tema geliştirme ve yönetim aracı",
5
5
  "type": "module",
6
+ "main": "src/index.js",
6
7
  "bin": {
7
8
  "tsoft": "src/index.js"
8
9
  },
@@ -5,14 +5,35 @@ import path from 'path';
5
5
  import AdmZip from 'adm-zip';
6
6
  import chokidar from 'chokidar';
7
7
  import {createApiClient} from '../api-client.js';
8
+ import open from 'open';
8
9
  import {normalizeStoreDomain, slugify, setActiveTheme, getActiveTheme, getActiveStore} from '../storage.js';
9
10
  import {brandedConfirm, brandedSelect, showWarning, showError, showInfo} from '../ui/prompt-wrapper.js';
10
11
  import { t } from '../i18n.js';
11
12
 
12
- async function getThemeUuid(themeSlug) {
13
+ async function fetchAllThemes() {
13
14
  const apiClient = await createApiClient();
14
- const response = await apiClient.get('/theme');
15
- const themes = response.data || [];
15
+ const all = [];
16
+ let page = 1;
17
+
18
+ // Server ignores per_page; we walk every page until last_page is reached.
19
+ // Hard cap at 20 pages to avoid accidental infinite loops.
20
+ for (let i = 0; i < 20; i++) {
21
+ const response = await apiClient.get(`/theme?per_page=500&page=${page}`);
22
+ const batch = response.data || [];
23
+ all.push(...batch);
24
+
25
+ // Laravel paginator puts last_page at the top level (not under meta).
26
+ // Some envelopes wrap it under meta — support both.
27
+ const lastPage = response.last_page ?? response.meta?.last_page ?? 1;
28
+ if (page >= lastPage || batch.length === 0) break;
29
+ page++;
30
+ }
31
+
32
+ return all;
33
+ }
34
+
35
+ async function getThemeUuid(themeSlug) {
36
+ const themes = await fetchAllThemes();
16
37
 
17
38
  const theme = themes.find(t => slugify(t.name) === themeSlug);
18
39
 
@@ -169,6 +190,45 @@ async function startFileWatcher(themeUuid, themePath) {
169
190
  console.log(chalk.gray(t('dev.env_running')));
170
191
  console.log(chalk.gray(' ' + t('dev.exit_tip') + '\n'));
171
192
 
193
+ // Preview URL — backend HMAC token uretip dev temayi onizleme link'i veriyor
194
+ let previewUrl = null;
195
+ try {
196
+ const apiClient = await createApiClient();
197
+ const previewRes = await apiClient.get(`/theme/${themeUuid}/preview-url`);
198
+ previewUrl = previewRes?.data?.preview_url || null;
199
+ if (previewUrl) {
200
+ const expIso = previewRes?.data?.expires_at;
201
+ console.log(chalk.bgGreen.black(' [P] Preview ') + ' ' + chalk.white(previewUrl));
202
+ console.log(chalk.gray(' ' + chalk.dim('basın: ') + chalk.bold('P') + chalk.dim(' tarayıcıda aç')) + (expIso ? chalk.gray(` ${chalk.dim('exp:')} ${expIso}`) : '') + '\n');
203
+ }
204
+ } catch (err) {
205
+ console.log(chalk.gray(' (preview link alinamadi: ' + (err?.message || 'unknown') + ')\n'));
206
+ }
207
+
208
+ // Klavye dinleyici — 'P' tarayıcıda preview URL'ini acar, Ctrl+C cikis
209
+ if (previewUrl && process.stdin.isTTY) {
210
+ try {
211
+ process.stdin.setRawMode(true);
212
+ process.stdin.resume();
213
+ process.stdin.setEncoding('utf8');
214
+ process.stdin.on('data', async (key) => {
215
+ if (key === '\u0003') { // Ctrl+C
216
+ process.exit(0);
217
+ }
218
+ if (key === 'p' || key === 'P') {
219
+ console.log(chalk.cyan(' tarayıcıda açılıyor: ') + chalk.white(previewUrl));
220
+ try {
221
+ await open(previewUrl);
222
+ } catch (e) {
223
+ console.log(chalk.red(' tarayıcı acilamadi: ' + e.message));
224
+ }
225
+ }
226
+ });
227
+ } catch (e) {
228
+ // raw mode bazi terminallerde calismaz — sessizce gec
229
+ }
230
+ }
231
+
172
232
  const watcher = chokidar.watch(themePath, {
173
233
  ignored: /(^|[\/\\])\../, persistent: true, ignoreInitial: true, awaitWriteFinish: {
174
234
  stabilityThreshold: 300, pollInterval: 100
@@ -127,7 +127,8 @@ export async function themeCreateCommand() {
127
127
  description: description.trim() || null,
128
128
  active: 0,
129
129
  hidden: 0,
130
- forceUpdate: false
130
+ forceUpdate: false,
131
+ minimal: minimal,
131
132
  };
132
133
 
133
134
  console.log(chalk.yellow(t('theme.saving_db')));