How to disable Electron.js panel window movable area click event on macOS?

In Electron.js, a panel type Browser window has an approximately 28-pixel top area that acts as a moveable area. Clicking in this area focuses the application, bringing the app menu to the top taskbar, while clicking elsewhere does not. I want to prevent this focusing behavior when clicking the top area to create a spotlight-like panel window. Here is my Electron app’s index.ts code.

//index.ts
import { app, BrowserWindow, globalShortcut, ipcMain } from "electron";
// This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Webpack
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
// whether you're running in development or production).
declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;

declare const QUICK_WINDOW_WEBPACK_ENTRY: string;
declare const QUICK_WINDOW_PRELOAD_WEBPACK_ENTRY: string;

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require("electron-squirrel-startup")) {
  app.quit();
}

let mainWindow: BrowserWindow | null;
let quickWindow: BrowserWindow | null;

const createWindow = (): void => {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    height: 600,
    width: 1000,
    webPreferences: {
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
    },
  });

  quickWindow = new BrowserWindow({
    type: "panel",
    frame: false,
    show: false,
    height: 600,
    width: 600,

    movable: false,
    focusable: true,
    skipTaskbar: true,
    maximizable: false,
    vibrancy: 'sidebar',
    

    webPreferences: {
      preload: QUICK_WINDOW_PRELOAD_WEBPACK_ENTRY,
    },
  });

  // and load the index.html of the app.
  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
  quickWindow.loadURL(QUICK_WINDOW_WEBPACK_ENTRY);

  globalShortcut.register("CmdOrCtrl+Shift+1", () => {
    quickWindow?.show();
  });

  // Open the DevTools.
  // mainWindow.webContents.openDevTools();
};

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", createWindow);

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on("window-all-closed", () => {
  if (process.platform !== "darwin") {
    app.quit();
    mainWindow = null;
    quickWindow = null;
  }
});

