@nodaro/prompts 1.29.0 → 1.32.0

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/dist/index.cjs CHANGED
@@ -22222,10 +22222,10 @@ function projectOptionFields(o, detail, imageUrl) {
22222
22222
  function catalogSections(c, images) {
22223
22223
  const source = catalogTopics(c.catalogId);
22224
22224
  if (!source) return void 0;
22225
- const present2 = new Set((c.dimensions ?? []).map((d) => d.field));
22225
+ const present3 = new Set((c.dimensions ?? []).map((d) => d.field));
22226
22226
  return source.map((s) => {
22227
22227
  const imageUrl = images ? pickerSectionImageUrl(s.label, images) : void 0;
22228
- return { label: s.label, fields: s.fields.filter((f) => present2.has(f)), ...imageUrl ? { imageUrl } : {} };
22228
+ return { label: s.label, fields: s.fields.filter((f) => present3.has(f)), ...imageUrl ? { imageUrl } : {} };
22229
22229
  }).filter((s) => s.fields.length > 0);
22230
22230
  }
22231
22231
  function projectPickerCatalog(c, opts = {}) {
@@ -40852,13 +40852,13 @@ function applyPickerJson(current, pickerJson, mode, spec) {
40852
40852
  const patch = {};
40853
40853
  for (const d of spec.dimensions) {
40854
40854
  const incoming = pickerJson[d.dimension];
40855
- const present2 = !isEmptyValue(incoming);
40855
+ const present3 = !isEmptyValue(incoming);
40856
40856
  if (mode === "override") {
40857
- patch[d.field] = present2 ? coerce(incoming, d.limit) : void 0;
40857
+ patch[d.field] = present3 ? coerce(incoming, d.limit) : void 0;
40858
40858
  } else if (mode === "overwrite-detected") {
40859
- if (present2) patch[d.field] = coerce(incoming, d.limit);
40859
+ if (present3) patch[d.field] = coerce(incoming, d.limit);
40860
40860
  } else {
40861
- if (present2 && isEmptyValue(current[d.field])) patch[d.field] = coerce(incoming, d.limit);
40861
+ if (present3 && isEmptyValue(current[d.field])) patch[d.field] = coerce(incoming, d.limit);
40862
40862
  }
40863
40863
  }
40864
40864
  spec.cleanup?.(patch, mode);
@@ -41928,6 +41928,28 @@ function computeScriptTopic(data, { override, wired, refMap }) {
41928
41928
  return applyPromptAffixes(core === void 0 ? "" : rr(core, refMap), shared.readPromptAffixes(data), refMap);
41929
41929
  }
41930
41930
 
41931
+ // src/audio-tags.ts
41932
+ function removeBracketTags(text) {
41933
+ let out = "";
41934
+ let from = 0;
41935
+ let open = text.indexOf("[");
41936
+ while (open !== -1) {
41937
+ const close = text.indexOf("]", open + 1);
41938
+ if (close === -1) break;
41939
+ if (close === open + 1) {
41940
+ open = text.indexOf("[", open + 1);
41941
+ continue;
41942
+ }
41943
+ out += text.slice(from, open);
41944
+ from = close + 1;
41945
+ open = text.indexOf("[", from);
41946
+ }
41947
+ return out + text.slice(from);
41948
+ }
41949
+ function stripAudioTags(text) {
41950
+ return removeBracketTags(text).replace(/\s{2,}/g, " ").trim();
41951
+ }
41952
+
41931
41953
  // src/node-prompt-fields.ts
41932
41954
  var NODE_PROMPT_FIELDS = {
41933
41955
  // ── Image ──
@@ -45643,6 +45665,20 @@ var VOICE_DESIGN_PRESETS = [
45643
45665
  group: "Narration & Character",
45644
45666
  data: { voiceDescription: "A calm, soft-spoken meditation guide with a slow, breathy, reassuring delivery." }
45645
45667
  },
45668
+ {
45669
+ id: "voice-design/casual-creator-female",
45670
+ name: "Casual Creator (female)",
45671
+ description: "Relaxed, warm, everyday.",
45672
+ group: "Narration & Character",
45673
+ data: { voiceDescription: "An adult woman in her late twenties talking casually to the viewer as if telling a friend about something she tried: relaxed, warm and upbeat, a natural conversational pace with small pauses, a light neutral American accent, recorded close and dry in a quiet room, never polished or announcer-like." }
45674
+ },
45675
+ {
45676
+ id: "voice-design/casual-creator-male",
45677
+ name: "Casual Creator (male)",
45678
+ description: "Relaxed, warm, everyday.",
45679
+ group: "Narration & Character",
45680
+ data: { voiceDescription: "An adult man in his late twenties talking casually to the viewer as if telling a friend about something he tried: relaxed, warm and upbeat, a natural conversational pace with small pauses, a light neutral American accent, recorded close and dry in a quiet room, never polished or announcer-like." }
45681
+ },
45646
45682
  // ── Professional & Assistant ─────────────────────────────────────────────
45647
45683
  {
45648
45684
  id: "voice-design/hype-announcer",
@@ -45750,6 +45786,27 @@ var TEXT_TO_AUDIO_PRESETS = [
45750
45786
  group: "Ambiences (loopable)",
45751
45787
  data: { provider: "elevenlabs-sfx", duration: 22, loop: true, promptInfluence: 0.5, prompt: "low sci-fi ambient drone, ominous spaceship hum" }
45752
45788
  },
45789
+ {
45790
+ id: "text-to-audio/living-room-tone",
45791
+ name: "Living Room Tone",
45792
+ description: "Quiet, lived-in room.",
45793
+ group: "Ambiences (loopable)",
45794
+ data: { provider: "elevenlabs-sfx", duration: 22, loop: true, promptInfluence: 0.4, prompt: "quiet living room room tone, faint air and a soft distant household hum, steady and even, an empty room at rest" }
45795
+ },
45796
+ {
45797
+ id: "text-to-audio/kitchen-hum",
45798
+ name: "Kitchen Hum",
45799
+ description: "Fridge hum in a still kitchen.",
45800
+ group: "Ambiences (loopable)",
45801
+ data: { provider: "elevenlabs-sfx", duration: 22, loop: true, promptInfluence: 0.4, prompt: "home kitchen room tone, a low steady refrigerator hum with faint air, calm and continuous, an empty kitchen at rest" }
45802
+ },
45803
+ {
45804
+ id: "text-to-audio/cafe-murmur",
45805
+ name: "Caf\xE9 Murmur",
45806
+ description: "Soft, indistinct caf\xE9 chatter.",
45807
+ group: "Ambiences (loopable)",
45808
+ data: { provider: "elevenlabs-sfx", duration: 22, loop: true, promptInfluence: 0.4, prompt: "caf\xE9 background ambience, a soft indistinct murmur of distant conversation and the occasional faint cup clink, steady and even, no clear words" }
45809
+ },
45753
45810
  // ── UI & Stingers ────────────────────────────────────────────────────────
45754
45811
  {
45755
45812
  id: "text-to-audio/ui-click",
@@ -45905,6 +45962,16 @@ var LLM_CHAT_PRESETS = [
45905
45962
  temperature: 0.7
45906
45963
  }
45907
45964
  },
45965
+ {
45966
+ id: "llm-chat/hook-generator",
45967
+ name: "Hook Generator",
45968
+ description: "10 opening lines for a talking-to-camera video.",
45969
+ group: "Writing & Marketing",
45970
+ data: {
45971
+ systemPrompt: "You write opening lines for short vertical videos in which one person talks straight to the camera about a product, a site or an idea. Given the user's topic, write 10 different opening lines.\n\nEvery line:\n- is one short spoken clause of at most 8 words;\n- leads with the payload the viewer can check: a number, a price, a time saved, a before and after, a contrast with the usual way, or a named result;\n- opens on the claim itself \u2014 no greeting, no filler word, no introduction of the product before the claim;\n- sounds said, not written: plain words, contractions welcome, no hashtags, no emoji, no stage directions;\n- spells the product or brand exactly as the user gives it and writes numbers as digits.\n\nVary the angle across the 10: a surprising number, a price, time saved, a mistake to avoid, a before and after, a contrast, a named result, a direct question. Write in the language the user writes in. Return only the numbered list, one line each.",
45972
+ temperature: 0.9
45973
+ }
45974
+ },
45908
45975
  // ── Utility ──────────────────────────────────────────────────────────────
45909
45976
  {
45910
45977
  id: "llm-chat/prompt-enhancer",
@@ -46086,6 +46153,16 @@ var IMAGE_TO_TEXT_PRESETS = [
46086
46153
  group: "Extraction",
46087
46154
  data: { detailLevel: "detailed", customPrompt: "Write a compelling e-commerce product description based on this product image." }
46088
46155
  },
46156
+ {
46157
+ id: "image-to-text/site-screenshot-summary",
46158
+ name: "Site Screenshot Summary",
46159
+ description: "Section, headings and figures from a web page.",
46160
+ group: "Extraction",
46161
+ data: {
46162
+ detailLevel: "structured",
46163
+ customPrompt: 'Describe this website screenshot as five labelled lines. Section: which part of the site it is (for example the top of the home page, a feature, how it works, reviews, pricing, sign-up, navigation or footer). Headings: every heading exactly as written. Figures: every number, price, rating and date exactly as written, each with what it refers to. Brand: the product or company name exactly as spelled. First look: what a viewer notices first, in one sentence. Write "none" on a line with nothing to report.'
46164
+ }
46165
+ },
46089
46166
  // ── Creative ─────────────────────────────────────────────────────────────
46090
46167
  {
46091
46168
  id: "image-to-text/scene-description",
@@ -46197,6 +46274,20 @@ var ADD_CAPTIONS_PRESETS = [
46197
46274
  description: "Subtitles along the top.",
46198
46275
  group: "Caption Styles",
46199
46276
  data: { style: "subtitle", position: "top", fontSize: 36, color: "#FFFFFF", autoTranscribe: true }
46277
+ },
46278
+ {
46279
+ id: "add-captions/hook-plate",
46280
+ name: "Hook Plate",
46281
+ description: "Your opening line as a bold plate near the top.",
46282
+ group: "Caption Styles",
46283
+ data: { style: "subtitle", look: "clean", position: "top", positionY: 20, fontSize: 32, fontWeight: 800, maxWordsPerLine: 4, color: "#111111", backgroundColor: "#FFFFFF", autoTranscribe: false }
46284
+ },
46285
+ {
46286
+ id: "add-captions/body-captions",
46287
+ name: "Body Captions",
46288
+ description: "Word-by-word captions under the face.",
46289
+ group: "Caption Styles",
46290
+ data: { style: "word-highlight", look: "outline", position: "bottom", positionY: 72, fontSize: 32, maxWordsPerLine: 3, color: "#FFFFFF", autoTranscribe: true }
46200
46291
  }
46201
46292
  ];
46202
46293
  var COMBINE_VIDEOS_PRESETS = [
@@ -46758,6 +46849,80 @@ function getFactoryPresets(nodeType) {
46758
46849
  return FACTORY_PRESETS[nodeType] ?? [];
46759
46850
  }
46760
46851
 
46852
+ // src/caption-treatments.ts
46853
+ var HOOK_PLATE_PRESET_ID = "add-captions/hook-plate";
46854
+ var BODY_CAPTIONS_PRESET_ID = "add-captions/body-captions";
46855
+ var CAPTION_LEVERS_BY_LANGUAGE = {
46856
+ he: { fontFamily: "Rubik", uppercase: false }
46857
+ };
46858
+ var CAPTION_SEGMENT_LEVER_KEYS = [
46859
+ "style",
46860
+ "position",
46861
+ "fontSize",
46862
+ "color",
46863
+ "backgroundColor",
46864
+ "look",
46865
+ "fontFamily",
46866
+ "fontWeight",
46867
+ "strokeColor",
46868
+ "strokeWidth",
46869
+ "highlightColor",
46870
+ "uppercase",
46871
+ "positionY",
46872
+ "animate",
46873
+ "maxWordsPerLine"
46874
+ ];
46875
+ var LEVER_KEYS = new Set(CAPTION_SEGMENT_LEVER_KEYS);
46876
+ function pickLevers(source) {
46877
+ const out = {};
46878
+ if (!source) return out;
46879
+ for (const [k, v] of Object.entries(source)) if (LEVER_KEYS.has(k) && v !== void 0 && v !== null) out[k] = v;
46880
+ return out;
46881
+ }
46882
+ function presetLevers(id) {
46883
+ const preset = ADD_CAPTIONS_PRESETS.find((p) => p.id === id);
46884
+ if (!preset) throw new Error(`The ${id} caption preset is missing from the catalog.`);
46885
+ return pickLevers(preset.data);
46886
+ }
46887
+ var PLATE_LEVERS = presetLevers(HOOK_PLATE_PRESET_ID);
46888
+ var BODY_LEVERS = presetLevers(BODY_CAPTIONS_PRESET_ID);
46889
+ function languageLevers(tag) {
46890
+ if (typeof tag !== "string") return {};
46891
+ const primary = tag.trim().split(/[-_]/)[0].toLowerCase();
46892
+ return Object.hasOwn(CAPTION_LEVERS_BY_LANGUAGE, primary) ? CAPTION_LEVERS_BY_LANGUAGE[primary] : {};
46893
+ }
46894
+ var isTime = (n) => typeof n === "number" && Number.isFinite(n);
46895
+ function hookPlateCaptionSegments(plan, opts = {}) {
46896
+ const hookEnd = plan.hookEndMs;
46897
+ if (!isTime(hookEnd) || hookEnd < 0) return { segments: [], dropped: ["hook", "body"] };
46898
+ const language = languageLevers(plan.language);
46899
+ const limit = typeof plan.videoDurationMs === "number" ? plan.videoDurationMs : Number.POSITIVE_INFINITY;
46900
+ const segments = [];
46901
+ const dropped = [];
46902
+ const plateEnd = Math.min(hookEnd, limit);
46903
+ if (plateEnd > 0 && typeof plan.hookText === "string" && /\S/.test(plan.hookText)) {
46904
+ segments.push({ startMs: 0, endMs: plateEnd, text: plan.hookText, ...PLATE_LEVERS, ...language });
46905
+ } else {
46906
+ dropped.push("hook");
46907
+ }
46908
+ const bodyEnd = Math.min(plan.bodyEndMs, limit);
46909
+ const words = Array.isArray(plan.captions) ? plan.captions : [];
46910
+ const chosen = pickLevers(opts.bodyLevers);
46911
+ const bodyLevers = Object.keys(chosen).length > 0 ? chosen : BODY_LEVERS;
46912
+ if (bodyEnd > hookEnd && words.length > 0) {
46913
+ segments.push({
46914
+ startMs: hookEnd,
46915
+ endMs: bodyEnd,
46916
+ captions: words.map((w) => ({ text: w.text, startMs: w.startMs, endMs: w.endMs })),
46917
+ ...bodyLevers,
46918
+ ...language
46919
+ });
46920
+ } else {
46921
+ dropped.push("body");
46922
+ }
46923
+ return { segments, dropped };
46924
+ }
46925
+
46761
46926
  // src/style-presets.ts
46762
46927
  var STYLE_PRESETS = [
46763
46928
  {
@@ -47420,6 +47585,57 @@ ${input.body}`);
47420
47585
  Analyst focus from the user: ${input.focus}`);
47421
47586
  return lines.join("\n");
47422
47587
  }
47588
+ var WIRED_OUTPUT_NODE_TYPES = /* @__PURE__ */ new Set(["webhook-output", ...shared.SOCIAL_POST_NODE_TYPES]);
47589
+ var TEXT_REQUIRED_NODE_TYPES = /* @__PURE__ */ new Set([
47590
+ "llm-chat",
47591
+ "ai-writer",
47592
+ "generate-script",
47593
+ "text-to-speech",
47594
+ "generate-music",
47595
+ "text-to-audio",
47596
+ // Save to Collection refuses an empty record ("nothing to save"): a feed
47597
+ // with nothing new wired straight into it must skip it, not fail the run.
47598
+ "collection-write",
47599
+ // Generate Image: an empty prompt is a legal request in general (references
47600
+ // alone), but when the text it was WIRED produced nothing this run — the skip
47601
+ // rule's precondition — an empty prompt is a paid picture of nothing.
47602
+ "generate-image"
47603
+ ]);
47604
+ var withoutAffixes = (data) => ({ ...data, promptPrefix: void 0, promptSuffix: void 0 });
47605
+ var present2 = (s) => typeof s === "string" && s.trim().length > 0;
47606
+ function computeAiWriterInput(data, { override, wired }) {
47607
+ return [override, wired, data.userInput, data.prompt].find(present2) ?? "";
47608
+ }
47609
+ function computeNodeSendText(nodeType, data, args) {
47610
+ const bare = withoutAffixes(data);
47611
+ switch (nodeType) {
47612
+ case "llm-chat":
47613
+ return computeLlmChatFields(bare, {
47614
+ override: args.override,
47615
+ wiredUserInput: args.wired,
47616
+ wiredSystemPrompt: args.wiredSystemPrompt,
47617
+ refMap: args.refMap
47618
+ }).userInput;
47619
+ case "ai-writer":
47620
+ return computeAiWriterInput(bare, args);
47621
+ case "generate-script":
47622
+ return computeScriptTopic(bare, { override: args.override, wired: args.wired, refMap: args.refMap });
47623
+ case "generate-music": {
47624
+ const prompt = computeNodePrompt(nodeType, bare, { override: args.override, wired: args.wired, refMap: args.refMap });
47625
+ return present2(prompt) ? prompt : present2(data.lyrics) ? data.lyrics : "";
47626
+ }
47627
+ case "text-to-speech":
47628
+ case "text-to-audio":
47629
+ case "generate-image":
47630
+ return computeNodePrompt(nodeType, bare, { override: args.override, wired: args.wired, refMap: args.refMap });
47631
+ case "collection-write": {
47632
+ const typed = [data.title, data.text, data.link].find((v) => present2(v));
47633
+ return [args.override, args.wired, typed].find(present2) ?? "";
47634
+ }
47635
+ default:
47636
+ return void 0;
47637
+ }
47638
+ }
47423
47639
 
47424
47640
  exports.ACTION_FX = ACTION_FX;
47425
47641
  exports.ACTION_FX_CATEGORY_LABELS = ACTION_FX_CATEGORY_LABELS;
@@ -47446,6 +47662,7 @@ exports.BACKDROPS = BACKDROPS;
47446
47662
  exports.BACKDROP_CATEGORY_LABELS = BACKDROP_CATEGORY_LABELS;
47447
47663
  exports.BACKDROP_CATEGORY_ORDER = BACKDROP_CATEGORY_ORDER;
47448
47664
  exports.BACKDROP_IDS = BACKDROP_IDS;
47665
+ exports.BODY_CAPTIONS_PRESET_ID = BODY_CAPTIONS_PRESET_ID;
47449
47666
  exports.BRAND_PRESETS = BRAND_PRESETS;
47450
47667
  exports.BRAND_PRESET_IDS = BRAND_PRESET_IDS;
47451
47668
  exports.BRAND_PRESET_META = BRAND_PRESET_META;
@@ -47455,6 +47672,8 @@ exports.CAMERA_MOTIONS = CAMERA_MOTIONS;
47455
47672
  exports.CAMERA_MOTION_CATEGORY_LABELS = CAMERA_MOTION_CATEGORY_LABELS;
47456
47673
  exports.CAMERA_MOTION_CATEGORY_ORDER = CAMERA_MOTION_CATEGORY_ORDER;
47457
47674
  exports.CAMERA_MOTION_IDS = CAMERA_MOTION_IDS;
47675
+ exports.CAPTION_LEVERS_BY_LANGUAGE = CAPTION_LEVERS_BY_LANGUAGE;
47676
+ exports.CAPTION_SEGMENT_LEVER_KEYS = CAPTION_SEGMENT_LEVER_KEYS;
47458
47677
  exports.CHARACTER_ART_FAMILIES = CHARACTER_ART_FAMILIES;
47459
47678
  exports.CHARACTER_ART_FILES = CHARACTER_ART_FILES;
47460
47679
  exports.CHARACTER_ART_PATH = CHARACTER_ART_PATH;
@@ -47512,6 +47731,7 @@ exports.HELD_PROPS = HELD_PROPS;
47512
47731
  exports.HELD_PROP_CATEGORY_LABELS = HELD_PROP_CATEGORY_LABELS;
47513
47732
  exports.HELD_PROP_CATEGORY_ORDER = HELD_PROP_CATEGORY_ORDER;
47514
47733
  exports.HELD_PROP_IDS = HELD_PROP_IDS;
47734
+ exports.HOOK_PLATE_PRESET_ID = HOOK_PLATE_PRESET_ID;
47515
47735
  exports.IMAGE_HINT_MODE_DEFAULT = IMAGE_HINT_MODE_DEFAULT;
47516
47736
  exports.IMAGE_REFERENCE_PROMPT_DOCTRINE = IMAGE_REFERENCE_PROMPT_DOCTRINE;
47517
47737
  exports.IMAGE_WIZARD_CATEGORIES = IMAGE_WIZARD_CATEGORIES;
@@ -47657,6 +47877,7 @@ exports.TEMPORAL_CATEGORY_ORDER = TEMPORAL_CATEGORY_ORDER;
47657
47877
  exports.TEMPORAL_FIELD_BY_CATEGORY = TEMPORAL_FIELD_BY_CATEGORY;
47658
47878
  exports.TEMPORAL_IDS = TEMPORAL_IDS;
47659
47879
  exports.TERM_MAX_CHARS = TERM_MAX_CHARS;
47880
+ exports.TEXT_REQUIRED_NODE_TYPES = TEXT_REQUIRED_NODE_TYPES;
47660
47881
  exports.TEXT_WIZARD_CATEGORIES = TEXT_WIZARD_CATEGORIES;
47661
47882
  exports.TRANSITIONS = TRANSITIONS;
47662
47883
  exports.TRANSITION_CATEGORY_LABELS = TRANSITION_CATEGORY_LABELS;
@@ -47687,6 +47908,7 @@ exports.WARDROBE_DIMENSION_ORDER = WARDROBE_DIMENSION_ORDER;
47687
47908
  exports.WARDROBE_FIELD_BY_DIMENSION = WARDROBE_FIELD_BY_DIMENSION;
47688
47909
  exports.WHITE_FLASH_STYLE = WHITE_FLASH_STYLE;
47689
47910
  exports.WIPE_DIRECTION = WIPE_DIRECTION;
47911
+ exports.WIRED_OUTPUT_NODE_TYPES = WIRED_OUTPUT_NODE_TYPES;
47690
47912
  exports.__resetCatalogIdGuardForTests = __resetCatalogIdGuardForTests;
47691
47913
  exports.appendField = appendField;
47692
47914
  exports.appendMusicMeta = appendMusicMeta;
@@ -47790,8 +48012,10 @@ exports.composeVideoPromptText = composeVideoPromptText;
47790
48012
  exports.composedHas = composedHas;
47791
48013
  exports.composedOption = composedOption;
47792
48014
  exports.composedOptionIndex = composedOptionIndex;
48015
+ exports.computeAiWriterInput = computeAiWriterInput;
47793
48016
  exports.computeLlmChatFields = computeLlmChatFields;
47794
48017
  exports.computeNodePrompt = computeNodePrompt;
48018
+ exports.computeNodeSendText = computeNodeSendText;
47795
48019
  exports.computePackSidecarCoverage = computePackSidecarCoverage;
47796
48020
  exports.computeScriptTopic = computeScriptTopic;
47797
48021
  exports.containsMinorAgeHint = containsMinorAgeHint;
@@ -48005,6 +48229,7 @@ exports.groupFactoryPresets = groupFactoryPresets;
48005
48229
  exports.hasCatalogPacksFor = hasCatalogPacksFor;
48006
48230
  exports.hasScene3DLayoutScopingLine = hasScene3DLayoutScopingLine;
48007
48231
  exports.hasUpstreamCharacter = hasUpstreamCharacter;
48232
+ exports.hookPlateCaptionSegments = hookPlateCaptionSegments;
48008
48233
  exports.identityRefsSentence = identityRefsSentence;
48009
48234
  exports.insertBeforeStyleSection = insertBeforeStyleSection;
48010
48235
  exports.isAnalyzablePicker = isAnalyzablePicker;
@@ -48080,6 +48305,7 @@ exports.setComposedCatalogResolver = setComposedCatalogResolver;
48080
48305
  exports.soundArtFilePath = soundArtFilePath;
48081
48306
  exports.soundArtPath = soundArtPath;
48082
48307
  exports.splitStyleSection = splitStyleSection;
48308
+ exports.stripAudioTags = stripAudioTags;
48083
48309
  exports.styleSectionFromClauses = styleSectionFromClauses;
48084
48310
  exports.styleSlotFor = styleSlotFor;
48085
48311
  exports.subjectFieldsForSurface = subjectFieldsForSurface;