dsh-novel-generator 0.1.2 → 0.1.3

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.
Files changed (3) hide show
  1. package/client.js +17 -10
  2. package/index.js +4 -3
  3. package/package.json +1 -1
package/client.js CHANGED
@@ -437,9 +437,10 @@ body[data-ds-dark-theme] .dsn-shell{color-scheme:dark}
437
437
  const [busy, setBusy] = useState(false);
438
438
  const [error, setError] = useState('');
439
439
 
440
- // The title becomes a folder name inside the working directory.
441
- const effectiveTitle = title.trim() === '' ? t('未命名作品', "Untitled work") : title;
442
- const titleIssue = titleProblem(effectiveTitle);
440
+ // The title becomes a folder name inside the working directory, and it is
441
+ // required: an empty name is reported when the user tries to create.
442
+ const trimmedTitle = title.trim();
443
+ const titleIssue = trimmedTitle === '' ? '' : titleProblem(trimmedTitle);
443
444
 
444
445
  const groups = (boot.models && boot.models.groups) || [];
445
446
  const models = useMemo(() => {
@@ -469,6 +470,14 @@ body[data-ds-dark-theme] .dsn-shell{color-scheme:dark}
469
470
 
470
471
  const create = useCallback(async () => {
471
472
  setError('');
473
+ if (trimmedTitle === '') {
474
+ setError(t('作品名称不能为空', "Title cannot be empty."));
475
+ return;
476
+ }
477
+ if (titleIssue) {
478
+ setError(titleIssue);
479
+ return;
480
+ }
472
481
  if (!cwd.trim()) {
473
482
  setError(t('请先点「选择」挑一个工作目录', "Pick a working directory with \"Select\" first."));
474
483
  return;
@@ -476,7 +485,7 @@ body[data-ds-dark-theme] .dsn-shell{color-scheme:dark}
476
485
  setBusy(true);
477
486
  try {
478
487
  const result = await api('/project/create', {
479
- title: title.trim() || t('未命名作品', "Untitled work"),
488
+ title: trimmedTitle,
480
489
  cwd: cwd.trim(),
481
490
  agentPreset: preset || undefined,
482
491
  model: provider && model ? { provider, model } : null,
@@ -489,7 +498,7 @@ body[data-ds-dark-theme] .dsn-shell{color-scheme:dark}
489
498
  } finally {
490
499
  setBusy(false);
491
500
  }
492
- }, [title, cwd, preset, provider, model, onOpen, onReload]);
501
+ }, [trimmedTitle, titleIssue, cwd, preset, provider, model, onOpen, onReload]);
493
502
 
494
503
  const remove = useCallback(async (project) => {
495
504
  if (!window.confirm(t('确定删除作品「{title}」?这会同时删除工作目录下的作品文件夹(正文、梗概、世界快照)。作品的会话本身保留。', 'Delete the work “{title}”? This also deletes its folder in the working directory (chapters, outlines, world snapshots). The session itself stays.', { title: project.title }))) return;
@@ -555,14 +564,12 @@ body[data-ds-dark-theme] .dsn-shell{color-scheme:dark}
555
564
  h('button', {
556
565
  className: 'dsn-btn primary',
557
566
  type: 'button',
558
- disabled: busy || Boolean(titleIssue),
559
- title: titleIssue || '',
567
+ // Enabled even when the form is incomplete: the click itself says why.
568
+ disabled: busy,
560
569
  onClick: create,
561
570
  }, busy ? t('创建中…', "Creating…") : t('创建作品并开会话', "Create work and open its session")))),
562
571
 
563
- error || titleIssue
564
- ? h('div', { className: 'dsn-error', style: { margin: 0 } }, error || titleIssue)
565
- : null,
572
+ error ? h('div', { className: 'dsn-error', style: { margin: 0 } }, error) : null,
566
573
 
567
574
  h('div', { className: 'dsn-list' },
568
575
  boot.projects.length === 0
package/index.js CHANGED
@@ -982,7 +982,7 @@ async function bootstrap(ctx, root) {
982
982
  }
983
983
  }
984
984
  return {
985
- revision: '2026-10-20.14',
985
+ revision: '2026-10-20.15',
986
986
  projects,
987
987
  models: {
988
988
  default: modelCatalog.default || null,
@@ -1005,8 +1005,9 @@ async function bootstrap(ctx, root) {
1005
1005
  async function createProject(ctx, root, body) {
1006
1006
  const controller = ctx.get('sessionController');
1007
1007
  if (controller === undefined) throw httpError(503, '当前部署缺少 sessionController 服务,无法创建会话');
1008
- const title = asText(body.title) || '未命名作品';
1009
- // The title becomes a folder name inside the chosen directory.
1008
+ // The title is required and becomes a folder name inside the chosen
1009
+ // directory, so an empty or illegal one is refused rather than defaulted.
1010
+ const title = asText(body.title);
1010
1011
  const titleIssue = titleProblem(title);
1011
1012
  if (titleIssue !== null) throw httpError(400, titleIssue);
1012
1013
  const cwd = asText(body.cwd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-novel-generator",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "Novel studio for DeepSeek Harness: a draggable story timeline, world snapshots, chapter outlines, and chapters written by the work's own agent.",
6
6
  "license": "MIT",