app.on("activate", () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.

ipcMain.handle("hideQuickAccess", () => {
  quickWindow && quickWindow.hide();
});

ipcMain.handle("resizeToExtended", () => {
  if (quickWindow) {
    quickWindow.setBounds({ height: 450, width: 500 });
  }
});

ipcMain.handle("resizeToNormal", () => {
  if (quickWindow) {
    quickWindow.setBounds({ height: 400, width: 300 });
  }
});

Illegal Invocation when calling this.spread.savePDF

Framework: VueJs
SpreadJs Version: 16.2.1

I am trying to save a PDF of the workbook but every time it hits this line of code

this.spread.savePDF((data: Blob) => {
          // dowload function
        }, (err: any) => {
          // error handling
        })

It will cause an invocation error below.

Illegal invocation
TypeError: Illegal invocation
at e.nextTick.i [as nextTick] (webpack-internal:///./node_modules/.pnpm/@[email protected]/node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js:17:22486)
at f (webpack-internal:///./node_modules/.pnpm/@[email protected]/node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js:17:36290)
at onwrite (webpack-internal:///./node_modules/.pnpm/@[email protected]/node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js:17:35023)
at c._write (webpack-internal:///./node_modules/.pnpm/@[email protected]/node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js:17:1979)
at K (webpack-internal:///./node_modules/.pnpm/@[email protected]/node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js:17:35945)
at P (webpack-internal:///./node_modules/.pnpm/@[email protected]/node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js:17:35854)
at a.write (webpack-internal:///./node_modules/.pnpm/@[email protected]/node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js:17:37406)
at o.P (webpack-internal:///./node_modules/.pnpm/@[email protected]/node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js:17:280659)
at o.n [as emit] (webpack-internal:///./node_modules/.pnpm/@[email protected]/node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js:17:120991)
at f.read (webpack-internal:///./node_modules/.pnpm/@[email protected]/node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js:17:279930)

Tried using other functions of this.spread like this.spread.getSheetCount() works. So it’s not about ‘this’ losing its reference/binding.

Why does my populateDropdown function affect the handleSearch functionality?

I am working on a furniture search and filtering system in JavaScript, where I have two main blocks of code:

  1. The populateDropdown function:
    This function is responsible for dynamically populating dropdown menus with filtered options as the user types into an input field.
function populateDropdown(inputId, dropdownId, options) { 
  const input = document.getElementById(inputId);
  const dropdown = document.getElementById(dropdownId);
  let isTextCopied = false;

  input.addEventListener('focus', function () {
    const query = input.value.toLowerCase();
    populateOptions(query, options, dropdown);
    if (!isTextCopied) dropdown.style.display = 'block';
  });

  input.addEventListener('click', function () {
    if (!isTextCopied) {
      const query = input.value.toLowerCase();
      populateOptions(query, options, dropdown);
      dropdown.style.display = 'block';
    }
  });

  input.addEventListener('input', function () {
    const query = this.value.toLowerCase();
    populateOptions(query, options, dropdown);
    if (!isTextCopied) dropdown.style.display = 'block';
  });

  document.addEventListener('mousedown', function (e) {
    if (!input.contains(e.target) && !dropdown.contains(e.target)) {
      dropdown.style.display = 'none';
    }
  });

  dropdown.addEventListener('mousedown', function (e) {
    e.stopPropagation();
  });

  function populateOptions(query, options, dropdown) {
    dropdown.innerHTML = '';
    const groupedOptions = {};

    options.forEach(option => {
      if (option.name.toLowerCase().includes(query)) {
        if (!groupedOptions[option.group]) {
          groupedOptions[option.group] = [];
        }
        groupedOptions[option.group].push(option);
      }
    });

    if (Object.keys(groupedOptions).length > 0) {
      for (const [group, groupOptions] of Object.entries(groupedOptions)) {
        const groupItem = document.createElement('li');
        groupItem.className = 'optgroup';
        groupItem.textContent = group;
        dropdown.appendChild(groupItem);

        groupOptions.forEach(option => {
          const listItem = document.createElement('li');
          let specialtypeKey = (option.name.indexOf('-') > -1) ? parseInt(option.name.split('-')[0].trim(), 10) : option.name;
          listItem.innerHTML = `<img src="${option.img}" alt="${option.name}"><span class="ddlabel">${option.name}</span>`;
          listItem.setAttribute('data-value', specialtypeKey);

          listItem.addEventListener('click', function (e) {
            e.stopPropagation();
            input.value = option.name;
            input.setAttribute('data-selected', specialtypeKey);
            dropdown.style.display = 'none';
            isTextCopied = true;
          });

          dropdown.appendChild(listItem);
        });
      }
    } else {
      dropdown.style.display = 'none';
    }
  }

  input.addEventListener('input', function () {
    if (input.value.length === 0) {
      input.value = '';
      input.removeAttribute('data-selected');
      isTextCopied = false;
    }
  });

  input.addEventListener('click', function () {
    if (isTextCopied) dropdown.style.display = 'none';
  });
}
  1. The handleSearch function and search filters:
    The handleSearch function applies different filters and updates the displayed results based on user input.
function handleSearch() {
    // Obtener los valores de los inputs y filtros con validación
    const query = document.getElementById("query").value.toLowerCase();
    const selectedLine = (document.getElementById("furniLines").value || "").trim();
    const selectedCategory = (document.getElementById("furniCategories").value || "").trim();
    const selectedEnvironment = (document.getElementById("furniEnvironments").value || "").trim();
    let selectedSpecialtype = (document.getElementById("furniSpecialtypes").value || "").trim();

    // Mantener el valor actual de "per_page" directamente desde el input
    per_page = parseInt(document.getElementById("per_page_results").value, 10) || 100;

    // Procesar el valor de selectedSpecialtype
    if (selectedSpecialtype.includes(" - ")) {
        selectedSpecialtype = selectedSpecialtype.split(" - ")[0];
    }
    selectedSpecialtype = selectedSpecialtype ? parseInt(selectedSpecialtype, 10) : "";

    // Obtener valores de filtros de tipo de muebles
    const allClothingChecked = document.getElementById("allclothing").checked;
    const normalClothingChecked = document.getElementById("clothing").checked;
    const rareClothingChecked = document.getElementById("rareclothing").checked;
    const collectibleClothingChecked = document.getElementById("collectibleclothing").checked;
    const collectibleFurniChecked = document.getElementById("furniCollectibles").checked;

    const furniWallInput = document.getElementById("furniWall");
    const furniFloorInput = document.getElementById("furniFloor");

    const selectedFurniType = furniWallInput.checked ? 'wall' : furniFloorInput.checked ? 'floor' : 'none';

    // Filtrar por tipo de colección
    let filteredData = allFurnis.slice(); // Copiar el array de furnis

    // Si "allclothing" está marcado, filtrar solo por "clothingtypes" y sus subcategorías
    if (allClothingChecked) {
        filteredData = furnidata.clothingtypes.clothingitemtypes.furnitype
            .concat(furnidata.clothingtypes.LTDclothingitemtypes.furnitype)
            .concat(furnidata.clothingtypes.rareclothingitemtypes.furnitype)
            .concat(furnidata.clothingtypes.collectibleclothingitemtypes.furnitype);
    } else {
        // Si no está marcado, seguir con el flujo normal de filtros
        if (collectibleFurniChecked) {
            filteredData = furnidata.collectibleitemtypes.furnitype;
        } else {
            filteredData = [];
            if (normalClothingChecked) filteredData = filteredData.concat(furnidata.clothingtypes.clothingitemtypes.furnitype);
            if (rareClothingChecked) filteredData = filteredData.concat(furnidata.clothingtypes.rareclothingitemtypes.furnitype);
            if (collectibleClothingChecked) filteredData = filteredData.concat(furnidata.clothingtypes.collectibleclothingitemtypes.furnitype);
            if (!normalClothingChecked && !rareClothingChecked && !collectibleClothingChecked) filteredData = allFurnis.slice();
        }
    }

    // Filtrar por tipo especial, ambiente, categoría, y colección (si se seleccionaron)
    filteredData = filteredData.filter(furni => {
    return (
        (query === "" || furni.name.toLowerCase().includes(query) || JSON.stringify(furni).toLowerCase().includes(query)) &&
        (selectedCategory === "" || (Array.isArray(furni.category) 
            ? furni.category.some(category => category.toLowerCase() === selectedCategory.toLowerCase())
            : furni.category.toLowerCase() === selectedCategory.toLowerCase())) &&
        (selectedLine === "" || furni.furniline === selectedLine) &&
        (selectedEnvironment === "" || furni.environment === selectedEnvironment) &&
        (selectedSpecialtype === "" || furni.specialtype === selectedSpecialtype) &&
        (!document.getElementById("onShop").checked || furni.offerid !== -1) &&
        (selectedFurniType === 'wall' ? furnidata.wallitemtypes.furnitype.some(item => item.id === furni.id) : true) &&
        (selectedFurniType === 'floor' ? furnidata.roomitemtypes.furnitype.some(item => item.id === furni.id) : true)
    );
});


    // Filtrar por "ltdclothing" si está marcado
    const ltdclothingChecked = document.getElementById("ltdclothing").checked;
    if (ltdclothingChecked) {
        filteredData = filteredData.filter(furni => furnidata.ltdclothingitemtypes.furnitype.some(item => item.id === furni.id));
    }

    // Filtrar específicamente con los inputs de tipo especial, ambiente, categoría, y colección
    // Filtrar por tipo especial solo con el objeto JSON "specialtype"
    const specialtypeInput = document.getElementById("furniSpecialtypesInput").value.toLowerCase();
    if (specialtypeInput) {
        filteredData = filteredData.filter(furni => furni.specialtype.toLowerCase().includes(specialtypeInput));
    }

    // Filtrar por ambiente solo con el objeto JSON "environment"
    const environmentInput = document.getElementById("furniEnvironmentsInput").value.toLowerCase();
    if (environmentInput) {
        filteredData = filteredData.filter(furni => furni.environment.toLowerCase().includes(environmentInput));
    }

    // Filtrar por categoría solo con el objeto JSON "category"
    const categoryInput = document.getElementById("furniCategoriesInput").value.toLowerCase();
if (categoryInput) {
    filteredData = filteredData.filter(furni => furni.category.toLowerCase().includes(categoryInput));
}

    // Filtrar por colección solo con el objeto JSON "furniline"
    const lineInput = document.getElementById("furniLinesInput").value.toLowerCase();
    if (lineInput) {
        filteredData = filteredData.filter(furni => furni.furniline.toLowerCase().includes(lineInput));
    }

    // Actualizar los datos globales con los resultados filtrados
    window.filteredDataGlobal = filteredData;

    // Calcular el número de páginas
    const totalPages = Math.ceil(filteredData.length / per_page);
    if (page > totalPages) {
        page = totalPages || 1;
    }

    // Mostrar los resultados filtrados
    mostrarResultados();
}
});

Before integrating the populateDropdown function, the filters in handleSearch worked as expected. However, after adding the populateDropdown functionality, it seems to be interfering with the search results.

How can I ensure that the populateDropdown function and the handleSearch functionality work together without interfering with each other? Specifically, I need to understand why interactions with the populateDropdown seem to break or overwrite the filters in handleSearch.

How can I extract text from a specific HTML tag using JavaScript? [duplicate]

I’m working on a simple web project where I need to dynamically extract the text from a specific HTML element using JavaScript. For example, I have a with an id, and I want to get the text content from inside that element and use it elsewhere in my script.

I’ve tried using document.getElementById(“quote”) but I’m not sure if I should use .value, .innerHTML, or .textContent to get just the plain text. I also want to make sure this works reliably in most browsers and doesn’t return any extra HTML or quotes — just the clean string.

Also, if there are better ways to access or manipulate text content from the DOM, I’d appreciate a quick explanation or best practices.

This gave me the text, but I read that innerHTML might include HTML tags if there are any, which I don’t want.

Using const instead of let in Javascript

I have seen quite a few cases (Specially in FreecodeCamp) where they use const for defining loop variables:
i.e.-
for(const i =0;i<5;i++) instead of for(let i=0;i<5;i++).
What is the purpose of it? Is there actually any benefit of using const ? Are there any drawbacks if I use let ?

How to add autocomplete suggestions to st.chat_input() in Streamlit (like Google Search)?

I want to show autocomplete suggestions while the user is typing — like Google Search. For example, when a user types “how”, it should suggest things like: how it work, how to do and etc.

# Chat input
    if query1 := st.chat_input("Ask Here", key="chat1"):
        with st.spinner("AI is analyzing ..."):
            try:
                st.chat_message("user").markdown(query1)
                st.session_state.sales_messages.append({"role": "user", "content": query1})
                # (AI response generation logic here)

is there anyway I can add autocomplete to st.chat_input()?

I’m okay using JavaScript or streamlit.components.v1 if needed.

nutpi-chinese-number-format: A powerful UTS plugin for formatting Chinese numbers (Compatible with HarmonyOS)

Preface

In mobile application development, the localized display of numbers is a common requirement. Especially in a Chinese environment, we often need to convert Arabic numerals to Chinese numerals or perform the opposite conversion. Today, I would like to introduce to you a powerful UTS plugin – nutpi-chinese-number-format, which is specifically designed to address various requirements for formatting Chinese numbers.

Plugin Overview

nutpi-chinese-number-format is a Chinese number formatting plugin specifically designed for the uni-app and uni-app x projects. It is developed based on UTS (UniApp TypeScript) technology and provides complete TypeScript type definitions to ensure type safety during the development process.

Core characteristics

  • Bidirectional conversion: Supports bidirectional conversion between Arabic numerals and Chinese numerals
  • Uppercase support: Supports conversion of Chinese numerals to uppercase format (e.g. One, Two, three)
  • Unit processing: Intelligent processing of Chinese digital units such as tens of thousands and billions
  • Approximate representation: Supports approximate Chinese representation of large numbers
  • Month conversion: Supports conversion from numeric months to Chinese months (including traditional month names)
  • ulti-region support: Supports both Simplified Chinese (zh-CN) and Traditional Chinese (zh-TW)
  • Cross-platform compatibility: Supports multiple platforms including App (Android/iOS/Harmony), H5 and mini programs
  • Type safety: Complete TypeScript type definitions

Development process

We need to create the project in the way shown in the figure first, and then select uniappx and vue3
enter image description here

Next, we can create a new uni_modules plugin. Among them, we select the uts plugin -API plugin.

enter image description here

The following is the process of everyone writing code. It should be noted here that since the connection between this plugin and the platform is not very strong, we just need to create index.uts in the root directory of the plugin to implement my logic.

Technical architecture

Technical advantages of UTS

This plugin is developed using UTS (UniApp TypeScript) technology and has the following advantages compared with traditional JavaScript plugins:

  1. Performance optimization: The performance of the code compiled by UTS is closer to native
  2. Type safety: Full TypeScript support to reduce runtime errors
  3. Cross-platform consistency: maintaining consistent behavior across different platforms
  4. Development experience: Better IDE support and code hints

Core algorithm design

The plugin adopts an efficient mapping table design internally:

// Basic number mapping
const NUMBER_MAPS = {
  base: {
    "0": ["0", "0", "零", "〇"],
    "1": ["1", "1", "一", "壹"],
    "2": ["2", "2", "二", "貳", "贰"],
    // ...
  },
"zh-TW": {
    units: ["", "十", "百", "千"],
    bigUnits: [
      "",
      "萬",
      "億",
      "兆",
      "京",
      "垓",
      "秭",
      "穰",
      "溝",
      "澗",
      "正",
      "載",
    ],
    point: "點",
    uppercase: {
      /* Traditional uppercase mapping */
    },
  },
"zh-CN": {
    units: ["", "十", "百", "千"],
    bigUnits: [
      "",
      "万",
      "亿",
      "兆",
      "京",
      "垓",
      "秭",
      "穰",
      "沟",
      "涧",
      "正",
      "载",
    ],
    point: "点",
    uppercase: {
      /* Simplified Capital Letter mapping */
    },
  },
};

To optimize performance, the plugin also expects to calculate the reverse mapping table:

// Pre-calculate the reverse mapping table to optimize the performance of the toNumber function
const REVERSE_BASE_MAP: Record<string, string> = {};
for (const numKey in NUMBER_MAPS.base) {
  NUMBER_MAPS.base[numKey].forEach((charVariant) => {
    REVERSE_BASE_MAP[charVariant] = numKey;
  });
}

Detailed Explanation of Functions

1. Basic digital conversion

Digital to Chinese

import { toChinese } from "@/uni_modules/nutpi-chinese-number-format";

// Basic transformation
const result1 = toChinese(123); // "一二三"
const result2 = toChinese(123.45); // "一二三點四五"

// Designated area
const result3 = toChinese(123, "zh-CN"); // "一二三"
const result4 = toChinese(123.45, "zh-CN"); // "一二三点四五"

Chinese to digital

import { toNumber } from "@/uni_modules/nutpi-chinese-number-format";

const num1 = toNumber("一二三"); // 123
const num2 = toNumber("一二三點四五"); // 123.45
const num3 = toNumber("一萬二千三百四十五"); // 12345

2. Chinese representation with units

import { toChineseWithUnits } from "@/uni_modules/nutpi-chinese-number-format";

// Automatically add appropriate units
const result1 = toChineseWithUnits(12345); // "一萬二千三百四十五"
const result2 = toChineseWithUnits(12345, "zh-CN"); // "一万二千三百四十五"
const result3 = toChineseWithUnits(123456789); // "一億二千三百四十五萬六千七百八十九"
  1. Capitalization conversion
import { toUpperCase } from "@/uni_modules/nutpi-chinese-number-format";

// Convert to capital Chinese numerals
const result1 = toUpperCase("一二三"); // "壹貳參"
const result2 = toUpperCase("一二三", "zh-CN"); // "壹贰叁"
  1. Approximate representation of large numbers
import { toChineseApproximate } from "@/uni_modules/nutpi-chinese-number-format";

// Approximate representation of large numbers
const result1 = toChineseApproximate(123456789); // "一點二億"
const result2 = toChineseApproximate(123456789, {
  locale: "zh-CN",
  precision: 2,
}); // "一点二三亿"
  1. Month conversion
import { toChineseMonth } from "@/uni_modules/nutpi-chinese-number-format";

// Simple format
const month1 = toChineseMonth(10); // "十月"
const month2 = toChineseMonth(11); // "十一月"

// Traditional format
const month3 = toChineseMonth(1, { format: "traditional" }); // "正月"
const month4 = toChineseMonth(12, {
  format: "traditional",
  locale: "zh-CN",
}); // "The twelfth lunar month"

Actual application scenarios

  1. Price display in e-commerce applications
// Display the Chinese price on the product detail page
const price = 12888;
const chinesePrice = toChineseWithUnits(price, "zh-CN"); // "一万二千八百八十八"
  1. The amount in the financial application should be capitalized

// The amount in the invoice or receipt is capitalized
const amount = "一万二千三百四十五";
const uppercaseAmount = toUpperCase(amount, "zh-CN"); // "壹万贰仟叁佰肆拾伍"
  1. Month display in the date picker

// The month display in the traditional calendar
const months = [];
for (let i = 1; i <= 12; i++) {
  months.push(toChineseMonth(i, { format: "traditional", locale: "zh-CN" }));
}
// ["正月", "二月", "三月", ..., "腊月"]
  1. Large number display in data statistics
// Friendly display of user volume statistics
const userCount = 1234567;
const friendlyCount = toChineseApproximate(userCount, {
  locale: "zh-CN",
  precision: 1,
}); // "One hundred and twenty thousand"

A complete usage example in Vue components

<template>
  <view class="number-demo">
    <view class="section">
      <text class="title">Basic transformation</text>
      <text>Digital {{ originalNumber }} Convert to Chinese:{{ chineseNumber }}</text>
      <text>Chinese to digital:{{ convertedNumber }}</text>
    </view>

    <view class="section">
      <text class="title">With unit conversion</text>
      <text>{{ largeNumber }} → {{ chineseWithUnits }}</text>
    </view>

    <view class="section">
      <text class="title">Capitalization conversion</text>
      <text>{{ chineseText }} → {{ uppercaseText }}</text>
    </view>

    <view class="section">
      <text class="title">Month conversion</text>
      <text>{{ currentMonth }}月 → {{ chineseMonth }}</text>
      <text>Traditional format:{{ traditionalMonth }}</text>
    </view>
  </view>
</template>

<script setup lang="ts">
import {
  toChinese,
  toChineseWithUnits,
  toNumber,
  toUpperCase,
  toChineseApproximate,
  toChineseMonth,
  type Locales,
} from "@/uni_modules/nutpi-chinese-number-format";

// Responsive data
const originalNumber = ref(12345);
const largeNumber = ref(123456789);
const chineseText = ref("一二三四五");
const currentMonth = ref(10);

// computational attribute
const chineseNumber = computed(() => toChinese(originalNumber.value, "zh-CN"));

const convertedNumber = computed(() => toNumber("一二三四五"));

const chineseWithUnits = computed(() =>
  toChineseWithUnits(largeNumber.value, "zh-CN")
);

const uppercaseText = computed(() => toUpperCase(chineseText.value, "zh-CN"));

const chineseMonth = computed(() => toChineseMonth(currentMonth.value));

const traditionalMonth = computed(() =>
  toChineseMonth(currentMonth.value, {
    format: "traditional",
    locale: "zh-CN",
  })
);
</script>
  1. Pre-computed mapping table

The plugin pre-calculates the reverse mapping table during initialization, avoiding repetitive calculations at runtime:

// Pre-compute the reverse mapping table to enhance the performance of the toNumber function
const REVERSE_BASE_MAP: Record<string, string> = {};
for (const numKey in NUMBER_MAPS.base) {
  NUMBER_MAPS.base[numKey].forEach((charVariant) => {
    REVERSE_BASE_MAP[charVariant] = numKey;
  });
}
  1. Efficient string processing
    When dealing with large numbers, the plugin adopts a group processing method, which improves the conversion efficiency:
// Group and process by 4 bits to improve the efficiency of processing large numbers
const groups = numStr
  .split("")
  .reverse()
  .reduce((acc: string[][], digit: string, i: number) => {
    const groupIndex = Math.floor(i / 4);
    if (!acc[groupIndex]) acc[groupIndex] = [];
    acc[groupIndex].unshift(digit);
    return acc;
  }, []);

Error handling and boundary situations

  1. Input validation
// Error handling of the toNumber function
exportfunction toNumber(str: string): number {
let numberStr = "";
let hasInvalidChar = false;

for (const char of str) {
    const digit = REVERSE_BASE_MAP[char];
    if (digit !== undefined) {
      numberStr += digit;
    } else {
      hasInvalidChar = true;
      break;
    }
  }

if (hasInvalidChar || numberStr.length === 0) {
    returnNaN; // Return NaN when the conversion fails
  }

// Handle the situation of multiple decimal points
const parts = numberStr.split(".");
if (parts.length > 1) {
    numberStr = parts[0] + "." + parts.slice(1).join("");
  }

returnNumber(numberStr);
}
  1. Monthly verification
// Boundary check of the toChineseMonth function
export function toChineseMonth(
  month: number,
  options: MonthOptions = {}
): string {
  // Check whether the month is between 1 and 12 and is an integer
  if (month < 1 || month > 12 || !Number.isInteger(month)) {
    return ""; // An invalid month returns an empty string
  }
  // ... Other processing logics
}

Installation and configuration

1. Install through uni_modules

  1. Copy the “nutpi-chinese-number-format” folder to the “uni_modules” directory of the project
  2. Recompile the project in HBuilderX

2. Environmental requirements

  • HBuilderX: 3.6.8 or later version
  • uni-app: Supports Vue 2 and Vue 3
  • uni-app x: Fully supported
  • Platform support: App (Android/iOS/Harmony), H5, mini-programs, etc

3. TypeScript configuration

If your project uses TypeScript, the plugin provides complete type definitions:

// Type import
import type {
  Locales,
  Options,
  MonthOptions,
} from "@/uni_modules/nutpi-chinese-number-format";

// Use type
const locale: Locales = "zh-CN";
const options: Options = {
  locale: "zh-CN",
  precision: 2,
};

Best practice

1. Region Settings selection

// Automatically select the region based on the language of the user's device
const getLocale = (): Locales => {
  const systemLocale = uni.getSystemInfoSync().language;
  return systemLocale.includes("TW") || systemLocale.includes("HK")
    ? "zh-TW"
    : "zh-CN";
};

const userLocale = getLocale();
const result = toChinese(123, userLocale);

2. Error handling

// Secure digital conversion
const safeToNumber = (str: string): number | null => {
const result = toNumber(str);
returnisNaN(result) ? null : result;
};

// Usage example
const userInput = "一二三";
constnumber = safeToNumber(userInput);
if (number !== null) {
console.log(`Conversion successful: ${number}`);
} else {
console.log("Conversion failed. Please check the input format");
}

3. Performance optimization suggestions

// For scenarios with frequent calls, the results can be cached
const numberCache = new Map<string, string>();

const cachedToChinese = (num: number, locale: Locales = "zh-CN"): string => {
const key = `${num}_${locale}`;
if (numberCache.has(key)) {
    return numberCache.get(key)!;
  }

const result = toChinese(num, locale);
  numberCache.set(key, result);
return result;
};

Summary

nutpi-chinese-number-format is a comprehensive and high-performance Chinese number formatting plugin. It not only offers rich conversion functions, but also has the following advantages:

  • Advanced technology: Based on UTS technology, its performance is close to native
  • Complete functions: It covers various scenarios of Chinese digital processing
  • Type safety: Full TypeScript support
  • Cross-platform: Supports all platforms of the uni-app ecosystem
  • Easy to use: The simple API design makes it easy to get started
  • Performance optimization: Pre-computed mapping table, efficient algorithm implementation

Whether it’s developing e-commerce applications, financial systems, or other applications that require Chinese localization, this plugin can provide you with a reliable solution for formatting Chinese numbers.

Thank you, stackoverflow, for allowing my article to be displayed

Rails 7 – Duplicate javascript files are loaded into the web server

With Rails 7.2.2.1
I am using importmaps to load my javascript files. My problem is that some of the files have multiple copies with slightly different names loaded into the web server, and I cannot figure out why it is happening and how to prevent it. Below are two scenarios that I ran to illustrate the problem.
Scenario 1: Duplicate js files
My import map looks like this:

pin "application"
pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
pin_all_from "app/javascript/controllers", under: "controllers"

pin "jquery", to: "https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"
pin "jquery-ui", to: "https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery-ui.min.js"


pin "confnosprdpicks", to: "confnosprdpicks.js"
pin "seasongameset_schedules", to: "seasongameset_schedules.js"
pin "survivorpicks", to: "survivorpicks.js"
pin "seasongameset_schedule_bets", to: "seasongameset_schedule_bets.js"
pin "gm_total_bets", to: "gm_total_bets.js"
pin "spread_bets", to: "spread_bets.js"
pin "spread_bet_teams_season_action", to: "spread_bet_teams_season_action.js"
pin "predictions", to: "predictions.js"
pin "super_bowl_bets", to: "super_bowl_bets.js"

If I start a Rails server and bring up a page, this is the list of javascript files that are loaded:

enter image description here

enter image description here

There are duplicate files for:
css and css.js
datetimes and datetimes.js
seasongameset_schedules and seasongameset_schedules-d7d77…js
spread_bets.js and spread_bets-7bc8d…js

The file contents for each pair is identical

Scenario 2: No duplicate js files Now, if I comment out only the last line in importmap.rb, so that it looks like this:

pin "application"
pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
pin_all_from "app/javascript/controllers", under: "controllers"

pin "jquery", to: "https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"
pin "jquery-ui", to: "https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery-ui.min.js"


pin "confnosprdpicks", to: "confnosprdpicks.js"
pin "seasongameset_schedules", to: "seasongameset_schedules.js"
pin "survivorpicks", to: "survivorpicks.js"
pin "seasongameset_schedule_bets", to: "seasongameset_schedule_bets.js"
pin "gm_total_bets", to: "gm_total_bets.js"
pin "spread_bets", to: "spread_bets.js"
pin "spread_bet_teams_season_action", to: "spread_bet_teams_season_action.js"
pin "predictions", to: "predictions.js"
# pin "super_bowl_bets", to: "super_bowl_bets.js" 

…and restart the Rails server and bring up the same page, I get the following list of javascript files loaded to the web server – the duplicate files do not appear:

enter image description here
enter image description here

The duplicate javascript files are gone (the 2 application-nnn.js files are distinct, one being from app/javascript and one from app/javascript/controllers).

I don’t understand why multiple js files appear in the first scenario, but do not appear in the second scenario. The second scenario looks to me like what I would expect to see. Can anyone provide some insight? Thanks.

How can I track a variable across different sessions/users?

I’m making a NeoCities site using HTML/CSS/Javascript. PHP doesn’t seem to be supported without add-ons, so for the time being, i’m not considering that.

I want to see if I can have a button that simply says how many times it has been clicked, not just by the current user but by any user. I know that if I wanted it to show how many times a user clicked it, I could use localstorage or sessionstorage, but that does not seem to accomplish what I want to do. How can I store a variable that can be changed with an event that persists across users and sessions?

Javascript errors say variables are undefined in many instances

function RecordAction (Action,Table='Visits',Referrer='See%20page%20access%20line%20to%20see%20referrer...') {
[... some code not bugged or so it seems...]
document.getElementById('ActionRecorder').src = 'https://www.handsearseyes.fun/XXX/XXX.php?Table='+Table+'&Action='+Action+'&Referrer='+Referrer+'&TimeStamp='+TimeStamp;
}


    // Form field value changes logging function
function RecordFieldValueGiven(Field,DBTable='HexKeyboardVisits') {
RecordAction('Toggled%20Form%20Field%20%5C%22'+Field.name+'%5C%22%20to%20Value%20%5C%22'+Field.value+'%5C%22',DBTAble);
}

I get “cannot set property src on null” and the html does contain the iframe having ActionRecorder for Id, not name don’t worry on that…

and also “DBTable” is not defined while the variable is defined within the parameters…

whole site is half crashed :
https://www.handsearseyes.fun/Ears/EarTrainer/Main.php
or https://handsearseyes.fun/Ears/HexKeyboard/HexKeyboard.php
changing most field’s values won’t work…

I need validate a problem with a array because not find the solution

img

test test test tes
123 123 123 123123
123 123 123 123 123
twat what what whta
test test test tes
123 123 123 123123
123 123 123 123 123
twat what what whtatest test test tes
123 123 123 123123
123 123 123 123 123
twat what what whtatest test test tes
123 123 123 123123
123 123 123 123 123
twat what what whtatest test test tes
123 123 123 123123
123 123 123 123 123
twat what what whtatest test test tes
123 123 123 123123
123 123 123 123 123
twat what what whtatest test test tes
123 123 123 123123
123 123 123 123 123
twat what what whtatest test test tes
123 123 123 123123
123 123 123 123 123
twat what what whtatest test test tes
123 123 123 123123
123 123 123 123 123
twat what what whta

Multiple functions for click event in React

I am new to React. I am working on a simple project where clicking a +/- button will increment or decrement the date by one day but am having trouble getting the two functions to work for a click event, please see code below. I am not sure why error message ‘newDate is undefined’ keeps coming up when the variable is defined and the state has been set. If anybody has any solutions please let me know. Thanks.
*** N.b I am just working on the ‘minus’ button for now.

import React from "react";
import ReactDOM from "react-dom/client";
import { useState } from "react";

function App() {
  const date = new Date("june 21 2027");
  const [newDate, setNewDate] = useState(date);
  const [count, setCount] = useState(1);

  return (
    <div>
      {/* <div>
        <button>-</button>
        <span>Step : Step 1</span>
        <button>+</button>
      </div> */}
      <div>
        <button
          className={"minus"}
          id={1}
          onClick={function () {
            setCount(function (c) {
              return c - 1;
            });
            setNewDate(function (d) {
              d.setDate(date.getDate() - count);
              return;
            });
          }}
        >
          -
        </button>
        <span>Count : {count}</span>
        <button
          className={"plus"}
          id={2}
          onClick={function () {
            return setCount(function (c) {
              return c + 1;
            });
          }}
        >
          +
        </button>
      </div>
      <span>Today is {`${newDate.toDateString()}`}</span>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(<App />);

I have tried returning the setNewDate function into the setCount function but that didn’t work.

How can I use custom triggers to control object movement in Geometry Dash 2.2?

I’m building a custom level in Geometry Dash 2.2 and I want to create a looped movement for certain objects (like platforms floating up and down or side to side). I’ve tried using the Move trigger with easing and timing setups, but I can’t get it to loop smoothly without issues like jittering or breaking the flow.

What I’ve tried:
Applied Move triggers with Ease In Out for smoother transitions.

Tried repeating the motion using Repeat and Toggle triggers.

Grouped objects logically and timed delays between movements.

What I expected:
Smooth, continuous looping of object movement.

Clean transitions without stutters or sync issues.

The ability to stop or alter movement based on player progress.

What actually happens:
Movement sometimes becomes choppy or resets awkwardly.

Looped motion doesn’t stay consistent after a few cycles.

Objects sometimes lag behind or break sequence.

I’ve shared tutorials and experiences like this on my Geometry Dash-focused site: thegeometrydashes.com, but I couldn’t find a solid answer for this specific problem.

i have tried everything that i share above