Свой тип файла для проектов: *.nlve больше не текст

Внутри проект — обычный JSON, и система считала его просто текстом:
открывала редактором и рисовала значок текстового файла.

Теперь у него свой тип application/x-nlve-project, свой значок (лист с
загнутым углом и знаком программы) и открытие двойным щелчком. Тип
объявлен наследником application/json, но не простого текста — иначе
редактор снова оказался бы программой по умолчанию.

Всё, что нужно для встраивания в рабочий стол, лежит в packaging и
ставится скриптом tools/install-desktop.sh в домашний каталог, без прав
администратора. Раньше пункт меню и значки существовали только на одной
машине и нигде не были записаны.
This commit is contained in:
4aiman 2026-08-31 00:13:50 +03:00
commit fb6b60bc3f
7 changed files with 200 additions and 0 deletions

75
tools/install-desktop.sh Executable file
View file

@ -0,0 +1,75 @@
#!/bin/bash
# Встраивание в рабочий стол: пункт меню, значки и тип файла проекта.
#
# Что делает:
# - кладёт значок программы и значок файла проекта в ~/.local/share/icons
# - описывает тип application/x-nlve-project, чтобы *.nlve перестали
# считаться просто текстом
# - кладёт пункт меню, который открывает такие файлы нашей программой
#
# Всё в домашнем каталоге: прав администратора не нужно, другим
# пользователям системы ничего не меняется.
#
# Запуск: tools/install-desktop.sh [путь к программе]
# По умолчанию берётся nlve рядом с этим репозиторием.
set -u
HERE=$(cd "$(dirname "$0")/.." && pwd)
BIN=${1:-$HERE/nlve}
PKG=$HERE/packaging
DATA=${XDG_DATA_HOME:-$HOME/.local/share}
APPS=$DATA/applications
MIME=$DATA/mime/packages
ICONS=$DATA/icons/hicolor
note() { echo "== $*"; }
fail() { echo "$*" >&2; exit 1; }
[ -x "$BIN" ] || fail "программы нет или она не запускается: $BIN"
[ -d "$PKG" ] || fail "нет каталога $PKG"
note "Значки"
for SIZE in 32 48 64 128; do
mkdir -p "$ICONS/${SIZE}x${SIZE}/apps" "$ICONS/${SIZE}x${SIZE}/mimetypes"
# convert есть не у всех; без него кладём один размер как есть.
if command -v convert >/dev/null 2>&1; then
convert "$PKG/nlve.png" -resize ${SIZE}x${SIZE} \
"$ICONS/${SIZE}x${SIZE}/apps/nlve.png"
convert "$PKG/application-x-nlve-project.png" -resize ${SIZE}x${SIZE} \
"$ICONS/${SIZE}x${SIZE}/mimetypes/application-x-nlve-project.png"
elif [ "$SIZE" = 128 ]; then
cp -f "$PKG/nlve.png" "$ICONS/128x128/apps/nlve.png"
cp -f "$PKG/application-x-nlve-project.png" \
"$ICONS/128x128/mimetypes/application-x-nlve-project.png"
fi
done
note "Тип файла проекта"
mkdir -p "$MIME"
cp -f "$PKG/application-x-nlve-project.xml" "$MIME/nlve.xml"
if command -v update-mime-database >/dev/null 2>&1; then
update-mime-database "$DATA/mime"
else
echo "update-mime-database не найдена: тип не зарегистрируется" >&2
fi
note "Пункт меню"
mkdir -p "$APPS"
sed -e "s|@BIN@|$BIN|g" -e "s|@DIR@|$(dirname "$BIN")|g" \
"$PKG/nlve.desktop.in" > "$APPS/nlve.desktop"
chmod 644 "$APPS/nlve.desktop"
command -v update-desktop-database >/dev/null 2>&1 &&
update-desktop-database "$APPS"
note "Программа по умолчанию для *.nlve"
command -v xdg-mime >/dev/null 2>&1 &&
xdg-mime default nlve.desktop application/x-nlve-project
command -v gtk-update-icon-cache >/dev/null 2>&1 &&
gtk-update-icon-cache -f -t "$ICONS" >/dev/null 2>&1
echo
echo "готово. Проверить: xdg-mime query filetype <файл>.nlve"
echo "Файловый менеджер может показать новый значок не сразу:"
echo "ему нужно перечитать базу (обычно достаточно перезапуска)."

62
tools/make_file_icon.py Normal file
View file

@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""
Делает значок файла проекта из значка программы.
Зачем отдельный. Значок программы и значок её файлов — разные вещи: в
списке файлов должно быть видно, что это документ, а не сама программа.
Поэтому берётся лист с загнутым углом, а знак программы кладётся на него.
Запуск: python3 tools/make_file_icon.py
Итог: packaging/application-x-nlve-project.png
"""
from pathlib import Path
from PIL import Image, ImageDraw
ROOT = Path(__file__).resolve().parent.parent
SRC = ROOT / 'packaging' / 'nlve.png'
DST = ROOT / 'packaging' / 'application-x-nlve-project.png'
SIZE = 128
# Лист занимает не всю клетку: значкам документов принято оставлять поля.
MARGIN_X = 16
MARGIN_Y = 6
FOLD = 30
PAPER = (245, 245, 245, 255)
FOLD_COLOR = (205, 205, 205, 255)
EDGE = (120, 120, 120, 255)
def main():
if not SRC.exists():
raise SystemExit(f'нет файла {SRC}')
img = Image.new('RGBA', (SIZE, SIZE), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
left, right = MARGIN_X, SIZE - MARGIN_X
top, bottom = MARGIN_Y, SIZE - MARGIN_Y
# Лист с срезанным правым верхним углом.
body = [
(left, top), (right - FOLD, top), (right, top + FOLD),
(right, bottom), (left, bottom),
]
d.polygon(body, fill=PAPER, outline=EDGE)
# Сам загнутый угол.
d.polygon([(right - FOLD, top), (right, top + FOLD),
(right - FOLD, top + FOLD)], fill=FOLD_COLOR, outline=EDGE)
# Знак программы поверх листа, в нижней его части: сверху угол.
mark = Image.open(SRC).convert('RGBA')
side = 66
mark = mark.resize((side, side), Image.LANCZOS)
img.alpha_composite(mark, ((SIZE - side) // 2, bottom - side - 8))
img.save(DST)
print('готов:', DST)
if __name__ == '__main__':
main()