Source: app/utils/sanitizePastedJson.js

/*
 * Copyright 2026 The Ray Optics Simulation authors and contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Remove leading markdown fence (e.g. ```json) and trailing ``` from AI-style pastes.
 * @param {string} text
 * @returns {string}
 */
export function stripMarkdownCodeFence(text) {
  let s = text.trim()
  if (!s.startsWith('```')) return text

  let i = 3
  while (i < s.length && /[a-zA-Z0-9_-]/.test(s[i])) i++
  while (i < s.length && /\s/.test(s[i])) {
    const ch = s[i]
    i++
    if (ch === '\n' || (ch === '\r' && s[i] === '\n')) {
      if (ch === '\r' && s[i] === '\n') i++
      break
    }
  }

  s = s.slice(i)
  s = s.replace(/\s*```\s*$/, '')
  return s.trim()
}

/**
 * Strip // and /* *\/ comments from pasted text while respecting string literals.
 * `://` is not treated as a line comment (e.g. http://).
 * @param {string} text
 * @returns {string}
 */
export function stripJsonComments(text) {
  let result = ''
  let i = 0
  let inString = false
  let stringQuote = ''
  let escape = false

  while (i < text.length) {
    const c = text[i]

    if (inString) {
      result += c
      if (escape) {
        escape = false
      } else if (c === '\\') {
        escape = true
      } else if (c === stringQuote) {
        inString = false
        stringQuote = ''
      }
      i++
      continue
    }

    if (c === '"' || c === "'") {
      inString = true
      stringQuote = c
      result += c
      i++
      continue
    }

    if (c === '/' && text[i + 1] === '*') {
      i += 2
      while (i < text.length - 1) {
        if (text[i] === '*' && text[i + 1] === '/') {
          i += 2
          break
        }
        i++
      }
      continue
    }

    if (c === '/' && text[i + 1] === '/') {
      if (i > 0 && text[i - 1] === ':') {
        result += '/'
        i++
        continue
      }
      i += 2
      while (i < text.length && text[i] !== '\n' && text[i] !== '\r') i++
      continue
    }

    result += c
    i++
  }

  return result
}

/**
 * Prepare clipboard text for the scene JSON editor (fences + comments).
 * @param {string} text
 * @returns {string}
 */
export function sanitizePastedJson(text) {
  if (text == null || text === '') return text
  const unfenced = stripMarkdownCodeFence(text)
  return stripJsonComments(unfenced)
}